Exemplo n.º 1
0
    void ApprovalCheck(byte[] _compressedSerializedData, ulong _clientId, NetworkingManager.ConnectionApprovedDelegate _callback)
    {
        Debug.Log("Client " + _clientId + " is being approved.");

        bool approve            = true;
        bool createPlayerObject = true;



        ConnectionApprovalData connectionData = _compressedSerializedData.GetDecompressedAndDeserialized <ConnectionApprovalData>();

        // I don't actually know that this is going to do us much good, but it seems like if they hack their game this might catch it and I already coded it :p
        approve = true || (connectionData.playerPrefabHash == SpawnManager.GetPrefabHashFromGenerator("Player"));


        if (!connectionDataCache.ContainsKey(_clientId))
        {
            connectionDataCache.Add(_clientId, connectionData);
        }

        //If approve is true, the connection gets added. If it's false. The client gets disconnected
        _callback(createPlayerObject, null, approve, null, null);
        Debug.Log("Client " + _clientId + " approved, spawning player object");

        // set a reference to the NetworkedClient on the Player class for later networking


        CustomMessagingManager.SendNamedMessage("JoinConnectionAccepted", _clientId, Stream.Null);
    }
Exemplo n.º 2
0
    // this happens after approval (96% sure :p)
    private void ML_OnClientConnected(ulong _clientId)
    {
        if (!NetworkingManager.Singleton.IsHost && !NetworkingManager.Singleton.IsServer)
        {
            return;
        }

        Debug.Log("Client " + _clientId + " connected");

        NetworkedClient client = NetworkingManager.Singleton.ConnectedClients[_clientId];
        Player          player = client.GetPlayer();



        player.ClientId.Value = _clientId;

        player.Name.Value = connectionDataCache[_clientId].playerName;

        connectionDataCache.Remove(_clientId);

        using (PooledBitStream stream = PooledBitStream.Get())
        {
            using (PooledBitWriter w = PooledBitWriter.Get(stream))
            {
                w.WriteUInt64(player.NetworkedObject.NetworkId);
                CustomMessagingManager.SendNamedMessage("ClientConnected", AllClientIDs(), stream);
            }
        }
    }
        public void CustomMessageManagerAssigned()
        {
            var gameObject     = new GameObject(nameof(CustomMessageManagerAssigned));
            var networkManager = gameObject.AddComponent <NetworkManager>();
            var transport      = gameObject.AddComponent <DummyTransport>();

            // MLAPI sets this in validate
            networkManager.NetworkConfig = new NetworkConfig()
            {
                // Set the current scene to prevent unexpected log messages which would trigger a failure
                RegisteredScenes = new List <string>()
                {
                    SceneManager.GetActiveScene().name
                }
            };

            // Set dummy transport that does nothing
            networkManager.NetworkConfig.NetworkTransport = transport;

            CustomMessagingManager preManager = networkManager.CustomMessagingManager;

            // Start server to cause init
            networkManager.StartServer();

            Debug.Assert(preManager == null);
            Debug.Assert(networkManager.CustomMessagingManager != null);

            Object.DestroyImmediate(gameObject);
        }
Exemplo n.º 4
0
 public CustomMessages()
 {
     CustomMessagingManager.RegisterNamedMessageHandler(MessageName, (sender, payload) => {
         Debug.Log("MESSAGE HANDLER");
         using (var reader = PooledBitReader.Get(payload)) {
             ChatReceived?.Invoke(this, reader.ReadString().ToString());
         }
     });
 }
