Пример #1
0
        void CheckSendRate()
        {
            if (SendMessagesAllowed && syncInterval > 0 && sendTimer < Time.time)
            {
                sendTimer = Time.time + syncInterval;

                using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
                {
                    if (WriteParameters(writer))
                    {
                        SendAnimationParametersMessage(writer.ToArray());
                    }
                }
            }
        }
Пример #2
0
        /// <summary>
        /// This function invokes the registered handler function for a message.
        /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para>
        /// </summary>
        /// <typeparam name="T">The message type to unregister.</typeparam>
        /// <param name="msg">The message object to process.</param>
        /// <returns>Returns true if the handler was successfully invoked</returns>
        public bool InvokeHandler <T>(T msg, int channelId) where T : NetworkMessage
        {
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                // if it is a value type,  just use typeof(T) to avoid boxing
                // this works because value types cannot be derived
                // if it is a reference type (for example NetworkMessage),
                // ask the message for the real type
                int msgType = MessagePacker.GetId(default(T) != null ? typeof(T) : msg.GetType());

                MessagePacker.Pack(msg, writer);
                ArraySegment <byte> segment = writer.ToArraySegment();
                using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(segment))
                    return(InvokeHandler(msgType, networkReader, channelId));
            }
        }
Пример #3
0
        void CheckSendRate()
        {
            float now = Time.time;

            if (SendMessagesAllowed && syncInterval >= 0 && now > nextSendTime)
            {
                nextSendTime = now + syncInterval;

                using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) {
                    if (WriteParameters(writer))
                    {
                        SendAnimationParametersMessage(writer.ToArray());
                    }
                }
            }
        }
        void UpdateClient()
        {
            // send to server if we have local authority (and aren't the server)
            // -> only if connectionToServer has been initialized yet too
            // check only each 'syncInterval'
            if (!IsServer && IsClientWithAuthority && Time.time - lastClientSendTime >= syncInterval)
            {
                if (HasEitherMovedRotatedScaled())
                {
                    // serialize
                    // local position/rotation for VR support
                    using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
                    {
                        SerializeIntoWriter(writer, TargetComponent.transform.localPosition, TargetComponent.transform.localRotation, TargetComponent.transform.localScale);

                        // send to server
                        CmdClientToServerSync(writer.ToArray());
                    }
                }
                lastClientSendTime = Time.time;
            }

            // apply interpolation on client for all players
            // unless this client has authority over the object. could be
            // himself or another object that he was assigned authority over
            if (!IsClientWithAuthority && goal != null)
            {
                // teleport or interpolate
                if (NeedsTeleport())
                {
                    // local position/rotation for VR support
                    ApplyPositionRotationScale(goal.LocalPosition, goal.LocalRotation, goal.LocalScale);

                    // reset data points so we don't keep interpolating
                    start = null;
                    goal  = null;
                }
                else
                {
                    // local position/rotation for VR support
                    ApplyPositionRotationScale(InterpolatePosition(start, goal, TargetComponent.transform.localPosition),
                                               InterpolateRotation(start, goal, TargetComponent.transform.localRotation),
                                               InterpolateScale(start, goal, TargetComponent.transform.localScale));
                }
            }
        }
Пример #5
0
        internal void SendSpawnMessage(NetworkIdentity identity, NetworkConnection conn)
        {
            if (identity.serverOnly)
            {
                return;
            }

            // for easier debugging
            if (LogFilter.Debug)
            {
                Debug.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.NetId);
            }

            // one writer for owner, one for observers
            using (PooledNetworkWriter ownerWriter = NetworkWriterPool.GetWriter(), observersWriter = NetworkWriterPool.GetWriter())
            {
                // serialize all components with initialState = true
                // (can be null if has none)
                (int ownerWritten, int observersWritten) = identity.OnSerializeAllSafely(true, ownerWriter, observersWriter);

                // convert to ArraySegment to avoid reader allocations
                // (need to handle null case too)
                ArraySegment <byte> ownerSegment     = ownerWritten > 0 ? ownerWriter.ToArraySegment() : default;
                ArraySegment <byte> observersSegment = observersWritten > 0 ? observersWriter.ToArraySegment() : default;

                var msg = new SpawnMessage
                {
                    netId         = identity.NetId,
                    isLocalPlayer = conn.Identity == identity,
                    isOwner       = identity.ConnectionToClient == conn,
                    sceneId       = identity.sceneId,
                    assetId       = identity.AssetId,
                    // use local values for VR support
                    position = identity.transform.localPosition,
                    rotation = identity.transform.localRotation,
                    scale    = identity.transform.localScale
                };

                // use owner segment if 'conn' owns this identity, otherwise
                // use observers segment
                msg.payload = msg.isOwner ? ownerSegment : observersSegment;

                conn.Send(msg);
            }
        }
Пример #6
0
        public static byte[] PackMessage(int msgType, MessageBase msg)
        {
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                try
                {
                    // write message type
                    writer.WriteInt16((short)msgType);

                    // serialize message into writer
                    msg.Serialize(writer);

                    // return byte[]
                    return(writer.ToArray());
                }
                finally { }
            }
        }
