예제 #1
0
    public override void ServerSyncFixedUpdate()
    {
        base.ServerSyncFixedUpdate();

        // It seems like "OperationAddSyncState" shouldn't be done in InitStart..
        if (!firstUpdateDone)
        {
            firstUpdateDone = true;
            if (GameManager.GetPlayer(playerOwner).playerObject != null)
            {
                // trip wire won't go up or down, it'll stay straight:
                Vector3 pos = GameManager.GetPlayer(playerOwner).playerObject.transform.position;
                //pos.y = transform.position.y - 1.1f;

                RaycastHit hit;
                if (Physics.Raycast(transform.position + Vector3.down * 1.5f, Vector3.Normalize(pos - (transform.position + Vector3.down * 1.5f)), out hit, 12f, LayerLogic.WorldColliders()))
                {
                    // hmm..
                }

                GameObject obj = (GameObject)Instantiate(arrowTrigger, pos + Vector3.down, Quaternion.identity);
                obj.GetComponent <PlayerMade> ().playerOwner = playerOwner;
                OperationNetwork.OperationAddSyncState(obj);
            }
        }

        GeneralUpdate();
    }
예제 #2
0
 public static void Disconnect()
 {
     OperationNetwork.connected = false;
     Debug.LogError("Disconnect");
     try
     {
         if (OperationNetwork.isServer)
         {
             if (OperationNetwork.getClient(32767) != null)
             {
                 OperationNetwork.getClient(32767).file.Close();
             }
             if (myServer != null)
             {
                 NetworkTransport.Shutdown();
             }
         }
         else
         {
             if (myClient != null)
             {
                 myClient.disconnect();
             }
         }
     }
     catch (Exception e)
     {
         Debug.LogError("On Disconnect: " + e.Message);
     }
 }
예제 #3
0
    public override void UpdateServerAndInterp(bool building, bool dieing, float lifeTime)
    {
        // Reveals the trap over the initial time:
        if (lifeTime < getBuildTime())
        {
            // Build handles this
        }
        else
        {
            if (OperationNetwork.isServer)
            {
                ticksSinceLaunch++;
                if (ticksSinceLaunch % 100 == 0)                  // 50 ticks is 1 second
                {
                    float maxMissAngle = 14f;                     // Was 12f
                    // Launch arrow:
                    Vector2 randInUnitCircle = UnityEngine.Random.insideUnitCircle;
                    Vector3 direction        = randInUnitCircle * maxMissAngle * Mathf.PI / 180.0f;              // This is here because the accuracy, is on average less when using a cone to do inaccuracy. This increases the amount of shots that are more accurate. In the future it could just use tan, or maybe tan with this.
                    direction.z = 1.0f;
                    //direction = transform.TransformDirection(direction.normalized);
                    Quaternion fireRot = Quaternion.LookRotation(transform.forward) * Quaternion.LookRotation(direction.normalized);
                    Vector3    fireDir = fireRot * Vector3.forward;

                    GameObject arrowObj = (GameObject)Instantiate(bullet, transform.position + transform.forward * 0.3f, fireRot);
                    arrowObj.GetComponent <SyncGameState> ().playerOwner = playerOwner;
                    arrowObj.GetComponent <Projectile>().SetInitialVelocity(fireDir * Random.Range(5, 8));
                    OperationNetwork.OperationAddSyncState(arrowObj);
                }
            }
        }
    }
예제 #4
0
 // Throwable does NOT blow up! It creates a death sphere!
 public override void OnDeath()
 {
     if (deathExplosionPrefab != null && OperationNetwork.isServer)
     {
         GameObject deathExplosion = (GameObject)Instantiate(deathExplosionPrefab, transform.position, transform.rotation);
         deathExplosion.GetComponent <SphereDamage> ().playerOwner = playerOwner;
         deathExplosion.GetComponent <SphereDamage> ().team        = team;
         OperationNetwork.OperationAddSyncState(deathExplosion);
     }
 }
예제 #5
0
 static int AddPlayerMade(GameObject obj, byte[] data, int fromWho)
 {
     if (obj)
     {
         // Must be alive
         byte placePlayerMadeType = data [0];
         PlacePlayerMade.AddObject(obj.GetComponent <PlayerMove>(), OperationNetwork.getVector3(data, 1), OperationNetwork.getQuaternion(data, 13), placePlayerMadeType, obj.GetComponent <Combat>().team, (short)fromWho);
     }
     return(29);
 }
예제 #6
0
    // Server side:
    void SpawnPlayer()
    {
        // Picks a spawn point here:
        GameObject sP = LevelLogic.getSpawnPoint(team);
        // Picks a spawn point here:
        GameObject playerObject = (GameObject)MonoBehaviour.Instantiate(playerObjects[team * (playerObjects.Length / 2) + classNum], sP.transform.position, sP.transform.rotation);

        playerObject.GetComponent <SyncGameState> ().playerOwner = playerOwner;
        OperationNetwork.OperationAddSyncState(playerObject);
    }
    public void FireBullet(Vector3 cameraForward)
    {
        GameObject bullet = (GameObject)MonoBehaviour.Instantiate(
            bulletPrefab, parentPlayerMove.transform.position + parentPlayerMove.GetMainCameraLocalPos() * parentPlayerMove.transform.localScale.x +
            cameraForward * 0.12f + Quaternion.LookRotation(cameraForward) * Vector3.up * -0.1f + Quaternion.LookRotation(cameraForward) * Vector3.right * 0.11f,
            Quaternion.LookRotation(cameraForward));

        bullet.GetComponent <SyncGameState> ().playerOwner = parentPlayerMove.plyr;
        bullet.GetComponent <Projectile>().SetInitialVelocity(cameraForward * GetSpeed());
        OperationNetwork.OperationAddSyncState(bullet);
    }
예제 #8
0
    void Update()
    {
        if (GetComponent <PlayerMove> ().thisIsMine)
        {
            // Get Input Commands. ALL of them. Turns out stuff like hitscan that causes damage needs to send all this information.

            GetComponent <PlayerMove>().PlayerMoveRun();
            OperationNetwork.CheckForPlayerInputSend();

            // Send data to server.
        }
    }
예제 #9
0
 //
 static int ResendTicks(GameObject obj, byte[] data, int fromWho)
 {
     // Full Resend is done..
     if (GameManager.PlayerExists((short)fromWho))
     {
         OperationNetwork.getClient((short)fromWho).shouldNextSendOutBeFullSendOut = true;
     }
     else
     {
         // hmm
     }
     return(0);
 }