Exemplo n.º 5
0
 public void SendChatMessage(ulong clientId, string message)
 {
     using (var messageStream = PooledBitStream.Get()) {
         using (var writer = PooledBitWriter.Get(messageStream)) {
             writer.WriteString(message);
             CustomMessagingManager.SendNamedMessage(MessageName, clientId, messageStream);
         }
     }
 }
 private void OnDestroy()
 {
     if (Singleton == this)
     {
         CustomMessagingManager.UnregisterNamedMessageHandler("NetworkInput");
         CustomMessagingManager.UnregisterNamedMessageHandler("NetworkState");
         Singleton = null;
     }
 }
    private void Start()
    {
        timer      = Stopwatch.StartNew();
        entities   = new Dictionary <ulong, Entity>();
        LastUpdate = CurrentTime;

        transport = NetworkingManager.Singleton.GetComponent <LiteNetLibTransport.LiteNetLibTransport>();
        CustomMessagingManager.RegisterNamedMessageHandler("NetworkInput", ReceiveInput);
        CustomMessagingManager.RegisterNamedMessageHandler("NetworkState", ReceiveState);
    }
Exemplo n.º 8
0
    void Start()
    {
        Instance = this;

        NetworkingManager.Singleton.OnClientConnectedCallback  += ML_OnClientConnected;
        NetworkingManager.Singleton.OnClientDisconnectCallback += ML_OnClientDisconnect;

        CustomMessagingManager.RegisterNamedMessageHandler("ClientConnected", ClientConnected);
        CustomMessagingManager.RegisterNamedMessageHandler("ClientDisconnected", ClientDisconnected);
    }
Exemplo n.º 9
0
    void StartListenForDownloadRequests()
    {
        Debug.Log("Listening for download requests");
        if (!NetworkingManager.Singleton.IsServer)
        {
            Debug.LogWarning("Only run ListenForDownloadRequests() on the server");
            return;
        }

        CustomMessagingManager.RegisterNamedMessageHandler("DownloadFilesRequest", DownloadRequestReceived);
    }
Exemplo n.º 10
0
        protected override void RegisterClientMessageHandlers()
        {
            CustomMessagingManager.RegisterNamedMessageHandler(NetMessageName.s2c_ConnectResult, (ulSenderClientId, hStream) =>
            {
                using (var hReader = PooledNetworkReader.Get(hStream))
                {
                    ConnectStatus eStatus = (ConnectStatus)hReader.ReadInt32();

                    m_hConnectFinishCallback?.Invoke(eStatus);
                }
            });
        }
Exemplo n.º 11
0
    public override void Connect()
    {
        // Register receiving packets on the client from the server
        CustomMessagingManager.RegisterNamedMessageHandler("DissonanceToClient", (senderClientId, stream) =>
        {
            Int32 length  = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length);
            Byte[] buffer = new Byte[length];
            stream.Read(buffer, 0, length);

            base.NetworkReceivedPacket(new ArraySegment <byte>(buffer));
        });
        Connected();
    }
Exemplo n.º 12
0
    public void SendCommand <T>(T message) where T : ICommand, new()
    {
        using (var stream = PooledBitStream.Get())
        {
            using (var writer = PooledBitWriter.Get(stream))
            {
                writer.WriteInt32((int)message.CommandCode);
                message.Write(stream);
            }

            CustomMessagingManager.SendUnnamedMessage(null, stream);
        }
    }
Exemplo n.º 13
0
    void RequestDownloads()
    {
        ProgressUI.SetMessage("Downloading files", "Waiting for headers...");
        ProgressUI.Show();

        Debug.Log("Requesting downloads");
        LargeRPC download = new LargeRPC("InitialGameDownload");

        download.ListenForDownload();
        download.OnDownloadComplete += FinishedDownloadingFromHost;
        download.OnProgressUpdated  += UpdateProgressBar;

        CustomMessagingManager.SendNamedMessage("DownloadFilesRequest", NetworkingManager.Singleton.ServerClientId, Stream.Null);
    }
 public void SendInputMessage(InputMessage msg)
 {
     using (PooledBitStream stream = PooledBitStream.Get())
     {
         using (PooledBitWriter writer = PooledBitWriter.Get(stream))
         {
             writer.WriteInt32(msg.messageID);
             writer.WriteInt32(msg.timeCreated);
             writer.WriteUInt32(msg.playerId);
             writer.WriteShortArray(msg.keycode.ToArray());
             CustomMessagingManager.SendNamedMessage("Input", this.ServerClientId, stream, "customChannel");
         }
     }
 }
