/// <summary> /// Called when a state is received from server /// </summary> /// <param name="newState"></param> public void ReceiveState(CharacterNetworkSync.CharacterState newState) { //Other Clients: Shift buffer and store at first position for (int i = bufferedStates.Length - 1; i >= 1; i--) { bufferedStates[i] = bufferedStates[i - 1]; } bufferedStates[0] = newState; bufferedStatesCount = Mathf.Min(bufferedStatesCount + 1, bufferedStates.Length); //Other Clients: Check that states are in good order for (int i = 0; i < bufferedStatesCount - 1; i++) { if (bufferedStates[i].state < bufferedStates[i + 1].state) { Debug.LogWarning("Warning, State are in wrong order"); } } lastBufferedStateTime = Time.time; }
/// <summary> /// Receive a good state from the server /// Discard input older than this good state /// Replay missing inputs on top of it /// </summary> /// <param name="serverRecvState"></param> /// <param name="serverRecvPosition"></param> /// <param name="serverRecvRotation"></param> void ServerState(CharacterNetworkSync.CharacterState characterState) { int serverRecvState = characterState.state; Vector3 serverRecvPosition = characterState.position; Quaternion serverRecvRotation = characterState.rotation; //Client: Check that we received a new state from server (not some delayed packet) if (clientAckState < serverRecvState) { //Client: Set the last server ack state clientAckState = serverRecvState; //Client: Discard all input states where state are before the ack state bool loop = true; while (loop && inputStates.Count > 0) { CharacterInput.InputState state = inputStates.Peek(); if (state.inputState <= clientAckState) { inputStates.Dequeue(); } else { loop = false; } } //Client: store actual Character position, rotation and velocity along with current input CharacterInput.InputState oldState = characterInput.currentInput; Vector3 oldPos = transform.position; Quaternion oldRot = transform.rotation; //Client: move back the player to the received server position serverLastRecvPosition = serverRecvPosition; serverLastRecvRotation = serverRecvRotation; transform.position = serverLastRecvPosition; transform.rotation = serverLastRecvRotation; //Client: replay all input based on new correct position foreach(CharacterInput.InputState state in inputStates) { //Set the input characterInput.currentInput = state; //Run the simulation characterMovement.RunUpdate(Time.fixedDeltaTime); characterRotation.RunUpdate(Time.fixedDeltaTime); } //Client: save the new predicted character position serverLastPredPosition = transform.position; serverLastPredRotation = transform.rotation; //Client: restore initial position, rotation and velocity characterInput.currentInput = oldState; transform.position = oldPos; transform.rotation = oldRot; //Client: Check if a prediction error occured in the past //Debug.Log("States in queue: " + inputStates.Count + " Predicted distance: " + Vector3.Distance(transform.position, serverLastPredPosition)); if (Vector3.Distance(transform.position, serverLastPredPosition) > MAX_SERVER_DISTANCE_SNAP) { //Client: Snap to correct position Debug.LogWarning("Prediction error!"); transform.position = Vector3.Lerp(transform.position, serverLastPredPosition, Time.fixedDeltaTime * 10); transform.rotation = Quaternion.Lerp(transform.rotation, serverLastPredRotation, Time.fixedDeltaTime * 10); } } }