예제 #10
0
    // Server
    public void Launch()
    {
        if (!launched)
        {
            launched = true;

            icicle = (GameObject)Instantiate(icicle, transform.position, transform.rotation);
            icicle.GetComponent <Rigidbody> ().velocity        = transform.forward * 15f;
            icicle.GetComponent <SyncGameState> ().playerOwner = playerOwner;
            OperationNetwork.OperationAddSyncState(icicle);

            // Destroy this: (No need to do alive set)
            exists = false;
        }
    }
예제 #11
0
    // This could be done in the same way bullets are done..

    // Server only:
    public void FireTakeHealth(Vector3 cameraForward)
    {
        // This comes from the gun, oddly enough:
        GameObject hB = (GameObject)MonoBehaviour.Instantiate(healingBlast, parentPlayerMove.mainCamera.transform.position + cameraForward * 2.8f, Quaternion.LookRotation(cameraForward)); // It gets made at the tip of the gun.

        hB.GetComponent <SyncGameState> ().playerOwner = parentPlayerMove.plyr;                                                                                                             // Redudant.. not used..

        OperationNetwork.OperationAddSyncState(hB);                                                                                                                                         // hB doesn't have any syncing properties beside position / rotation. (TODO implement)

        // Server also calculates the collision right here:

        // Note that HEALING #s are hard coded within BlowUp!
        ExplodeDetection.BlowUp(parentPlayerMove.mainCamera.transform.position + parentPlayerMove.mainCamera.transform.forward * 2.6f,          // Note how it heals in a different location!
                                parentPlayerMove.mainCamera.transform.position, 20f, 2.2f, 1f, 0.5f, parentPlayerMove.gameObject,
                                parentPlayerMove.GetComponent <Combat> ().team, ref healthTaken, parentPlayerMove.plyr);
    }
예제 #12
0
    // InitialConnect is key.
    public static void InitialConnect()
    {
        if (OperationNetwork.isServer)
        {
            GameObject lSync = Instantiate(levelSync);
            OperationNetwork.OperationAddSyncState(lSync);

            OperationNetwork.connected = true;             // Server connection works like this.
            PlayerConnect(PlayerInformation.steamName, OperationNetwork.FromServerClient);
        }
        else
        {
            // Sends out request for "Connect" (with name). ID is sent to the player.
            OperationView.RPC(null, "PlayerConnect", OperationNetwork.ToServer, PlayerInformation.steamName);
        }
    }
예제 #13
0
    // Server only. This is called manually by the server.
    // OperationRPC
    public static void PlayerConnect(string name, short who)     // Information that should be sent to new players: team, kills, deaths
    {
        if (who != OperationNetwork.FromServerClient)
        {
            OperationNetwork.getClient(who).connected = true;              // This is so no data is sent before the following data:

            OperationNetwork.sendDataToSpecificClient(BitConverter.GetBytes(who), who);
        }

        GameObject pSObject = MonoBehaviour.Instantiate(playerSync);

        // Set ID, name, etc. (todo)
        pSObject.GetComponent <Player> ().playerOwner = who;
        pSObject.GetComponent <Player> ().playerName  = name;
        pSObject.GetComponent <Player> ().team        = 0; // Default (Could be based on an "autobalance" system.
        pSObject.GetComponent <Player> ().classNum    = 0; // Default (Could be random)
        OperationNetwork.OperationAddSyncState(pSObject);
    }
예제 #14
0
    // Note that placeType is only used for its type currently. It does not use PlacePlayerMade placePlayerMades byte dictionary because it does not need to.
    public static void AddObject(PlayerMove parentPlayerMove, Vector3 pos, Quaternion rot, byte placeType, byte team, short playerOwner)
    {
        try {
            GameObject obj = (GameObject)MonoBehaviour.Instantiate(placementSphere,
                                                                   parentPlayerMove.transform.TransformPoint(parentPlayerMove.GetComponent <CapsuleCollider> ().center) /* Approximate for now.*/,
                                                                   parentPlayerMove.mainCamera.transform.rotation);

            GameObject cutOut = cutOuts [placeType];

            obj.GetComponent <PlacementSphere>().cutOut      = cutOut;
            obj.GetComponent <PlacementSphere>().team        = team;
            obj.GetComponent <PlacementSphere>().playerOwner = playerOwner;
            obj.GetComponent <PlacementSphere> ().placeType  = placeType;
            obj.GetComponent <PlacementSphere>().toPosition  = pos;           // fromPosition is set by initStart.
            obj.GetComponent <PlacementSphere>().objRotation = rot;

            OperationNetwork.OperationAddSyncState(obj);
        } catch (Exception e) {
            Debug.LogError("Failure in adding trap: " + placeType + ", " + e.Message);
        }
    }
예제 #15
0
    public override void deathAnimation(float deathTime)
    {
        // Reveals the trap over the initial time:
        if (!playedLaunchSound)
        {
            SoundHandler.soundHandler.PlayVoiceLine((byte)4, (byte)255, transform);
            playedLaunchSound = true;
        }
        if (deathTime < delayTime)
        {
            if (!GetComponent <Renderer> ().enabled)
            {
                GetComponent <Renderer> ().enabled = true;
            }
            SetTransparency((delayTime - deathTime) / delayTime);
        }
        else
        {
            if (OperationNetwork.isServer)
            {
                ticksSinceLaunch++;
                if (ticksSinceLaunch <= 150 && ticksSinceLaunch % 5 == 0)                   // 150 ticks is 3 seconds.
                {
                    float maxMissAngle = 25f;
                    // Launch arrow:
                    Vector2 randInUnitCircle = UnityEngine.Random.insideUnitCircle;
                    Vector3 direction        = randInUnitCircle * maxMissAngle * Mathf.PI / 180.0f;              // This is here because the accuracy, is on average less when using a cone to do inaccuracy. This increases the amount of shots that are more accurate. In the future it could just use tan, or maybe tan with this.
                    direction.z = 1.0f;
                    //direction = transform.TransformDirection(direction.normalized);
                    Quaternion fireRot = Quaternion.LookRotation(transform.forward) * Quaternion.LookRotation(direction.normalized);
                    Vector3    fireDir = fireRot * Vector3.forward;

                    GameObject arrowObj = (GameObject)Instantiate(arrow, transform.position + transform.forward * 0.6f + transform.right * 1f * randInUnitCircle.x + transform.up * 1f * randInUnitCircle.y, fireRot);
                    arrowObj.GetComponent <SyncGameState> ().playerOwner = playerOwner;
                    arrowObj.GetComponent <Projectile>().SetInitialVelocity(fireDir * Random.Range(24, 32));
                    OperationNetwork.OperationAddSyncState(arrowObj);
                }
            }
        }
    }