Exemplo n.º 15
0
    private void ML_OnClientDisconnect(ulong _clientId)
    {
        if (!NetworkingManager.Singleton.IsHost && !NetworkingManager.Singleton.IsServer)
        {
            return;
        }

        Debug.Log("Client " + _clientId + " connected: ");

        using (PooledBitStream stream = PooledBitStream.Get())
        {
            using (PooledBitWriter writer = PooledBitWriter.Get(stream))
            {
                CustomMessagingManager.SendNamedMessage("ClientDisconnected", AllClientIDs(), stream);
            }
        }
    }
Exemplo n.º 16
0
 public void SendReplicationMessage(ReplicationMessage msg)
 {
     using (PooledBitStream stream = PooledBitStream.Get())
     {
         using (PooledBitWriter writer = PooledBitWriter.Get(stream))
         {
             writer.WriteInt32(msg.messageID);
             writer.WriteInt32(msg.timeCreated);
             writer.WriteUInt32(msg.entityId);
             writer.WriteInt16((byte)msg.shape);
             writer.WriteVector2(msg.pos);
             writer.WriteVector2(msg.speed);
             writer.WriteDouble(msg.size);
             CustomMessagingManager.SendNamedMessage("Replication", null, stream, "customChannel");
         }
     }
 }
Exemplo n.º 17
0
 protected override void SendUnreliable(ArraySegment <byte> packet)
 {
     if (NetworkingManager.Singleton.IsHost)
     {
         // As we are the host in this scenario we should send the packet directly to the server rather than over the network and avoid loopback issues
         _network.server.NetworkReceivedPacket(new MlapiConn(), packet);
     }
     else
     {
         using (PooledBitStream stream = PooledBitStream.Get())
         {
             using (PooledBitWriter writer = PooledBitWriter.Get(stream))
             {
                 for (var i = 0; i < packet.Count; i++)
                 {
                     writer.WriteByte(packet.Array[i + packet.Offset]);
                 }
                 CustomMessagingManager.SendNamedMessage("DissonanceToServer", NetworkingManager.Singleton.ServerClientId, stream, "MLAPI_TIME_SYNC");
             }
         }
     }
 }
Exemplo n.º 18
0
    void JoinServer(string _ip, int _port = 20202, string _password = "")
    {
        if (Transport == null)
        {
            Transport = NetworkingManager.Singleton.GetComponent <UnetTransport>();
        }

        Transport.ConnectAddress = _ip; //takes string
        Transport.ConnectPort    = _port;

        Debug.Log("Connecting to " + _ip + ":" + _port);

        ulong?hsh = SpawnManager.GetPrefabHashFromGenerator("Player");

        Debug.Log(hsh);
        ConnectionApprovalData connectionData = new ConnectionApprovalData((ulong)hsh, playerNameField.text, _password);

        NetworkingManager.Singleton.NetworkConfig.ConnectionData = connectionData.GetSerializedAndCompressed();

        NetworkingManager.Singleton.StartClient();

        Debug.Log("registered JoinConnectionAccepted");
        CustomMessagingManager.RegisterNamedMessageHandler("JoinConnectionAccepted", OnJoinConnectionAccepted);
    }
Exemplo n.º 19
0
 protected override void SendUnreliable(MlapiConn destination, ArraySegment <byte> packet)
 {
     using (PooledBitStream stream = PooledBitStream.Get())
     {
         using (PooledBitWriter writer = PooledBitWriter.Get(stream))
         {
             var count  = packet.Count;
             var offset = packet.Offset;
             for (var i = 0; i < count; i++)
             {
                 writer.WriteByte(packet.Array[i + offset]);
             }
             if (NetworkingManager.Singleton.LocalClientId == destination.clientId)
             {
                 // As we are the host in this scenario we should send the packet directly to the client rather than over the network and avoid loopback issues
                 _network.client.NetworkReceivedPacket(packet);
             }
             else
             {
                 CustomMessagingManager.SendNamedMessage("DissonanceToClient", destination.clientId, stream, "MLAPI_TIME_SYNC");
             }
         }
     }
 }
