using System; using System.Collections.Generic; using System.IO; using System.Linq; using FlaxEngine; using FlaxEngine.Networking; using Console = Game.Console; namespace Game { public enum GameModeMessageType : byte { WelcomePlayer, SpawnPlayer, PlayerInput, PlayerPosition, // debug } public static class GameModeManager { private class WorldState { public ulong frame; public List actors; public Dictionary playerFrameHistory; public WorldState() { actors = new List(); playerFrameHistory = new Dictionary(); } } private class PlayerFrame { public ulong frame; public Float3 position; } private static Dictionary players; private static Dictionary playerConnections; private static bool spawned = false; private static WorldState worldState; private static PlayerFrame[] localPlayerFrameHistory; public static void Init() { players = new Dictionary(); playerConnections = new Dictionary(); localPlayerFrameHistory = new PlayerFrame[120]; worldState = new WorldState(); NetworkManager.OnMessage += OnMessage; Level.SceneLoaded += OnLevelLoaded; Scripting.LateUpdate += OnLateUpdatePre; } public static void Cleanup() { NetworkManager.OnMessage -= OnMessage; Level.SceneLoaded -= OnLevelLoaded; Scripting.LateUpdate -= OnLateUpdatePre; } private static PlayerFrame GetPlayerFrame(uint playerIndex, ulong playerFrameIndex) { PlayerFrame[] playerFrames = worldState.playerFrameHistory[playerIndex]; PlayerFrame playerFrame = playerFrames[playerFrameIndex % 120]; if (playerFrame.frame != playerFrameIndex) return null; return playerFrame; } public static void OnLevelLoaded(Scene scene, Guid assetGuid) { worldState.frame = 0; Console.Print("level loaded"); } public static void OnLateUpdatePre() { try { NetworkManager.IsServer = NetworkManager.server != null; NetworkManager.IsClient = NetworkManager.client != null && NetworkManager.server == null; OnLateUpdate(); } finally { NetworkManager.IsServer = false; NetworkManager.IsClient = false; } } public static void OnLateUpdate() { if (NetworkManager.IsServer) { foreach (KeyValuePair kv in players) { var playerId = kv.Key; var playerActor = kv.Value; var playerFrames = worldState.playerFrameHistory[playerId]; var playerFrame = playerFrames[worldState.frame % 120]; playerFrame.frame = worldState.frame; playerFrame.position = playerActor.Position; } foreach (KeyValuePair kv in players) { var playerId = kv.Key; foreach (KeyValuePair kv2 in players) { if (kv2.Key == playerId) continue; var otherPlayerActor = kv2.Value; // TODO: relevancy checks here etc. { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerPosition); message.WriteUInt64(worldState.frame); message.WriteUInt32(kv2.Key); message.WriteSingle(otherPlayerActor.Position.X); message.WriteSingle(otherPlayerActor.Position.Y); message.WriteSingle(otherPlayerActor.Position.Z); NetworkManager.ServerEndSendMessage(ref message, playerConnections[playerId]); } } } } if (NetworkManager.IsClient) { if (!spawned) return; PlayerActor playerActor = Level.GetActors().First(x => x.GetScript().PlayerId == NetworkManager.LocalPlayerClientId); var playerFrame = localPlayerFrameHistory[worldState.frame % 120]; playerFrame.frame = worldState.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); } } worldState.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 static 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; //Console.Print($"GameModeManager: {messageType}"); switch (messageType) { case GameModeMessageType.WelcomePlayer: { if (NetworkManager.IsClient) { worldState.frame = networkEvent.Message.ReadUInt64(); int numActors = (int)networkEvent.Message.ReadUInt32(); for (int i=0; i 0.0001) { Console.Print("player drifted, len: " + (playerFramePosition - reportedPosition).Length); { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerPosition); /*message.WriteUInt64(reportedFrame); message.WriteUInt32(playerId); message.WriteSingle(playerFramePosition.X); message.WriteSingle(playerFramePosition.Y); message.WriteSingle(playerFramePosition.Z);*/ message.WriteUInt64(worldState.frame); message.WriteUInt32(playerId); message.WriteSingle(playerActor.Position.X); message.WriteSingle(playerActor.Position.Y); message.WriteSingle(playerActor.Position.Z); NetworkManager.ServerEndSendMessage(ref message, playerConnections[playerId]); } } } } else if (NetworkManager.IsClient) { Console.Print($"we drifted, corrected. client frame: {worldState.frame}, server frame: {reportedFrame}"); PlayerActor playerActor = Level.GetActors().FirstOrDefault(x => x.GetScript().PlayerId == reportedPlayerId); if (playerActor != null) playerActor.SetPosition(reportedPosition); } break; } default: Console.PrintError($"GameModeManager: Unhandled message type: {messageType}"); return false; } return true; } public static bool OnClientConnecting(NetworkConnection connection) { if (connection.ConnectionId != NetworkManager.LocalPlayerClientId) { Console.Print("sending welcome: frame " + worldState.frame); NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.WelcomePlayer); message.WriteUInt64(worldState.frame); message.WriteUInt32((uint)worldState.actors.Count); foreach (PlayerActor playerActor in worldState.actors) { message.WriteUInt32(playerActor.GetScript().PlayerId); message.WriteSingle(playerActor.Position.X); message.WriteSingle(playerActor.Position.Y); message.WriteSingle(playerActor.Position.Z); } NetworkManager.ServerEndSendMessage(ref message, connection); } return true; } public static bool OnClientConnected(NetworkConnection connection) { var spawns = Level.GetActors().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray(); Console.Print($"found {spawns.Length} spawns"); var randomSpawn = spawns.First(); uint playerId = connection.ConnectionId; 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(); worldState.playerFrameHistory.Add(playerId, playerFrames); SpawnPlayer(playerId, position, eulerAngles); { NetworkMessage message = NetworkManager.ServerBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.SpawnPlayer); message.WriteUInt32(playerId); message.WriteUInt64(worldState.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 static void SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles) { if (NetworkManager.IsLocalClient) 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 = PrefabManager.SpawnPrefab(playerPrefab).As(); playerActor.Initialize(playerId); playerActor.Teleport(position, eulerAngles); if (!players.ContainsKey(playerId)) players.Add(playerId, null); players[playerId] = playerActor; if (NetworkManager.IsServer) worldState.actors.Add(playerActor); } private static void UpdatePlayerInput(uint playerId, PlayerInputState inputState) { PlayerActor playerActor = players[playerId]; playerActor.UpdateNetworkInput(inputState); } } }