From 353db087a682ae0c25c40ea005101e9335857137 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 19 Apr 2023 21:38:28 +0300 Subject: [PATCH] netcode good condition --- .gitignore | 1 + Source/Game/Console/Console.cs | 18 ++ Source/Game/GameMode/GameModeManager_old.cs | 147 +++++++-- Source/Game/GameMode/NetworkManager_Server.cs | 6 + Source/Game/Player/PlayerActor.cs | 3 +- Source/Game/Player/PlayerInput.cs | 18 +- Source/Game/Player/PlayerInputLocal.cs | 4 +- Source/Game/Player/PlayerMovement.cs | 285 ++++++++++++++---- 8 files changed, 403 insertions(+), 79 deletions(-) diff --git a/.gitignore b/.gitignore index 4e1d89a..61c8811 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ Assets/desktop.ini Assets/Maps/autosave/ Demos/ omnisharp.json +console.log diff --git a/Source/Game/Console/Console.cs b/Source/Game/Console/Console.cs index 3199757..880baae 100644 --- a/Source/Game/Console/Console.cs +++ b/Source/Game/Console/Console.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; @@ -78,6 +79,7 @@ namespace Game public static ReadOnlySpan Lines => instance.Lines; + public static void Init() { if (instance != null) @@ -210,8 +212,15 @@ namespace Game public bool ShowExecutedLines = true; + private StreamWriter logStream; + internal ConsoleInstance() { +#if FLAX_EDITOR + logStream = new StreamWriter(@"C:\dev\GoakeFlax\console.log", false); +#else + logStream = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "console.log"), false); +#endif } public bool IsOpen { get; internal set; } = true; @@ -225,6 +234,12 @@ namespace Game public void Dispose() { + if (logStream != null) + { + logStream.Flush(); + logStream.Dispose(); + logStream = null; + } } // Initializes the Console system. @@ -413,6 +428,7 @@ namespace Game { ConsoleLine lineEntry = new ConsoleLine(line); consoleLines.Add(lineEntry); + logStream.WriteLine(line); OnPrint?.Invoke(text); } } @@ -420,8 +436,10 @@ namespace Game { ConsoleLine lineEntry = new ConsoleLine(text); consoleLines.Add(lineEntry); + logStream.WriteLine(text); OnPrint?.Invoke(text); } + logStream.Flush(); if (Debugger.IsAttached) System.Diagnostics.Debug.WriteLine(text); } diff --git a/Source/Game/GameMode/GameModeManager_old.cs b/Source/Game/GameMode/GameModeManager_old.cs index d99121e..165a830 100644 --- a/Source/Game/GameMode/GameModeManager_old.cs +++ b/Source/Game/GameMode/GameModeManager_old.cs @@ -45,6 +45,8 @@ namespace Game private static Dictionary players; private static Dictionary playerConnections; + public static Dictionary playerLastReceivedFrames; + private static Dictionary playerLastFrame; private static bool welcomed = false; private static WorldState serverWorldState; @@ -65,6 +67,8 @@ namespace Game players = new Dictionary(); playerConnections = new Dictionary(); + playerLastReceivedFrames = new Dictionary(); + playerLastFrame = new Dictionary(); serverWorldState = new WorldState(); clientWorldState = new WorldState(); @@ -138,24 +142,28 @@ namespace Game foreach (KeyValuePair kv in players) { - var playerId = kv.Key; + var otherPlayerId = kv.Key; foreach (KeyValuePair kv2 in players) { //if (kv2.Key == playerId) // continue; - var otherPlayerActor = kv2.Value; + var playerActor = kv2.Value; + var playerId = kv2.Key; // TODO: relevancy checks here etc. - PlayerMovement playerMovement = otherPlayerActor.GetScript(); + PlayerMovement playerMovement = playerActor.GetScript(); //PlayerActorState actorState = playerMovement.input.GetCurrentActorState(); //PlayerInputState inputState = playerMovement.input.GetCurrentInputState(); - playerMovement.input.GetState(serverWorldState.frame, out var inputState, out var actorState); + var playerFrame = playerLastFrame[playerId]; + if (!playerMovement.input.GetState(playerFrame, out var inputState, out var actorState)) + Console.Print("send input failure to client"); + { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerPosition); - message.WriteUInt64(serverWorldState.frame); + message.WriteUInt64(playerFrame); message.WriteUInt32(kv2.Key); message.WriteSingle(actorState.position.X); message.WriteSingle(actorState.position.Y); @@ -171,6 +179,8 @@ namespace Game message.WriteSingle(actorState.viewAngles.Y); message.WriteSingle(actorState.viewAngles.Z); message.WriteSingle(actorState.lastJumpTime); + message.WriteInt32(actorState.numJumps); + message.WriteBoolean(actorState.jumped); //inputState.frame message.WriteSingle(inputState.viewDeltaX); @@ -180,7 +190,7 @@ namespace Game message.WriteBoolean(inputState.attacking); message.WriteBoolean(inputState.jumping); - NetworkManager.ServerEndSendMessage(ref message, playerConnections[playerId]); + NetworkManager.ServerEndSendMessage(ref message, playerConnections[otherPlayerId]); } } } @@ -195,6 +205,13 @@ namespace Game foreach (PlayerActor playerActor in Level.GetActors()) { var playerId = playerActor.PlayerId; + if (!clientWorldState.playerFrameHistory.ContainsKey(playerId)) + { + var playerFrames = new PlayerFrame[120]; + for (int j = 0; j < playerFrames.Length; j++) + playerFrames[j] = new PlayerFrame(); + clientWorldState.playerFrameHistory.Add(playerId, playerFrames); + } var playerFrameHistory = clientWorldState.playerFrameHistory[playerId]; var playerFrame = playerFrameHistory[clientWorldState.frame % 120]; @@ -274,6 +291,11 @@ namespace Game if (NetworkManager.IsClient || NetworkManager.IsLocalClient) { var serverFrame = networkEvent.Message.ReadUInt64(); + if (!NetworkManager.IsLocalClient) + serverWorldState.frame = serverFrame; + //lastReceivedServerFrame = serverFrame; + clientWorldState.frame += serverFrame; + ClientTime = networkEvent.Message.ReadSingle(); int numActors = (int)networkEvent.Message.ReadUInt32(); for (int i = 0; i < numActors; i++) @@ -294,11 +316,6 @@ namespace Game Console.Print("received welcome: frame " + serverWorldState.frame); - if (!NetworkManager.IsLocalClient) - serverWorldState.frame = serverFrame; - //lastReceivedServerFrame = serverFrame; - clientWorldState.frame += serverFrame; - if (!players.ContainsKey(NetworkManager.LocalPlayerClientId)) // listen server players.Add(NetworkManager.LocalPlayerClientId, null); //playerConnections.Add(NetworkManager.LocalPlayerClientId, connection); @@ -340,11 +357,15 @@ namespace Game inputState.jumping = networkEvent.Message.ReadBoolean(); UpdatePlayerInput(playerId, inputState); + + + + playerLastReceivedFrames[playerId] = inputState.frame; break; } case GameModeMessageType.PlayerPosition: { - uint playerId = networkEvent.Sender.ConnectionId; + //uint playerId = networkEvent.Sender.ConnectionId; PlayerInputState inputState = default; //? PlayerActorState actorState; @@ -364,6 +385,8 @@ namespace Game actorState.viewAngles.Y = networkEvent.Message.ReadSingle(); actorState.viewAngles.Z = networkEvent.Message.ReadSingle(); actorState.lastJumpTime = networkEvent.Message.ReadSingle(); + actorState.numJumps = networkEvent.Message.ReadInt32(); + actorState.jumped = networkEvent.Message.ReadBoolean(); inputState.frame = reportedFrame; inputState.viewDeltaX = networkEvent.Message.ReadSingle(); @@ -373,16 +396,21 @@ namespace Game inputState.attacking = networkEvent.Message.ReadBoolean(); inputState.jumping = networkEvent.Message.ReadBoolean(); + //if (actorState.viewAngles != new Float3(90f, 0f, 0f)) + // Console.Print($"{reportedFrame} has viewangles: {actorState.viewAngles}"); + //Assert.IsTrue(reportedFrame >= lastReceivedServerFrame); - if (reportedFrame < lastReceivedServerFrame) + if (reportedPlayerId == NetworkManager.LocalPlayerClientId && reportedFrame < lastReceivedServerFrame) { - //Console.Print($"packet wrong order, received {lastReceivedServerFrame}, new {reportedFrame}"); + Console.Print($"packet wrong order, received {lastReceivedServerFrame}, new {reportedFrame}"); break; } if (NetworkManager.IsClient) { - lastReceivedServerFrame = reportedFrame; + if (reportedPlayerId == NetworkManager.LocalPlayerClientId) + lastReceivedServerFrame = reportedFrame; + //Console.Print($"we drifted, corrected. client frame: {serverWorldState.frame}, server frame: {reportedFrame}"); PlayerActor playerActor = Level.GetActors().FirstOrDefault(x => x.GetScript().PlayerId == reportedPlayerId); @@ -392,9 +420,19 @@ namespace Game { PlayerInput playerInput = playerActor.GetScript().input; - playerInput.SetState(reportedFrame, inputState, actorState); - { + if (NetworkManager.IsLocalClient && reportedPlayerId == NetworkManager.LocalPlayerClientId) + { } + else + playerInput.SetState(reportedFrame, inputState, actorState); + if (!NetworkManager.IsLocalClient && playerInput is PlayerInputNetwork) + { + playerActor.Position = actorState.position; + playerActor.GetScript().currentVelocity = actorState.velocity; + playerActor.GetScript().lastJumped = actorState.lastJumpTime; + playerActor.GetScript().numJumps = actorState.numJumps; + playerActor.GetScript().jumped = actorState.jumped; + playerActor.GetScript().SetCameraEulerAngles(actorState.viewAngles, true); } //playerActor.SetPosition(reportedPosition); } @@ -426,8 +464,20 @@ namespace Game message.WriteSingle(playerActor.Position.X); message.WriteSingle(playerActor.Position.Y); message.WriteSingle(playerActor.Position.Z); + + playerActor.GetScript().input.SetState(serverWorldState.frame, new PlayerInputState(), new PlayerActorState() + { + position = playerActor.Position, + velocity = playerActor.GetScript().currentVelocity, + orientation = playerActor.GetScript().rootActor.Orientation, + viewAngles = playerActor.GetScript().viewAngles, + lastJumpTime = playerActor.GetScript().lastJumped, + numJumps = playerActor.GetScript().numJumps, + jumped = playerActor.GetScript().jumped, + }); } NetworkManager.ServerEndSendMessage(ref message, connection); + } return true; } @@ -475,7 +525,10 @@ namespace Game private static void SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles) { - if (NetworkManager.IsLocalClient) + if (NetworkManager.IsServer && !playerLastFrame.ContainsKey(playerId)) + playerLastFrame.Add(playerId, serverWorldState.frame); + + if (NetworkManager.IsLocalClient && playerId == NetworkManager.LocalPlayerClientId) return; // Handled by listenserver //spawned = true; @@ -495,13 +548,71 @@ namespace Game PlayerInput playerInput = playerActor.GetScript().input; playerInput.frame = (NetworkManager.IsServer) ? serverWorldState.frame : clientWorldState.frame; if (NetworkManager.IsServer) + { serverWorldState.actors.Add(playerActor); + if (!playerLastReceivedFrames.ContainsKey(playerId)) + playerLastReceivedFrames.Add(playerId, 0); + } } + private static void UpdatePlayerInput(uint playerId, PlayerInputState inputState) { + if (playerId == NetworkManager.LocalPlayerClientId) + { + playerLastFrame[playerId] = inputState.frame; + return; + } + PlayerActor playerActor = players[playerId]; + PlayerMovement playerMovement = playerActor.GetScript(); playerActor.UpdateNetworkInput(inputState); + + ulong startFrame = playerLastFrame[playerId]; + + if (startFrame >= inputState.frame) + return; // dropped frame, ignore + + + // simulate at least one or more frame when receiving input frames from player. missing frames use inputs from previous frames + var simframs = 0; + ulong frame = startFrame+1; + PlayerInputState prevInputState; + playerMovement.input.GetState(frame - 1, out prevInputState, out var _); + for (; frame <= inputState.frame; frame++) + { + if (!playerMovement.input.GetState(frame, out var lastInputState, out var lastActorState)) + { + // dropped frame, use previous input + lastInputState = prevInputState; + lastInputState.frame = frame; + } + + playerMovement.ApplyInputToCamera(lastInputState, true); + playerMovement.SimulatePlayerMovement(lastInputState); + + playerMovement.input.SetState(frame, lastInputState, new PlayerActorState() + { + position = playerActor.Position, + velocity = playerMovement.currentVelocity, + orientation = playerMovement.rootActor.Orientation, + viewAngles = playerMovement.viewAngles, + lastJumpTime = playerMovement.lastJumped, + numJumps = playerMovement.numJumps, + jumped = playerMovement.jumped, + }); + + simframs++; + prevInputState = lastInputState; + } + + if (playerActor.Position.Length < 1.0f) + simframs = simframs; + + playerLastFrame[playerId] = inputState.frame;//frame; + + if (simframs > 1) + Console.Print($"simulated {simframs} frames"); } } } diff --git a/Source/Game/GameMode/NetworkManager_Server.cs b/Source/Game/GameMode/NetworkManager_Server.cs index 9706438..f28c53a 100644 --- a/Source/Game/GameMode/NetworkManager_Server.cs +++ b/Source/Game/GameMode/NetworkManager_Server.cs @@ -96,6 +96,12 @@ namespace Game server.EndSendMessage(channelType, message, connection); } + + public static void ServerEndSendMessage(ref NetworkMessage message, NetworkConnection[] connections, NetworkChannelType channelType = NetworkChannelType.Reliable) + { + server.EndSendMessage(channelType, message, connections); + } + public static NetworkMessage ClientBeginSendMessage() { NetworkMessage message = client.BeginSendMessage(); diff --git a/Source/Game/Player/PlayerActor.cs b/Source/Game/Player/PlayerActor.cs index e39ac21..c8bf992 100644 --- a/Source/Game/Player/PlayerActor.cs +++ b/Source/Game/Player/PlayerActor.cs @@ -127,7 +127,8 @@ namespace Game return; //PlayerInputState inputState = new PlayerInputState(frame, viewDeltaXYMoveForwardRight.X, viewDeltaXYMoveForwardRight.Y, viewDeltaXYMoveForwardRight.Z, viewDeltaXYMoveForwardRight.W, attacking, jumping); - playerInputNetwork.currentState.input = inputState; + //playerInputNetwork.currentState.input = inputState; + playerInputNetwork.SetState(inputState.frame, inputState); } public void SetPosition(Float3 newPosition) diff --git a/Source/Game/Player/PlayerInput.cs b/Source/Game/Player/PlayerInput.cs index 72e9bd1..7248c3f 100644 --- a/Source/Game/Player/PlayerInput.cs +++ b/Source/Game/Player/PlayerInput.cs @@ -76,6 +76,8 @@ namespace Game public Quaternion orientation; public Float3 viewAngles; // yaw, pitch, roll public float lastJumpTime; + public int numJumps; + public bool jumped; } [StructLayout(LayoutKind.Sequential)] @@ -106,8 +108,8 @@ namespace Game public virtual void OnEndFrame() { - Console.Print("recorded frame " + frame); - currentState.input.frame = frame; + //Console.Print("recorded frame " + frame); + states[frame % 120] = currentState; /*ulong oldest = ulong.MaxValue; @@ -116,10 +118,14 @@ namespace Game oldestFrame = oldest;*/ frame++; + currentState.input.frame = frame; } public virtual void RecordCurrentActorState(PlayerActorState actorState) { + if (actorState.position.Length <= 0.01) + Console.Print("wrong recorded position?"); + currentState.actor = actorState; } public bool GetState(ulong frame, out PlayerInputState inputState, out PlayerActorState actorState) @@ -137,6 +143,14 @@ namespace Game return true; } + public void SetState(ulong frame, PlayerInputState inputState) + { + int frameIndex = (int)frame % 120; + states[frameIndex].input = inputState; + states[frameIndex].input.frame = frame; + states[frameIndex].actor = new PlayerActorState(); + } + public void SetState(ulong frame, PlayerInputState inputState, PlayerActorState actorState) { int frameIndex = (int)frame % 120; diff --git a/Source/Game/Player/PlayerInputLocal.cs b/Source/Game/Player/PlayerInputLocal.cs index 5825920..f5c35ac 100644 --- a/Source/Game/Player/PlayerInputLocal.cs +++ b/Source/Game/Player/PlayerInputLocal.cs @@ -116,9 +116,7 @@ namespace Game if (!IsRecording) return; - if (actorState.position.Length <= 0.01) - Console.Print("wrong recorded position?"); - currentState.actor = actorState; + base.RecordCurrentActorState(actorState); } public void FlushDemo() diff --git a/Source/Game/Player/PlayerMovement.cs b/Source/Game/Player/PlayerMovement.cs index 1a0572d..938eabc 100644 --- a/Source/Game/Player/PlayerMovement.cs +++ b/Source/Game/Player/PlayerMovement.cs @@ -86,17 +86,17 @@ namespace Game //public int currentInputFrame; //private int currentInputFrame2; - private Float3 currentVelocity; + public Float3 currentVelocity; public PlayerInput input; //private bool physicsInteractions = false; - private bool jumped; + public bool jumped; //private int lastInputFrame; - private float lastJumped = -1f; + public float lastJumped = -1f; private float lastLanded = -1f; - private int numJumps; + public int numJumps; private float simulationTime = 0f; [ReadOnly] public bool onGround; @@ -104,8 +104,7 @@ namespace Game private Actor cameraHolder; private PlayerActor playerActor; - private Actor rootActor; - private float startupTime; + public Actor rootActor; public Float3 viewAngles; private Float3 viewAnglesLastFrame; @@ -164,8 +163,6 @@ namespace Game //rigidBody.CollisionEnter += OnCollisionEnter; //rigidBody.TriggerEnter += OnTriggerEnter; //rigidBody.TriggerExit += OnTriggerExit; - - startupTime = Time.TimeSinceStartup; } public void SetInput(uint playerId) @@ -279,23 +276,31 @@ namespace Game viewRoll = actorState.viewRoll; }*/ - PlayerInputState inputState = input.GetCurrentInputState(); - - //if (inputState.viewDeltaX != 0 || inputState.viewDeltaY != 0) - // inputState = inputState; - viewAngles = viewAnglesLastFrame; - ApplyInputToCamera(inputState); - input.RecordCurrentActorState(new PlayerActorState + if (input is not PlayerInputNetwork) { - position = Actor.Position, - velocity = currentVelocity, - orientation = rootActor.Orientation, - viewAngles = viewAngles, - lastJumpTime = lastJumped, - //viewAngles = new Float3(viewAngles.Y, viewAngles.X, viewAngles.Z) - }); + + PlayerInputState inputState = input.GetCurrentInputState(); + + //if (inputState.viewDeltaX != 0 || inputState.viewDeltaY != 0) + // inputState = inputState; + + + ApplyInputToCamera(inputState); + + input.RecordCurrentActorState(new PlayerActorState + { + position = Actor.Position, + velocity = currentVelocity, + orientation = rootActor.Orientation, + viewAngles = viewAngles, + lastJumpTime = lastJumped, + numJumps = numJumps, + jumped = jumped, + //viewAngles = new Float3(viewAngles.Y, viewAngles.X, viewAngles.Z) + }); + } /*input.RecordCurrentActorState(new PlayerActorState() { @@ -372,6 +377,106 @@ namespace Game }*/ } } + else if (input is PlayerInputNetwork) + { +#if false + bool canpredict = true; + if (false && input.Predict && GameModeManager.ClientFrame > 0 && GameModeManager.ServerFrame > 0) + { + ulong maxFrame = /*NetworkManager.IsServer ? GameModeManager.playerLastReceivedFrames[PlayerId] :*/ GameModeManager.ClientFrame; + ulong currentFrame = GameModeManager.ServerFrame; + for (; currentFrame <= maxFrame; currentFrame++) + { + if (!input.GetState(currentFrame, out var pastInputState, out var pastActorState)) + { + //Console.Print($"not predicting"); + //canpredict = false; + break; + } + } + + ulong lastFrame = currentFrame; + if (input is PlayerInputNetwork) + { + //canpredict = true; + //lastFrame = GameModeManager.ServerFrame+1; + } + + predicting = true; + currentFrame = GameModeManager.ServerFrame; + for (; currentFrame < lastFrame; currentFrame++) + { + if (!input.GetState(currentFrame, out var pastInputState, out var pastActorState)) + { + Console.Print($"unexpected predict failure: {currentFrame}"); + break; + } + + if (currentFrame == inputState.frame) + jumped = jumped; + + if (currentFrame == GameModeManager.ServerFrame) + { + Actor.Position = pastActorState.position; + currentVelocity = pastActorState.velocity; + lastJumped = pastActorState.lastJumpTime; + numJumps = pastActorState.numJumps; + jumped = pastActorState.jumped; + SetCameraEulerAngles(pastActorState.viewAngles, true); + + if (Actor.Position.Length < 0.1) + jumped = jumped; + + continue; + //rootActor.Orientation = pastActorState.orientation; + //viewAngles = pastActorState.viewAngles; + //viewAngles = new Float3(pastActorState.viewAngles.Y, pastActorState.viewAngles.X, pastActorState.viewAngles.Z); + } + else + ApplyInputToCamera(pastInputState, true); + //SetCameraEulerAngles(pastActorState.viewAngles, true); + + //if (currentVelocity.Length > 0) + // currentVelocity = currentVelocity; + + //else + // ApplyInputToCamera(pastInputState, true); + SimulatePlayerMovement(pastInputState); + + break; + + /*if ((Actor.Position - pastActorState.position).Length > 0.001) + Console.Print($"mispredicted position"); + if ((currentVelocity - pastActorState.velocity).Length > 0.001) + Console.Print($"mispredicted velocity"); + if ((viewAngles - pastActorState.viewAngles).Length > 0.001) + Console.Print($"mispredicted viewangles: {viewAngles - oldAngles}");*/ + + //Console.Print($"predicted: {currentFrame}"); + } + + /*if (input is PlayerInputNetwork) + { + currentFrame = lastFrame - 1; + if (input.GetState(currentFrame, out var lastInputState, out var lastActorState)) + { + for (; currentFrame < GameModeManager.ClientFrame; currentFrame++) + { + ApplyInputToCamera(lastInputState, true); + SimulatePlayerMovement(lastInputState); + } + } + }*/ + + predicting = false; + } + else + { + //ApplyInputToCamera(inputState, true); + //SimulatePlayerMovement(inputState); + } +#endif + } else { if (Actor.Position.Length < 0.1) @@ -379,41 +484,59 @@ namespace Game //viewAngles = viewAnglesLastFrame; //ApplyInputToCamera(inputState); - var oldAngles = viewAngles; + //viewAngles = viewAnglesLastFrame; bool canpredict = true; if (true && input.Predict && GameModeManager.ClientFrame > 0 && GameModeManager.ServerFrame > 0) { - for (ulong currentFrame = GameModeManager.ServerFrame; currentFrame < GameModeManager.ClientFrame; currentFrame++) + ulong currentFrame = GameModeManager.ServerFrame; + for (; currentFrame < GameModeManager.ClientFrame; currentFrame++) { if (!input.GetState(currentFrame, out var pastInputState, out var pastActorState)) { - Console.Print($"not predicting"); + //Console.Print($"not predicting"); canpredict = false; break; } } + ulong lastFrame = currentFrame; + + if (canpredict) - { - //var oldPos = Actor.Position; - //var oldVel = currentVelocity; + { + var oldAngles = viewAngles; + var oldPos = Actor.Position; + var oldVel = currentVelocity; + + predicting = true; - for (ulong currentFrame = GameModeManager.ServerFrame; currentFrame < GameModeManager.ClientFrame; currentFrame++) + currentFrame = GameModeManager.ServerFrame; + for (; currentFrame < lastFrame; currentFrame++) { if (!input.GetState(currentFrame, out var pastInputState, out var pastActorState)) { - Console.Print($"predict failure: {currentFrame}"); + Console.Print($"unexpected predict failure: {currentFrame}"); break; } + if (currentFrame == inputState.frame) + jumped = jumped; + if (currentFrame == GameModeManager.ServerFrame) { Actor.Position = pastActorState.position; currentVelocity = pastActorState.velocity; lastJumped = pastActorState.lastJumpTime; + numJumps = pastActorState.numJumps; + jumped = pastActorState.jumped; SetCameraEulerAngles(pastActorState.viewAngles, true); + //cameraHolder.Orientation = Quaternion.Euler(pastActorState.viewAngles.Y, pastActorState.viewAngles.X, pastActorState.viewAngles.Z); + //ApplyInputToCamera(pastInputState, true); + + //if (pastActorState.viewAngles != new Float3(90f, 0f, 0f)) + // Console.Print($"moved server frame: {currentFrame}, {pastActorState.viewAngles}"); if (Actor.Position.Length < 0.1) jumped = jumped; @@ -434,35 +557,45 @@ namespace Game // ApplyInputToCamera(pastInputState, true); SimulatePlayerMovement(pastInputState); - if ((Actor.Position - pastActorState.position).Length > 0.001) + /*if ((Actor.Position - pastActorState.position).Length > 0.001) Console.Print($"mispredicted position"); if ((currentVelocity - pastActorState.velocity).Length > 0.001) Console.Print($"mispredicted velocity"); if ((viewAngles - pastActorState.viewAngles).Length > 0.001) - Console.Print($"mispredicted viewangles: {viewAngles - oldAngles}"); + Console.Print($"mispredicted viewangles: {viewAngles - oldAngles}");*/ //Console.Print($"predicted: {currentFrame}"); } predicting = false; - /*if ((Actor.Position - oldPos).Length > 0.001) + if ((Actor.Position - oldPos).Length > 0.001) Console.Print($"mispredicted final position"); if ((currentVelocity - oldVel).Length > 0.001) - Console.Print($"mispredicted final velocity");*/ + Console.Print($"mispredicted final velocity"); - ApplyInputToCamera(inputState, true); - // Ensure orientation is always up-to-date after predicting - //rootActor.Orientation = Quaternion.Euler(0, viewAngles.X, 0); - cameraHolder.Orientation = Quaternion.Euler(viewAngles.Y, viewAngles.X, viewAngles.Z); + //if (input is not PlayerInputNetwork) + { + /*if ((Actor.Position - oldPos).Length > 0.001) + Console.Print($"mispredicted final position"); + if ((currentVelocity - oldVel).Length > 0.001) + Console.Print($"mispredicted final velocity");*/ - SimulatePlayerMovement(inputState); + ApplyInputToCamera(inputState, true); + + // Ensure orientation is always up-to-date after predicting + //rootActor.Orientation = Quaternion.Euler(0, viewAngles.X, 0); + cameraHolder.Orientation = Quaternion.Euler(viewAngles.Y, viewAngles.X, viewAngles.Z); + + SimulatePlayerMovement(inputState); + } if ((viewAngles - oldAngles).Length > 0.001) - Console.Print($"mispredicted final viewangles: {viewAngles - oldAngles}"); + Console.Print($"mispredicted final viewangles: {viewAngles} <- {oldAngles}"); - + //if (viewAngles != new Float3(90f, 0f, 0f)) + // Console.Print($"moved client frame: {GameModeManager.ClientFrame}, {viewAngles}"); //Console.Print($"current: {inputState.frame}"); //if (GameModeManager.ClientFrame - GameModeManager.ServerFrame > 0) @@ -474,7 +607,6 @@ namespace Game if (!canpredict) { - SetCameraEulerAngles(viewAnglesLastFrame, true); ApplyInputToCamera(inputState, true); SimulatePlayerMovement(inputState); @@ -503,21 +635,30 @@ namespace Game //SetCameraEulerAngles(new Float3(pastActorState.viewAngles.Y, pastActorState.viewAngles.X, pastActorState.viewAngles.Z), true); //SetCameraEulerAngles(new Float3(viewAnglesLastFrame.Y, viewAnglesLastFrame.X, viewAnglesLastFrame.Z), true); //SimulatePlayerMovement(inputState); + + input.RecordCurrentActorState(new PlayerActorState + { + position = Actor.Position, + velocity = currentVelocity, + orientation = rootActor.Orientation, + viewAngles = viewAngles, + lastJumpTime = lastJumped, + numJumps = numJumps, + jumped = jumped, + //viewAngles = new Float3(viewAngles.Y, viewAngles.X, viewAngles.Z) + }); + + //Console.Print($"recording frame {input.frame}, client: {GameModeManager.ClientFrame}, server: {GameModeManager.ServerFrame}"); + input.OnEndFrame(); } if (Actor.Position.Length < 0.1) jumped = jumped; - input.RecordCurrentActorState(new PlayerActorState - { - position = Actor.Position, - velocity = currentVelocity, - orientation = rootActor.Orientation, - viewAngles = viewAngles, - lastJumpTime = lastJumped, - //viewAngles = new Float3(viewAngles.Y, viewAngles.X, viewAngles.Z) - }); - input.OnEndFrame(); + if (input is PlayerInputNetwork) + jumped = jumped; + + //lastInputFrame = currentInputFrame; @@ -533,7 +674,7 @@ namespace Game }*/ } - private void ApplyInputToCamera(PlayerInputState inputState, bool wrapAround = true) + public void ApplyInputToCamera(PlayerInputState inputState, bool wrapAround = true) { if (inputState.viewDeltaX == 0.0f && inputState.viewDeltaY == 0.0f) return; @@ -543,7 +684,7 @@ namespace Game SetCameraEulerAngles(new Float3(viewYaw, viewPitch, viewAngles.Z), wrapAround); } - private void SetCameraEulerAngles(Float3 angles, bool wrapAround = true) + public void SetCameraEulerAngles(Float3 angles, bool wrapAround = true) { if (viewAngles == angles) return; @@ -785,7 +926,40 @@ namespace Game } else if (boxCollider && boxCollider.IsActive) { - DebugDraw.DrawWireBox(boxCollider.OrientedBox.GetBoundingBox(), Color.GreenYellow * 0.8f); + var clientBbox = boxCollider.OrientedBox.GetBoundingBox(); + + if (false) + { + if (GameModeManager.ServerFrame > 0 && GameModeManager.ClientFrame > 0) + for (ulong frame = GameModeManager.ServerFrame; frame < GameModeManager.ClientFrame; frame++) + { + if (!input.GetState(frame, out var pastInputState, out var pastActorState)) + continue; + + var bbox = clientBbox; + bbox.Center = pastActorState.position; + + Float4 color1 = new Float4(Color.Red.R, Color.Red.G, Color.Red.B, Color.Red.A); + Float4 color2 = new Float4(Color.Blue.R, Color.Blue.G, Color.Blue.B, Color.Blue.A); + Float4 color3 = Float4.Lerp(color1, color2, (float)(frame - GameModeManager.ServerFrame) / (float)(GameModeManager.ClientFrame - GameModeManager.ServerFrame)); + Color color = new Color(color3.X, color3.Y, color3.Z, color3.W); + DebugDraw.DrawBox(bbox, color * 1f); + } + } + else + { + var serverBbox = boxCollider.OrientedBox.GetBoundingBox(); + if (input.GetState(GameModeManager.ServerFrame, out var serverInputState, out var serverActorState)) + serverBbox.Center = serverActorState.position; + + if (serverBbox.Center == clientBbox.Center) + DebugDraw.DrawBox(clientBbox, Color.Magenta * 0.6f); + else + { + DebugDraw.DrawBox(serverBbox, Color.Red * 0.6f); + DebugDraw.DrawBox(clientBbox, Color.Blue * 0.6f); + } + } } } #endif @@ -1051,6 +1225,7 @@ namespace Game if (onGround && jumpAction && !jumped) if (OnJump(traceGround, ref position, ref velocity)) { + //Console.Print($"{inputState.frame} jumped " + ", predicting: " + predicting + ", vel: " + velocity.Y); jumped = true; lastJumped = simulationTime; numJumps++;