_net prog

This commit is contained in:
2025-06-21 20:59:56 +03:00
parent 429b0c4468
commit 0d324c0ff0
16 changed files with 275 additions and 108 deletions

View File

@@ -2,6 +2,8 @@
using FlaxEngine;
using Console = Game.Console;
using System.Linq;
using System.Threading;
#if FLAX_EDITOR
using FlaxEditor;
#endif

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using FlaxEngine;
using FlaxEngine.Assertions;
@@ -71,16 +72,16 @@ internal class ConsoleVariable
if (type == typeof(string))
{
if (field != null)
return (string)field.GetValue(null);
return Unsafe.As<string>(field.GetValue(null));
if (getter != null)
return ((float)getter.Invoke(null, null)).ToString();
return Unsafe.As<string>(getter.Invoke(null, null));
}
else if (type == typeof(float))
{
if (field != null)
return field.GetValue(null).ToString();
if (getter != null)
return ((float)getter.Invoke(null, null)).ToString();
return getter.Invoke(null, null).ToString();
}
else
throw new Exception($"ConsoleVariable: Unsupported variable type '{type.FullName}'");

View File

@@ -161,10 +161,10 @@ public static partial class NetworkManager
public static void Cleanup()
{
Scripting.Exit -= Cleanup;
Scripting.FixedUpdate -= OnDemoUpdate;
Scripting.FixedUpdate -= OnServerNetworkUpdate;
Scripting.Update -= OnDemoUpdate;
Scripting.Update -= OnServerNetworkUpdate;
Level.ActorSpawned -= OnServerActorSpawned;
Scripting.FixedUpdate -= OnClientNetworkUpdate;
Scripting.Update -= OnClientNetworkUpdate;
Level.ActorSpawned -= OnClientActorSpawned;
if (server != null)

View File

@@ -61,7 +61,7 @@ public static partial class NetworkManager
}*/
World.InitClient();
Scripting.FixedUpdate += OnClientNetworkUpdate;
Scripting.Update += OnClientNetworkUpdate;
if (!listenServer)
{
Scripting.Exit += Cleanup;

View File

@@ -59,7 +59,7 @@ public static unsafe partial class NetworkManager
return false;
}*/
Scripting.FixedUpdate += OnDemoUpdate;
Scripting.Update += OnDemoUpdate;
//if (!listenServer)
{
Scripting.Exit += Cleanup;

View File

@@ -26,15 +26,16 @@ public static partial class NetworkManager
ConnectedClients = new List<NetworkConnection>(MaximumClients);
//var driver = Object.New(typeof(ENetDriver));
//ServerNetworkDriver = null;
//INetworkDriver driver = Object.New<ENetDriver>();
NetworkLagDriver driver = Object.New<NetworkLagDriver>();
driver.Lag = 200f;
if (driver is NetworkLagDriver networkLagDriver)
networkLagDriver.Lag = 200f;
ServerNetworkDriver = driver;
server = NetworkPeer.CreatePeer(new NetworkConfig
{
NetworkDriver = driver,
NetworkDriver = (Object)driver,
ConnectionsLimit = MaximumClients,
MessagePoolSize = 2048,
MessageSize = MTU,
@@ -47,7 +48,7 @@ public static partial class NetworkManager
return false;
}
Scripting.FixedUpdate += OnServerNetworkUpdate;
Scripting.Update += OnServerNetworkUpdate;
Scripting.Exit += Cleanup;
Level.ActorSpawned += OnServerActorSpawned;

View File

@@ -22,29 +22,23 @@ public enum GameModeMessageType2 : byte
LastMessageType = 128,
}
public class PlayerFrame
{
public ulong frame;
public Float3 position;
public PlayerInputState2 inputState;
public PlayerMovementState movementState;
}
public interface IClientInfo
{
public NetworkConnection Connection { get; set; }
public PlayerActor PlayerActor { get; set; }
public PlayerFrame[] FrameHistory { get; set; }
}
public class World
{
protected class ClientInfo
{
public NetworkConnection Connection;
public PlayerActor PlayerActor;
public PlayerFrame[] FrameHistory = new PlayerFrame[120];
public ClientInfo()
{
for (int i = 0; i < FrameHistory.Length; i++)
FrameHistory[i] = new PlayerFrame();
}
}
public class PlayerFrame
{
public ulong frame;
public Float3 position;
public PlayerInputState2 inputState;
public PlayerMovementState movementState;
}
public bool IsServer => this is ServerWorld;
public bool IsClient => !IsServer;
public ulong Frame { get; protected set; }
@@ -54,7 +48,6 @@ public class World
protected Scene _scene;
protected Actor _worldSpawn;
protected Dictionary<uint, ClientInfo> _clients = new();
public static void InitClient()
{
@@ -65,6 +58,9 @@ public class World
{
WorldStore.ServerWorld = new ServerWorld();
WorldStore.ServerWorld.Init();
Scripting.Update += WorldStore.ServerWorld.OnUpdate;
Scripting.LateUpdate += WorldStore.ServerWorld.OnUpdateLate;
Scripting.FixedUpdate += WorldStore.ServerWorld.OnFixedUpdate;
}
public static void CleanupClient()
{
@@ -79,12 +75,17 @@ public class World
virtual protected void Init()
{
Scripting.LateFixedUpdate += OnLateUpdate;
}
virtual protected void Cleanup()
{
//Level.SceneLoaded -= OnLevelLoaded;
//Scripting.LateUpdate -= OnLateUpdatePre;
Scripting.Update -= OnUpdate;
Scripting.LateUpdate -= OnUpdateLate;
Scripting.FixedUpdate -= OnFixedUpdate;
Scripting.LateFixedUpdate -= OnLateUpdate;
if (_scene)
@@ -99,6 +100,21 @@ public class World
}
}
void OnUpdate()
{
Console.Print("server Update");
}
void OnUpdateLate()
{
Console.Print("server LateUpdate");
}
void OnFixedUpdate()
{
Console.Print("server FixedUpdate");
}
protected void CreateScene(string sceneNamePrefix, string sceneGuid)
{
string physicsSceneName = $"{sceneNamePrefix}PhysicsScene";
@@ -155,7 +171,7 @@ public class World
_scene.Name = $"{sceneNamePrefix}Scene";
//Level.SceneLoaded += OnLevelLoaded;
Scripting.LateFixedUpdate += OnLateUpdate;
var importer = FlaxEngine.Object.New<Q3MapImporter>();
importer.mapPath = @"C:\dev\GoakeFlax\Assets\Maps\aerowalk.map";
@@ -167,16 +183,12 @@ public class World
Assert.IsTrue(_worldSpawn);
}
public void OnLateUpdate()
virtual protected void OnLateUpdate()
{
Frame++;
}
protected void OnClientConnected(NetworkConnection connection)
{
uint playerId = connection.ConnectionId;
_clients.Add(playerId, new ClientInfo() { Connection = connection });
}
virtual protected IClientInfo GetClient(uint playerId) => null;
virtual protected PlayerActor SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles)
{
@@ -189,7 +201,8 @@ public class World
playerActor.Initialize(playerId, position, eulerAngles);
//playerActor.Teleport(position, eulerAngles);
_clients[playerId].PlayerActor = playerActor;
IClientInfo player = GetClient(playerId);
player.PlayerActor = playerActor;
IPlayerInput playerInput = playerActor.GetScript<PlayerMovement>().Input;
playerInput.SetFrame(Frame);
/*if (IsServer)
@@ -213,7 +226,7 @@ public class World
public PlayerFrame GetPlayerFrame(uint playerId, ulong frame)
{
ClientInfo player = _clients[playerId];
IClientInfo player = GetClient(playerId);
PlayerFrame playerFrame = player.FrameHistory[frame % 120];
if (playerFrame.frame != frame)
return null;
@@ -229,10 +242,52 @@ public class World
inputState = playerFrame?.inputState ?? default;
return playerFrame != null;
}
virtual public void UpdatePlayerInputState(uint playerId, ulong frame, PlayerInputState2 inputState, PlayerMovementState movementState)
{
IClientInfo player = GetClient(playerId);
PlayerFrame playerFrame = player.FrameHistory[frame % 120];
if (playerFrame.frame == frame)
{
playerFrame.inputState = inputState with
{
ViewDelta = playerFrame.inputState.ViewDelta + inputState.ViewDelta,
MoveForward = MathF.MaxMagnitude(playerFrame.inputState.MoveForward, inputState.MoveForward),
MoveRight = MathF.MaxMagnitude(playerFrame.inputState.MoveRight, inputState.MoveRight),
Attack = playerFrame.inputState.Attack || inputState.Attack,
Jump = playerFrame.inputState.Jump || inputState.Jump,
};
}
else
playerFrame.inputState = inputState;
playerFrame.movementState = movementState;
playerFrame.frame = frame;
}
}
file class ServerWorld : World
{
protected class ClientInfo : IClientInfo
{
public NetworkConnection Connection { get; set; }
public PlayerActor PlayerActor { get; set; }
public PlayerFrame[] FrameHistory { get; set; } = new PlayerFrame[120];
public ulong LastReceivedFrame = 0;
public ulong LastSentDeltaFrame = 0; // TODO: Accumulate deltas since this frame
public int SendRate = 0; // How many updates the client wants to receive per second
public ClientInfo()
{
for (int i = 0; i < FrameHistory.Length; i++)
FrameHistory[i] = new PlayerFrame();
}
}
protected Dictionary<uint, ClientInfo> _clients = new();
protected override IClientInfo GetClient(uint playerId) => _clients[playerId];
protected override void Init()
{
NetworkManager.RegisterServerCallbacks(OnMessage, OnClientConnecting, OnClientConnected);
@@ -249,6 +304,12 @@ file class ServerWorld : World
base.Cleanup();
}
protected override void OnLateUpdate()
{
Console.Print("server LateFixedUpdate");
base.OnLateUpdate();
}
public bool OnMessage(ref NetworkEvent networkEvent)
{
byte messageTypeByte = networkEvent.Message.ReadByte();
@@ -266,23 +327,9 @@ file class ServerWorld : World
{
case GameModeMessageType2.AcceptConnection: // Client
{
AcceptConnectionMessage message = AcceptConnectionMessage.Read(ref networkEvent.Message);
Frame += message.Frame;
GameTime = message.GameTime;
ServerFrame = message.Frame;
/*foreach (var player in message.Players)
{
SpawnPlayer(player.PlayerId, player.PlayerPosition, new Float3(0));
var playerFrames = new PlayerFrame[120];
for (int j = 0; j < playerFrames.Length; j++)
playerFrames[j] = new PlayerFrame();
clientWorldState.playerFrameHistory.Add(player.PlayerId, playerFrames);
}*/
break;
}
break;
case GameModeMessageType2.PlayerPosition:
case GameModeMessageType2.PlayerPosition: // FIXME: Should be the delta frame
{
//uint playerId = networkEvent.Sender.ConnectionId;
PlayerInputState2 inputState; //?
@@ -316,8 +363,40 @@ file class ServerWorld : World
inputState.Jump = networkEvent.Message.ReadBoolean();
ServerFrame = receivedFrame;
break;
}
case GameModeMessageType2.PlayerInput:
{
PlayerInputState2 inputState; //?
ulong receivedFrame = networkEvent.Message.ReadUInt64();
uint reportedPlayerId = networkEvent.Message.ReadUInt32();
inputState.Frame = receivedFrame;
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();
Console.Print($"server receive client frame {receivedFrame}, current server frame {Frame}");
var asdf = receivedFrame;
receivedFrame = Frame;
if (inputState.MoveForward != 0)
receivedFrame = receivedFrame;
// Sanity check
if (GetPlayerInputState(reportedPlayerId, receivedFrame, out var prevInputState))
{
Console.PrintWarning($"Duplicate frame received from client: {asdf}");
}
PlayerMovementState movementState = default;
UpdatePlayerInputState(reportedPlayerId, receivedFrame, inputState, movementState);
break;
}
break;
default:
break;
}
@@ -346,11 +425,10 @@ file class ServerWorld : World
return true;
}
public new void OnClientConnected(NetworkConnection connection)
public void OnClientConnected(NetworkConnection connection)
{
base.OnClientConnected(connection);
uint playerId = connection.ConnectionId;
_clients.Add(playerId, new ClientInfo() { Connection = connection });
var spawns = _worldSpawn.GetChildren<Actor>().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray();
Console.Print($"found {spawns.Length} spawns");
@@ -375,12 +453,44 @@ file class ServerWorld : World
NetworkManager.ServerEndSendMessage(ref message, connection);
}
}
public override void UpdatePlayerInputState(uint playerId, ulong frame, PlayerInputState2 inputState, PlayerMovementState movementState)
{
base.UpdatePlayerInputState(playerId, frame, inputState, movementState);
ClientInfo player = GetClient(playerId) as ClientInfo;
player.LastReceivedFrame = frame; // Dropped frames should be ignored?
}
protected override PlayerActor SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles)
{
var playerActor = base.SpawnPlayer(playerId, position, eulerAngles);
playerActor.Input = new PlayerInputNetwork2(playerId, this);
return playerActor;
}
}
file class ClientWorld : World
{
protected class ClientInfo : IClientInfo
{
public NetworkConnection Connection { get; set; }
public PlayerActor PlayerActor { get; set; }
public PlayerFrame[] FrameHistory { get; set; } = new PlayerFrame[120];
public ClientInfo()
{
for (int i = 0; i < FrameHistory.Length; i++)
FrameHistory[i] = new PlayerFrame();
}
}
private uint _localPlayerId;
protected Dictionary<uint, ClientInfo> _clients = new();
protected override IClientInfo GetClient(uint playerId) => _clients[playerId];
protected override void Init()
{
NetworkManager.RegisterClientCallbacks(OnMessage, null, OnClientConnected);
@@ -396,6 +506,44 @@ file class ClientWorld : World
base.Cleanup();
}
override protected void OnLateUpdate()
{
foreach (var player in _scene.GetChildren<PlayerActor>())
{
if (player.PlayerId != _localPlayerId)
continue;
ClientInfo playerClient = GetClient(player.PlayerId) as ClientInfo;
PlayerInputState2 inputState = player.Input.GetState();
PlayerMovementState movementState = player.GetScript<PlayerMovement>().movementState;
if (inputState.Frame == 0)
inputState = inputState;
/*public Float2 ViewDelta;
public float MoveForward;
public float MoveRight;
public bool Attack;
public bool Jump;*/
Console.Print($"client send {Frame}");
UpdatePlayerInputState(player.PlayerId, Frame, inputState, movementState);
//Console.Print($"cframe: {Frame}");
NetworkMessage message = NetworkManager.ClientBeginSendMessage();
message.WriteByte((byte)GameModeMessageType2.PlayerInput);
message.WriteUInt64(Frame);
message.WriteUInt32(player.PlayerId);
message.WriteVector2(inputState.ViewDelta);
message.WriteSingle(inputState.MoveForward);
message.WriteSingle(inputState.MoveRight);
message.WriteBoolean(inputState.Attack);
message.WriteBoolean(inputState.Jump);
NetworkManager.ClientEndSendMessage(ref message);
}
base.OnLateUpdate();
}
public bool OnMessage(ref NetworkEvent networkEvent)
{
byte messageTypeByte = networkEvent.Message.ReadByte();
@@ -426,8 +574,8 @@ file class ClientWorld : World
}
Console.Print("received welcome: frame " + ServerFrame);
break;
}
break;
case GameModeMessageType2.SpawnPlayer:
{
uint playerId = networkEvent.Message.ReadUInt32();
@@ -447,8 +595,8 @@ file class ClientWorld : World
//if (NetworkManager.IsClient)
//players[playerId].GetScript<PlayerMovement>().Input.SetFrame(ClientFrame);
break;
}
break;
#if false
case GameModeMessageType2.PlayerPosition:
{
@@ -484,8 +632,8 @@ file class ClientWorld : World
inputState.Jump = networkEvent.Message.ReadBoolean();
ServerFrame = receivedFrame;
break;
}
break;
#endif
default:
break;
@@ -496,10 +644,12 @@ file class ClientWorld : World
public override bool IsLocalPlayer(uint playerId) => playerId == _localPlayerId;
public new void OnClientConnected(NetworkConnection connection)
public void OnClientConnected(NetworkConnection connection)
{
base.OnClientConnected(connection);
_localPlayerId = connection.ConnectionId;
uint playerId = connection.ConnectionId;
_clients.Add(playerId, new ClientInfo() { Connection = connection });
_localPlayerId = playerId;
Console.Print($"ClientWorld: Connected, playerId: {_localPlayerId}");
}

