private static void WriteNetObject(NetObject netObject, ref DataStreamWriter streamWriter) { streamWriter.WriteInt(netObject.ID); DataStreamWriter sizeWriter = streamWriter; streamWriter.WriteInt(0); int length = streamWriter.Length; netObject.Serialize(ref streamWriter, false); sizeWriter.WriteInt(streamWriter.Length - length); }
private static void WriteNetObject(NetObject netObject, ref DataStreamWriter streamWriter) { if (!netObject.IsMine) { return; } streamWriter.WriteInt(netObject.ID); DataStreamWriter sizeWriter = streamWriter; streamWriter.WriteInt(0); int length = streamWriter.Length; netObject.Serialize(ref streamWriter, false, b => b.HasAuthority); sizeWriter.WriteInt(streamWriter.Length - length); }
public NetObject SpawnNetObject(NetObject netObjectPrefab, Vector3 position, Quaternion rotation, string sceneName = null, int actorNumber = ServerActorNumber) { if (State != ServerState.Started && State != ServerState.Debug) { throw new InvalidOperationException("Cannot spawn NetObject: Server not running"); } NetObject netObject = NetObjectManager.Instance.SpawnOnServer(netObjectPrefab, position, rotation, sceneName, actorNumber); if (State == ServerState.Started) { SendSpawnMessage(new[] { netObject }, _connections); } return(netObject); }
public void UnspawnNetObject(NetObject netObject) { UnspawnNetObjects(new[] { netObject }); }
public void SendNetObjectsUpdate() { if (State == ServerState.Debug) { return; } if (State != ServerState.Started) { throw new InvalidOperationException("Cannot set NetObject update: Server not running"); } Profiler.BeginSample("NetObject Update"); NetObject[] netObjects = NetObjectManager.Instance.NetObjects; const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var objectWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var objectIndex = 0; // compose new message if objects left to send or serialize while (objectIndex < netObjects.Length || objectWriter.Length > 0) { // header streamWriter.Clear(); streamWriter.WriteInt(Commands.UpdateNetObjects); DataStreamWriter objectCountWriter = streamWriter; streamWriter.WriteInt(0); // add items as long as they fit var objectsInMessage = 0; while (streamWriter.Length + objectWriter.Length <= MaxBytesPerMessage) { if (objectWriter.Length > 0) { streamWriter.WriteBytes(objectWriter.AsNativeArray()); objectWriter.Clear(); objectsInMessage++; } // next object. Write if dirty if (objectIndex < netObjects.Length) { NetObject netObject = netObjects[objectIndex++]; if (netObject.IsDirty) { WriteNetObject(netObject, ref objectWriter); } } else { break; } } objectCountWriter.WriteInt(objectsInMessage); // message complete. Send if payload exists if (objectsInMessage == 0) { break; } for (var connectionIndex = 0; connectionIndex < _connections.Length; connectionIndex++) { DataStreamWriter writer = _serverDriver.BeginSend(_reliablePipeline, _connections[connectionIndex]); writer.WriteBytes(streamWriter.AsNativeArray()); _serverDriver.EndSend(writer); } } Profiler.EndSample(); }
private void SendSpawnMessage(NetObject[] netObjects, NativeList <NetworkConnection> connections) { if (connections.Length == 0) { return; } AssertActive(); const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var objectWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var objectIndex = 0; // compose new message if objects left to send or copy to message stream while (objectIndex < netObjects.Length || objectWriter.Length > 0) { streamWriter.Clear(); // write header streamWriter.WriteInt(Commands.SpawnNetObjects); DataStreamWriter objectCountWriter = streamWriter; streamWriter.WriteInt(0); // copy data over to message stream and write to object stream var objectsInMessage = 0; while (streamWriter.Length + objectWriter.Length <= MaxBytesPerMessage) { if (objectWriter.Length > 0) { streamWriter.WriteBytes(objectWriter.AsNativeArray()); objectWriter.Clear(); objectsInMessage++; } if (objectIndex < netObjects.Length) { NetObject netObject = netObjects[objectIndex++]; objectWriter.WriteInt(netObject.ID); objectWriter.WriteUShort(netObject.PrefabIndex); objectWriter.WriteInt(netObject.OwnerActorNumber); objectWriter.WriteVector3(netObject.transform.position); objectWriter.WriteQuaternion(netObject.transform.rotation); objectWriter.WriteInt(netObject.gameObject.scene.buildIndex); DataStreamWriter objectSizeWriter = objectWriter; objectWriter.WriteInt(0); int length = objectWriter.Length; netObject.Serialize(ref objectWriter, true); objectSizeWriter.WriteInt(objectWriter.Length - length); } else { break; } } objectCountWriter.WriteInt(objectsInMessage); // message complete. Send if payload present if (objectsInMessage == 0) { return; } for (var connectionIndex = 0; connectionIndex < connections.Length; connectionIndex++) { DataStreamWriter writer = _serverDriver.BeginSend(_reliablePipeline, connections[connectionIndex]); writer.WriteBytes(streamWriter.AsNativeArray()); _serverDriver.EndSend(writer); } } }
private void ReadDataEvent(NetworkConnection connection, DataStreamReader streamReader) { try { int senderActorNumber = connection.InternalId; int command = streamReader.ReadInt(); switch (command) { case Commands.AcknowledgeActorNumber: { if (_acceptAllPlayers) { AcceptPlayer(senderActorNumber); } break; } case Commands.PlayerData: { string playerData = streamReader.ReadManagedString(); PlayerDataReceived?.Invoke(senderActorNumber, playerData); break; } case Commands.Ping: _lastPingTimes[connection] = Time; float sendLocalTime = streamReader.ReadFloat(); DataStreamWriter writer = _serverDriver.BeginSend(_unreliablePipeline, connection); writer.WriteInt(Commands.Ping); writer.WriteFloat(sendLocalTime); writer.WriteFloat(Time); writer.WriteFloat(UnityEngine.Time.deltaTime); _serverDriver.EndSend(writer); break; case Commands.RequestSpawnMessage: { var connections = new NativeList <NetworkConnection>(1, Allocator.Temp) { connection }; SendSpawnMessage(NetObjectManager.Instance.NetObjects, connections); connections.Dispose(); break; } case Commands.UpdateNetObjects: { int objectsInMessage = streamReader.ReadInt(); for (var obj = 0; obj < objectsInMessage; obj++) { int netObjID = streamReader.ReadInt(); int size = streamReader.ReadInt(); NetObject netObject = NetObjectManager.Instance.Exists(netObjID) ? NetObjectManager.Instance.Get(netObjID) : null; // ignore illegal updates and those from local host client if (netObject == null || netObject.OwnerActorNumber != senderActorNumber || // cheater? Client.IsHost && Client.Instance.ActorNumber == senderActorNumber) { streamReader.DiscardBytes(size); } else { int bytesRead = streamReader.GetBytesRead(); try { netObject.Deserialize(ref streamReader, b => b.ClientAuthoritative); } catch (Exception e) { Debug.LogException(e); int remainingBytes = size + bytesRead - streamReader.GetBytesRead(); streamReader.DiscardBytes(remainingBytes); } } } break; } case Commands.GameAction: { int gameActionID = streamReader.ReadInt(); int actorNumber = streamReader.ReadInt(); float triggerTime = streamReader.ReadFloat(); try { GameAction gameAction = GameActionManager.Instance.Get(gameActionID); GameAction.IParameters parameters = gameAction.DeserializeParameters(ref streamReader); gameAction.ReceiveOnServer(parameters, actorNumber, senderActorNumber, triggerTime); break; } catch (Exception e) { Debug.LogException(e); break; } } case Commands.NetAssetRPC: { float sentServerTime = streamReader.ReadFloat(); int netAssetID = streamReader.ReadInt(); NetAsset netAsset = NetAssetManager.Instance.Get(netAssetID); NetAsset.RPC rpc = netAsset.DeserializeRPC(ref streamReader); var messageInfo = new MessageInfo { SentServerTime = sentServerTime, SenderActorNumber = senderActorNumber }; rpc.Invoke(messageInfo); break; } case Commands.NetObjectRPC: { float sentServerTime = streamReader.ReadFloat(); int netObjectID = streamReader.ReadInt(); if (!NetObjectManager.Instance.Exists(netObjectID)) { Debug.LogWarning("Ignoring received RPC, because NetObject was not found."); break; } NetObject netObject = NetObjectManager.Instance.Get(netObjectID); ushort netBehaviourID = streamReader.ReadUShort(); NetBehaviour netBehaviour = netObject.Get(netBehaviourID); NetObjectManager.RPC rpc = NetObjectManager.Instance.DeserializeRPC(ref streamReader, netBehaviour); var messageInfo = new MessageInfo { SentServerTime = sentServerTime, SenderActorNumber = senderActorNumber }; rpc.Invoke(messageInfo); break; } default: Debug.LogException(new NetException($"Unknown command {command}")); break; } } catch (Exception e) { Debug.LogException(new NetException("Failed to handle data event", e)); } }
public void SendBatchedNetObjectsUpdate() { if (State == ClientState.Debug) { return; } if (State != ClientState.Connected) { Debug.LogWarning($"Cannot send messages in client state {State}"); return; } if (IsHost) { return; } NetObject[] netObjects = NetObjectManager.Instance.NetObjects; const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var objectWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var objectIndex = 0; // compose new message if objects left to send or serialize while (objectIndex < netObjects.Length || objectWriter.Length > 0) { // header streamWriter.Clear(); streamWriter.WriteInt(Commands.UpdateNetObjects); DataStreamWriter objectCountWriter = streamWriter; streamWriter.WriteInt(0); // add items as long as they fit var objectsInMessage = 0; while (streamWriter.Length + objectWriter.Length <= MaxBytesPerMessage) { if (objectWriter.Length > 0) { streamWriter.WriteBytes(objectWriter.AsNativeArray()); objectWriter.Clear(); objectsInMessage++; } // next object. Write if dirty and controlled by this client if (objectIndex < netObjects.Length) { NetObject netObject = netObjects[objectIndex++]; if (netObject.IsDirty && netObject.IsMine) { WriteNetObject(netObject, ref objectWriter); } } else { break; } } objectCountWriter.WriteInt(objectsInMessage); // message complete. Send if payload exists if (objectsInMessage > 0) { DataStreamWriter writer = _clientDriver.BeginSend(_reliablePipeline, _clientToServerConnection); writer.WriteBytes(streamWriter.AsNativeArray()); _clientDriver.EndSend(writer); DataSent?.Invoke(writer.Length); } } }
public void Tick() { if (!_clientDriver.IsCreated) { return; } _clientDriver.ScheduleUpdate().Complete(); if (_timeout > 0 && IsConnected && Time.time - LastPongTime > _timeout) { Debug.LogWarning("Disconnected due to timeout"); Disconnect(); return; } // listen for events NetworkEvent.Type eventType; while ((eventType = _clientToServerConnection.PopEvent(_clientDriver, out DataStreamReader streamReader)) != NetworkEvent.Type.Empty) { if (eventType == NetworkEvent.Type.Connect) { Debug.Log("Connected!"); State = ClientState.Connected; Connected?.Invoke(); } else if (eventType == NetworkEvent.Type.Data) { DataReceived?.Invoke(streamReader.Length); int command = streamReader.ReadInt(); switch (command) { case Commands.AssignActorNumber: { ActorNumber = streamReader.ReadInt(); Debug.Log($"Got assigned actor number {ActorNumber}"); DataStreamWriter writer = _clientDriver.BeginSend(_reliablePipeline, _clientToServerConnection); writer.WriteInt(Commands.AcknowledgeActorNumber); _clientDriver.EndSend(writer); DataSent?.Invoke(writer.Length); break; } case Commands.AcceptPlayer: { DataStreamWriter writer = _clientDriver.BeginSend(_reliablePipeline, _clientToServerConnection); writer.WriteInt(Commands.RequestSpawnMessage); writer.WriteInt(SceneManager.sceneCount); for (var i = 0; i < SceneManager.sceneCount; i++) { writer.WriteInt(SceneManager.GetSceneAt(i).buildIndex); } SceneManager.sceneLoaded += OnSceneLoaded; _clientDriver.EndSend(writer); break; } case Commands.Ping: { LastPongTime = Time.time; float sendLocalTime = streamReader.ReadFloat(); float serverTime = streamReader.ReadFloat(); float serverDeltaTime = streamReader.ReadFloat(); RoundTripTime = Time.time - sendLocalTime; Latency = IsHost ? 0 : Mathf.Max(0, (RoundTripTime - serverDeltaTime / 2 - Time.deltaTime / 2) / 2); // estimated server time NOW is received serverTime + latency for one trip + average frame wait on client side float serverTimeOffset = serverTime - Time.time + Latency + Time.deltaTime / 2; _averageServerTimeOffset = Mathf.Abs(_averageServerTimeOffset - serverTime) > 0.5f ? serverTimeOffset : 0.9f * _averageServerTimeOffset + 0.1f * serverTimeOffset; PingReceived?.Invoke(RoundTripTime, Latency); break; } case Commands.SpawnNetObjects: { if (IsHost) { break; } int count = streamReader.ReadInt(); for (var i = 0; i < count; i++) { int netObjID = streamReader.ReadInt(); ushort prefabIndex = streamReader.ReadUShort(); int ownerActorNumber = streamReader.ReadInt(); Vector3 position = streamReader.ReadVector3(); Quaternion rotation = streamReader.ReadQuaternion(); int sceneBuildIndex = streamReader.ReadInt(); int size = streamReader.ReadInt(); int bytesRead = streamReader.GetBytesRead(); Scene scene = SceneManager.GetSceneByBuildIndex(sceneBuildIndex); var deserialized = false; if (scene != null && scene.isLoaded) { NetObject netObject = NetObjectManager.Instance.SpawnOnClient(netObjID, prefabIndex, ownerActorNumber, position, rotation, scene); if (netObject != null) { netObject.Deserialize(ref streamReader, behaviour => true); deserialized = true; } } if (!deserialized) { streamReader.DiscardBytes(size); } if (streamReader.GetBytesRead() - bytesRead != size) { Debug.LogWarning("Did not deserialize properly!"); } } break; } case Commands.UpdateNetAssets: { if (IsHost) { break; } int assetsInMessage = streamReader.ReadInt(); for (var i = 0; i < assetsInMessage; i++) { int assetNetID = streamReader.ReadInt(); int size = streamReader.ReadInt(); int bytesRead = streamReader.GetBytesRead(); try { NetAsset netAsset = NetAssetManager.Instance.Get(assetNetID); netAsset.Deserialize(ref streamReader); } catch (Exception e) { Debug.LogException(new NetException($"Failed to update net asset {assetNetID}", e)); streamReader.DiscardBytes(size + bytesRead - streamReader.GetBytesRead()); } } break; } case Commands.UpdateNetObjects: { if (IsHost) { break; } int objectsInMessage = streamReader.ReadInt(); for (var i = 0; i < objectsInMessage; i++) { int netObjID = streamReader.ReadInt(); int size = streamReader.ReadInt(); if (NetObjectManager.Instance.Exists(netObjID)) { NetObject netObject = NetObjectManager.Instance.Get(netObjID); int bytesRead = streamReader.GetBytesRead(); try { netObject.Deserialize(ref streamReader, behaviour => !behaviour.HasAuthority); } catch (Exception e) { Debug.LogException( new NetException($"Failed to update net object {netObjID}", e)); streamReader.DiscardBytes(size + bytesRead - streamReader.GetBytesRead()); } } else { streamReader.DiscardBytes(size); } } break; } case Commands.UnspawnNetObjects: { if (IsHost) { break; } int count = streamReader.ReadInt(); for (var i = 0; i < count; i++) { int netObjID = streamReader.ReadInt(); NetObjectManager.Instance.Unspawn(netObjID); } break; } case Commands.GameAction: { if (IsHost) { break; } int gameActionID = streamReader.ReadInt(); int actorNumber = streamReader.ReadInt(); float triggerTime = streamReader.ReadFloat(); bool valid = streamReader.ReadBool(); try { GameAction gameAction = GameActionManager.Instance.Get(gameActionID); GameAction.IParameters parameters = gameAction.DeserializeParameters(ref streamReader); gameAction.ReceiveOnClient(parameters, valid, actorNumber, triggerTime); break; } catch (Exception e) { Debug.LogException(e); break; } } case Commands.NetAssetRPC: { if (IsHost) { break; } float sentServerTime = streamReader.ReadFloat(); int netAssetID = streamReader.ReadInt(); NetAsset netAsset = NetAssetManager.Instance.Get(netAssetID); NetAsset.RPC rpc = netAsset.DeserializeRPC(ref streamReader); var messageInfo = new MessageInfo { SentServerTime = sentServerTime, SenderActorNumber = Server.ServerActorNumber }; rpc.Invoke(messageInfo); break; } case Commands.NetObjectRPC: { if (IsHost) { break; } float sentServerTime = streamReader.ReadFloat(); int netObjectID = streamReader.ReadInt(); if (!NetObjectManager.Instance.Exists(netObjectID)) { Debug.LogWarning("Ignoring received RPC, because NetObject was not found."); break; } NetObject netObject = NetObjectManager.Instance.Get(netObjectID); ushort netBehaviourID = streamReader.ReadUShort(); NetBehaviour netBehaviour = netObject.Get(netBehaviourID); NetObjectManager.RPC rpc = NetObjectManager.Instance.DeserializeRPC(ref streamReader, netBehaviour); var messageInfo = new MessageInfo { SentServerTime = sentServerTime, SenderActorNumber = Server.ServerActorNumber }; rpc.Invoke(messageInfo); break; } default: Debug.LogError($"Unknown event type {eventType}"); break; } } else if (eventType == NetworkEvent.Type.Disconnect) { Debug.Log("Disconnected!"); SceneManager.sceneLoaded -= OnSceneLoaded; Disconnected?.Invoke(); State = ClientState.Disconnected; _clientToServerConnection = default; } } }