Exemplo n.º 20
0
 void StopListenForDownloadRequests()
 {
     CustomMessagingManager.UnregisterNamedMessageHandler("DownloadFilesRequest");
 }
 public void RegisterClientNetworkHandlers()
 {
     CustomMessagingManager.RegisterNamedMessageHandler("Replication", HandleReplicationMessage);
 }
Exemplo n.º 22
0
 public void StopListening()
 {
     CustomMessagingManager.UnregisterNamedMessageHandler(MessageName);
     Debug.Log("Stopped Listening");
 }
Exemplo n.º 23
0
 void Awake()
 {
     CustomMessagingManager.RegisterNamedMessageHandler(GameRoomMessagingHubConstants.SEND_SERVER_PROCESS_DOWN_COMMAND, MessageHandler_Server_SendServerProcessDownCommand);
     CustomMessagingManager.RegisterNamedMessageHandler(GameRoomMessagingHubConstants.SEND_SYSTEM_USERID_TO_SERVER, MessageHandler_Server_SendSystemUserIdToServer);
     CustomMessagingManager.RegisterNamedMessageHandler(GameRoomMessagingHubConstants.SEND_PLAYER_EJECTION_COMMAND, MessageHandler_Server_SendPlayerEjectionCommand);
 }
Exemplo n.º 24
0
 public void ListenForDownload()
 {
     ChangeState(LargeRPCState.Receive_AwaitingFirstPacket);
     CustomMessagingManager.RegisterNamedMessageHandler(MessageName, ReceiveFilesDownloadPieceFromSender);
     Debug.Log("Started Listening");
 }
Exemplo n.º 25
0
    // Essentially, when this is passed an empty list, the okay will be sent to the server
    IEnumerator SendNeededFilesListToSender(List <int> _fileIDs)
    {
        bool allFilesReceived = _fileIDs.Count == 0;

        Debug.Log("sending needed files, files: " + _fileIDs.Count);

        PooledBitStream bitStream = PooledBitStream.Get();
        PooledBitWriter writer    = PooledBitWriter.Get(bitStream);

        if (allFilesReceived)
        {
            writer.WriteBit(true);
            writer.WriteIntArray(_fileIDs.ToArray());
            writer.WriteBit(true);

            CustomMessagingManager.SendNamedMessage(MessageName, SenderID, bitStream, "MLAPI_INTERNAL");

            bitStream.Dispose();
            writer.Dispose();
            _fileIDs.Clear();

            StopListening();

            if (OnDownloadComplete != null)
            {
                OnDownloadComplete(SendOrReceiveFlag.Receive, SenderID);
            }

            ChangeState(LargeRPCState.Idle);
        }
        else
        {
            var        i   = 0;
            List <int> ids = _fileIDs;
            foreach (var id in ids)
            {
                i++;

                bool isFinalPacket = i >= _fileIDs.Count;

                if (i >= _fileIDs.Count)
                {
                    writer.WriteBit(isFinalPacket);
                    writer.WriteIntArray(_fileIDs.ToArray());
                    writer.WriteBit(false);

                    CustomMessagingManager.SendNamedMessage(MessageName, SenderID, bitStream, "MLAPI_INTERNAL");

                    bitStream.Dispose();
                    writer.Dispose();

                    bitStream = PooledBitStream.Get();
                    writer    = PooledBitWriter.Get(bitStream);

                    _fileIDs.Clear();

                    yield return(new WaitForSeconds(1 / 8));

                    break;
                }
            }
        }

        yield break;
    }
