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) NetworkWriterPooled message = messages.Dequeue(); ArraySegment <byte> segment = message.ToArraySegment(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // return the writer to pool NetworkWriterPool.Return(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); }
protected void SendTargetRPCInternal(NetworkConnection conn, Type invokeClass, string rpcName, NetworkWriter writer, int channelId) { // this was in Weaver before if (!NetworkServer.active) { Debug.LogError("TargetRPC Function " + rpcName + " called on client."); return; } // connection parameter is optional. assign if null. if (conn == null) { conn = connectionToClient; } // this was in Weaver before if (conn is ULocalConnectionToServer) { Debug.LogError("TargetRPC Function " + rpcName + " called on connection to server"); return; } // This cannot use NetworkServer.active, as that is not specific to this object. if (!isServer) { Debug.LogWarning("TargetRpc " + rpcName + " called on un-spawned object: " + name); return; } // construct the message RpcMessage message = new RpcMessage { netId = netId, componentIndex = ComponentIndex, functionHash = GetMethodHash(invokeClass, rpcName), // type+func so Inventory.RpcUse != Equipment.RpcUse payload = writer.ToArraySegment() // segment to avoid reader allocations }; conn.Send(message, channelId); NetworkWriterPool.Recycle(writer); }
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()); } } }
internal static void Update() { // local or remote connection? if (isLocalClient) { // process internal messages so they are applied at the correct time while (localClientPacketQueue.Count > 0) { NetworkWriter writer = localClientPacketQueue.Dequeue(); // TODO avoid serializing and deserializng the message // just pass it as is OnDataReceived(writer.ToArraySegment(), Channels.DefaultReliable); NetworkWriterPool.Recycle(writer); } } else { // only update things while connected if (active && connectState == ConnectState.Connected) { NetworkTime.UpdateClient(); } } }
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 NetworkWriter writer = NetworkWriterPool.GetWriter(); SerializeIntoWriter(writer, targetComponent.transform.localPosition, targetComponent.transform.localRotation, compressRotation, targetComponent.transform.localScale); // send to server CmdClientToServerSync(writer.ToArray()); NetworkWriterPool.Recycle(writer); } 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.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)); } } } } }
// 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 NetworkWriterPooled writer = batches.Dequeue(); NetworkWriterPool.Return(writer); // do we have another batch? if (batches.Count > 0) { // point reader to the next batch. // we'll return the reader below. NetworkWriterPooled 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 void Dispose() { NetworkWriterPool.Recycle(this); }
internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnection conn) { if (identity.serverOnly) { return; } if (LogFilter.Debug) { Debug.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.netId); // for easier debugging } // serialize all components with initialState = true // (can be null if has none) // convert to ArraySegment to avoid reader allocations // (need to handle null case too) ArraySegment <byte> segment = identity.OnSerializeAllSafely(true, out NetworkWriter serialized) ? serialized.ToArraySegment() : default; // 'identity' is a prefab that should be spawned if (identity.sceneId == 0) { SpawnPrefabMessage msg = new SpawnPrefabMessage { netId = identity.netId, owner = conn?.playerController == identity, assetId = identity.assetId, // use local values for VR support position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale, payload = segment }; // conn is != null when spawning it for a client if (conn != null) { conn.Send(msg); } // conn is == null when spawning it for the local player else { SendToReady(identity, msg); } } // 'identity' is a scene object that should be spawned again else { SpawnSceneObjectMessage msg = new SpawnSceneObjectMessage { netId = identity.netId, owner = conn?.playerController == identity, sceneId = identity.sceneId, // use local values for VR support position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale, payload = segment }; // conn is != null when spawning it for a client if (conn != null) { conn.Send(msg); } // conn is == null when spawning it for the local player else { SendToReady(identity, msg); } } if (serialized != null) { NetworkWriterPool.Recycle(serialized); } }
public void Dispose() => NetworkWriterPool.Return(this);
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); } }