public void FixedUpdate() { //Is time synced? syncedTime.SendTimePacket(id); if (!syncedTime.isReady) { return; } //Initialize ticks if (ticks.Count == 0) { InitInputAndTicks(); } //Get client time float currentTime = syncedTime.GetClientTime(); //Clone multithreaded lists List <UserInput> tempUnprocessedUserInput = unprocessedUserInput.Clone(); tempUnprocessedUserInput.RemoveAll(u => u.Time < ticks.First().Time || u.Time < currentTime - saveTime); unprocessedUserInput = new ThreadSafeList <UserInput>(tempUnprocessedUserInput); List <UserInput> uInput = unprocessedUserInput.Clone(); //Is Alive if (health > 0f) { //Has new client input if (uInput.Count > 0) { Vector2 _inputDirection = Vector2.zero; uInput = uInput.OrderBy(x => x.Time).ToList(); //Merge processed + unprocessed inputs List <UserInput> allInputs = new List <UserInput>(); allInputs.AddRange(processedUserInput); allInputs.AddRange(uInput); //Server Reconciliation //Create new ticks if (uInput.First().Time < ticks.Last().Time) { //Rewind ticks till oldest unprocessedUserInput ticks.RemoveAll(t => t.Time > uInput.First().Time); //Set last valid tick information characterController.enabled = false; characterController.transform.position = ticks.Last().Position; characterController.transform.rotation = ticks.Last().Rotation; yVelocity = ticks.Last().YVelocity; characterController.enabled = true; characterController.Move(Vector3.zero); //Reset isGrounded if (ticks.Count > 2) { animationController.RewindAnimation(ticks.Last().AnimationTime, ticks.Last().Position - ticks[ticks.Count - 2].Position, transform.forward); } float newTickTime; do { float lastTickTime = ticks.Last().Time; newTickTime = lastTickTime + Time.fixedDeltaTime; if (newTickTime > currentTime) { newTickTime = currentTime; } List <UserInput> movesThisTick = allInputs.Where(a => a.Time > lastTickTime && a.Time <= newTickTime).OrderBy(ui => ui.Time).ToList(); UserInput prevTickMove = allInputs.Where(a => a.Time <= lastTickTime).OrderByDescending(x => x.Time).FirstOrDefault(); if (prevTickMove != default && prevTickMove != null) { prevTickMove.Time = ticks.Last().Time; movesThisTick.Insert(0, prevTickMove); } if (movesThisTick.Count > 0) { //Apply tick inputs for (int i = 0; i < movesThisTick.Count; i++) { float moveTime; if (i == movesThisTick.Count - 1) { moveTime = newTickTime - movesThisTick[i].Time; } else { moveTime = movesThisTick[i + 1].Time - movesThisTick[i].Time; } Move(movesThisTick[i].Inputs, movesThisTick[i].Rotation, moveTime); } } ticks.Add(new PlayerState { Position = transform.position, Rotation = transform.rotation, Time = newTickTime, YVelocity = yVelocity, AnimationTime = animationController.GetNormalizedTime() }); } while (newTickTime != currentTime); } //Create new tick else { //Execute last ProcessedInput from last tick till first UnProcessedInput float moveTime = uInput.First().Time - ticks.Last().Time; Move(processedUserInput.Last().Inputs, processedUserInput.Last().Rotation, moveTime); //Execute unprocessed Input for (int i = 0; i < uInput.Count; i++) { if (i == uInput.Count - 1) { moveTime = currentTime - uInput[i].Time; } else { moveTime = uInput[i + 1].Time - uInput[i].Time; } Move(uInput[i].Inputs, uInput[i].Rotation, moveTime); } ticks.Add(new PlayerState { Position = transform.position, Rotation = transform.rotation, Time = currentTime, YVelocity = yVelocity, AnimationTime = animationController.GetNormalizedTime() }); } } //No new unprocessed input //Execute last processed input //Create server tick else { Move(processedUserInput.Last().Inputs, processedUserInput.Last().Rotation, currentTime - ticks.Last().Time); ticks.Add(new PlayerState { Position = transform.position, Rotation = transform.rotation, Time = currentTime, YVelocity = yVelocity, AnimationTime = animationController.GetNormalizedTime() }); } } //Is Dead else { yVelocity = 0; ticks.Add(new PlayerState { Position = transform.position, Rotation = transform.rotation, Time = currentTime, YVelocity = yVelocity, AnimationTime = animationController.GetNormalizedTime() }); } //move from unprocessed to processed foreach (UserInput u in uInput) { unprocessedUserInput.Remove(u); processedUserInput.Add(u); } //remove old input/ticks if (ticks.Count > saveTime / Time.fixedDeltaTime) { ticks.RemoveAt(0); } while (processedUserInput.Count > saveTime / Time.fixedDeltaTime) { processedUserInput.RemoveAt(0); } processedUserInput = processedUserInput.OrderBy(pui => pui.Time).ToList(); //send new tick to all players ServerSend.PlayerData(id, ticks.Last(), (int)score.damageDone, score.kills, score.deaths); Logs(); }