1 Commits

Author SHA1 Message Date
54ac069797 _dynamic list 2025-06-21 21:00:22 +03:00
20 changed files with 171 additions and 583 deletions

View File

@@ -1,7 +1,7 @@
{ {
"ID": "82e58c9d462fba5a0df1a599417ff684", "ID": "82e58c9d462fba5a0df1a599417ff684",
"TypeName": "FlaxEngine.Prefab", "TypeName": "FlaxEngine.Prefab",
"EngineBuild": 6704, "EngineBuild": 6340,
"Data": [ "Data": [
{ {
"ID": "a50f3639419a8306036ecfab7115e772", "ID": "a50f3639419a8306036ecfab7115e772",
@@ -17,11 +17,7 @@
"ID": "bc518e2f40ec06a4523d78b52809c668", "ID": "bc518e2f40ec06a4523d78b52809c668",
"TypeName": "Game.PlayerMovement", "TypeName": "Game.PlayerMovement",
"ParentID": "a50f3639419a8306036ecfab7115e772", "ParentID": "a50f3639419a8306036ecfab7115e772",
"V": { "V": {},
"maskTest": {
"Mask": 1
}
},
"Enabled": false "Enabled": false
}, },
{ {
@@ -166,8 +162,8 @@
} }
}, },
"StaticFlags": 0, "StaticFlags": 0,
"Material": "d99b8b6f40198beea4703dadd642150e",
"ContactOffset": 0.0, "ContactOffset": 0.0,
"Material": "d99b8b6f40198beea4703dadd642150e",
"Radius": 16.0, "Radius": 16.0,
"Height": 52.0 "Height": 52.0
}, },
@@ -177,8 +173,8 @@
"ParentID": "a50f3639419a8306036ecfab7115e772", "ParentID": "a50f3639419a8306036ecfab7115e772",
"Name": "BoxCollider", "Name": "BoxCollider",
"StaticFlags": 0, "StaticFlags": 0,
"Material": "ffe0e84c49607480f67a3994a7fe11a8",
"ContactOffset": 0.0, "ContactOffset": 0.0,
"Material": "ffe0e84c49607480f67a3994a7fe11a8",
"Size": { "Size": {
"X": 30.0, "X": 30.0,
"Y": 56.0, "Y": 56.0,
@@ -215,9 +211,9 @@
"Name": "PlayerModel", "Name": "PlayerModel",
"Transform": { "Transform": {
"Scale": { "Scale": {
"X": 0.30000001192092896, "X": 0.30000001192092898,
"Y": 0.5600000023841858, "Y": 0.5600000023841858,
"Z": 0.30000001192092896 "Z": 0.30000001192092898
} }
}, },
"StaticFlags": 0, "StaticFlags": 0,

View File

@@ -1,7 +1,7 @@
{ {
"ID": "af2e52554f7faed7b4937181dd22d166", "ID": "af2e52554f7faed7b4937181dd22d166",
"TypeName": "FlaxEditor.Content.Settings.BuildSettings", "TypeName": "FlaxEditor.Content.Settings.BuildSettings",
"EngineBuild": 6705, "EngineBuild": 6605,
"Data": { "Data": {
"OutputName": "${PROJECT_NAME}", "OutputName": "${PROJECT_NAME}",
"MaxAssetsPerPackage": 4096, "MaxAssetsPerPackage": 4096,
@@ -27,9 +27,18 @@
"Name": "Preset 3", "Name": "Preset 3",
"Targets": [ "Targets": [
{ {
"Name": "Windows 64bit", "Name": "Win64 Release",
"Output": "Output\\Win64", "Output": "Output\\WindowsRelease",
"Platform": 13, "Platform": 2,
"Mode": 2,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
},
{
"Name": "Win64 Development",
"Output": "Output\\WindowsDevelopment",
"Platform": 2,
"Mode": 1, "Mode": 1,
"CustomDefines": null, "CustomDefines": null,
"PreBuildAction": null, "PreBuildAction": null,

View File

@@ -1,13 +1,13 @@
{ {
"ID": "f94d5aae457aeba67033a8a4ca753214", "ID": "f94d5aae457aeba67033a8a4ca753214",
"TypeName": "FlaxEditor.Content.Settings.GraphicsSettings", "TypeName": "FlaxEditor.Content.Settings.GraphicsSettings",
"EngineBuild": 6705, "EngineBuild": 6602,
"Data": { "Data": {
"UseVSync": false, "UseVSync": false,
"AAQuality": 3, "AAQuality": 3,
"SSRQuality": 3, "SSRQuality": 3,
"SSAOQuality": 3, "SSAOQuality": 0,
"VolumetricFogQuality": 0, "VolumetricFogQuality": 3,
"ShadowsQuality": 3, "ShadowsQuality": 3,
"ShadowMapsQuality": 3, "ShadowMapsQuality": 3,
"AllowCSMBlending": true, "AllowCSMBlending": true,
@@ -19,7 +19,6 @@
"GenerateSDFOnModelImport": false, "GenerateSDFOnModelImport": false,
"GIQuality": 3, "GIQuality": 3,
"GIProbesSpacing": 100.0, "GIProbesSpacing": 100.0,
"GICascadesBlending": false,
"GlobalSurfaceAtlasResolution": 4096, "GlobalSurfaceAtlasResolution": 4096,
"PostProcessSettings": { "PostProcessSettings": {
"AO": { "AO": {
@@ -50,10 +49,8 @@
"Enabled": false, "Enabled": false,
"Intensity": 1.0, "Intensity": 1.0,
"Threshold": 3.0, "Threshold": 3.0,
"ThresholdKnee": 0.5, "BlurSigma": 4.0,
"Clamp": 3.0, "Limit": 10.0
"BaseMix": 0.6,
"HighMix": 1.0
}, },
"ToneMapping": { "ToneMapping": {
"OverrideFlags": 4, "OverrideFlags": 4,
@@ -201,7 +198,7 @@
"HistogramHighPercent": 98.0 "HistogramHighPercent": 98.0
}, },
"CameraArtifacts": { "CameraArtifacts": {
"OverrideFlags": 1, "OverrideFlags": 9,
"VignetteIntensity": 0.0, "VignetteIntensity": 0.0,
"VignetteColor": { "VignetteColor": {
"X": 0.0, "X": 0.0,
@@ -209,8 +206,8 @@
"Z": 0.001 "Z": 0.001
}, },
"VignetteShapeFactor": 0.125, "VignetteShapeFactor": 0.125,
"GrainAmount": 0.22999999, "GrainAmount": 0.0,
"GrainParticleSize": 1.0, "GrainParticleSize": 1.6,
"GrainSpeed": 1.0, "GrainSpeed": 1.0,
"ChromaticDistortion": 0.0, "ChromaticDistortion": 0.0,
"ScreenFadeColor": { "ScreenFadeColor": {
@@ -286,11 +283,7 @@
"TAA_JitterSpread": 0.75, "TAA_JitterSpread": 0.75,
"TAA_Sharpness": 0.0, "TAA_Sharpness": 0.0,
"TAA_StationaryBlending": 0.95, "TAA_StationaryBlending": 0.95,
"TAA_MotionBlending": 0.4, "TAA_MotionBlending": 0.4
"CAS_SharpeningAmount": 0.0,
"CAS_EdgeSharpening": 0.5,
"CAS_MinEdgeThreshold": 0.03,
"CAS_OverBlurLimit": 1.0
}, },
"PostFxMaterials": { "PostFxMaterials": {
"Materials": null "Materials": null

View File

@@ -1,7 +1,7 @@
{ {
"ID": "8ec53dba4c238bfbea1d62922e612a4d", "ID": "8ec53dba4c238bfbea1d62922e612a4d",
"TypeName": "FlaxEditor.Content.Settings.InputSettings", "TypeName": "FlaxEditor.Content.Settings.InputSettings",
"EngineBuild": 6705, "EngineBuild": 6335,
"Data": { "Data": {
"ActionMappings": [ "ActionMappings": [
{ {
@@ -84,8 +84,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 0, "PositiveButton": 0,
"NegativeButton": 0, "NegativeButton": 0,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.0, "DeadZone": 0.0,
"Sensitivity": 0.11, "Sensitivity": 0.11,
"Gravity": 1.0, "Gravity": 1.0,
@@ -98,8 +96,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 0, "PositiveButton": 0,
"NegativeButton": 0, "NegativeButton": 0,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.0, "DeadZone": 0.0,
"Sensitivity": 0.11, "Sensitivity": 0.11,
"Gravity": 1.0, "Gravity": 1.0,
@@ -112,11 +108,9 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 68, "PositiveButton": 68,
"NegativeButton": 65, "NegativeButton": 65,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.01, "DeadZone": 0.01,
"Sensitivity": 0.0, "Sensitivity": 5.0,
"Gravity": 0.0, "Gravity": 5.0,
"Scale": 1.0, "Scale": 1.0,
"Snap": true "Snap": true
}, },
@@ -126,11 +120,9 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 87, "PositiveButton": 87,
"NegativeButton": 83, "NegativeButton": 83,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.01, "DeadZone": 0.01,
"Sensitivity": 0.0, "Sensitivity": 5.0,
"Gravity": 0.0, "Gravity": 5.0,
"Scale": 1.0, "Scale": 1.0,
"Snap": true "Snap": true
}, },
@@ -140,8 +132,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 39, "PositiveButton": 39,
"NegativeButton": 37, "NegativeButton": 37,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.0, "DeadZone": 0.0,
"Sensitivity": 1.0, "Sensitivity": 1.0,
"Gravity": 800.0, "Gravity": 800.0,
@@ -154,8 +144,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 38, "PositiveButton": 38,
"NegativeButton": 40, "NegativeButton": 40,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.0, "DeadZone": 0.0,
"Sensitivity": 1.0, "Sensitivity": 1.0,
"Gravity": 800.0, "Gravity": 800.0,
@@ -168,8 +156,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 0, "PositiveButton": 0,
"NegativeButton": 0, "NegativeButton": 0,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.19, "DeadZone": 0.19,
"Sensitivity": 1.0, "Sensitivity": 1.0,
"Gravity": 1.0, "Gravity": 1.0,
@@ -182,8 +168,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 0, "PositiveButton": 0,
"NegativeButton": 0, "NegativeButton": 0,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.19, "DeadZone": 0.19,
"Sensitivity": 1.0, "Sensitivity": 1.0,
"Gravity": 1.0, "Gravity": 1.0,
@@ -196,8 +180,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 0, "PositiveButton": 0,
"NegativeButton": 0, "NegativeButton": 0,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.19, "DeadZone": 0.19,
"Sensitivity": 1.0, "Sensitivity": 1.0,
"Gravity": 1.0, "Gravity": 1.0,
@@ -210,8 +192,6 @@
"Gamepad": 0, "Gamepad": 0,
"PositiveButton": 0, "PositiveButton": 0,
"NegativeButton": 0, "NegativeButton": 0,
"GamepadPositiveButton": 0,
"GamepadNegativeButton": 0,
"DeadZone": 0.19, "DeadZone": 0.19,
"Sensitivity": 1.0, "Sensitivity": 1.0,
"Gravity": 1.0, "Gravity": 1.0,

View File

@@ -1,11 +1,11 @@
{ {
"ID": "0c69a0c7471f8d0805965caf343d2f27", "ID": "0c69a0c7471f8d0805965caf343d2f27",
"TypeName": "FlaxEditor.Content.Settings.NetworkSettings", "TypeName": "FlaxEditor.Content.Settings.NetworkSettings",
"EngineBuild": 6705, "EngineBuild": 6340,
"Data": { "Data": {
"MaxClients": 100, "MaxClients": 100,
"ProtocolVersion": 1, "ProtocolVersion": 1,
"NetworkFPS": 1.0, "NetworkFPS": 60.0,
"Address": "127.0.0.1", "Address": "127.0.0.1",
"Port": 59183, "Port": 59183,
"NetworkDriver": "FlaxEngine.Networking.ENetDriver" "NetworkDriver": "FlaxEngine.Networking.ENetDriver"

View File

@@ -1,7 +1,7 @@
{ {
"ID": "4bd8a4cc460399b5f1975fbe0a668e3f", "ID": "4bd8a4cc460399b5f1975fbe0a668e3f",
"TypeName": "FlaxEditor.Content.Settings.PhysicsSettings", "TypeName": "FlaxEditor.Content.Settings.PhysicsSettings",
"EngineBuild": 6705, "EngineBuild": 6510,
"Data": { "Data": {
"DefaultGravity": { "DefaultGravity": {
"X": 0.0, "X": 0.0,
@@ -13,7 +13,6 @@
"RestitutionCombineMode": 0, "RestitutionCombineMode": 0,
"DisableCCD": false, "DisableCCD": false,
"BroadPhaseType": 3, "BroadPhaseType": 3,
"EnableEnhancedDeterminism": false,
"SolverType": 0, "SolverType": 0,
"MaxDeltaTime": 0.1, "MaxDeltaTime": 0.1,
"EnableSubstepping": false, "EnableSubstepping": false,

View File

@@ -1,8 +1,9 @@
{ {
"ID": "a55dc3c04da4ea3744b7f1994565beac", "ID": "a55dc3c04da4ea3744b7f1994565beac",
"TypeName": "FlaxEditor.Content.Settings.TimeSettings", "TypeName": "FlaxEditor.Content.Settings.TimeSettings",
"EngineBuild": 6705, "EngineBuild": 6332,
"Data": { "Data": {
"UpdateMode": 1,
"UpdateFPS": 120.0, "UpdateFPS": 120.0,
"PhysicsFPS": 120.0, "PhysicsFPS": 120.0,
"DrawFPS": 120.0, "DrawFPS": 120.0,

View File

@@ -1,8 +1,7 @@
// comment // comment
r_shadows 1 r_shadows 1
r_lighting 1 r_lighting 1
cl_maxfps 120 cl_maxfps 240
r_upscaling 0 r_upscaling 0
r_gi 0 r_gi 0
r_staticbatch 1 r_staticbatch 1
sensitivity 0.5

View File

@@ -5,8 +5,8 @@
"gulp.autoDetect": "off", "gulp.autoDetect": "off",
"jake.autoDetect": "off", "jake.autoDetect": "off",
"grunt.autoDetect": "off", "grunt.autoDetect": "off",
"omnisharp.useModernNet": true, "omnisharp.defaultLaunchSolution": "GoakeFlax.sln",
"dotnet.defaultSolution": "GoakeFlax.sln" "omnisharp.useModernNet": true
}, },
"folders": [ "folders": [
{ {

View File

@@ -137,7 +137,7 @@ public class CameraRender : PostProcessEffect
private bool lastRescale = false; private bool lastRescale = false;
public override void OnUpdate() public override void OnUpdate()
{ {
/*#if FLAX_EDITOR #if FLAX_EDITOR
if (Input.GetKeyDown(KeyboardKeys.F7)) if (Input.GetKeyDown(KeyboardKeys.F7))
{ {
PhysicsSettings physicsSettings = GameSettings.Load<PhysicsSettings>(); PhysicsSettings physicsSettings = GameSettings.Load<PhysicsSettings>();
@@ -145,7 +145,7 @@ public class CameraRender : PostProcessEffect
GameSettings.Save(physicsSettings); GameSettings.Save(physicsSettings);
//GameSettings.Apply(); //GameSettings.Apply();
} }
#endif*/ #endif
if (lastEnabled != camera.IsActive) if (lastEnabled != camera.IsActive)
{ {

View File

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

View File

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

View File

@@ -22,16 +22,4 @@ public static class Utilities
Profiler.EndEvent(); Profiler.EndEvent();
} }
} }
}
public static class VectorExtensions
{
/*extension(Float3 value)
{
Float2 XY => new Float2(value.X, value.Y);
}*/
public static Float2 XY(this Float3 vec) => new Float2(vec.X, vec.Y);
public static Vector2 XY(this Vector3 vec) => new Vector2(vec.X, vec.Y);
} }

View File

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

View File

@@ -24,17 +24,14 @@ public static partial class NetworkManager
//var driver = Object.New(typeof(ENetDriver)); //var driver = Object.New(typeof(ENetDriver));
//ClientNetworkDriver = null; //ClientNetworkDriver = null;
NetworkLagDriver driver = Object.New<NetworkLagDriver>();
//INetworkDriver driver = Object.New<ENetDriver>(); driver.Lag = 0f;
INetworkDriver driver = Object.New<NetworkLagDriver>();
if (driver is NetworkLagDriver networkLagDriver)
networkLagDriver.Lag = 50.0f;
ClientNetworkDriver = driver; ClientNetworkDriver = driver;
client = NetworkPeer.CreatePeer(new NetworkConfig client = NetworkPeer.CreatePeer(new NetworkConfig
{ {
NetworkDriver = (Object)driver, NetworkDriver = driver,
ConnectionsLimit = MaximumClients, ConnectionsLimit = MaximumClients,
MessagePoolSize = 2048, MessagePoolSize = 2048,
MessageSize = MTU, MessageSize = MTU,
@@ -64,7 +61,7 @@ public static partial class NetworkManager
}*/ }*/
World.InitClient(); World.InitClient();
Scripting.Update += OnClientNetworkUpdate; Scripting.FixedUpdate += OnClientNetworkUpdate;
if (!listenServer) if (!listenServer)
{ {
Scripting.Exit += Cleanup; Scripting.Exit += Cleanup;

View File

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

View File

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

View File

@@ -22,25 +22,31 @@ public enum GameModeMessageType2 : byte
LastMessageType = 128, LastMessageType = 128,
} }
public class PlayerFrame
{
public ulong frame => inputState.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 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 IsServer => this is ServerWorld;
public bool IsClient => this is ClientWorld; public bool IsClient => !IsServer;
public ulong Frame { get; protected set; } public ulong Frame { get; protected set; }
public ulong ServerFrame { get; protected set; } // Last received frame from server public ulong ServerFrame { get; protected set; } // Last received frame from server
public float GameTime { get; protected set; } // The join time public float GameTime { get; protected set; } // The join time
@@ -48,6 +54,7 @@ public class World
protected Scene _scene; protected Scene _scene;
protected Actor _worldSpawn; protected Actor _worldSpawn;
protected Dictionary<uint, ClientInfo> _clients = new();
public static void InitClient() public static void InitClient()
{ {
@@ -58,9 +65,6 @@ public class World
{ {
WorldStore.ServerWorld = new ServerWorld(); WorldStore.ServerWorld = new ServerWorld();
WorldStore.ServerWorld.Init(); WorldStore.ServerWorld.Init();
Scripting.Update += WorldStore.ServerWorld.OnUpdate;
Scripting.LateUpdate += WorldStore.ServerWorld.OnUpdateLate;
Scripting.FixedUpdate += WorldStore.ServerWorld.OnFixedUpdate;
} }
public static void CleanupClient() public static void CleanupClient()
{ {
@@ -75,17 +79,12 @@ public class World
virtual protected void Init() virtual protected void Init()
{ {
Scripting.LateFixedUpdate += OnLateUpdate;
} }
virtual protected void Cleanup() virtual protected void Cleanup()
{ {
//Level.SceneLoaded -= OnLevelLoaded; //Level.SceneLoaded -= OnLevelLoaded;
//Scripting.LateUpdate -= OnLateUpdatePre; //Scripting.LateUpdate -= OnLateUpdatePre;
Scripting.Update -= OnUpdate;
Scripting.LateUpdate -= OnUpdateLate;
Scripting.FixedUpdate -= OnFixedUpdate;
Scripting.LateFixedUpdate -= OnLateUpdate; Scripting.LateFixedUpdate -= OnLateUpdate;
if (_scene) if (_scene)
@@ -100,35 +99,14 @@ 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) protected void CreateScene(string sceneNamePrefix, string sceneGuid)
{ {
if (false && sceneNamePrefix == "Client") string physicsSceneName = $"{sceneNamePrefix}PhysicsScene";
{ PhysicsScene localPhysicsScene = Physics.FindOrCreateScene(physicsSceneName);
_scene = Level.Scenes.FirstOrDefault(x => !x.Name.StartsWith("Server"));
}
else
{
string physicsSceneName = $"{sceneNamePrefix}PhysicsScene";
PhysicsScene localPhysicsScene = Physics.FindOrCreateScene(physicsSceneName);
Guid guid = JsonSerializer.ParseID(sceneGuid); Guid guid = JsonSerializer.ParseID(sceneGuid);
string guidStr = JsonSerializer.GetStringID(guid); string guidStr = JsonSerializer.GetStringID(guid);
string sceneData = $@" string sceneData = $@"
{{ {{
""ID"": ""{guidStr}"", ""ID"": ""{guidStr}"",
""TypeName"": ""FlaxEngine.SceneAsset"", ""TypeName"": ""FlaxEngine.SceneAsset"",
@@ -151,26 +129,25 @@ public class World
] ]
}}"; }}";
{
var onSceneLoaded = (Scene loadedScene, Guid id) =>
{ {
var onSceneLoaded = (Scene loadedScene, Guid id) => if (guid == id)
{ {
if (guid == id) loadedScene.PhysicsScene = localPhysicsScene;
{ //scene = loadedScene;
loadedScene.PhysicsScene = localPhysicsScene; }
//scene = loadedScene; };
}
};
try try
{ {
Level.SceneLoaded += onSceneLoaded; Level.SceneLoaded += onSceneLoaded;
//Level.LoadScene(new SceneReference(guid)); //Level.LoadScene(new SceneReference(guid));
_scene = Level.LoadSceneFromBytes(Encoding.ASCII.GetBytes(sceneData)); _scene = Level.LoadSceneFromBytes(Encoding.ASCII.GetBytes(sceneData));
} }
finally finally
{ {
Level.SceneLoaded -= onSceneLoaded; Level.SceneLoaded -= onSceneLoaded;
}
} }
} }
Assert.IsTrue(_scene); Assert.IsTrue(_scene);
@@ -178,7 +155,7 @@ public class World
_scene.Name = $"{sceneNamePrefix}Scene"; _scene.Name = $"{sceneNamePrefix}Scene";
//Level.SceneLoaded += OnLevelLoaded; //Level.SceneLoaded += OnLevelLoaded;
Scripting.LateFixedUpdate += OnLateUpdate;
var importer = FlaxEngine.Object.New<Q3MapImporter>(); var importer = FlaxEngine.Object.New<Q3MapImporter>();
importer.mapPath = @"C:\dev\GoakeFlax\Assets\Maps\aerowalk.map"; importer.mapPath = @"C:\dev\GoakeFlax\Assets\Maps\aerowalk.map";
@@ -190,12 +167,16 @@ public class World
Assert.IsTrue(_worldSpawn); Assert.IsTrue(_worldSpawn);
} }
virtual protected void OnLateUpdate() public void OnLateUpdate()
{ {
Frame++; Frame++;
} }
virtual protected IClientInfo GetClient(uint playerId) => null; protected void OnClientConnected(NetworkConnection connection)
{
uint playerId = connection.ConnectionId;
_clients.Add(playerId, new ClientInfo() { Connection = connection });
}
virtual protected PlayerActor SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles) virtual protected PlayerActor SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles)
{ {
@@ -208,8 +189,7 @@ public class World
playerActor.Initialize(playerId, position, eulerAngles); playerActor.Initialize(playerId, position, eulerAngles);
//playerActor.Teleport(position, eulerAngles); //playerActor.Teleport(position, eulerAngles);
IClientInfo player = GetClient(playerId); _clients[playerId].PlayerActor = playerActor;
player.PlayerActor = playerActor;
IPlayerInput playerInput = playerActor.GetScript<PlayerMovement>().Input; IPlayerInput playerInput = playerActor.GetScript<PlayerMovement>().Input;
playerInput.SetFrame(Frame); playerInput.SetFrame(Frame);
/*if (IsServer) /*if (IsServer)
@@ -231,17 +211,14 @@ public class World
virtual public bool IsLocalPlayer(uint playerId) => false; virtual public bool IsLocalPlayer(uint playerId) => false;
virtual public ulong GetLastProcessedFrame(uint playerId) => 0;
virtual public ulong GetLastReceivedFrame(uint playerId) => 0;
public PlayerFrame GetPlayerFrame(uint playerId, ulong frame) public PlayerFrame GetPlayerFrame(uint playerId, ulong frame)
{ {
IClientInfo player = GetClient(playerId); ClientInfo player = _clients[playerId];
PlayerFrame playerFrame = player.FrameHistory[frame % 120]; PlayerFrame playerFrame = player.FrameHistory[frame % 120];
if (playerFrame.inputState.Frame == frame || playerFrame.movementState.frame == frame) if (playerFrame.frame != frame)
return playerFrame; return null;
return null;
return playerFrame;
} }
public bool HasPlayerFrame(uint playerId, ulong frame) => GetPlayerFrame(playerId, frame) != null; public bool HasPlayerFrame(uint playerId, ulong frame) => GetPlayerFrame(playerId, frame) != null;
@@ -252,70 +229,10 @@ public class World
inputState = playerFrame?.inputState ?? default; inputState = playerFrame?.inputState ?? default;
return playerFrame != null; 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)
{
var old = playerFrame.inputState with { Frame = inputState.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,
};
if (old != playerFrame.inputState)
old = old;
}
else*/
playerFrame.inputState = inputState;
playerFrame.inputState.Frame = frame;
//playerFrame.movementState = movementState;
//playerFrame.frame = frame;
}
virtual public void UpdatePlayerState(uint playerId, ulong frame, PlayerMovementState movementState)
{
IClientInfo player = GetClient(playerId);
PlayerFrame playerFrame = player.FrameHistory[frame % 120];
playerFrame.movementState = movementState;
playerFrame.movementState.frame = frame;
}
} }
file class ServerWorld : World 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 ulong LastProcessedFrame = 0; // In case player is lagged behind, these frames should be caught up during next simulation
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();
FrameHistory[i].inputState.Frame = ulong.MaxValue;
FrameHistory[i].movementState.frame = ulong.MaxValue;
}
}
}
protected Dictionary<uint, ClientInfo> _clients = new();
protected override IClientInfo GetClient(uint playerId) => _clients[playerId];
protected override void Init() protected override void Init()
{ {
NetworkManager.RegisterServerCallbacks(OnMessage, OnClientConnecting, OnClientConnected); NetworkManager.RegisterServerCallbacks(OnMessage, OnClientConnecting, OnClientConnected);
@@ -332,18 +249,6 @@ file class ServerWorld : World
base.Cleanup(); base.Cleanup();
} }
protected override void OnLateUpdate()
{
//Console.Print("server LateFixedUpdate");
base.OnLateUpdate();
foreach (var client in _clients.Values)
{
// We assume all the scripts have processed the frames during this cycle
client.LastProcessedFrame = client.LastReceivedFrame;
}
}
public bool OnMessage(ref NetworkEvent networkEvent) public bool OnMessage(ref NetworkEvent networkEvent)
{ {
byte messageTypeByte = networkEvent.Message.ReadByte(); byte messageTypeByte = networkEvent.Message.ReadByte();
@@ -361,9 +266,23 @@ file class ServerWorld : World
{ {
case GameModeMessageType2.AcceptConnection: // Client case GameModeMessageType2.AcceptConnection: // Client
{ {
break; 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);
}*/
} }
case GameModeMessageType2.PlayerPosition: // FIXME: Should be the delta frame break;
case GameModeMessageType2.PlayerPosition:
{ {
//uint playerId = networkEvent.Sender.ConnectionId; //uint playerId = networkEvent.Sender.ConnectionId;
PlayerInputState2 inputState; //? PlayerInputState2 inputState; //?
@@ -389,70 +308,16 @@ file class ServerWorld : World
actorState.jumped = networkEvent.Message.ReadBoolean(); actorState.jumped = networkEvent.Message.ReadBoolean();
//inputState.frame = receivedFrame; //inputState.frame = receivedFrame;
//inputState.ViewDelta.X = networkEvent.Message.ReadSingle(); inputState.ViewDelta.X = networkEvent.Message.ReadSingle();
//inputState.ViewDelta.Y = networkEvent.Message.ReadSingle(); inputState.ViewDelta.Y = networkEvent.Message.ReadSingle();
inputState.MoveForward = networkEvent.Message.ReadSingle(); inputState.MoveForward = networkEvent.Message.ReadSingle();
inputState.MoveRight = networkEvent.Message.ReadSingle(); inputState.MoveRight = networkEvent.Message.ReadSingle();
inputState.Attack = networkEvent.Message.ReadBoolean(); inputState.Attack = networkEvent.Message.ReadBoolean();
inputState.Jump = networkEvent.Message.ReadBoolean(); inputState.Jump = networkEvent.Message.ReadBoolean();
ServerFrame = receivedFrame; ServerFrame = receivedFrame;
break;
}
case GameModeMessageType2.PlayerInput:
{
PlayerInputState2 inputState; //?
ulong receivedFrame = inputState.Frame = networkEvent.Message.ReadUInt64();
uint playerId = networkEvent.Message.ReadUInt32();
float time = networkEvent.Message.ReadSingle();
float delay = Time.TimeSinceStartup - time;
inputState.ViewDelta = Float2.Zero;
inputState.ViewAngles.X = networkEvent.Message.ReadSingle();
inputState.ViewAngles.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("recv: " + receivedFrame.ToString());
ClientInfo player = GetClient(playerId) as ClientInfo;
if (receivedFrame <= player.LastReceivedFrame)
{
if (receivedFrame < player.LastProcessedFrame)
{
Console.PrintWarning($"Dropping late frame from client: {receivedFrame}, last processed: {player.LastProcessedFrame}");
break;
}
else
{
//Console.PrintWarning($"Out-of-order frame received from client: {receivedFrame}, latest: {player.LastReceivedFrame}");
}
}
player.LastReceivedFrame = Math.Max(receivedFrame, player.LastReceivedFrame);
//if (player.LastProcessedFrame == 0)
// player.LastProcessedFrame = receivedFrame - 1;
//Console.Print($"server receive client frame {receivedFrame}, current server frame {Frame}");
var asdf = receivedFrame;
//receivedFrame = Frame; // TODO: receive all frames from players, simulate all unprocessed frames in next simulation cycle?
if (inputState.MoveForward != 0)
receivedFrame = receivedFrame;
//Console.Print($"delay: {delay * 1000}ms");
// Sanity check
if (GetPlayerInputState(playerId, receivedFrame, out var prevInputState))
{
Console.PrintWarning($"Duplicate frame received from client: {asdf}, delay: {delay*1000}ms");
//break;
}
UpdatePlayerInputState(playerId, receivedFrame, inputState, default);
break;
} }
break;
default: default:
break; break;
} }
@@ -481,10 +346,11 @@ file class ServerWorld : World
return true; return true;
} }
public void OnClientConnected(NetworkConnection connection) public new void OnClientConnected(NetworkConnection connection)
{ {
base.OnClientConnected(connection);
uint playerId = connection.ConnectionId; uint playerId = connection.ConnectionId;
_clients.Add(playerId, new ClientInfo() { Connection = connection });
var spawns = _worldSpawn.GetChildren<Actor>().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray(); var spawns = _worldSpawn.GetChildren<Actor>().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray();
Console.Print($"found {spawns.Length} spawns"); Console.Print($"found {spawns.Length} spawns");
@@ -509,56 +375,12 @@ file class ServerWorld : World
NetworkManager.ServerEndSendMessage(ref message, connection); NetworkManager.ServerEndSendMessage(ref message, connection);
} }
} }
public override ulong GetLastProcessedFrame(uint playerId)
{
ClientInfo player = GetClient(playerId) as ClientInfo;
return player.LastProcessedFrame;
}
public override ulong GetLastReceivedFrame(uint playerId)
{
ClientInfo player = GetClient(playerId) as ClientInfo;
return player.LastReceivedFrame;
}
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 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; private uint _localPlayerId;
protected Dictionary<uint, ClientInfo> _clients = new();
protected override IClientInfo GetClient(uint playerId) => _clients[playerId];
protected override void Init() protected override void Init()
{ {
NetworkManager.RegisterClientCallbacks(OnMessage, null, OnClientConnected); NetworkManager.RegisterClientCallbacks(OnMessage, null, OnClientConnected);
@@ -574,49 +396,6 @@ file class ClientWorld : World
base.Cleanup(); 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.WriteSingle(Time.TimeSinceStartup);
message.WriteSingle(movementState.viewAngles.X);
message.WriteSingle(movementState.viewAngles.Y);
//message.WriteSingle(movementState.viewAngles.Z);
message.WriteSingle(inputState.MoveForward);
message.WriteSingle(inputState.MoveRight);
message.WriteBoolean(inputState.Attack);
message.WriteBoolean(inputState.Jump);
NetworkManager.ClientEndSendMessage(ref message);
//Console.Print("send: " + Frame.ToString());
}
base.OnLateUpdate();
}
public bool OnMessage(ref NetworkEvent networkEvent) public bool OnMessage(ref NetworkEvent networkEvent)
{ {
byte messageTypeByte = networkEvent.Message.ReadByte(); byte messageTypeByte = networkEvent.Message.ReadByte();
@@ -647,8 +426,8 @@ file class ClientWorld : World
} }
Console.Print("received welcome: frame " + ServerFrame); Console.Print("received welcome: frame " + ServerFrame);
break;
} }
break;
case GameModeMessageType2.SpawnPlayer: case GameModeMessageType2.SpawnPlayer:
{ {
uint playerId = networkEvent.Message.ReadUInt32(); uint playerId = networkEvent.Message.ReadUInt32();
@@ -668,8 +447,8 @@ file class ClientWorld : World
//if (NetworkManager.IsClient) //if (NetworkManager.IsClient)
//players[playerId].GetScript<PlayerMovement>().Input.SetFrame(ClientFrame); //players[playerId].GetScript<PlayerMovement>().Input.SetFrame(ClientFrame);
break;
} }
break;
#if false #if false
case GameModeMessageType2.PlayerPosition: case GameModeMessageType2.PlayerPosition:
{ {
@@ -705,8 +484,8 @@ file class ClientWorld : World
inputState.Jump = networkEvent.Message.ReadBoolean(); inputState.Jump = networkEvent.Message.ReadBoolean();
ServerFrame = receivedFrame; ServerFrame = receivedFrame;
break;
} }
break;
#endif #endif
default: default:
break; break;
@@ -717,24 +496,10 @@ file class ClientWorld : World
public override bool IsLocalPlayer(uint playerId) => playerId == _localPlayerId; public override bool IsLocalPlayer(uint playerId) => playerId == _localPlayerId;
public override ulong GetLastProcessedFrame(uint playerId) public new void OnClientConnected(NetworkConnection connection)
{ {
//ClientInfo player = GetClient(playerId) as ClientInfo; base.OnClientConnected(connection);
return 0; _localPlayerId = connection.ConnectionId;
}
public override ulong GetLastReceivedFrame(uint playerId)
{
//ClientInfo player = GetClient(playerId) as ClientInfo;
return 0;
}
public void OnClientConnected(NetworkConnection connection)
{
uint playerId = connection.ConnectionId;
_clients.Add(playerId, new ClientInfo() { Connection = connection });
_localPlayerId = playerId;
Console.Print($"ClientWorld: Connected, playerId: {_localPlayerId}"); Console.Print($"ClientWorld: Connected, playerId: {_localPlayerId}");
} }

View File

@@ -1,49 +1,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FlaxEngine; using FlaxEngine;
namespace Game; namespace Game;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct PlayerInputState2 : IEquatable<PlayerInputState2> public struct PlayerInputState2
{ {
public ulong Frame; //public ulong frame;
public Float2 ViewAngles; public Float2 ViewDelta;
public float MoveForward; public float MoveForward;
public float MoveRight; public float MoveRight;
public bool Attack; public bool Attack;
public bool Jump; public bool Jump;
public Float2 ViewDelta; // Only used when gathering input for local player
/*public override bool Equals([NotNullWhen(true)] object obj)
{
if (obj is not PlayerInputState2 other)
return false;
return Frame == other.Frame && ViewDelta == other.ViewDelta && MoveForward == other.MoveForward && MoveRight == other.MoveRight && Attack == other.Attack && Jump == other.Jump;
}*/
public bool Equals(PlayerInputState2 other)
{
return Frame == other.Frame && ViewAngles == other.ViewAngles && MoveForward == other.MoveForward && MoveRight == other.MoveRight && Attack == other.Attack && Jump == other.Jump && ViewDelta == other.ViewDelta;
}
public static bool operator ==(PlayerInputState2 left, PlayerInputState2 right) => left.Equals(right);
public static bool operator !=(PlayerInputState2 left, PlayerInputState2 right) => !left.Equals(right);
// Apply delta to calculate the final view angles for this state
public void UpdateViewAngles(Float2 lastInputViewAngles)
{
ViewAngles = lastInputViewAngles + ViewDelta;
ViewDelta = Float2.Zero;
}
} }
public interface IPlayerInput public interface IPlayerInput
@@ -80,13 +53,8 @@ public class PlayerInput2 : IPlayerInput
[ConsoleVariable("sensitivity")] [ConsoleVariable("sensitivity")]
private static float _sensitivity { get; set; } = 1.0f; private static float _sensitivity { get; set; } = 1.0f;
private ulong _frame;
public void SetFrame(ulong frame) public void SetFrame(ulong frame)
{ {
if (_frame != frame)
ResetState();
_frame = frame;
} }
public void UpdateState() public void UpdateState()
@@ -100,20 +68,12 @@ public class PlayerInput2 : IPlayerInput
float turnSpeed = 100.0f; float turnSpeed = 100.0f;
_recordState.ViewDelta += new Float2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y")) * _sensitivity; _recordState.ViewDelta += new Float2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y")) * _sensitivity;
_recordState.ViewDelta += new Float2(Input.GetAxisRaw("LookRight"), Input.GetAxisRaw("LookUp")) * Time.DeltaTime * turnSpeed; _recordState.ViewDelta += new Float2(Input.GetAxisRaw("LookRight"), Input.GetAxisRaw("LookUp")) * Time.DeltaTime * turnSpeed;
_recordState.MoveForward = MathF.MaxMagnitude(_recordState.MoveForward, Input.GetAxis("Vertical")); _recordState.MoveForward = MathF.MaxMagnitude(_recordState.MoveForward, Input.GetAxis("Vertical"));
_recordState.MoveRight = MathF.MaxMagnitude(_recordState.MoveRight, Input.GetAxis("Horizontal")); _recordState.MoveRight = MathF.MaxMagnitude(_recordState.MoveRight, Input.GetAxis("Horizontal"));
_recordState.Attack |= Input.GetAction("Attack"); _recordState.Attack |= Input.GetAction("Attack");
_recordState.Jump |= Input.GetAction("Jump"); _recordState.Jump |= Input.GetAction("Jump");
_recordState.Frame = _frame;
if (Input.GetKeyDown(KeyboardKeys.F))
Thread.Sleep(20);
var asdf = Input.GetAxis("Vertical");
if (asdf != 0.0f)
asdf = asdf;
} }
public PlayerInputState2 GetState() => _recordState; public PlayerInputState2 GetState() => _recordState;

View File

@@ -8,6 +8,7 @@ using FlaxEngine;
using FlaxEngine.Assertions; using FlaxEngine.Assertions;
using FlaxEngine.Networking; using FlaxEngine.Networking;
using Console = Game.Console; using Console = Game.Console;
using System.Buffers;
namespace Game; namespace Game;
@@ -74,7 +75,6 @@ public struct PlayerMovementState
public int numJumps; public int numJumps;
public bool jumped; public bool jumped;
public bool onGround; public bool onGround;
public ulong frame;
public PlayerMovementState() public PlayerMovementState()
@@ -84,7 +84,7 @@ public struct PlayerMovementState
public struct TraceInfo public struct TraceInfo
{ {
public RayCastHit[] hitInfos; public List<RayCastHit> hitInfos;
public bool startSolid; public bool startSolid;
// closest hit // closest hit
@@ -137,15 +137,13 @@ public class PlayerMovement : Script
private PlayerActor playerActor; private PlayerActor playerActor;
public Actor rootActor; public Actor rootActor;
public Float3 viewAngles public Float3 viewAngles;
{
get => movementState.viewAngles;
set => movementState.viewAngles = value;
}
private Float3 viewAnglesLastFrame; private Float3 viewAnglesLastFrame;
public uint PlayerId => playerActor ? playerActor.PlayerId : 0; public uint PlayerId => playerActor ? playerActor.PlayerId : 0;
public LayersMask maskTest;
[ReadOnly] [ReadOnly]
public float CurrentVelocity public float CurrentVelocity
{ {
@@ -255,27 +253,14 @@ public class PlayerMovement : Script
viewAnglesLastFrame = viewAngles; viewAnglesLastFrame = viewAngles;
} }
private static int framedropped = 0;
public override void OnUpdate() public override void OnUpdate()
{ {
if (World.IsServer && Mathf.Abs(Time.DeltaTime - (1.0f / Time.UpdateFPS)) > (1.0f / Time.UpdateFPS) * 0.999f)
{
Console.Print($"drop: {Time.DeltaTime*1000.0f}ms");
framedropped = 3;
}
else if (World.IsServer && framedropped > 0)
{
Console.Print("dropping...");
framedropped--;
}
//Console.Print($"{(World.IsClient ? "[cl] " : "[sv] ") + World.Frame.ToString()} playerMovement OnUpdate");
//input.OnUpdate(); //input.OnUpdate();
//if (Input is PlayerInputDemo /*&& currentInputFrame2 >= currentInputFrame*/) //if (Input is PlayerInputDemo /*&& currentInputFrame2 >= currentInputFrame*/)
// return; // return;
Input.SetFrame(World.Frame); Input.SetFrame(World.Frame);
Input.UpdateState(); Input.UpdateState();
@@ -329,14 +314,13 @@ public class PlayerMovement : Script
public override void OnFixedUpdate() public override void OnFixedUpdate()
{ {
//Console.Print($"{(World.IsClient ? "[cl] " : "[sv] ") + World.Frame.ToString()} playerMovement OnFixedUpdate");
float timeDeltaDiff = Time.DeltaTime - 1.0f / Time.PhysicsFPS; float timeDeltaDiff = Time.DeltaTime - 1.0f / Time.PhysicsFPS;
if (Time.PhysicsFPS > 0 && Math.Abs(timeDeltaDiff) > 0.0001f) if (Time.PhysicsFPS > 0 && Math.Abs(timeDeltaDiff) > 0.0001f)
Console.Print("Time.DeltaTime is not stable: " + timeDeltaDiff); Console.Print("Time.DeltaTime is not stable: " + timeDeltaDiff);
//Input.OnFixedUpdate(); //Input.OnFixedUpdate();
PlayerInputState2 inputState = Input.GetState(); PlayerInputState2 inputState = Input.GetState();
bool predict = World.IsClient && false; bool predict = World.IsClient;
if (predict) if (predict)
{ {
// Get the latest frame we have predicted // Get the latest frame we have predicted
@@ -427,95 +411,13 @@ public class PlayerMovement : Script
} }
} }
if (World.IsServer) if (!predict)
{
ulong currentFrame = World.GetLastProcessedFrame(PlayerId);
{
// Rewind to last confirmed state
var frameInfo = World.GetPlayerFrame(PlayerId, currentFrame);
if (frameInfo != null && frameInfo.movementState.frame == currentFrame)
{
// Reset all state to latest confirmed state from server
movementState.position = frameInfo.movementState.position;
movementState.currentVelocity = frameInfo.movementState.currentVelocity;
movementState.lastJumped = frameInfo.movementState.lastJumped;
movementState.numJumps = frameInfo.movementState.numJumps;
movementState.jumped = frameInfo.movementState.jumped;
Actor.Position = movementState.position;
SetCameraEulerAngles(frameInfo.movementState.viewAngles, true);
//ApplyInputToCamera(frameInfo.inputState, true);
}
else if (frameInfo == null)
{
// First frame, update the known state
movementState.viewAngles = viewAngles;
World.UpdatePlayerState(PlayerId, currentFrame, movementState);
}
currentFrame++;
}
//Console.Print("before:" + viewAngles.ToString());
ulong lastFrame = World.GetLastReceivedFrame(PlayerId);
PlayerFrame lastFrameInfo = null;
for (; currentFrame < lastFrame+1; currentFrame++)
{
var frameInfo = World.GetPlayerFrame(PlayerId, currentFrame);
if (frameInfo == null || frameInfo.inputState.Frame != currentFrame)
//if (!Input.GetState(currentFrame, out var pastInputState, out var pastActorState))
{
Console.Print($"frame {currentFrame} missing");
frameInfo = lastFrameInfo; // Use last known input
if (frameInfo == null)
{
// FIXME
frameInfo = new PlayerFrame()
{
inputState =
{
Frame = currentFrame,
ViewAngles = movementState.viewAngles.XY(),
},
movementState = movementState,
};
frameInfo.movementState.frame = currentFrame;
}
//continue;
//Console.Print($"not predicting");
//predict = false;
//break;
}
else
{
Input.SetFrame(currentFrame);
Input.UpdateState();
SetCameraEulerAngles(new Float3(frameInfo.inputState.ViewAngles.X, frameInfo.inputState.ViewAngles.Y, viewAngles.Z), true);
//ApplyInputToCamera(frameInfo.inputState, true);
lastFrameInfo = frameInfo;
}
SimulatePlayerMovement(frameInfo.inputState, currentFrame);
movementState.viewAngles = viewAngles;
//Console.Print("tick: " + viewAngles.ToString());
//if (frameInfo.movementState.frame != currentFrame)
World.UpdatePlayerState(PlayerId, currentFrame, movementState);
//else
// currentFrame = currentFrame;
}
}
else if (!predict)
{ {
SetCameraEulerAngles(viewAnglesLastFrame, true); SetCameraEulerAngles(viewAnglesLastFrame, true);
ApplyInputToCamera(inputState, true); ApplyInputToCamera(inputState, true);
SimulatePlayerMovement(inputState, World.Frame); // MAYBE? SimulatePlayerMovement(inputState, World.Frame); // MAYBE?
} }
//if (!movementState.currentVelocity.IsZero)
// Console.Print($"{(World.IsClient ? "client" : "server")} {World.Frame} vel: {movementState.currentVelocity}, pos: {movementState.position}");
viewAnglesLastFrame = viewAngles; viewAnglesLastFrame = viewAngles;
#if false #if false
@@ -858,15 +760,17 @@ public class PlayerMovement : Script
//viewAnglesLastFrame = angles; //viewAnglesLastFrame = angles;
} }
private static bool SweepPlayerCollider(PlayerActor actor, Float3 start, Vector3 end, out RayCastHit[] hits) private static List<RayCastHit> _cachedHits = new();
private static bool SweepPlayerCollider(PlayerActor actor, Float3 start, Vector3 end, out List<RayCastHit> hits)
{ {
Vector3 delta = end - start; Vector3 delta = end - start;
float distance = delta.Length; float distance = delta.Length;
Vector3 direction = delta.Normalized; Vector3 direction = delta.Normalized;
//_cachedHits.Clear();
if (distance < 0.00000001f) if (distance < 0.00000001f)
{ {
hits = new RayCastHit[0];//Array.Empty<RayCastHit>(); hits = _cachedHits;//Array.Empty<RayCastHit>();
return false; return false;
} }
@@ -874,7 +778,7 @@ public class PlayerMovement : Script
CapsuleCollider capsuleCollider = actor.capsuleCollider; CapsuleCollider capsuleCollider = actor.capsuleCollider;
BoxCollider boxCollider = actor.boxCollider; BoxCollider boxCollider = actor.boxCollider;
MeshCollider meshCollider = actor.meshCollider; MeshCollider meshCollider = actor.meshCollider;
if (capsuleCollider && capsuleCollider.IsActive) /*if (capsuleCollider && capsuleCollider.IsActive)
collided = Physics.CapsuleCastAll(start, collided = Physics.CapsuleCastAll(start,
capsuleCollider.Radius, capsuleCollider.Height, capsuleCollider.Radius, capsuleCollider.Height,
direction, out hits, capsuleCollider.Orientation, distance, direction, out hits, capsuleCollider.Orientation, distance,
@@ -886,16 +790,16 @@ public class PlayerMovement : Script
direction, out hits, meshCollider.Orientation, distance, direction, out hits, meshCollider.Orientation, distance,
uint.MaxValue, uint.MaxValue,
false); false);
else if (boxCollider && boxCollider.IsActive) else*/ if (boxCollider && boxCollider.IsActive)
collided = Physics.BoxCastAll(start, collided = Physics.BoxCastAll(start,
boxCollider.OrientedBox.Extents, boxCollider.OrientedBox.Extents,
direction, out hits, boxCollider.Orientation, distance, direction, ref _cachedHits, boxCollider.Orientation, distance,
uint.MaxValue, uint.MaxValue,
false); false);
else else
throw new Exception("Player does not have a collider"); throw new Exception("Player does not have a collider");
hits = _cachedHits;
return collided; return collided;
} }
@@ -958,6 +862,7 @@ public class PlayerMovement : Script
return collided; return collided;
} }
private static List<RayCastHit> _cachedHitInfos = new List<RayCastHit>();
/// <summary> /// <summary>
/// Sweeps the player rigidbody in world and returns geometry which was hit during the trace. /// Sweeps the player rigidbody in world and returns geometry which was hit during the trace.
/// </summary> /// </summary>
@@ -968,6 +873,7 @@ public class PlayerMovement : Script
private static TraceInfo TracePlayer(PlayerActor actor, Vector3 start, Vector3 end) private static TraceInfo TracePlayer(PlayerActor actor, Vector3 start, Vector3 end)
{ {
TraceInfo traceInfo = new TraceInfo(); TraceInfo traceInfo = new TraceInfo();
traceInfo.hitInfos = _cachedHitInfos;
Vector3 delta = end - start; Vector3 delta = end - start;
float maxDistance = delta.Length; float maxDistance = delta.Length;
@@ -1022,7 +928,7 @@ public class PlayerMovement : Script
if (hitInfo.Distance < closest.Distance) if (hitInfo.Distance < closest.Distance)
closest = hitInfo; closest = hitInfo;
traceInfo.hitInfos = hitInfosFiltered.ToArray(); traceInfo.hitInfos = hitInfosFiltered;
traceInfo.fraction = closest.Distance / maxDistance; traceInfo.fraction = closest.Distance / maxDistance;
traceInfo.hitNormal = closest.Normal; traceInfo.hitNormal = closest.Normal;
@@ -1044,7 +950,7 @@ public class PlayerMovement : Script
if (!collided) if (!collided)
{ {
traceInfo.hitInfos = new RayCastHit[0]; traceInfo.hitInfos = new List<RayCastHit>();
traceInfo.fraction = 1f; traceInfo.fraction = 1f;
traceInfo.endPosition = end; traceInfo.endPosition = end;
} }
@@ -1063,7 +969,7 @@ public class PlayerMovement : Script
if (capsuleCollider && capsuleCollider.IsActive) if (capsuleCollider && capsuleCollider.IsActive)
{ {
Quaternion rotation = capsuleCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f); Quaternion rotation = capsuleCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f);
DebugDraw.DrawWireCapsule(capsuleCollider.Position, rotation, capsuleCollider.Radius, DebugDraw.DrawWireTube(capsuleCollider.Position, rotation, capsuleCollider.Radius,
capsuleCollider.Height, Color.GreenYellow * 0.8f); capsuleCollider.Height, Color.GreenYellow * 0.8f);
} }
else if (meshCollider && meshCollider.IsActive) else if (meshCollider && meshCollider.IsActive)
@@ -1071,7 +977,7 @@ public class PlayerMovement : Script
//Quaternion rotation = meshCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f); //Quaternion rotation = meshCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f);
DebugDraw.DrawWireCylinder(meshCollider.Position, meshCollider.Orientation, capsuleCollider.Radius, DebugDraw.DrawWireCylinder(meshCollider.Position, meshCollider.Orientation, capsuleCollider.Radius,
capsuleCollider.Height + capsuleCollider.Radius * 2, Color.GreenYellow * 0.8f); capsuleCollider.Height + capsuleCollider.Radius * 2, Color.GreenYellow * 0.8f);
//DebugDraw.DrawWireCapsule(meshCollider.Position, rotation, meshCollider.Radius, meshCollider.Height, Color.GreenYellow * 0.8f); //DebugDraw.DrawWireTube(meshCollider.Position, rotation, meshCollider.Radius, meshCollider.Height, Color.GreenYellow * 0.8f);
} }
else if (boxCollider && boxCollider.IsActive) else if (boxCollider && boxCollider.IsActive)
{ {