Exemplo n.º 26
0
 public void ListenForFilesNeededListOrCompletion()
 {
     CustomMessagingManager.RegisterNamedMessageHandler(MessageName, ReceiveFilesNeededListFromReceiver);
     Debug.Log("Started Listening");
 }
Exemplo n.º 27
0
    /// <summary>
    /// this function splits FILES into MEMORY SAFE sized chunks and safely sends one before starting another
    ///
    /// files receipient needs to receive the same number of headers with each header packet (packet 1 counts as a header packet)
    /// </summary>
    public IEnumerator SendFilesDownloadRoutine(string[] _paths, ulong _clientID)
    {
        Debug.Log("coroutine started");
        if (State != LargeRPCState.Idle)
        {
            Debug.LogWarning("Cannot start sending files while files are being sent, waiting for Idle state to begin");
            yield break;
        }

        ReceiverID = _clientID;

        ChangeState(LargeRPCState.Send_SendingHeaders);

        #region comment -- header sizes

        /* -- Header sizes --
         * packet 1
         * int fileCount 4b | long downloadSize 8b | <start headers>
         *
         * header packets
         * int fileID 4b | string filename varsize | byte[256] hash 32b | long fileLength 8b | bool isLastInPacket 1bit
         *
         * subsequent packets
         * int fileID 4b | int filedata_length 4b | byte[var] filedata <=netChunkSize | bool isLastInPacket 1byte
         */
        #endregion

        #region Grab Download Information

        // grab info for headers
        foreach (var path in _paths)
        {
            if (File.Exists(path))
            {
                using (FileStream fs = File.Open(path, FileMode.Open))
                {
                    Debug.Log(fs.Name);
                    int    id       = Headers.Count;
                    byte[] fileHash = fs.sha256();
                    yield return(new WaitForEndOfFrame());

                    FileHeader header = new FileHeader(id, Path.GetFileName(path), fileHash, fs.Length);
                    Headers.Add(header);

                    DownloadSize += header.fileSize;
                }

                // let it exit if it needs to, giving StopRoutine() a chance
                yield return(0);
            }
            else
            {
                Debug.LogWarning("File not found, skipping: " + path);
            }
        }

        #endregion

        #region send headers

        PooledBitStream bitStream = PooledBitStream.Get();
        PooledBitWriter writer    = PooledBitWriter.Get(bitStream);

        // fileCount
        writer.WriteInt32(Headers.Count);

        // downloadSize
        writer.WriteInt64(DownloadSize);


        var headersThisPacket = 0;
        var packetsSent       = 0;
        Debug.Log("Sending headers");
        foreach (var header in Headers)
        {
            // let it exit if it needs to, giving StopRoutine() a chance
            yield return(0);

            Debug.Log(headersThisPacket + "          " + packetsSent);

            var path = header.path;
            var id   = header.id;

            headersThisPacket++;

            // fileID
            writer.WriteInt32(header.id);

            // filename
            writer.WriteString(path);

            Debug.Log(Encoding.Unicode.GetString(header.hash));
            // hash
            writer.WriteByteArray(header.hash, 32);

            // fileLength
            writer.WriteInt64(header.fileSize);

            bool isLastPacket = id >= Headers.Count - 1;

            // send it off if we've filled up a packet
            if (headersThisPacket >= headersPerPacket || isLastPacket)
            {
                Debug.Log("message going out");
                // isLastInPacket
                writer.WriteBit(true);



                CustomMessagingManager.SendNamedMessage(MessageName, _clientID, bitStream, "MLAPI_INTERNAL");

                /* headers are pretty small, they really don't need the receiver to check in here unless it becomes a problem
                 *
                 * // if we haven't sent any packets yet when we get here, wait for an okay from the receiver
                 * if (packetsSent == 0)
                 * {
                 *  ChangeState(LargeRPCState.Send_AwaitingOkayToSend);
                 *
                 *  while (State == LargeRPCState.Send_AwaitingOkayToSend)
                 *  {
                 *      yield return new WaitForSeconds(0.5f);
                 *  }
                 * }*/

                packetsSent++;
                Debug.Log("headers: " + headersThisPacket + "          packets: " + packetsSent);
                headersThisPacket = 0;

                writer.Dispose();
                bitStream.Dispose();

                bitStream = PooledBitStream.Get();
                writer    = PooledBitWriter.Get(bitStream);

                // don't wait on the last one
                if (!isLastPacket)
                {
                    yield return(new WaitForSeconds(1 / 14));
                }
            }
            else
            {
                writer.WriteBit(false);
            }
        }

        writer.Dispose();
        bitStream.Dispose();

        #endregion

        ChangeState(LargeRPCState.Send_AwaitingFilesNeededList);


        ListenForFilesNeededListOrCompletion();

        // loop start
        while (State != LargeRPCState.Complete)
        {
            // let it exit if it needs to, giving StopRoutine() a chance
            yield return(0);

            Debug.Log("Not done, running not-complete loop");
            #region wait for needed files list

            while (State == LargeRPCState.Send_AwaitingFilesNeededList || State == LargeRPCState.Send_EnsuringIntegrity)
            {
                Debug.Log("waiting for list");
                yield return(new WaitForSeconds(0.5f));
            }

            Debug.Log("No longer waiting for list");

            #endregion
            // runs ReceiveFilesNeededListFromReceiver, changes state to either Send_SendingFiles or Complete


            if (filesToSend.Count > 0)
            {
                Debug.Log("client still needs more files, sending");

                #region send files

                bitStream = PooledBitStream.Get();
                writer    = PooledBitWriter.Get(bitStream);

                foreach (var header in Headers)
                {
                    // let it exit if it needs to, giving StopRoutine() a chance
                    yield return(0);

                    Debug.Log("processing header");
                    if (File.Exists(header.path) && filesToSend.Contains(header.id))
                    {
                        Debug.Log("file is needed");
                        using (FileStream fs = File.Open(header.path, FileMode.Open))
                        {
                            // while loop pulled from fs.Read docs from microsoft, a little confusing to the glance but works and will be fast

                            int numBytesToRead = (int)fs.Length;
                            int numBytesRead   = 0;
                            while (numBytesToRead > 0)
                            {
                                Debug.Log("still bytes left");
                                int thisFileChunkSize = fileChunkSize;
                                thisFileChunkSize = Mathf.Min(thisFileChunkSize, numBytesToRead);

                                byte[] fileChunk = new byte[thisFileChunkSize];

                                // Read may return anything from 0 to numBytesToRead.
                                int n = fs.Read(fileChunk, numBytesRead, thisFileChunkSize);



                                foreach (byte[] netChunk in fileChunk.Slices(netChunkSize, false))
                                {
                                    Debug.Log("processing next chunk");

                                    // fileID
                                    writer.WriteInt32(header.id);

                                    //writer.WriteInt32(netChunk.Length);
                                    Debug.Log("netchunk len: " + netChunk.Length);
                                    // filedata
                                    writer.WriteByteArray(netChunk);

                                    // isLastInPacket, need to add in its own size
                                    bool isLastInPacket = bitStream.Length + 1 >= netChunkSize || netChunk.Length < netChunkSize;
                                    writer.WriteBit(isLastInPacket);

                                    if (isLastInPacket)
                                    {
                                        CustomMessagingManager.SendNamedMessage(MessageName, _clientID, bitStream, "MLAPI_INTERNAL");
                                        Debug.Log("packet sent");

                                        yield return(new WaitForSeconds(1 / 14));

                                        writer.Dispose();
                                        bitStream.Dispose();

                                        bitStream = PooledBitStream.Get();
                                        writer    = PooledBitWriter.Get(bitStream);
                                    }
                                }

                                // Break when the end of the file is reached.
                                if (n == 0)
                                {
                                    Debug.Log("end of file reached, this is a failsafe");
                                    break;
                                }

                                numBytesRead   += n;
                                numBytesToRead -= n;
                            }
                        }
                    }
                }

                Debug.Log("all headers processed");

                filesToSend.Clear();

                // just failsafing these, should be disposed of already
                writer.Dispose();
                bitStream.Dispose();

                #endregion

                ChangeState(LargeRPCState.Send_EnsuringIntegrity);
            }

            Debug.Log("Waiting before checking completion again");
            yield return(new WaitForSeconds(1f));
        }

        StopListening();

        Debug.Log("files sent");
        if (OnDownloadComplete != null)
        {
            OnDownloadComplete(SendOrReceiveFlag.Send, ReceiverID);
        }

        ChangeState(LargeRPCState.Idle);

        yield break;
    }
