#if true using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.Json; using FlaxEngine.Networking; using Console = Game.Console; namespace Game; public enum GameModeMessageType : byte { AcceptConnection, // WelcomePlayerMessage SpawnPlayer, PlayerInput, PlayerPosition, // world update PlayerSnapshot, // send world state delta to client since last client's acknowledged frame LastMessageType = 128, } // Manages world simulation and handles network messages public class WorldStateManager { private class WorldState { public ulong frame; public List actors; public Dictionary playerFrameHistory; public WorldState() { actors = new List(); playerFrameHistory = new Dictionary(); } } public class PlayerFrame { public ulong frame; public Float3 position; public PlayerInputState2 inputState; public PlayerMovementState movementState; } private Dictionary players; private Dictionary playerConnections; public Dictionary playerLastReceivedFrames; private Dictionary playerLastFrame; private bool welcomed = false; private WorldState serverWorldState; private WorldState clientWorldState; private GameMode gameMode; private Scene scene; private Actor worldSpawn; private ulong lastReceivedServerFrame = 0; public ulong ServerFrame => /*NetworkManager.server != null ? serverWorldState.frame :*/ lastReceivedServerFrame; public ulong ClientFrame => clientWorldState.frame; public ulong Frame => IsServer ? serverWorldState.frame : clientWorldState.frame; public float ServerTime = 0f; public float ClientTime = 0f; public bool IsServer = false; public bool IsClient = false; public bool IsLocalClient = false; // Context dependant, true when message is handled by local client public WorldStateManager(bool isServer = false, bool isClient = false, bool isLocalClient = false) { IsServer = isServer; IsClient = isClient; IsLocalClient = isLocalClient; Assert.IsTrue(isServer || isClient || isLocalClient); Init(); } public void Init() { //welcomed = false; //lastReceivedServerFrame = 0; ServerTime = Time.GameTime; //ClientTime = 0f; players = new Dictionary(); playerConnections = new Dictionary(); playerLastReceivedFrames = new Dictionary(); playerLastFrame = new Dictionary(); serverWorldState = new WorldState(); clientWorldState = new WorldState(); gameMode = new GameMode(); /*if (IsServer) NetworkManager.RegisterServerCallbacks(OnMessage); else NetworkManager.RegisterClientCallbacks(OnMessage);*/ Debug.Log($"WorldStateManager Init is server: {IsServer}"); Debug.Log($"WorldStateManager Init is local client: {IsLocalClient}"); PhysicsScene localPhysicsScene = Physics.FindOrCreateScene(IsLocalClient ? "ClientPhysicsScene" : "ServerPhysicsScene"); Guid sceneGuid = JsonSerializer.ParseID(IsLocalClient ? "c095f9ac4989a46afd7fe3821f086e2e" : "59dd37cc444d5d7015759389c6153c4c"); string sceneData = $@" {{ ""ID"": ""{JsonSerializer.GetStringID(sceneGuid)}"", ""TypeName"": ""FlaxEngine.SceneAsset"", ""EngineBuild"": 65046, ""Data"": [ {{ ""ID"": ""{JsonSerializer.GetStringID(sceneGuid)}"", ""TypeName"": ""FlaxEngine.Scene"", ""LightmapSettings"": {{ ""IndirectLightingIntensity"": 1.0, ""GlobalObjectsScale"": 1.0, ""ChartsPadding"": 3, ""AtlasSize"": 1024, ""BounceCount"": 1, ""CompressLightmaps"": true, ""UseGeometryWithNoMaterials"": true, ""Quality"": 10 }} }} ] }}"; { var onSceneLoaded = (Scene loadedScene, Guid id) => { if (sceneGuid == id) { loadedScene.PhysicsScene = localPhysicsScene; //scene = loadedScene; } }; try { Level.SceneLoaded += onSceneLoaded; //Level.LoadScene(new SceneReference(sceneGuid)); scene = Level.LoadSceneFromBytes(Encoding.ASCII.GetBytes(sceneData)); } finally { Level.SceneLoaded -= onSceneLoaded; } } Assert.IsTrue(scene); scene.Name = IsLocalClient ? "ClientScene" : "ServerScene"; Level.SceneLoaded += OnLevelLoaded; //Scripting.LateUpdate += OnLateUpdatePre; Scripting.LateFixedUpdate += OnLateUpdatePre; var importer = FlaxEngine.Object.New(); importer.mapPath = @"C:\dev\GoakeFlax\Assets\Maps\aerowalk.map"; importer.LoadCollidersOnly = IsServer; importer.Parent = scene; //importer.Enabled = true; worldSpawn = scene.FindActor("WorldSpawn");// ?? Level.FindActor("WorldSpawn"); Assert.IsTrue(worldSpawn); } public void Cleanup() { /*if (IsServer) NetworkManager.UnregisterServerCallbacks(OnMessage); else NetworkManager.UnregisterClientCallbacks(OnMessage);*/ Level.SceneLoaded -= OnLevelLoaded; //Scripting.LateUpdate -= OnLateUpdatePre; Scripting.LateFixedUpdate -= OnLateUpdatePre; if (scene) foreach (var player in scene.GetChildren()) { FlaxEngine.Object.Destroy(player); } if (scene) { if (Level.UnloadScene(scene)) throw new Exception("Failed to unload scene"); scene = null; } } public PlayerFrame GetPlayerFrame(uint playerIndex, ulong frame) { WorldState worldState = /*NetworkManager.server != null*/IsServer ? serverWorldState : clientWorldState;//NetworkManager.server != null ? serverWorldState : clientWorldState; PlayerFrame[] playerFrames = worldState.playerFrameHistory[playerIndex]; PlayerFrame playerFrame = playerFrames[frame % 120]; if (playerFrame.frame != frame) return null; return playerFrame; } public bool HasPlayerFrame(uint playerId, ulong frame) { return GetPlayerFrame(playerId, frame) != null; } public bool GetPlayerInputState(uint playerId, ulong frame, out PlayerInputState2 inputState) { PlayerFrame frameInfo = GetPlayerFrame(playerId, frame); inputState = frameInfo?.inputState ?? default; return frameInfo != null; } public void RecordPlayerInput(uint playerId, ulong frame, PlayerInputState2 inputState, PlayerMovementState movementState) { WorldState worldState = /*NetworkManager.server != null*/IsServer ? serverWorldState : clientWorldState; if (!IsServer && playerId != NetworkManager.LocalPlayerClientId) return; // Do not record input for networked players PlayerFrame[] playerFrames = worldState.playerFrameHistory[playerId]; PlayerFrame playerFrame = playerFrames[frame % 120]; playerFrame.frame = frame; playerFrame.inputState = inputState; playerFrame.movementState = movementState; Console.Print($"recorded frame {frame}, client: {IsClient || IsLocalClient}"); } public void OnLevelLoaded(Scene scene, Guid assetGuid) { serverWorldState.frame = 0; Console.Print("level loaded"); } public void OnLateUpdatePre() { try { //NetworkManager.IsServer = NetworkManager.server != null; //NetworkManager.IsClient = (NetworkManager.client != null || NetworkManager.IsDemoPlaying) && NetworkManager.server == null; //NetworkManager.IsLocalClient = (NetworkManager.client != null || NetworkManager.IsDemoPlaying) && NetworkManager.server != null; OnLateUpdate(); } finally { //NetworkManager.IsServer = false; //NetworkManager.IsClient = false; //NetworkManager.IsLocalClient = false; } } public void OnLateUpdate() { if (IsServer) { foreach (KeyValuePair kv in players) { var playerId = kv.Key; var playerActor = kv.Value; var playerFrames = serverWorldState.playerFrameHistory[playerId]; var playerFrame = playerFrames[serverWorldState.frame % 120]; playerFrame.frame = serverWorldState.frame; //playerFrame.position = playerActor.Position; PlayerMovement playerMovement = playerActor.GetScript(); //playerMovement.Input.GetState(serverWorldState.frame, out var inputState, out var actorState); playerFrame.position = playerMovement.movementState.position; } foreach (KeyValuePair kv in players) { var otherPlayerId = kv.Key; foreach (KeyValuePair kv2 in players) { //if (kv2.Key == playerId) // continue; var playerActor = kv2.Value; var playerId = kv2.Key; // TODO: relevancy checks here etc. PlayerMovement playerMovement = playerActor.GetScript(); //PlayerActorState actorState = playerMovement.input.GetCurrentActorState(); //PlayerInputState inputState = playerMovement.input.GetCurrentInputState(); var playerFrame = playerLastFrame[playerId]; var frameInfo = GetPlayerFrame(playerId, playerFrame); //if (!playerMovement.Input.GetState(playerFrame, out var inputState, out var actorState)) if (frameInfo == null) { Console.Print($"send input failure to client, frame {playerFrame}"); continue; } { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerPosition); message.WriteUInt64(playerFrame); message.WriteUInt32(kv2.Key); message.WriteSingle(frameInfo.movementState.position.X); message.WriteSingle(frameInfo.movementState.position.Y); message.WriteSingle(frameInfo.movementState.position.Z); message.WriteSingle(frameInfo.movementState.currentVelocity.X); message.WriteSingle(frameInfo.movementState.currentVelocity.Y); message.WriteSingle(frameInfo.movementState.currentVelocity.Z); //message.WriteSingle(frameInfo.movementState.orientation.X); //message.WriteSingle(frameInfo.movementState.orientation.Y); //message.WriteSingle(frameInfo.movementState.orientation.Z); //message.WriteSingle(frameInfo.movementState.orientation.W); message.WriteSingle(frameInfo.movementState.viewAngles.X); message.WriteSingle(frameInfo.movementState.viewAngles.Y); message.WriteSingle(frameInfo.movementState.viewAngles.Z); message.WriteSingle(frameInfo.movementState.lastJumped); message.WriteInt32(frameInfo.movementState.numJumps); message.WriteBoolean(frameInfo.movementState.jumped); //inputState.frame message.WriteSingle(frameInfo.inputState.ViewDelta.X); message.WriteSingle(frameInfo.inputState.ViewDelta.Y); message.WriteSingle(frameInfo.inputState.MoveForward); message.WriteSingle(frameInfo.inputState.MoveRight); message.WriteBoolean(frameInfo.inputState.Attack); message.WriteBoolean(frameInfo.inputState.Jump); NetworkManager.ServerEndSendMessage(ref message, playerConnections[otherPlayerId]); } } } } if (IsClient || IsLocalClient) { //if (!welcomed) // return; if (welcomed) { foreach (PlayerActor playerActor in scene.GetChildren()) { 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[ClientFrame % 120]; playerFrame.frame = ClientFrame; playerFrame.position = playerActor.Position; if (playerId == NetworkManager.LocalPlayerClientId && GetPlayerInputState(playerId, clientWorldState.frame, out var inputState)) { var message = NetworkManager.ClientBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerInput); message.WriteUInt64(clientWorldState.frame); message.WriteSingle(inputState.ViewDelta.X); message.WriteSingle(inputState.ViewDelta.Y); message.WriteSingle(inputState.MoveForward); message.WriteSingle(inputState.MoveRight); message.WriteBoolean(inputState.Attack); message.WriteBoolean(inputState.Jump); NetworkManager.ClientEndSendMessage(ref message); } } } clientWorldState.frame++; /*PlayerActor playerActor = scene.GetChildren().FirstOrDefault(x => x.GetScript().PlayerId == NetworkManager.LocalPlayerClientId); if (playerActor == null) return;*/ //var playerFrame = localPlayerFrameHistory[serverWorldState.frame % 120]; //playerFrame.frame = serverWorldState.frame; //playerFrame.position = playerActor.Position; /*{ NetworkMessage message = NetworkManager.ClientBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerPosition); message.WriteUInt64(worldState.frame); message.WriteUInt32(NetworkManager.LocalPlayerClientId); message.WriteSingle(playerActor.Position.X); message.WriteSingle(playerActor.Position.Y); message.WriteSingle(playerActor.Position.Z); NetworkManager.ClientEndSendMessage(ref message); }*/ } else if (IsLocalClient) { } serverWorldState.frame++; /*foreach (KeyValuePair kv in players) { var playerId = kv.Key; var playerActor = kv.Value; var playerConnection = playerConnections[playerId]; if (NetworkManager.IsLocalClient && playerId == NetworkManager.LocalPlayerClientId) continue; { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerPosition); message.WriteUInt64(0); message.WriteSingle(playerActor.Position.X); message.WriteSingle(playerActor.Position.Y); message.WriteSingle(playerActor.Position.Z); NetworkManager.ServerEndSendMessage(ref message, playerConnections[playerId]); } }*/ } public bool OnMessageServer(ref NetworkEvent networkEvent) { return OnMessage(ref networkEvent); } public bool OnMessageClient(ref NetworkEvent networkEvent) { return OnMessage(ref networkEvent /*NetworkMessage message, NetworkConnection sender*/); } public bool OnMessage(ref NetworkEvent networkEvent) { byte messageTypeByte = networkEvent.Message.ReadByte(); if (!Enum.IsDefined(typeof(GameModeMessageType), messageTypeByte)) { //Console.PrintError($"GameModeManager: Unsupported message type received from client: {messageTypeByte}"); return false; } GameModeMessageType messageType = (GameModeMessageType)messageTypeByte; NetworkManager.DebugLastHandledMessage = messageType.ToString(); //Console.Print($"GameModeManager: {messageType}"); switch (messageType) { case GameModeMessageType.AcceptConnection: { welcomed = true; if (IsClient || IsLocalClient) { AcceptConnectionMessage welcomePlayer = AcceptConnectionMessage.Read(ref networkEvent.Message); if (!IsLocalClient) serverWorldState.frame = welcomePlayer.Frame; //lastReceivedServerFrame = welcomePlayer.frame; clientWorldState.frame += welcomePlayer.Frame; ClientTime = welcomePlayer.GameTime; foreach (var playerInfo in welcomePlayer.Players) { SpawnPlayer(playerInfo.PlayerId, playerInfo.PlayerPosition, new Float3(0)); var playerFrames = new PlayerFrame[120]; for (int j = 0; j < playerFrames.Length; j++) playerFrames[j] = new PlayerFrame(); clientWorldState.playerFrameHistory.Add(playerInfo.PlayerId, playerFrames); } Console.Print("received welcome: frame " + serverWorldState.frame); if (!players.ContainsKey(NetworkManager.LocalPlayerClientId)) // listen server players.Add(NetworkManager.LocalPlayerClientId, null); //playerConnections.Add(NetworkManager.LocalPlayerClientId, connection); } break; } case GameModeMessageType.SpawnPlayer: { uint playerId = networkEvent.Message.ReadUInt32(); ulong playerFrameIndex = networkEvent.Message.ReadUInt64(); 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); } SpawnPlayer(playerId, new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()), new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle())); //if (NetworkManager.IsClient) players[playerId].GetScript().Input.SetFrame(ClientFrame); break; } case GameModeMessageType.PlayerInput: { uint playerId = networkEvent.Sender.ConnectionId; PlayerInputState2 inputState = new PlayerInputState2(); ulong frame = networkEvent.Message.ReadUInt64(); inputState.ViewDelta.X = networkEvent.Message.ReadSingle(); inputState.ViewDelta.Y = networkEvent.Message.ReadSingle(); inputState.MoveForward = networkEvent.Message.ReadSingle(); inputState.MoveRight = networkEvent.Message.ReadSingle(); inputState.Attack = networkEvent.Message.ReadBoolean(); inputState.Jump = networkEvent.Message.ReadBoolean(); UpdatePlayerInput(playerId, frame, inputState); playerLastReceivedFrames[playerId] = frame; break; } case GameModeMessageType.PlayerPosition: { //uint playerId = networkEvent.Sender.ConnectionId; PlayerInputState2 inputState; //? PlayerActorState actorState; ulong reportedFrame = networkEvent.Message.ReadUInt64(); uint reportedPlayerId = networkEvent.Message.ReadUInt32(); actorState.position.X = networkEvent.Message.ReadSingle(); actorState.position.Y = networkEvent.Message.ReadSingle(); actorState.position.Z = networkEvent.Message.ReadSingle(); actorState.velocity.X = networkEvent.Message.ReadSingle(); actorState.velocity.Y = networkEvent.Message.ReadSingle(); actorState.velocity.Z = networkEvent.Message.ReadSingle(); /*actorState.orientation.X = networkEvent.Message.ReadSingle(); actorState.orientation.Y = networkEvent.Message.ReadSingle(); actorState.orientation.Z = networkEvent.Message.ReadSingle(); actorState.orientation.W = networkEvent.Message.ReadSingle();*/ actorState.viewAngles.X = networkEvent.Message.ReadSingle(); 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.ViewDelta.X = networkEvent.Message.ReadSingle(); inputState.ViewDelta.Y = networkEvent.Message.ReadSingle(); inputState.MoveForward = networkEvent.Message.ReadSingle(); inputState.MoveRight = networkEvent.Message.ReadSingle(); inputState.Attack = networkEvent.Message.ReadBoolean(); inputState.Jump = networkEvent.Message.ReadBoolean(); //if (actorState.viewAngles != new Float3(90f, 0f, 0f)) // Console.Print($"{reportedFrame} has viewangles: {actorState.viewAngles}"); //Assert.IsTrue(reportedFrame >= lastReceivedServerFrame); if (reportedPlayerId == NetworkManager.LocalPlayerClientId && reportedFrame < lastReceivedServerFrame) { //Console.Print($"packet wrong order, last received: {lastReceivedServerFrame}, new: {reportedFrame}"); break; } if (IsClient) { if (reportedPlayerId == NetworkManager.LocalPlayerClientId) lastReceivedServerFrame = reportedFrame; //Console.Print($"we drifted, corrected. client frame: {serverWorldState.frame}, server frame: {reportedFrame}"); PlayerActor playerActor = scene.GetChildren().FirstOrDefault(x => x.GetScript().PlayerId == reportedPlayerId); if (playerActor != null) { IPlayerInput playerInput = playerActor.GetScript().Input; /*if (IsLocalClient && reportedPlayerId == NetworkManager.LocalPlayerClientId) { } else playerInput.SetFrame(reportedFrame);*///playerInput.SetState(reportedFrame, inputState, actorState); if (!IsLocalClient && playerInput is PlayerInputNetwork2) { playerActor.Position = actorState.position; playerActor.GetScript().movementState.currentVelocity = actorState.velocity; playerActor.GetScript().movementState.lastJumped = actorState.lastJumpTime; playerActor.GetScript().movementState.numJumps = actorState.numJumps; playerActor.GetScript().movementState.jumped = actorState.jumped; playerActor.GetScript().SetCameraEulerAngles(actorState.viewAngles, true); } //playerActor.SetPosition(reportedPosition); } } break; } default: Console.PrintError($"GameModeManager: Unhandled message type: {messageType}"); return false; } return true; } public bool OnClientConnecting(NetworkConnection connection) { //if (connection.ConnectionId != NetworkManager.LocalPlayerClientId) { Console.Print("sending welcome: frame " + serverWorldState.frame); AcceptConnectionMessage welcomeMessage = new AcceptConnectionMessage(); welcomeMessage.Frame = serverWorldState.frame; welcomeMessage.GameTime = ServerTime; welcomeMessage.Players = new AcceptConnectionMessage.PlayerInfo[serverWorldState.actors.Count]; for (int i = 0; i < serverWorldState.actors.Count; i++) { PlayerActor playerActor = serverWorldState.actors[i]; ref AcceptConnectionMessage.PlayerInfo playerInfo = ref welcomeMessage.Players[i]; playerInfo.PlayerId = playerActor.GetScript().PlayerId; playerInfo.PlayerPosition = playerActor.Position; /*playerActor.GetScript().Input.SetState(serverWorldState.frame, new PlayerInputState(), new PlayerActorState() { position = playerActor.Position, velocity = playerActor.GetScript().movementState.currentVelocity, orientation = playerActor.GetScript().rootActor.Orientation, viewAngles = playerActor.GetScript().viewAngles, lastJumpTime = playerActor.GetScript().movementState.lastJumped, numJumps = playerActor.GetScript().movementState.numJumps, jumped = playerActor.GetScript().movementState.jumped, //onGround = playerActor.GetScript().movementState.onGround, });*/ } NetworkMessage message = NetworkManager.ServerBeginSendMessage(); welcomeMessage.Write(ref message); NetworkManager.ServerEndSendMessage(ref message, connection); } return true; } public bool OnClientConnected(NetworkConnection connection) { uint playerId = connection.ConnectionId; if (NetworkManager.LocalPlayerClientId == 0) NetworkManager.LocalPlayerClientId = playerId; var spawns = worldSpawn.GetChildren().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray(); Console.Print($"found {spawns.Length} spawns"); var randomSpawn = spawns.First(); Float3 position = randomSpawn.Position + new Float3(0f, 4.1f, 0f); Float3 eulerAngles = randomSpawn.Orientation.EulerAngles; players.Add(playerId, null); playerConnections.Add(playerId, connection); var playerFrames = new PlayerFrame[120]; for (int i = 0; i < playerFrames.Length; i++) playerFrames[i] = new PlayerFrame(); serverWorldState.playerFrameHistory.Add(playerId, playerFrames); SpawnPlayer(playerId, position, eulerAngles); { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.SpawnPlayer); message.WriteUInt32(playerId); message.WriteUInt64(serverWorldState.frame); message.WriteSingle(position.X); message.WriteSingle(position.Y); message.WriteSingle(position.Z); message.WriteSingle(eulerAngles.X); message.WriteSingle(eulerAngles.Y); message.WriteSingle(eulerAngles.Z); NetworkManager.ServerEndSendMessage(ref message, connection); } return true; } private void SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles) { if (IsServer && !playerLastFrame.ContainsKey(playerId)) playerLastFrame.Add(playerId, serverWorldState.frame); //if (IsLocalClient && playerId == NetworkManager.LocalPlayerClientId) // return; // Handled by listenserver //spawned = true; string prefabPath = Path.Combine(AssetManager.ContentPath, "Common"); var playerPrefab = Content.Load(Path.Combine(prefabPath, "PlayerPrefab.prefab")); if (playerPrefab == null) Console.PrintError("GameModeManager: Failed to find PlayerPrefab"); PlayerActor playerActor = SpawnActor(playerPrefab); playerActor.Initialize(playerId, position, eulerAngles); //playerActor.Teleport(position, eulerAngles); if (!players.ContainsKey(playerId)) players.Add(playerId, null); players[playerId] = playerActor; IPlayerInput playerInput = playerActor.GetScript().Input; playerInput.SetFrame(IsServer ? serverWorldState.frame : ClientFrame); if (IsServer) { serverWorldState.actors.Add(playerActor); if (!playerLastReceivedFrames.ContainsKey(playerId)) playerLastReceivedFrames.Add(playerId, 0); } } private T SpawnActor(Prefab prefab) where T : Actor { T actor = PrefabManager.SpawnPrefab(prefab, scene).As(); actor.PhysicsScene = scene.PhysicsScene; return actor; } private void UpdatePlayerInput(uint playerId, ulong frame, PlayerInputState2 inputState) { #if false WorldState worldState = /*NetworkManager.server != null*/IsServer ? serverWorldState : clientWorldState; //if (!IsServer && playerId != NetworkManager.LocalPlayerClientId) // return; // Do not record input for networked players PlayerFrame[] playerFrames = worldState.playerFrameHistory[playerId]; PlayerFrame playerFrame = playerFrames[frame % 120]; playerFrame.frame = frame; playerFrame.inputState = inputState; //playerFrame.movementState = movementState; playerLastFrame[playerId] = frame; #endif Console.Print($"server received input frame {frame}"); #if true 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 >= 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 nextframe = startFrame+1; PlayerFrame prevFrame = GetPlayerFrame(playerId, startFrame); PlayerInputState2 prevInputState = prevFrame?.inputState ?? default; //playerMovement.Input.GetState(frame - 1, out prevInputState, out var _); for (; nextframe <= frame; nextframe++) { if (!GetPlayerInputState(playerId, nextframe, out PlayerInputState2 lastInputState)) { } if (nextframe == frame) lastInputState = inputState; //PlayerInputState2 lastInputState = frameInfo?.inputState ?? prevInputState; //if (!playerMovement.Input.GetState(frame, out var lastInputState, out var lastActorState)) /*if (frameInfo == null) { // dropped frame, use previous input lastInputState = prevInputState; lastInputState.frame = frame; }*/ playerMovement.ApplyInputToCamera(lastInputState, true); playerMovement.SimulatePlayerMovement(lastInputState, nextframe); RecordPlayerInput(playerId, Frame, lastInputState, playerMovement.movementState); /*playerMovement.Input.SetState(frame, lastInputState, new PlayerActorState() { position = playerActor.Position, velocity = playerMovement.movementState.currentVelocity, orientation = playerMovement.rootActor.Orientation, viewAngles = playerMovement.viewAngles, lastJumpTime = playerMovement.movementState.lastJumped, numJumps = playerMovement.movementState.numJumps, jumped = playerMovement.movementState.jumped, //onGround = playerMovement.movementState.onGround, });*/ simframs++; prevInputState = lastInputState; } if (playerActor.Position.Length < 1.0f) simframs = simframs; playerLastFrame[playerId] = frame;//frame; if (simframs > 1) Console.Print($"simulated {simframs} frames"); #endif } } #endif