View File

@@ -11,7 +11,7 @@ namespace Game;
[StructLayout(LayoutKind.Sequential)]
public struct PlayerInputState2
{
//public ulong frame;
public ulong Frame;
public Float2 ViewDelta;
public float MoveForward;
public float MoveRight;
@@ -53,8 +53,13 @@ public class PlayerInput2 : IPlayerInput
[ConsoleVariable("sensitivity")]
private static float _sensitivity { get; set; } = 1.0f;
private ulong _frame;
public void SetFrame(ulong frame)
{
if (_frame != frame)
ResetState();
_frame = frame;
}
public void UpdateState()
@@ -74,6 +79,7 @@ public class PlayerInput2 : IPlayerInput
_recordState.MoveRight = MathF.MaxMagnitude(_recordState.MoveRight, Input.GetAxis("Horizontal"));
_recordState.Attack |= Input.GetAction("Attack");
_recordState.Jump |= Input.GetAction("Jump");
_recordState.Frame = _frame;
}
public PlayerInputState2 GetState() => _recordState;

View File

@@ -252,12 +252,16 @@ public class PlayerMovement : Script
public override void OnUpdate()
{
Console.Print("playerMovement OnUpdate");
//input.OnUpdate();
//if (Input is PlayerInputDemo /*&& currentInputFrame2 >= currentInputFrame*/)
// return;
if (World.IsServer)
viewAngles = viewAngles;
Input.SetFrame(World.Frame);
Input.UpdateState();
@@ -311,13 +315,14 @@ public class PlayerMovement : Script
public override void OnFixedUpdate()
{
Console.Print("playerMovement OnFixedUpdate");
float timeDeltaDiff = Time.DeltaTime - 1.0f / Time.PhysicsFPS;
if (Time.PhysicsFPS > 0 && Math.Abs(timeDeltaDiff) > 0.0001f)
Console.Print("Time.DeltaTime is not stable: " + timeDeltaDiff);
//Input.OnFixedUpdate();
PlayerInputState2 inputState = Input.GetState();
bool predict = World.IsClient;
bool predict = World.IsClient && false;
if (predict)
{
// Get the latest frame we have predicted
@@ -962,7 +967,7 @@ public class PlayerMovement : Script
if (capsuleCollider && capsuleCollider.IsActive)
{
Quaternion rotation = capsuleCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f);
DebugDraw.DrawWireTube(capsuleCollider.Position, rotation, capsuleCollider.Radius,
DebugDraw.DrawWireCapsule(capsuleCollider.Position, rotation, capsuleCollider.Radius,
capsuleCollider.Height, Color.GreenYellow * 0.8f);
}
else if (meshCollider && meshCollider.IsActive)
@@ -970,7 +975,7 @@ public class PlayerMovement : Script
//Quaternion rotation = meshCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f);
DebugDraw.DrawWireCylinder(meshCollider.Position, meshCollider.Orientation, capsuleCollider.Radius,
capsuleCollider.Height + capsuleCollider.Radius * 2, Color.GreenYellow * 0.8f);
//DebugDraw.DrawWireTube(meshCollider.Position, rotation, meshCollider.Radius, meshCollider.Height, Color.GreenYellow * 0.8f);
//DebugDraw.DrawWireCapsule(meshCollider.Position, rotation, meshCollider.Radius, meshCollider.Height, Color.GreenYellow * 0.8f);
}
else if (boxCollider && boxCollider.IsActive)
{