Files
GoakeFlax/Source/Game/GameMode/GameModeManager.cs

410 lines
17 KiB
C#

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<PlayerActor> actors;
public Dictionary<uint, PlayerFrame[]> playerFrameHistory;
public WorldState()
{
actors = new List<PlayerActor>();
playerFrameHistory = new Dictionary<uint, PlayerFrame[]>();
}
}
private class PlayerFrame
{
public ulong frame;
public Vector3 position;
}
private static Dictionary<uint, PlayerActor> players;
private static Dictionary<uint, NetworkConnection> playerConnections;
private static bool spawned = false;
private static WorldState worldState;
private static PlayerFrame[] localPlayerFrameHistory;
public static void Init()
{
players = new Dictionary<uint, PlayerActor>();
playerConnections = new Dictionary<uint, NetworkConnection>();
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<uint,PlayerActor> 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<uint, PlayerActor> kv in players)
{
var playerId = kv.Key;
foreach (KeyValuePair<uint, PlayerActor> 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<PlayerActor>().First(x =>
x.GetScript<PlayerMovement>().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<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 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<numActors; i++)
{
uint playerId = networkEvent.Message.ReadUInt32();
Vector3 playerPosition;
playerPosition.X = networkEvent.Message.ReadSingle();
playerPosition.Y = networkEvent.Message.ReadSingle();
playerPosition.Z = networkEvent.Message.ReadSingle();
SpawnPlayer(playerId, playerPosition, new Vector3(0));
}
Console.Print("received welcome: frame " + worldState.frame);
players.Add(NetworkManager.LocalPlayerClientId, null);
//playerConnections.Add(NetworkManager.LocalPlayerClientId, connection);
localPlayerFrameHistory = new PlayerFrame[120];
for (int i = 0; i < localPlayerFrameHistory.Length; i++)
localPlayerFrameHistory[i] = new PlayerFrame();
}
break;
}
case GameModeMessageType.SpawnPlayer:
{
uint playerId = networkEvent.Message.ReadUInt32();
ulong playerFrameIndex = networkEvent.Message.ReadUInt64();
SpawnPlayer(playerId,
new Vector3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()),
new Vector3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()));
break;
}
case GameModeMessageType.PlayerInput:
{
uint playerId = networkEvent.Sender.ConnectionId;
PlayerInputState inputState = new PlayerInputState();
inputState.frame = networkEvent.Message.ReadUInt64();
inputState.viewDeltaX = networkEvent.Message.ReadSingle();
inputState.viewDeltaY = networkEvent.Message.ReadSingle();
inputState.moveForward = networkEvent.Message.ReadSingle();
inputState.moveRight = networkEvent.Message.ReadSingle();
inputState.attacking = networkEvent.Message.ReadBoolean();
inputState.jumping = networkEvent.Message.ReadBoolean();
UpdatePlayerInput(playerId, inputState);
break;
}
case GameModeMessageType.PlayerPosition:
{
uint playerId = networkEvent.Sender.ConnectionId;
Vector3 reportedPosition;
ulong reportedFrame = networkEvent.Message.ReadUInt64();
uint reportedPlayerId = networkEvent.Message.ReadUInt32();
reportedPosition.X = networkEvent.Message.ReadSingle();
reportedPosition.Y = networkEvent.Message.ReadSingle();
reportedPosition.Z = networkEvent.Message.ReadSingle();
if (NetworkManager.IsLocalClient && !NetworkManager.IsServer)
{
}
else if (NetworkManager.IsServer)
{
PlayerFrame playerFrame = GetPlayerFrame(playerId, reportedFrame);
PlayerActor playerActor = players[playerId];
if (playerFrame == null)
Console.Print("frame is in the past, unable to verify frame");
else
{
Vector3 playerFramePosition = playerFrame.position;
if ((playerFramePosition - reportedPosition).Length > 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<PlayerActor>().FirstOrDefault(x =>
x.GetScript<PlayerMovement>().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<PlayerMovement>().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<Actor>().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray();
Console.Print($"found {spawns.Length} spawns");
var randomSpawn = spawns.First();
uint playerId = connection.ConnectionId;
Vector3 position = randomSpawn.Position + new Vector3(0f, 4.1f, 0f);
Vector3 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, Vector3 position, Vector3 eulerAngles)
{
if (NetworkManager.IsLocalClient)
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 = PrefabManager.SpawnPrefab(playerPrefab).As<PlayerActor>();
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);
}
}
}