Пример #7
0
        public static void Send <T>(IEnumerable <NetworkConnection> connections, T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase
        {
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                // pack message into byte[] once
                MessagePacker.Pack(msg, writer);
                var segment = writer.ToArraySegment();
                int count   = 0;

                foreach (NetworkConnection conn in connections)
                {
                    // send to all connections, but don't wait for them
                    _ = conn.SendAsync(segment);
                    count++;
                }

                NetworkDiagnostics.OnSend(msg, channelId, segment.Count, count);
            }
        }
        void FixedUpdate()
        {
            if (!SendMessagesAllowed)
            {
                return;
            }

            CheckSendRate();

            if (m_MotionStateUpdate)
            {
                //Update the motionHash
                using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
                {
                    WriteParameters(writer);
                    SendMotionMessage(m_Motion.currentState.serializationKey, writer.ToArray());
                }
            }
        }
Пример #9
0
        /// <summary>
        /// This function invokes the registered handler function for a message.
        /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para>
        /// </summary>
        /// <typeparam name="T">The message type to unregister.</typeparam>
        /// <param name="msg">The message object to process.</param>
        /// <returns></returns>
        public bool InvokeHandler <T>(T msg, int channelId) where T : IMessageBase
        {
            // get writer from pool
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                // if it is a value type,  just use typeof(T) to avoid boxing
                // this works because value types cannot be derived
                // if it is a reference type (for example IMessageBase),
                // ask the message for the real type
                int msgType = MessagePacker.GetId(typeof(T).IsValueType ? typeof(T) : msg.GetType());

                Debug.Log("[NetworkConnection].InvokeHandler -- msg: " + msg.ToString());

                MessagePacker.Pack(msg, writer);
                ArraySegment <byte> segment = writer.ToArraySegment();
                using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(segment))
                    return(InvokeHandler(msgType, networkReader, channelId));
            }
        }
        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();
            }
        }
Пример #11
0
        internal void SendSpawnMessage(NetworkIdentity identity, INetworkConnection conn)
        {
            if (identity.serverOnly)
            {
                return;
            }

            // for easier debugging
            if (logger.LogEnabled())
            {
                logger.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.NetId);
            }

            // one writer for owner, one for observers
            using (PooledNetworkWriter ownerWriter = NetworkWriterPool.GetWriter(), observersWriter = NetworkWriterPool.GetWriter())
            {
                bool isOwner = identity.ConnectionToClient == conn;

                ArraySegment <byte> payload = CreateSpawnMessagePayload(isOwner, identity, ownerWriter, observersWriter);

                var msg = new SpawnMessage
                {
                    netId         = identity.NetId,
                    isLocalPlayer = conn.Identity == identity,
                    isOwner       = isOwner,
                    sceneId       = identity.sceneId,
                    assetId       = identity.AssetId,
                    // use local values for VR support
                    position = identity.transform.localPosition,
                    rotation = identity.transform.localRotation,
                    scale    = identity.transform.localScale,

                    payload = payload,
                };


                conn.Send(msg);
            }
        }
        // batch as many messages as possible into writer
        // returns true if any batch was made.
        public bool MakeNextBatch(NetworkWriter writer, double timeStamp)
        {
            // if we have no messages then there's nothing to do
            if (messages.Count == 0)
            {
                return(false);
            }

            // make sure the writer is fresh to avoid uncertain situations
            if (writer.Position != 0)
            {
                throw new ArgumentException($"MakeNextBatch needs a fresh writer!");
            }

            // write timestamp first
            // -> double precision for accuracy over long periods of time
            writer.WriteDouble(timeStamp);

            // do start no matter what
            do
            {
                // add next message no matter what. even if > threshold.
                // (we do allow > threshold sized messages as single batch)
                PooledNetworkWriter message = messages.Dequeue();
                ArraySegment <byte> segment = message.ToArraySegment();
                writer.WriteBytes(segment.Array, segment.Offset, segment.Count);

                // return the writer to pool
                NetworkWriterPool.Recycle(message);
            }
            // keep going as long as we have more messages,
            // AND the next one would fit into threshold.
            while (messages.Count > 0 &&
                   writer.Position + messages.Peek().Position <= threshold);

            // we had messages, so a batch was made
            return(true);
        }
Пример #13
0
        void FixedUpdate()
        {
            if (!SendMessagesAllowed)
            {
                return;
            }

            CheckSendRate();

            for (int i = 0; i < Animator.layerCount; i++)
            {
                if (!CheckAnimStateChanged(out int stateHash, out float normalizedTime, i))
                {
                    continue;
                }

                using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
                {
                    WriteParameters(writer);
                    SendAnimationMessage(stateHash, normalizedTime, i, writer.ToArray());
                }
            }
        }
Пример #14
0
 // helper function to start reading a batch.
 void StartReadingBatch(PooledNetworkWriter batch)
 {
     // point reader to it
     reader.SetBuffer(batch.ToArraySegment());
 }
