Files
GoakeFlax/Source/Game/GameMode/WorldStateManager.cs
2025-03-28 15:24:44 +02:00

817 lines
34 KiB
C#

#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<PlayerActor> actors;
public Dictionary<uint, PlayerFrame[]> playerFrameHistory;
public WorldState()
{
actors = new List<PlayerActor>();
playerFrameHistory = new Dictionary<uint, PlayerFrame[]>();
}
}
public class PlayerFrame
{
public ulong frame;
public Float3 position;
public PlayerInputState2 inputState;
public PlayerMovementState movementState;
}
private Dictionary<uint, PlayerActor> players;
private Dictionary<uint, NetworkConnection> playerConnections;
public Dictionary<uint, ulong> playerLastReceivedFrames;
private Dictionary<uint, ulong> 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<uint, PlayerActor>();
playerConnections = new Dictionary<uint, NetworkConnection>();
playerLastReceivedFrames = new Dictionary<uint, ulong>();
playerLastFrame = new Dictionary<uint, ulong>();
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<Q3MapImporter>();
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<PlayerActor>())
{
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<uint,PlayerActor> 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>();
//playerMovement.Input.GetState(serverWorldState.frame, out var inputState, out var actorState);
playerFrame.position = playerMovement.movementState.position;
}
foreach (KeyValuePair<uint, PlayerActor> kv in players)
{
var otherPlayerId = kv.Key;
foreach (KeyValuePair<uint, PlayerActor> 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<PlayerMovement>();
//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<PlayerActor>())
{
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<PlayerActor>().FirstOrDefault(x =>
x.GetScript<PlayerMovement>().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<uint,PlayerActor> 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<PlayerMovement>().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<PlayerActor>().FirstOrDefault(x =>
x.GetScript<PlayerMovement>().PlayerId == reportedPlayerId);
if (playerActor != null)
{
IPlayerInput playerInput = playerActor.GetScript<PlayerMovement>().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<PlayerMovement>().movementState.currentVelocity = actorState.velocity;
playerActor.GetScript<PlayerMovement>().movementState.lastJumped = actorState.lastJumpTime;
playerActor.GetScript<PlayerMovement>().movementState.numJumps = actorState.numJumps;
playerActor.GetScript<PlayerMovement>().movementState.jumped = actorState.jumped;
playerActor.GetScript<PlayerMovement>().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<PlayerMovement>().PlayerId;
playerInfo.PlayerPosition = playerActor.Position;
/*playerActor.GetScript<PlayerMovement>().Input.SetState(serverWorldState.frame, new PlayerInputState(), new PlayerActorState()
{
position = playerActor.Position,
velocity = playerActor.GetScript<PlayerMovement>().movementState.currentVelocity,
orientation = playerActor.GetScript<PlayerMovement>().rootActor.Orientation,
viewAngles = playerActor.GetScript<PlayerMovement>().viewAngles,
lastJumpTime = playerActor.GetScript<PlayerMovement>().movementState.lastJumped,
numJumps = playerActor.GetScript<PlayerMovement>().movementState.numJumps,
jumped = playerActor.GetScript<PlayerMovement>().movementState.jumped,
//onGround = playerActor.GetScript<PlayerMovement>().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<Actor>().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<Prefab>(Path.Combine(prefabPath, "PlayerPrefab.prefab"));
if (playerPrefab == null)
Console.PrintError("GameModeManager: Failed to find PlayerPrefab");
PlayerActor playerActor = SpawnActor<PlayerActor>(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<PlayerMovement>().Input;
playerInput.SetFrame(IsServer ? serverWorldState.frame : ClientFrame);
if (IsServer)
{
serverWorldState.actors.Add(playerActor);
if (!playerLastReceivedFrames.ContainsKey(playerId))
playerLastReceivedFrames.Add(playerId, 0);
}
}
private T SpawnActor<T>(Prefab prefab) where T : Actor
{
T actor = PrefabManager.SpawnPrefab(prefab, scene).As<T>();
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<PlayerMovement>();
//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