예제 #16
0
    public void ReadMessages()
    {
        int numData = 0;

        for (int i = 0; i < 250; i++)           // Up to 250 messages read per tick
        // Receive messages:
        {
            int              recHostId;
            int              recConnectionId;
            int              recChannelId;
            byte[]           recBuffer  = new byte[1500];  // Prepare for 1500. Warning on server is made at 1000 currently. Probably will have to adjust this one day.
            int              bufferSize = 1500;
            int              dataSize;
            byte             error;
            NetworkEventType networkEvent = NetworkTransport.Receive(out recHostId, out recConnectionId, out recChannelId, recBuffer, bufferSize, out dataSize, out error);

            NetworkError networkError = (NetworkError)error;
            if (networkError != NetworkError.Ok)
            {
                Debug.LogError(string.Format("Error recieving event: {0} with recHostId: {1}, recConnectionId: {2}, recChannelId: {3}", networkError, recHostId, recConnectionId, recChannelId));
            }

            switch (networkEvent)
            {
            case NetworkEventType.Nothing:
                return;

            case NetworkEventType.ConnectEvent:
                OperationNetwork.connected = true;
                Debug.LogError("We have connected! ");
                GameManager.InitialConnect();
                break;

            case NetworkEventType.DataEvent:

                numData++;

                dataInRate += dataSize;
                Interp.shiftBuffer(previousDataInRate);
                Interp.shiftBuffer(previousDataInTimes);
                previousDataInRate [0]  = dataSize;
                previousDataInTimes [0] = Time.time;

                if (OperationNetwork.initialConnected == 0)
                {
                    if (recChannelId == reliableSequencedChannelId)
                    {
                        Player.myID = BitConverter.ToInt16(recBuffer, 0);
                        OperationNetwork.initialConnected = 1;
                    }
                    else
                    {
                        // We send a RPC looking for a full game resend, if this is not a full game state:
                        if (BitConverter.ToInt32(recBuffer, 2) >= 0)
                        {
                            OperationView.RPC(null, "ResendTicks", OperationNetwork.ToServer);
                        }
                    }
                }
                else
                {
                    OperationNetwork.clientReceivedData(recBuffer);                      // Note that dataSize could be used as a way of trimming this.
                }

                break;

            case NetworkEventType.DisconnectEvent:
                Debug.LogError("Server Shut Down");
                break;
            }
        }
        Debug.LogError("Missing Messages! Num Data Messages (/250): " + numData);
    }
예제 #17
0
    // Static class devoted to methods that were originally in SyncGameState that involve the interpreting & representing of byte data into objects.


    public static object interpretObject(byte[] data, ref int bytePosition, object type, SyncGameState sgg, int tickNumber, bool isPlayerOwner)
    {
        object returnObject = null;

        if (type is float)
        {
            returnObject  = BitConverter.ToSingle(data, bytePosition);
            bytePosition += 4;
        }
        else if (type is int)
        {
            returnObject  = BitConverter.ToInt32(data, bytePosition);
            bytePosition += 4;
        }
        else if (type is short)
        {
            returnObject  = BitConverter.ToInt16(data, bytePosition);
            bytePosition += 2;
        }
        else if (type is byte)
        {
            returnObject  = data [bytePosition];
            bytePosition += 1;
        }
        else if (type is Vector3)             // Equals feels like good usage here.
        {
            returnObject  = OperationNetwork.getVector3(data, bytePosition);
            bytePosition += 12;
        }
        else if (type is bool)
        {
            returnObject  = BitConverter.ToBoolean(data, bytePosition);
            bytePosition += 1;
        }
        else if (type is Quaternion)
        {
            returnObject  = OperationNetwork.getQuaternion(data, bytePosition);
            bytePosition += 16;
        }
        else if (type is string)
        {
            // All strings have max 255 length.
            // Just because.
            byte length = data [bytePosition];
            bytePosition += 1;

            returnObject  = Encoding.ASCII.GetString(data, bytePosition, length);
            bytePosition += length;
        }
        else if (type is float[])
        {
            byte length = data [bytePosition];             // Max 255 length
            bytePosition += 1;

            returnObject = new float[length];
            for (int i = 0; i < length; i++)
            {
                ((float[])returnObject) [i] = BitConverter.ToSingle(data, bytePosition);
                bytePosition += 4;
            }
        }
        else if (type is Vector3[])
        {
            byte length = data [bytePosition];             // Max 255 length
            bytePosition += 1;

            returnObject = new Vector3[length];
            for (int i = 0; i < length; i++)
            {
                ((Vector3[])returnObject) [i] = OperationNetwork.getVector3(data, bytePosition);
                bytePosition += 12;
            }
        }
        else if (type is Vector3[][])
        {
            // Hitscan data:
            byte length = data [bytePosition];
            bytePosition += 1;
            returnObject  = new Vector3[length][];
            for (int i = 0; i < length; i++)
            {
                ((Vector3[][])returnObject) [i] = (Vector3[])interpretObject(data, ref bytePosition, new Vector3[0], null, -1, false);
            }
        }
        else if (type is SyncableType)
        {
            returnObject = ((SyncableType)type).createThis(data, ref bytePosition, sgg, tickNumber, isPlayerOwner);
        }
        else if (type is DamageNumber[])
        {
            byte length = data [bytePosition];             // Max 255 length
            bytePosition += 1;

            returnObject = new DamageNumber[length];
            for (int i = 0; i < length; i++)
            {
                ((DamageNumber[])returnObject) [i] = new DamageNumber(data, ref bytePosition);
            }
        }
        else if (type is byte[])
        {
            byte length = data [bytePosition];             // Max 255 length
            bytePosition += 1;
            returnObject  = new byte[length];
            for (int i = 0; i < length; i++)
            {
                ((byte[])returnObject) [i] = data[bytePosition];
                bytePosition += 1;
            }
        }
        return(returnObject);
    }
