public void EnqueueInput(NetCommand.PlayerInputCommand command, Player player, uint serverWorldTick) { // Monitoring. DebugUI.ShowValue("sv stale inputs", staleInputs); // Calculate the last tick in the incoming command. uint maxTick = command.StartWorldTick + (uint)command.Inputs.Length - 1; // Scan for inputs which haven't been handled yet. if (maxTick >= serverWorldTick) { uint start = serverWorldTick > command.StartWorldTick ? serverWorldTick - command.StartWorldTick : 0; for (int i = (int)start; i < command.Inputs.Length; ++i) { // Apply inputs to the associated player controller and simulate the world. var worldTick = command.StartWorldTick + i; var tickInput = new TickInput { WorldTick = (uint)worldTick, RemoteViewTick = (uint)(worldTick - command.ClientWorldTickDeltas[i]), Player = player, Inputs = command.Inputs[i], }; queue.Enqueue(tickInput, worldTick); // Store the latest input in case the simulation needs to repeat missed frames. latestPlayerInput[player.Id] = tickInput; } } else { staleInputs++; } }
// Process a single world tick update. protected override void Tick(float dt) { // Grab inputs. var sampled = handler.SampleInputs(); PlayerInputs inputs = sampled.HasValue ? sampled.Value : new PlayerInputs(); // If the oldest server state is too stale, freeze the player. if (Settings.FreezeClientOnStaleServer && WorldTick - lastServerWorldTick >= Settings.MaxStaleServerStateTicks) { Debug.Log("Server state is too old (is the network connection dead?)"); inputs = new PlayerInputs(); } // Update our snapshot buffers. uint bufidx = WorldTick % 1024; localPlayerInputsSnapshots[bufidx] = inputs; localPlayerStateSnapshots[bufidx] = localPlayer.Controller.ToNetworkState(); localPlayerWorldTickSnapshots[bufidx] = lastServerWorldTick; // Send a command for all inputs not yet acknowledged from the server. var unackedInputs = new List <PlayerInputs>(); var clientWorldTickDeltas = new List <short>(); // TODO: lastServerWorldTick is technically not the same as lastAckedInputTick, fix this. for (uint tick = lastServerWorldTick; tick <= WorldTick; ++tick) { unackedInputs.Add(localPlayerInputsSnapshots[tick % 1024]); clientWorldTickDeltas.Add((short)(tick - localPlayerWorldTickSnapshots[tick % 1024])); } var command = new NetCommand.PlayerInputCommand { StartWorldTick = lastServerWorldTick, Inputs = unackedInputs.ToArray(), ClientWorldTickDeltas = clientWorldTickDeltas.ToArray(), }; handler.SendInputs(command); // Prediction - Apply inputs to the associated player controller and simulate the world. localPlayer.Controller.SetPlayerInputs(inputs); SimulateWorld(dt); ++WorldTick; // Notify camera. // TODO: Fix this - This is needed because the camera is not hooked into the same // world simulation "Simulate()" fixed loop, it can only used its own FixedUpdate, and so // it needs to be synced up a bit better. // Need some interface for entities that participate. GameObject.FindObjectOfType <CPMCameraController>().PlayerPositionUpdated(); // Process a world state frame from the server if we have it. ProcessServerWorldState(); }
private void HandlePlayerInput(NetCommand.PlayerInputCommand cmd, NetPeer peer) { if (cmd.Inputs == null) { Debug.LogError("Shouldnt be null here"); } simulation.EnqueuePlayerInput( new WithPeer <NetCommand.PlayerInputCommand> { Peer = peer, Value = cmd }); }
public void SendInputs(NetCommand.PlayerInputCommand command) { netChannel.SendNSCommand(serverPeer, command); }