private void ApplyCorrectionsWithServerState(ServerStateMessage serverStateMessage, uint bufferSlot) { // rewind & replay StateProcessorComponent.ExecuteState(serverStateMessage.serverState); _correctionsMadeOnClient++; // capture the current predicted pos for smoothing Vector3 prevPosition = StateProcessorComponent.GetCurrentState().position + _clientPositionError; Quaternion prevRotation = StateProcessorComponent.GetCurrentState().rotation *_clientRotationError; uint rewindTickNumber = serverStateMessage.tickNumber; while (rewindTickNumber < _currentTickNumber) { bufferSlot = rewindTickNumber % _clientBufferSize; ClientStoreCurrentStateAndStep(ref _clientStateBuffer[bufferSlot], _clientInputBuffer[bufferSlot]); rewindTickNumber++; } //if the position error is greater than 2 meters, just snap if ((prevPosition - StateProcessorComponent.GetCurrentState().position).sqrMagnitude >= 4.0f) { _clientPositionError = Vector3.zero; _clientRotationError = Quaternion.identity; } else { _clientPositionError = prevPosition - StateProcessorComponent.GetCurrentState().position; _clientRotationError = Quaternion.Inverse(StateProcessorComponent.GetCurrentState().rotation) * prevRotation; } }
public void OnServerStateUpdated() { _serverTickAccumulator++; if (_serverTickAccumulator >= _serverSnapshotRate) { _serverTickAccumulator = 0; ServerStateMessage serverStateMsg = new ServerStateMessage(); serverStateMsg.packetId = _serverPacketID; serverStateMsg.deliveryTime = _networkClock.CurrentTime; serverStateMsg.tickNumber = _currentTickNumber; serverStateMsg.serverState = StateProcessorComponent.GetCurrentState(); //Send Message To Client NetworkServer.SendToClientOfPlayer(this.gameObject, _stateMessageReceivedID, serverStateMsg); _serverPacketID++; } //Send RPC Call to sync non local clients positions //TODO: Optimize this RpcTransformUpdate(StateProcessorComponent.GetCurrentState().position, StateProcessorComponent.GetCurrentState().rotation); }
void ClientUpdate() { _clientTimer += Time.deltaTime; while (_clientTimer >= Time.fixedDeltaTime) { _clientTimer -= Time.deltaTime; uint bufferSlot = _currentTickNumber % _clientBufferSize; _clientInputBuffer[bufferSlot] = InputProcessorComponent.GetCurrentInputs(); // store state for this tick, then use current state + input to step simulation ClientStoreCurrentStateAndStep(ref _clientStateBuffer[bufferSlot], InputProcessorComponent.GetCurrentInputs()); ClientPredictedMessage clientPredictedMessage = new ClientPredictedMessage(); clientPredictedMessage.packetId = _clientPacketID; clientPredictedMessage.deliveryTime = _networkClock.CurrentTime; clientPredictedMessage.startTickNumber = _sendRedundantInputsToServer ? _clientLastReceivedStateTickNumber : _currentTickNumber; var inputList = new List <Inputs>(); for (uint tick = clientPredictedMessage.startTickNumber; tick <= _currentTickNumber; tick++) { inputList.Add(_clientInputBuffer[tick % _clientBufferSize]); } clientPredictedMessage.inputs = inputList.ToArray(); //Send Input Message To Server connectionToServer.Send(_predictedMessageReceivedID, clientPredictedMessage); _clientPacketID++; _currentTickNumber++; } if (ClientHasStateMessage()) { ServerStateMessage serverStateMessage = _clientServerStateMessageBuffer.Dequeue().Element; while (ClientHasStateMessage() ) // make sure if there are any newer state messages available, we use those instead { serverStateMessage = _clientServerStateMessageBuffer.Dequeue().Element; } _clientLastReceivedStateTickNumber = serverStateMessage.tickNumber; //Broadcast this server state for easy access for debugging purposes if (OnValidSercerStateReceived != null) { OnValidSercerStateReceived(serverStateMessage.serverState); } //----------------------------- uint bufferSlot = serverStateMessage.tickNumber % _clientBufferSize; Vector3 positionError = serverStateMessage.serverState.position - _clientStateBuffer[bufferSlot].position; float rotationError = 1.0f - Quaternion.Dot(serverStateMessage.serverState.rotation, _clientStateBuffer[bufferSlot].rotation); //Validating position,rotation and extras rules defined in de State processor if (positionError.sqrMagnitude > 0.0000001f || rotationError > 0.00001f || !StateProcessorComponent.IsValidateState(ref serverStateMessage.serverState, ref _clientStateBuffer[bufferSlot])) { ApplyCorrectionsWithServerState(serverStateMessage, bufferSlot); } } SmoothTransformForModels(); }