예제 #18
0
    void HudUpdate()
    {
        GameManager.UpdateKillFeed();

        HealthBarUpdate();

        UpdateTrapSelectionZoneMenu();

        for (int i = 0; i < trapsHud.Length; i++)
        {
            trapsHud [i].SetActive(isTrapSelectionMenuOpen);
        }

        respawnHud.transform.parent.gameObject.SetActive(Player.thisPlayer != null && Player.thisPlayer.playerObject == null);
        if (Player.thisPlayer != null && Player.thisPlayer.playerObject == null)
        {
            respawnHud.fillAmount = Mathf.Clamp01(1 - Player.thisPlayer.getRespawnTimer() / Player.thisPlayer.getNetRespawnTimer());
            respawnHud.transform.parent.GetComponent <Image>().fillAmount = Mathf.Clamp01(Player.thisPlayer.getRespawnTimer() / Player.thisPlayer.getNetRespawnTimer());
            int respawnTimer = (int)(Player.thisPlayer.getRespawnTimer() + 1);
            if (respawnTimer == 1)
            {
                respawnHud.transform.parent.Find("Text").GetComponent <Text> ().text = "Respawning in 1 second.";
            }
            else
            {
                respawnHud.transform.parent.Find("Text").GetComponent <Text> ().text = "Respawning in " + respawnTimer + " seconds.";
            }
        }

        // Class Selection Hud gets priority
        if (Player.thisPlayer && Player.thisPlayer.team == 1 && !OptionsMenu.classSelectionMenuOpen)
        {
            bool needTrap = false;
            for (int i = 0; i < Player.thisPlayer.trapTypes.Length; i++)
            {
                if (Player.thisPlayer.trapTypes [i] == 255)
                {
                    needTrap = true;
                }
            }
            if (needTrap && !PlayerHud.isTrapSelectionMenuOpen)
            {
                PlayerHud.isTrapSelectionMenuOpen = true;

                OptionsMenu.ChangeLockState();
            }
            else if (!needTrap && PlayerHud.isTrapSelectionMenuOpen)
            {
                PlayerHud.isTrapSelectionMenuOpen = false;
                OptionsMenu.ChangeLockState();
            }
        }

        if (Player.thisPlayer && PlayerHud.isTrapSelectionMenuOpen)
        {
            for (int x = 0; x < trapsChoices.GetLength(0); x++)
            {
                for (int y = 0; y < trapsChoices.GetLength(1); y++)
                {
                    trapsChoices [x, y].transform.Find("Text").GetComponent <Text> ().text = BuyNewTrap.trapNames [BuyNewTrap.trapIndecies[Player.thisPlayer.myRandomTrapChoices[x * trapsChoices.GetLength(1) + y]]];
                    trapsChoices [x, y].GetComponent <BuyNewTrap> ().buyId = Player.thisPlayer.myRandomTrapChoices [x * trapsChoices.GetLength(1) + y];
                    trapsChoices [x, y].GetComponent <BuyNewTrap> ().rowID = (byte)x;
                }
            }
        }

        // This is how HUD should work for all unlocks:
        if (Player.thisPlayer)
        {
            for (int i = 0; i < displayTraps.Length; i++)
            {
                byte actual = Player.thisPlayer.trapTypes [i];
                if (Player.thisPlayer.team != 1)
                {
                    actual = 255;
                }
                if (displayTraps [i] != actual)
                {
                    displayTraps [i] = actual;
                    if (displayTraps [i] == 255 || Player.thisPlayer.team != 1)
                    {
                        trapPlayerHudTypes [i].SetActive(false);
                    }
                    else
                    {
                        trapPlayerHudTypes [i].SetActive(true);
                        trapPlayerHudTypes [i].transform.Find("Text").GetComponent <Text> ().text = "(" + OptionsMenu.getBindString(OptionsMenu.binds [OptionsMenu.TRAP_1 + i]) + ") " + BuyNewTrap.trapNames [BuyNewTrap.trapIndecies[displayTraps [i]]];
                    }
                }


                if (displayTraps[i] != 255 && Player.thisPlayer.playerObject)
                {
                    int trapTypeIndex = BuyNewTrap.trapIndecies[Player.thisPlayer.trapTypes [i]];
                    PlacePlayerMade.setCharge(trapPlayerHudTypes [i],
                                              Mathf.Clamp01((Player.thisPlayer.playerObject.GetComponent <SyncPlayer>().playerTime - Player.thisPlayer.trapCoolDownsStartedAt [i]) / (float)(BuyNewTrap.baseTrapCosts [trapTypeIndex] * BuyNewTrap.maxTrapsLoaded[trapTypeIndex] * Time.fixedDeltaTime)),
                                              1f / BuyNewTrap.maxTrapsLoaded[trapTypeIndex]);
                }
            }
        }

        if (OperationNetwork.connected)
        {
            if (Time.time - lastNetworkSettingsRead > 1)
            {
                if (OperationNetwork.isServer)
                {
                    dataSentPerSecond = OperationNetwork.getClient(32767).dataSent / (Time.time - lastNetworkSettingsRead);
                    OperationNetwork.getClient(32767).dataSent  = 0;
                    networkSettings.GetComponent <Text> ().text = (int)(dataSentPerSecond / 100) / 10.0f + " KB/s";
                }
                else
                {
                    dataSentPerSecond            = RunGame.myClient.dataOutRate / (Time.time - lastNetworkSettingsRead);
                    RunGame.myClient.dataOutRate = 0;
                    float dataRecievedPerSecond = RunGame.myClient.dataInRate / (Time.time - lastNetworkSettingsRead);
                    RunGame.myClient.dataInRate = 0;

                    networkSettings.GetComponent <Text> ().text = "Sent: " + (int)(dataSentPerSecond / 100) / 10.0f +
                                                                  " KB/s Recieved: " + (int)(dataRecievedPerSecond / 100) / 10.0f + " KB/s";
                }
                lastNetworkSettingsRead = Time.time;
            }
        }

        if (OperationNetwork.connected && Player.thisPlayer != null && Player.thisPlayer.playerObject != null)
        {
            PlayerMove pMove        = Player.thisPlayer.playerObject.GetComponent <PlayerMove> ();
            float      touchPercent = DamageCircle.isTouchingDamageCircle(pMove.timeSinceTouchingDamageCircle);
            if (touchPercent > 0)
            {
                buffHud.transform.parent.GetComponent <Image> ().enabled = true;
                buffHud.enabled    = true;
                buffHud.fillAmount = touchPercent;
            }
            else
            {
                // Disable the rendering of it:
                buffHud.transform.parent.GetComponent <Image> ().enabled = false;
                buffHud.enabled = false;
            }
            healthHud.enabled = true;
            healthHud.transform.parent.GetComponent <Image> ().enabled = true;
            healthHudText.enabled = true;
            healthHud.transform.parent.GetComponent <Image> ().fillAmount = 1 - pMove.GetComponent <Combat> ().health / pMove.GetComponent <Combat> ().maxHealth;
            healthHud.fillAmount = pMove.GetComponent <Combat> ().health / pMove.GetComponent <Combat> ().maxHealth;
            healthHudText.text   = (short)pMove.GetComponent <Combat> ().health + " HP";

            if (Player.thisPlayer.playerObject != playerObjectHudLoaded)
            {
                playerObjectHudLoaded = Player.thisPlayer.playerObject;

                float origNum = getHudHeightUsed(classHud.transform);

                // Unload old hud:
                foreach (Transform t in classHud.transform)
                {
                    if (t.name != "AdjacentClassHud")
                    {
                        Destroy(t.gameObject);
                    }
                }
                foreach (Transform t in classHud.transform.Find("AdjacentClassHud"))
                {
                    Destroy(t.gameObject);
                }

                // Create new hud:
                foreach (Unlock unlock in Player.thisPlayer.playerObject.GetComponent <ClassControl>().getUnlocks())
                {
                    if (unlock != null)
                    {
                        unlock.setHudElement(classHud.transform, origNum);
                    }
                }

                int classLoaded = Player.thisPlayer.playerObject.GetComponent <ClassControl> ().classNum;

                // Phase through & Speed Boost are not unlocks, despite having some similar qualities: (Like the HUD)
                if (classLoaded == 0)
                {
                    Player.thisPlayer.playerObject.GetComponent <PlayerMove> ().hudElement  = Unlock.setHudElement(classHud.transform, "Cooldown", "Phase Shift", OptionsMenu.MOVEMENT_ABILITY_BIND, null, origNum);
                    Player.thisPlayer.playerObject.GetComponent <PlayerMove> ().hudElement2 = Unlock.setHudElement(classHud.transform, "Armor", "Armor", OptionsMenu.MAIN_ABILITY, null, origNum);
                }
                else if (classLoaded == 3)
                {
                    Player.thisPlayer.playerObject.GetComponent <PlayerMove> ().hudElement = Unlock.setHudElement(classHud.transform, "Cooldown", "Speed Boost", OptionsMenu.MOVEMENT_ABILITY_BIND, null, origNum);
                }
                else if (classLoaded == 4)
                {
                    if (OptionsMenu.hudPanels.ContainsKey("Healthtaken"))
                    {
                        Player.thisPlayer.playerObject.GetComponent <PlayerMove> ().hudElement = Unlock.setHudElement(classHud.transform, "Healthtaken", "Health Taken", OptionsMenu.ULTIMATE_ABILITY, null, origNum);
                    }
                }

                // The updating for the HUD is handled within the unlocks / class itself
            }
        }
        else
        {
            // Hide hud completely:
            buffHud.transform.parent.GetComponent <Image> ().enabled = false;
            buffHud.enabled   = false;
            healthHud.enabled = false;
            healthHud.transform.parent.GetComponent <Image> ().enabled = false;
            healthHudText.enabled = false;
        }

        // SCOREBOARD:
        if (Input.GetKey(KeyCode.Tab))
        {
            scoreBoard.SetActive(true);
            for (byte team = 0; team < 2; team++)
            {
                List <Player> players = GameManager.getPlayersOnTeam(team);
                for (int i = 0; i < scoreBoardPlayers[team].Count; i++)
                {
                    if (i < players.Count)
                    {
                        scoreBoardPlayers [team] [i].SetActive(true);
                        scoreBoardPlayers [team] [i].transform.Find("Text").GetComponent <Text> ().text = players [i].playerName;
                        Color c = scoreBoardPlayers [team] [i].GetComponent <Image> ().color;
                        if (players [i].playerObject != null)
                        {
                            scoreBoardPlayers [team] [i].GetComponent <Image> ().color = new Color(c.r, c.g, c.b, 1f);
                        }
                        else
                        {
                            scoreBoardPlayers [team] [i].GetComponent <Image> ().color = new Color(c.r, c.g, c.b, 0.2f);
                        }
                        scoreBoardKills [team] [i].text  = "" + players[i].kills;
                        scoreBoardDeaths [team] [i].text = "" + players[i].deaths;
                    }
                    else
                    {
                        scoreBoardPlayers [team] [i].SetActive(false);
                        scoreBoardKills [team] [i].text  = "";
                        scoreBoardDeaths [team] [i].text = "";
                    }
                }
            }
        }
        else
        {
            scoreBoard.SetActive(false);
        }

        // Display kill feed:
        for (int i = 0; i < killFeed.Count; i++)
        {
            if (GameManager.recentPlayerKillers.Count > i)
            {
                killFeed [i].parent.gameObject.SetActive(true);
                string killDisplay = "";
                if (GameManager.PlayerExists(GameManager.recentPlayerKillers [i]))
                {
                    killDisplay = GameManager.GetPlayer(GameManager.recentPlayerKillers [i]).playerName + " killed ";
                }
                if (GameManager.PlayerExists(GameManager.recentPlayerDeaths [i]))
                {
                    killFeed [i].GetComponent <Text> ().text = killDisplay + GameManager.GetPlayer(GameManager.recentPlayerDeaths [i]).playerName;
                }
            }
            else
            {
                killFeed [i].parent.gameObject.SetActive(false);
            }
        }

        // Class Selection Menu:
        if (OptionsMenu.classSelectionMenuOpen && Player.thisPlayer != null)
        {
            classSelectionMenu.SetActive(true);
            Color color;
            if (Player.thisPlayer.team == 0)
            {
                color = new Color(0, 0, 1f, 0.5f);
            }
            else if (Player.thisPlayer.team == 1)
            {
                color = new Color(1f, 0, 0, 0.5f);
            }
            else
            {
                color = new Color(1f, 1f, 1f, 0.5f);
            }

            classSelectionMenu.GetComponent <Image> ().color = color;
            for (byte i = 0; i < classSelectionTeams.Length; i++)
            {
                if (Player.thisPlayer.team == i)
                {
                    classSelectionTeams [i].color = new Color(classSelectionTeams [i].color.r, classSelectionTeams [i].color.g, classSelectionTeams [i].color.b, 1f);
                }
                else
                {
                    classSelectionTeams [i].color = new Color(classSelectionTeams [i].color.r, classSelectionTeams [i].color.g, classSelectionTeams [i].color.b, 0.6f);
                }
            }
            for (byte i = 0; i < classTypes.Count; i++)
            {
                if (Player.thisPlayer.team == 0)
                {
                    classTypes [i].gameObject.SetActive(true);
                    if (Player.thisPlayer.classNum == i)
                    {
                        classTypes [i].color = new Color(0.2f, 0.2f, 1f, 1f);
                    }
                    else
                    {
                        classTypes [i].color = new Color(0.7f, 0.7f, 1f, 0.8f);
                    }
                }
                else if (Player.thisPlayer.team == 1)
                {
                    classTypes [i].gameObject.SetActive(true);
                    if (Player.thisPlayer.classNum == i)
                    {
                        classTypes [i].color = new Color(1f, 0.2f, 0.2f, 1f);
                    }
                    else
                    {
                        classTypes [i].color = new Color(1f, 0.7f, 0.7f, 0.8f);
                    }
                }
                else
                {
                    classTypes [i].gameObject.SetActive(false);
                }
            }
        }
        else
        {
            classSelectionMenu.SetActive(false);
        }
    }
