private void ProcessServerState() { ServerWorldState[] copyServerState = new ServerWorldState[serverStateBuffer.Count]; serverStateBuffer.CopyTo(copyServerState); serverStateBuffer.Clear(); foreach (ServerWorldState serverState in copyServerState) { if (GameManager.players.TryGetValue(serverState.clientId, out PlayerManager player)) { CharacterController characterController = player.GetComponent <CharacterController>(); if (player.id == Client.instance.myId) { ClientPlayerState clientStateAtTick = stateBuffer.Find(state => state.tick == serverState.tick); if (clientStateAtTick != null && AreEqualPositionsWithMargin(serverState.position, clientStateAtTick.position)) { Debug.Log($"Positions are equal at tick {serverState.tick} - {serverState.position}"); continue; // Skip when they're the same } else if (clientStateAtTick != null) { Debug.Log($"Positions at tick {serverState.tick} diverged with server at {serverState.position} and client at {clientStateAtTick.position}"); } // Set server position to player ApplyMovementDelta(GetDeltaPosition(serverState.position, player.transform)); transform.rotation = serverState.rotation; if (!controller.isGrounded) { // Then take over the yVelocity based on the server yVelocity = serverState.serverYVelocity; } // Remove all states from the buffer these have now been made obsolete by the server stateBuffer.RemoveAll(state => state.tick <= serverState.tick); // fixme clean remove? //PrintBuffer(serverState); for (int i = 0; i < stateBuffer.Count; i++) { // Apply buffered states that haven't been ack ClientPlayerState bufferedState = stateBuffer[i]; // Predict based on the new ack state Vector3 predictedDeltaPos = GetMovementVector(bufferedState.inputs); yVelocity = bufferedState.yVelocity; ApplyMovementDelta(predictedDeltaPos); } } else // This is always a new position packet (because it's form a different player), therefore apply it { player.SetPosition(serverState.position); } } } }
private async Task ListenGameState(Channel channel) { var mapState = new ClientMapData(); var playerState = new ClientPlayerState(); await mSyncContext.Execute(() => PlayerConnected?.Invoke(playerState), channel.ShutdownToken); try { while (true) { try { mGameService = new GameService.GameServiceClient(channel); using (var call = mGameService.ConnectAndListenState(new Empty())) using (var stateStream = call.ResponseStream) { while (await stateStream.MoveNext(channel.ShutdownToken)) { channel.ShutdownToken.ThrowIfCancellationRequested(); var state = stateStream.Current; if (state.Map != null) { mapState.State = new MapState(state.Map); } if (state.Player != null) { playerState.PlayerState = new PlayerState(state.Player); } channel.ShutdownToken.ThrowIfCancellationRequested(); if (!mMapLoaded) { mMapLoaded = true; await mSyncContext.Execute(() => { MapLoaded?.Invoke(mapState); BaseCreated?.Invoke(state.BasePos.ToUnity()); }, channel.ShutdownToken); var tChat = ListenChat(mGameService, channel); var t0 = mWorkerCreationStateListener.ListenCreations(mChannel); var t1 = mBuildingTemplateCreationStateListener.ListenCreations(mChannel); var t2 = mCentralBuildingCreationStateListener.ListenCreations(mChannel); var t3 = mMiningCampCreationListener.ListenCreations(mChannel); var t4 = mBarrakCreationListener.ListenCreations(mChannel); var t5 = mRangedWarriorCreationStateListener.ListenCreations(mChannel); var t6 = mMeeleeWarriorCreationStateListener.ListenCreations(mChannel); } } } break; } catch (RpcException e) { if (e.Status.StatusCode != StatusCode.Unavailable) { throw; } await Task.Delay(TimeSpan.FromSeconds(0.5)); } } } catch (Exception e) { Debug.LogError(e); DisconnectedFromServer?.Invoke(); throw; } }