Пример #15
0
        // get next message, unpacked from batch (if any)
        // timestamp is the REMOTE time when the batch was created remotely.
        public bool GetNextMessage(out NetworkReader message, out double remoteTimeStamp)
        {
            // getting messages would be easy via
            //   <<size, message, size, message, ...>>
            // but to save A LOT of bandwidth, we use
            //   <<message, message, ...>
            // in other words, we don't know where the current message ends
            //
            // BUT: it doesn't matter!
            // -> we simply return the reader
            //    * if we have one yet
            //    * and if there's more to read
            // -> the caller can then read one message from it
            // -> when the end is reached, we retire the batch!
            //
            // for example:
            //   while (GetNextMessage(out message))
            //       ProcessMessage(message);
            //
            message = null;

            // do nothing if we don't have any batches.
            // otherwise the below queue.Dequeue() would throw an
            // InvalidOperationException if operating on empty queue.
            if (batches.Count == 0)
            {
                remoteTimeStamp = 0;
                return(false);
            }

            // was our reader pointed to anything yet?
            if (reader.Length == 0)
            {
                remoteTimeStamp = 0;
                return(false);
            }

            // no more data to read?
            if (reader.Remaining == 0)
            {
                // retire the batch
                PooledNetworkWriter writer = batches.Dequeue();
                NetworkWriterPool.Recycle(writer);

                // do we have another batch?
                if (batches.Count > 0)
                {
                    // point reader to the next batch.
                    // we'll return the reader below.
                    PooledNetworkWriter next = batches.Peek();
                    StartReadingBatch(next);
                }
                // otherwise there's nothing more to read
                else
                {
                    remoteTimeStamp = 0;
                    return(false);
                }
            }

            // use the current batch's remote timestamp
            // AFTER potentially moving to the next batch ABOVE!
            remoteTimeStamp = readerRemoteTimeStamp;

            // if we got here, then we have more data to read.
            message = reader;
            return(true);
        }
 public static void Recycle(PooledNetworkWriter writer)
 {
     pool.Push(writer);
 }
Пример #17
0
        void Update()
        {
            // if server then always sync to others.
            if (isServer)
            {
                // just use OnSerialize via SetDirtyBit only sync when position
                // changed. set dirty bits 0 or 1
                SetDirtyBit(HasEitherMovedRotatedScaled() ? 1UL : 0UL);
            }

            // no 'else if' since host mode would be both
            if (isClient)
            {
                // send to server if we have local authority (and aren't the server)
                // -> only if connectionToServer has been initialized yet too
                if (!isServer && IsClientWithAuthority)
                {
                    // check only each 'syncInterval'
                    if (Time.time - lastClientSendTime >= syncInterval)
                    {
                        if (HasEitherMovedRotatedScaled())
                        {
                            // serialize
                            // local position/rotation for VR support
                            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
                            {
                                if (useGlobalPosition)
                                {
                                    SerializeIntoWriter(writer, targetComponent.transform.position, targetComponent.transform.rotation, targetComponent.transform.localScale);
                                }
                                else
                                {
                                    SerializeIntoWriter(writer, targetComponent.transform.localPosition, targetComponent.transform.localRotation, targetComponent.transform.localScale);
                                }

                                // send to server
                                CmdClientToServerSync(writer.ToArray());
                            }
                        }
                        lastClientSendTime = Time.time;
                    }
                }

                // apply interpolation on client for all players
                // unless this client has authority over the object. could be
                // himself or another object that he was assigned authority over
                if (!IsClientWithAuthority)
                {
                    // received one yet? (initialized?)
                    if (goal != null)
                    {
                        // teleport or interpolate
                        if (NeedsTeleport())
                        {
                            // local position/rotation for VR support
                            ApplyPositionRotationScale(goal.thePosition, goal.theRotation, goal.theScale);

                            // reset data points so we don't keep interpolating
                            start = null;
                            goal  = null;
                        }
                        else
                        {
                            // local position/rotation for VR support
                            if (useGlobalPosition)
                            {
                                ApplyPositionRotationScale(InterpolatePosition(start, goal, targetComponent.transform.position),
                                                           InterpolateRotation(start, goal, targetComponent.transform.rotation),
                                                           InterpolateScale(start, goal, targetComponent.transform.localScale));
                            }
                            else
                            {
                                ApplyPositionRotationScale(InterpolatePosition(start, goal, targetComponent.transform.localPosition),
                                                           InterpolateRotation(start, goal, targetComponent.transform.localRotation),
                                                           InterpolateScale(start, goal, targetComponent.transform.localScale));
                            }
                        }
                    }
                }
            }
        }
Пример #18
0
 /// <summary>Return a writer to the pool.</summary>
 public static void Recycle(PooledNetworkWriter writer)
 {
     Pool.Return(writer);
 }
Пример #19
0
 static ArraySegment <byte> CreateSpawnMessagePayload(bool isOwner, NetworkIdentity identity, PooledNetworkWriter ownerWriter, PooledNetworkWriter observersWriter)
 {
     // Only call OnSerializeAllSafely if there are NetworkBehaviours
     if (identity.NetworkBehaviours.Length == 0)
     {
         return(default);