예제 #19
0
    // sendToSelf is only for server use.
    public static void RPC(OperationView oV, string methodName, int sendToSelf, params object[] objects)
    {
        // The server doesn't send RPCs: (However the Server can send itself RPCs as if it was a client)
        if (OperationNetwork.isServer && sendToSelf != OperationNetwork.ToServer)
        {
            return;
        }

        // Demos aren't actually connected so they can't send RPCs.
        if (OperationNetwork.isDemo)
        {
            return;
        }

        // Needs to turn the data into bytes:
        byte[] data          = new byte[300];
        int    writeLocation = 3;

        for (int i = 0; i < objects.Length; i++)
        {
            if (objects[i] is float)
            {
                Buffer.BlockCopy(BitConverter.GetBytes((float)objects[i]), 0, data, writeLocation, 4);
                writeLocation += 4;
            }
            else if (objects[i] is int)
            {
                Buffer.BlockCopy(BitConverter.GetBytes((int)objects[i]), 0, data, writeLocation, 4);
                writeLocation += 4;
            }
            else if (objects[i] is bool)
            {
                data[writeLocation] = Convert.ToByte((bool)objects[i]);
                writeLocation      += 1;
            }
            else if (objects[i] is byte)
            {
                data[writeLocation] = (byte)objects[i];
                writeLocation      += 1;
            }
            else if (objects[i] is Vector3)
            {
                Buffer.BlockCopy(BitConverter.GetBytes(((Vector3)objects[i]).x), 0, data, writeLocation, 4);
                writeLocation += 4;
                Buffer.BlockCopy(BitConverter.GetBytes(((Vector3)objects[i]).y), 0, data, writeLocation, 4);
                writeLocation += 4;
                Buffer.BlockCopy(BitConverter.GetBytes(((Vector3)objects[i]).z), 0, data, writeLocation, 4);
                writeLocation += 4;
            }
            else if (objects[i] is Vector3[])
            {
                byte length = (byte)((Vector3[])objects[i]).Length;                 // Maximum length of 255
                data[writeLocation] = length;
                writeLocation      += 1;
                for (int sk = 0; sk < length; sk++)
                {
                    Buffer.BlockCopy(BitConverter.GetBytes(((Vector3[])objects[i])[sk].x), 0, data, writeLocation, 4);
                    writeLocation += 4;
                    Buffer.BlockCopy(BitConverter.GetBytes(((Vector3[])objects[i])[sk].y), 0, data, writeLocation, 4);
                    writeLocation += 4;
                    Buffer.BlockCopy(BitConverter.GetBytes(((Vector3[])objects[i])[sk].z), 0, data, writeLocation, 4);
                    writeLocation += 4;
                }
            }
            else if (objects[i] is Quaternion)
            {
                Buffer.BlockCopy(BitConverter.GetBytes(((Quaternion)objects[i]).x), 0, data, writeLocation, 4);
                writeLocation += 4;
                Buffer.BlockCopy(BitConverter.GetBytes(((Quaternion)objects[i]).y), 0, data, writeLocation, 4);
                writeLocation += 4;
                Buffer.BlockCopy(BitConverter.GetBytes(((Quaternion)objects[i]).z), 0, data, writeLocation, 4);
                writeLocation += 4;
                Buffer.BlockCopy(BitConverter.GetBytes(((Quaternion)objects[i]).w), 0, data, writeLocation, 4);
                writeLocation += 4;
            }
            else if (objects[i] is float[])
            {
                byte length = (byte)((float[])objects[i]).Length;                 // Maximum length of 255
                data[writeLocation] = length;
                writeLocation      += 1;
                Buffer.BlockCopy((float[])objects[i], 0, data, writeLocation, ((float[])objects[i]).Length * 4);
                writeLocation += ((float[])objects[i]).Length * 4;
            }
            else if (objects[i] is byte[])
            {
                if (((byte[])objects [i]).Length > 255)
                {
                    Debug.LogError("BYTE DATA OVERLOAD- Probably mirrors for playerInput hitscanData");
                }
                byte length = (byte)((byte[])objects[i]).Length;                 // Maximum length of 255
                data[writeLocation] = length;
                writeLocation      += 1;
                Buffer.BlockCopy((byte[])objects[i], 0, data, writeLocation, ((byte[])objects[i]).Length);
                writeLocation += ((byte[])objects[i]).Length;
            }
            else if (objects[i] is string)
            {
                byte[] stringBytes = Encoding.ASCII.GetBytes((string)objects[i]);
                if (stringBytes.Length > 255)
                {
                    // Cut it off:
                    byte[] extraStringBytes = stringBytes;
                    stringBytes = new byte[255];
                    Buffer.BlockCopy(extraStringBytes, 0, stringBytes, 0, 255);
                }
                data [writeLocation] = (byte)(stringBytes.Length);
                writeLocation       += 1;
                Buffer.BlockCopy(stringBytes, 0, data, writeLocation, stringBytes.Length);
                writeLocation += stringBytes.Length;
            }
            else if (objects[i] is short)
            {
                Buffer.BlockCopy(BitConverter.GetBytes((short)objects[i]), 0, data, writeLocation, 2);
                writeLocation += 2;
            }
        }
        // Trim data: (Probably should just use writeLocation in "sendData" instead)
        Array.Resize(ref data, writeLocation);

        // All OperationViews come with a SyncGameState.
        if (oV != null)
        {
            Buffer.BlockCopy(BitConverter.GetBytes(oV.GetComponent <SyncGameState> ().objectID), 0, data, 0, 2);
        }
        else
        {
            Buffer.BlockCopy(BitConverter.GetBytes((short)0), 0, data, 0, 2);
        }

        data[2] = rpcIDList[methodName];         // Which RPC call

        if (OperationNetwork.isServer)
        {
            // This is no longer how the server interacts with the players
            if (sendToSelf == OperationNetwork.ToServer)
            {
                byte[] sendData = new byte[data.Length - 3];
                Buffer.BlockCopy(data, 3, sendData, 0, data.Length - 3);
                oV.ReceiveRPC(data [2], OperationNetwork.FromServerClient, sendData);
            }
            return;
        }
        else
        {
            // Just send to server. (Client can't send anywhere else)
            OperationNetwork.sendDataToServer(data);
        }
    }
