| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 | using System;using System.Collections.Generic;using UnityEngine;namespace Mirror{    // a server's connection TO a LocalClient.    // sending messages on this connection causes the client's handler function to be invoked directly    public class LocalConnectionToClient : NetworkConnectionToClient    {        internal LocalConnectionToServer connectionToServer;        public LocalConnectionToClient() : base(LocalConnectionId) {}        public override string address => "localhost";        // Send stage two: serialized NetworkMessage as ArraySegment<byte>        internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)        {            // get a writer to copy the message into since the segment is only            // valid until returning.            // => pooled writer will be returned to pool when dequeuing.            // => WriteBytes instead of WriteArraySegment because the latter            //    includes a 4 bytes header. we just want to write raw.            //Debug.Log("Enqueue " + BitConverter.ToString(segment.Array, segment.Offset, segment.Count));            PooledNetworkWriter writer = NetworkWriterPool.GetWriter();            writer.WriteBytes(segment.Array, segment.Offset, segment.Count);            connectionToServer.queue.Enqueue(writer);        }        // true because local connections never timeout        internal override bool IsAlive(float timeout) => true;        internal void DisconnectInternal()        {            // set not ready and handle clientscene disconnect in any case            // (might be client or host mode here)            isReady = false;            RemoveFromObservingsObservers();        }        /// <summary>Disconnects this connection.</summary>        public override void Disconnect()        {            DisconnectInternal();            connectionToServer.DisconnectInternal();        }    }    // a localClient's connection TO a server.    // send messages on this connection causes the server's handler function to be invoked directly.    public class LocalConnectionToServer : NetworkConnectionToServer    {        internal LocalConnectionToClient connectionToClient;        // packet queue        internal readonly Queue<PooledNetworkWriter> queue = new Queue<PooledNetworkWriter>();        public override string address => "localhost";        // see caller for comments on why we need this        bool connectedEventPending;        bool disconnectedEventPending;        internal void QueueConnectedEvent() => connectedEventPending = true;        internal void QueueDisconnectedEvent() => disconnectedEventPending = true;        // Send stage two: serialized NetworkMessage as ArraySegment<byte>        internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)        {            if (segment.Count == 0)            {                Debug.LogError("LocalConnection.SendBytes cannot send zero bytes");                return;            }            // OnTransportData assumes batching.            // so let's make a batch with proper timestamp prefix.            Batcher batcher = GetBatchForChannelId(channelId);            batcher.AddMessage(segment);            // flush it to the server's OnTransportData immediately.            // local connection to server always invokes immediately.            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())            {                // make a batch with our local time (double precision)                if (batcher.MakeNextBatch(writer, NetworkTime.localTime))                {                    NetworkServer.OnTransportData(connectionId, writer.ToArraySegment(), channelId);                }                else Debug.LogError("Local connection failed to make batch. This should never happen.");            }        }        internal override void Update()        {            base.Update();            // should we still process a connected event?            if (connectedEventPending)            {                connectedEventPending = false;                NetworkClient.OnConnectedEvent?.Invoke();            }            // process internal messages so they are applied at the correct time            while (queue.Count > 0)            {                // call receive on queued writer's content, return to pool                PooledNetworkWriter writer = queue.Dequeue();                ArraySegment<byte> message = writer.ToArraySegment();                // OnTransportData assumes a proper batch with timestamp etc.                // let's make a proper batch and pass it to OnTransportData.                Batcher batcher = GetBatchForChannelId(Channels.Reliable);                batcher.AddMessage(message);                using (PooledNetworkWriter batchWriter = NetworkWriterPool.GetWriter())                {                    // make a batch with our local time (double precision)                    if (batcher.MakeNextBatch(batchWriter, NetworkTime.localTime))                    {                        NetworkClient.OnTransportData(batchWriter.ToArraySegment(), Channels.Reliable);                    }                }                NetworkWriterPool.Recycle(writer);            }            // should we still process a disconnected event?            if (disconnectedEventPending)            {                disconnectedEventPending = false;                NetworkClient.OnDisconnectedEvent?.Invoke();            }        }        /// <summary>Disconnects this connection.</summary>        internal void DisconnectInternal()        {            // set not ready and handle clientscene disconnect in any case            // (might be client or host mode here)            // TODO remove redundant state. have one source of truth for .ready!            isReady = false;            NetworkClient.ready = false;        }        /// <summary>Disconnects this connection.</summary>        public override void Disconnect()        {            connectionToClient.DisconnectInternal();            DisconnectInternal();            // simulate what a true remote connection would do:            // first, the server should remove it:            // TODO should probably be in connectionToClient.DisconnectInternal            //      because that's the NetworkServer's connection!            NetworkServer.RemoveLocalConnection();            // then call OnTransportDisconnected for proper disconnect handling,            // callbacks & cleanups.            // => otherwise OnClientDisconnected() is never called!            // => see NetworkClientTests.DisconnectCallsOnClientDisconnect_HostMode()            NetworkClient.OnTransportDisconnected();        }        // true because local connections never timeout        internal override bool IsAlive(float timeout) => true;    }}
 |