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 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; } } }