예제 #20
0
 public GameObject facadeObject = null;     // SERVER ONLY
 void SpawnFacade(GameObject facade)
 {
     facadeObject = (GameObject)MonoBehaviour.Instantiate(facade, transform.position, transform.rotation);
     facadeObject.GetComponent <SyncFacade> ().playerOwner = plyr;        // This is only used server side on SyncFacade
     OperationNetwork.OperationAddSyncState(facadeObject);
 }
예제 #21
0
    // There is a limited amount of information that must be sent to only the player.
    // ALL of this information can come in multiple / none form, with the exception of Predi
    // It includes:

    /*
     * PredictionError
     * DamageNumber Callback
     * DamageHit From (Vector3 / amount)
     */
    public override object getObjectThis(int num, bool isPlayerOwner)
    {
        if (!isPlayerOwner)
        {
            switch (num)
            {
            case 0:
                return(playerOwner);                // This has to be first.

            case 1:
                return((short)Mathf.CeilToInt(GetComponent <Combat> ().health));

            case 2:
                return(transform.position);

            case 3:
                return(new YRotation(transform.eulerAngles.y));

            case 4:
                return(new UpDownRotation(-GetComponent <PlayerMove> ().mainCamera.transform.eulerAngles.x));

            case 5:
                return(GetComponent <ClassControl> ().nextUnlock);

            case 6:
                return(currentTriggerSet);

            case 7:
                // Boolean data for player:
                byte returnByte = 0;
                if (GetComponent <PlayerMove> ().isGrounded)
                {
                    returnByte += (byte)(1 << 0);
                }
                if (GetComponent <PlayerMove> ().isCrouched)
                {
                    returnByte += (byte)(1 << 1);
                }
                if (GetComponent <PlayerMove> ().isPhasing() || GetComponent <PlayerMove> ().isSpeedBoosting())
                {
                    returnByte += (byte)(1 << 2);
                }
                if (GetComponent <PlayerMove> ().puttingArmorOn)
                {
                    returnByte += (byte)(1 << 3);
                }
                return(returnByte);

            case 8:
                if (GetComponent <ClassControl> ().classNum == 1)
                {
                    return(GetComponent <ClassControl> ().getUnlockEquippedWithType <Pistol> ().mode);
                }
                else
                {
                    return(null);
                }

            default:
                return(null);
            }
        }
        else
        {
            switch (num)
            {
            case 0:
                return(playerOwner);                // This has to be first.

            case 1:
                return((short)Mathf.CeilToInt(GetComponent <Combat> ().health));

            case 2:
                return(playerTriggerSet);

            case 3:
                if (OperationNetwork.isServer)
                {
                    return(OperationNetwork.getClient(playerOwner).lastPlayerInputGroupID);
                }
                else
                {
                    return((short)0);                    // This is just used for type anyways
                }

            default:
                if (GetComponent <ClassControl> ().classNum == 4 && num == 4)
                {
                    return(GetComponent <ClassControl> ().getHealthTaken());
                }
                return(PlayerState.getObject(num - getPlayerStateStartIndex(), this, GetComponent <PlayerMove>()));
            }
        }
    }