Exemplo n.º 28
0
 void OnDestroy()
 {
     CustomMessagingManager.UnregisterNamedMessageHandler(GameRoomMessagingHubConstants.SEND_SERVER_PROCESS_DOWN_COMMAND);
     CustomMessagingManager.UnregisterNamedMessageHandler(GameRoomMessagingHubConstants.SEND_SYSTEM_USERID_TO_SERVER);
     CustomMessagingManager.UnregisterNamedMessageHandler(GameRoomMessagingHubConstants.SEND_PLAYER_EJECTION_COMMAND);
 }
 public void RegisterServerNetworkHandlers()
 {
     CustomMessagingManager.RegisterNamedMessageHandler("Input", HandleInputMessage);
 }
    internal void Tick(uint tick)
    {
        if (IsServer)
        {
            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt32(tick);
                    Dictionary <ulong, IEntityState> states = new Dictionary <ulong, IEntityState>(entities.Count);

                    foreach (Entity entity in entities.Values)
                    {
                        IEntityState state = entity.DoStateTick(tick);
                        if (state != null)
                        {
                            states.Add(entity.NetworkId, state);
                        }
                    }

                    writer.WriteInt32(states.Count);

                    foreach (var state in states)
                    {
                        writer.WriteUInt64(state.Key);
                        state.Value.Serialize(writer);
                    }

                    CustomMessagingManager.SendNamedMessage("NetworkState", null, stream);
                }
            }
        }

        else
        {
            foreach (Entity entity in entities.Values)
            {
                entity.DoStateTick(tick);
            }

            uint predictTicks = (uint)Math.Ceiling(RTT / FixedStep) + 2 * Deviation;

            for (uint i = 0; i < predictTicks; i++)
            {
                if (tick + i > LastPredictedTick)
                {
                    using (PooledBitStream stream = PooledBitStream.Get())
                    {
                        using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                        {
                            LastPredictedTick = tick + i;
                            var authEntities = entities.Values.Where(e => e.IsOwner);
                            int count        = authEntities.Count();
                            Dictionary <ulong, IEntityInput> inputs = new Dictionary <ulong, IEntityInput>(count);

                            foreach (Entity entity in authEntities)
                            {
                                IEntityInput input = entity.DoInputTick(tick + i);
                                if (input != null)
                                {
                                    inputs.Add(entity.NetworkId, input);
                                }
                            }

                            writer.WriteUInt32(tick + i);
                            writer.WriteInt32(inputs.Count);

                            foreach (var input in inputs)
                            {
                                writer.WriteUInt64(input.Key);
                                input.Value.Serialize(writer);
                            }

                            CustomMessagingManager.SendNamedMessage("NetworkInput", NetworkingManager.Singleton.ServerClientId, stream);
                        }
                    }
                }

                foreach (Entity entity in entities.Values.Where(e => e.extrapolate || e.IsOwner))
                {
                    entity.DoStateTick(tick + i);
                }
            }

            foreach (Entity entity in entities.Values)
            {
                entity.Present();
            }
        }
    }