private void NetworkVariableUpdate(ulong clientId) { if (!CouldHaveDirtyNetworkVariables()) { return; } for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) { using (var buffer = PooledNetworkBuffer.Get()) { using (var writer = PooledNetworkWriter.Get(buffer)) { writer.WriteUInt64Packed(NetworkObjectId); writer.WriteUInt16Packed(NetworkObject.GetNetworkBehaviourOrderIndex(this)); // Write the current tick frame // todo: this is currently done per channel, per tick. The snapshot system might improve on this writer.WriteUInt16Packed(CurrentTick); bool writtenAny = false; for (int k = 0; k < NetworkVariableFields.Count; k++) { if (!m_ChannelMappedNetworkVariableIndexes[j].Contains(k)) { // This var does not belong to the currently iterating channel group. if (NetworkManager.Singleton.NetworkConfig.EnsureNetworkVariableLengthSafety) { writer.WriteUInt16Packed(0); } else { writer.WriteBool(false); } continue; } bool isDirty = NetworkVariableFields[k].IsDirty(); // cache this here. You never know what operations users will do in the dirty methods // if I'm dirty AND a client, write (server always has all permissions) // if I'm dirty AND the server AND the client can read me, send. bool shouldWrite = isDirty && (!IsServer || NetworkVariableFields[k].CanClientRead(clientId)); if (NetworkManager.Singleton.NetworkConfig.EnsureNetworkVariableLengthSafety) { if (!shouldWrite) { writer.WriteUInt16Packed(0); } } else { writer.WriteBool(shouldWrite); } if (shouldWrite) { writtenAny = true; // write the network tick at which this NetworkVariable was modified remotely // this will allow lag-compensation writer.WriteUInt16Packed(NetworkVariableFields[k].RemoteTick); if (NetworkManager.Singleton.NetworkConfig.EnsureNetworkVariableLengthSafety) { using (var varBuffer = PooledNetworkBuffer.Get()) { NetworkVariableFields[k].WriteDelta(varBuffer); varBuffer.PadBuffer(); writer.WriteUInt16Packed((ushort)varBuffer.Length); buffer.CopyFrom(varBuffer); } } else { NetworkVariableFields[k].WriteDelta(buffer); } if (!m_NetworkVariableIndexesToResetSet.Contains(k)) { m_NetworkVariableIndexesToResetSet.Add(k); m_NetworkVariableIndexesToReset.Add(k); } } } if (writtenAny) { InternalMessageSender.Send(clientId, NetworkConstants.NETWORK_VARIABLE_DELTA, m_ChannelsForNetworkVariableGroups[j], buffer); } } } } }
/// <summary> /// Used to serialize a NetworkObjects during scene synchronization that occurs /// upon a client being approved or a scene transition. /// </summary> /// <param name="writer">writer into the outbound stream</param> /// <param name="targetClientId">clientid we are targeting</param> internal void SerializeSceneObject(NetworkWriter writer, ulong targetClientId) { writer.WriteBool(IsPlayerObject); writer.WriteUInt64Packed(NetworkObjectId); writer.WriteUInt64Packed(OwnerClientId); NetworkObject parentNetworkObject = null; if (!AlwaysReplicateAsRoot && transform.parent != null) { parentNetworkObject = transform.parent.GetComponent <NetworkObject>(); } if (parentNetworkObject == null) { // We don't have a parent writer.WriteBool(false); } else { // We do have a parent writer.WriteBool(true); // Write the parent's NetworkObjectId to be used for linking back to the child writer.WriteUInt64Packed(parentNetworkObject.NetworkObjectId); } // Write if we are a scene object or not writer.WriteBool(IsSceneObject ?? true); // Write the hash for this NetworkObject writer.WriteUInt32Packed(GlobalObjectIdHash); if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId)) { // Set the position and rotation data marker to true (i.e. flag to know, when reading from the stream, that position and rotation data follows). writer.WriteBool(true); // Write position writer.WriteSinglePacked(transform.position.x); writer.WriteSinglePacked(transform.position.y); writer.WriteSinglePacked(transform.position.z); // Write rotation writer.WriteSinglePacked(transform.rotation.eulerAngles.x); writer.WriteSinglePacked(transform.rotation.eulerAngles.y); writer.WriteSinglePacked(transform.rotation.eulerAngles.z); } else { // Set the position and rotation data marker to false (i.e. flag to know, when reading from the stream, that position and rotation data *was not included*) writer.WriteBool(false); } // Write whether we are including network variable data writer.WriteBool(NetworkManager.NetworkConfig.EnableNetworkVariable); //If we are including NetworkVariable data if (NetworkManager.NetworkConfig.EnableNetworkVariable) { var buffer = writer.GetStream() as NetworkBuffer; // Write placeholder size, NOT as a packed value, initially as zero (i.e. we do not know how much NetworkVariable data will be written yet) writer.WriteUInt32(0); // Mark our current position before we potentially write any NetworkVariable data var positionBeforeNetworkVariableData = buffer.Position; // Write network variable data WriteNetworkVariableData(buffer, targetClientId); // If our current buffer position is greater than our positionBeforeNetworkVariableData then we wrote NetworkVariable data // Part 1: This will include the total NetworkVariable data size, if there was NetworkVariable data written, to the stream // in order to be able to skip past this entry on the deserialization side in the event this NetworkObject fails to be // constructed (See Part 2 below in the DeserializeSceneObject method) if (buffer.Position > positionBeforeNetworkVariableData) { // Store our current stream buffer position var endOfNetworkVariableData = buffer.Position; // Calculate the total NetworkVariable data size written var networkVariableDataSize = endOfNetworkVariableData - positionBeforeNetworkVariableData; // Move the stream position back to just before we wrote our size (we include the unpacked UInt32 data size placeholder) buffer.Position = positionBeforeNetworkVariableData - sizeof(uint); // Now write the actual data size written into our unpacked UInt32 placeholder position writer.WriteUInt32((uint)(networkVariableDataSize)); // Finally, revert the buffer position back to the end of the network variable data written buffer.Position = endOfNetworkVariableData; } } }
/// <summary> /// Returns a the NetworkBehaviour with a given BehaviourId for the current NetworkObject /// </summary> /// <param name="behaviourId">The behaviourId to return</param> /// <returns>Returns NetworkBehaviour with given behaviourId</returns> protected NetworkBehaviour GetNetworkBehaviour(ushort behaviourId) { return(NetworkObject.GetNetworkBehaviourAtOrderIndex(behaviourId)); }