예제 #22
0
    void Update()
    {
        if ((RunGame.myClient != null && !OperationNetwork.isServer) || OperationNetwork.isDemo)
        {
            if (RunGame.myClient != null)
            {
                RunGame.myClient.ReadMessages();
            }

            GetComponent <Interp>().InterpUpdate();
        }

        if (Input.GetKeyDown(KeyCode.G))
        {
            Time.timeScale = 0.02f;
        }
        else if (Input.GetKeyDown(KeyCode.H))
        {
            Time.timeScale = 1;
        }


        if (OperationNetwork.isDemo)
        {
            if (Input.GetKeyDown(KeyCode.Alpha1))
            {
                Time.timeScale *= 0.5f;
            }
            else if (Input.GetKeyDown(KeyCode.Alpha2))
            {
                Time.timeScale *= 2;
            }
            else if (Input.GetKeyDown(KeyCode.Alpha3))
            {
                Time.timeScale = 1;
            }

            // Read.. execute.. This will disconnect the client on the last ~10 seconds.

            try {
                byte[] readInData = new byte[OperationNetwork.maxDemoData];
                // Just in case:
                int x = 0;
                while (OperationNetwork.lastTickLoaded < Interp.getTickNumber() + 5)                   // Only laods in 5 ticks ahead
                {
                    OperationNetwork.timeReceivedForDemo = false;
                    Buffer.BlockCopy(OperationNetwork.dataToReadIn, OperationNetwork.currentByte, readInData, 0,
                                     Math.Min(OperationNetwork.maxDemoData, OperationNetwork.dataToReadIn.Length - OperationNetwork.currentByte));
                    int oldByte = OperationNetwork.currentByte;
                    OperationNetwork.clientReceivedData(readInData);                     // Any amount is fine, but more will generally be better
                    x++;
                    if (x > 100)
                    {
                        Debug.LogError("MAJOR DEMO FAILURE");
                        break;
                    }
                }
            } catch (Exception e) {
                Debug.LogError(e);
                OperationNetwork.isDemo = false;

                // This is not necessarily stable:
                RunGame.Disconnect();
                OptionsMenu.ResetStaticVariables();
                SceneManager.LoadScene(0);                 // Reloads map.
            }
        }
    }
예제 #23
0
    public void ReadMessages()
    {
        for (int i = 0; i < 5000; i++)
        {
            try {
                int              recHostId;
                int              recConnectionId;
                int              recChannelId;
                byte[]           recBuffer  = new byte[2048];
                int              bufferSize = 2048;
                int              dataSize;
                byte             error;
                NetworkEventType networkEvent = NetworkTransport.Receive(out recHostId, out recConnectionId, out recChannelId, recBuffer, bufferSize, out dataSize, out error);

                NetworkError networkError = (NetworkError)error;
                if (networkError != NetworkError.Ok)
                {
                    Debug.LogError(string.Format("Error recieving event: {0} with recHostId: {1}, recConnectionId: {2}, recChannelId: {3}", networkError, recHostId, recConnectionId, recChannelId));
                }

                switch (networkEvent)
                {
                case NetworkEventType.Nothing:
                    return;

                case NetworkEventType.ConnectEvent:
                    Debug.LogError(string.Format("incoming connection event received with connectionId: {0}, recHostId: {1}, recChannelId: {2}", recConnectionId, recHostId, recChannelId));

                    if (!serverClients.ContainsKey(recConnectionId))
                    {
                        // Add server client:
                        ServerPerson sP = new ServerPerson(this);
                        RunGame.myServerThreads.Add(sP);
                        // hmm...  player.players.Add(sP.id, new OtherPlayerInfo()); // Stick to default settings until a RPC is received.
                        sP.recConnectionId = recConnectionId;
                        serverClients.Add(recConnectionId, sP);
                    }
                    break;

                case NetworkEventType.DataEvent:
                    // Player Input!
                    // Delegate this to the "ServerClientData" first:
                    if (serverClients.ContainsKey(recConnectionId))
                    {
                        short id = serverClients [recConnectionId].id;

                        if (dataSize > 500)
                        {
                            Debug.LogError("Nearing Data limit on server: " + dataSize + ": " + recChannelId + "(" + reliableSequencedChannelId + " / " + unreliableChannelId + ")");
                        }

                        // todo: When removing RPCs, we're going to make this into a proper ref index sorta thing:
                        byte[] rpcData = new byte[dataSize];
                        Buffer.BlockCopy(recBuffer, 0, rpcData, 0, dataSize);

                        if (recChannelId == reliableSequencedChannelId)
                        {
                            OperationNetwork.serverReceivedData(rpcData, id);
                        }
                        else
                        {
                            short  packetID = BitConverter.ToInt16(rpcData, 0);
                            byte[] sData    = new byte[rpcData.Length - 2];
                            // This might be one of the most inefficient things on the server, TODO!! The copy of byte data!!
                            Buffer.BlockCopy(rpcData, 2, sData, 0, sData.Length);
                            OperationNetwork.serverReceivedInput(sData, id, packetID);
                        }
                    }
                    else
                    {
                        Debug.LogError("Client not connected: " + recConnectionId);
                    }
                    break;

                case NetworkEventType.DisconnectEvent:
                    Debug.LogError("remote client " + recConnectionId + " disconnected");
                    if (serverClients.ContainsKey(recConnectionId))
                    {
                        RunGame.myServerThreads.Remove(serverClients [recConnectionId]);
                        serverClients [recConnectionId].disconnect();
                        serverClients.Remove(recConnectionId);
                    }
                    break;
                }
            } catch (Exception e) {
                Debug.LogError("Error on client data receive: " + e.Message);
            }
        }
        Debug.LogError("Missing Messages on Server!");
    }
예제 #24
0
    public void ServerPlacementValidCheck()
    {
        bool isPlacementValid = IsPlacementValid();



        if (isPlacementValid)
        {
            GameObject properObj = (GameObject)Instantiate(playerMadeObjects[team], transform.position, transform.rotation);

            properObj.GetComponent <PlayerMade> ().playerOwner = playerOwner;

            OperationNetwork.OperationAddSyncState(properObj);
            Destroy(gameObject);
        }
        else
        {
            // Did not place!! Prediction FAILURE!! RESET COOLDOWN

            // Simple system now. Reset cooldown!!

            if (GameManager.PlayerExists(playerOwner))
            {
                bool found = false;
                if (GameManager.GetPlayer(playerOwner).playerObject != null)
                {
                    foreach (Unlock unlock in GameManager.GetPlayer(playerOwner).playerObject.GetComponent <ClassControl>().getUnlocks())
                    {
                        if (unlock is PlacePlayerMade && !(unlock is PlaceTrap) && ((PlacePlayerMade)unlock).cutOut.GetComponent <CollideCheck>().placeType == placeType)
                        {
                            if (unlock is PlaceIcicle)
                            {
                                ((PlaceIcicle)unlock).AmmoStored = Mathf.Min(((PlaceIcicle)unlock).AmmoStored + 1, PlaceIcicle.maxAmmoStored);
                            }
                            else
                            {
                                Debug.LogError("Resette");
                                ((PlacePlayerMade)unlock).coolDownStartedAt = -1000f;
                            }


                            found = true;
                            break;
                        }
                    }
                }
                // Could be trap:
                if (!found)
                {
                    for (int i = 0; i < GameManager.GetPlayer(playerOwner).trapTypes.Length; i++)
                    {
                        Debug.Log(placeType + ": " + GameManager.GetPlayer(playerOwner).trapTypes [i]);
                        if (GameManager.GetPlayer(playerOwner).trapTypes [i] == placeType)
                        {
                            Debug.Log("Reset");
                            GameManager.GetPlayer(playerOwner).trapCoolDownsStartedAt [i] = GameManager.GetPlayer(playerOwner).resetTrapCoolDownsTo [i];
                        }
                    }
                }
            }



            Destroy(gameObject);
            return;
        }
    }
예제 #25
0
    void Update(float timeSinceChargeStarted)
    {
        if (!launched && timeSinceChargeStarted > getAppearTime())
        {
            if (throwable == null)
            {
                appear();
            }

            // Launch
            if (timeSinceChargeStarted > getLaunchTime())
            {
                launched = true;
                if (throwable != null)
                {
                    MonoBehaviour.Destroy(throwable);                     // Hmm.. it'll desync on the player side. (And possibly client side, for that matter)
                    throwable = null;
                    if (OperationNetwork.isServer)
                    {
                        GameObject throwObj = (GameObject)MonoBehaviour.Instantiate(throwablePrefab, rightHand.transform.TransformPoint(-0.35f, 0, 0), rightHand.transform.rotation);


                        // Note how yDir is negative for both:
                        float yDir;
                        if (parentPlayerMove.thisIsMine || OperationNetwork.isServer)
                        {
                            yDir = -parentPlayerMove.mainCamera.transform.eulerAngles.x;
                        }
                        else
                        {
                            yDir = parentPlayerMove.currentPlayerRotUpDown;
                        }

                        yDir = yDir * Mathf.PI / 180.0f;
                        if (yDir > Math.PI)
                        {
                            yDir -= Mathf.PI * 2;
                        }
                        if (yDir < -Math.PI)
                        {
                            yDir += Mathf.PI * 2;
                        }

                        float yDirIncrease = Mathf.PI / 12.0f;

                        Vector3 fireDir;
                        if (yDir > -yDirIncrease)
                        {
                            fireDir = Vector3.RotateTowards(parentPlayerMove.transform.forward, Vector3.up, yDir + yDirIncrease, 1f);
                        }
                        else
                        {
                            fireDir = Vector3.RotateTowards(parentPlayerMove.transform.forward, Vector3.down, -yDir - yDirIncrease, 1f);
                        }

                        Throw(throwObj, fireDir);

                        OperationNetwork.OperationAddSyncState(throwObj);
                        // We could keep the moveDirection / effectDirection of the Z direction, and perhaps the Y direction (if it is > 0)
                    }

                    // Player and Server:
                    if (OperationNetwork.isServer || parentPlayerMove.thisIsMine)
                    {
                        setCoolDown();
                        isEnabled = false;
                        parentPlayerMove.GetComponent <ClassControl> ().defaultSetup(true);                         // This is a method which is only called when runEffects = true
                    }
                }
            }
        }
    }