diff --git a/Assets/Maps/aerowalk.map b/Assets/Maps/aerowalk.map index 0733356..08f2723 100644 --- a/Assets/Maps/aerowalk.map +++ b/Assets/Maps/aerowalk.map @@ -4616,301 +4616,209 @@ } // entity 10 { -"origin" "612 -512 72" -"classname" "ammo_shells" -} -// entity 11 -{ -"origin" "668 -512 72" -"classname" "ammo_bullets" -} -// entity 12 -{ "origin" "128 -464 72" "classname" "item_health_mega" } -// entity 13 +// entity 11 { "classname" "weapon_rocketlauncher" "origin" "-384 -448 144" } -// entity 14 +// entity 12 { "classname" "item_armor_green" "origin" "-544 -304 144" } -// entity 15 +// entity 13 { "classname" "item_ammo_lightning" "origin" "-192 32 144" } -// entity 16 -{ -"classname" "ammo_shells" -"origin" "492 -128 376" -} -// entity 17 +// entity 14 { "classname" "item_ammo_lightning" "origin" "436 -128 376" } -// entity 18 -{ -"classname" "weapon_shotgun" -"origin" "128 0 -48" -} -// entity 19 +// entity 15 { "classname" "item_ammo_rockets" "origin" "56 -352 -48" } -// entity 20 +// entity 16 { "classname" "info_player_deathmatch" "origin" "-608 -304 152" } -// entity 21 +// entity 17 { "origin" "928 -552 144" "classname" "item_health_5" } -// entity 22 +// entity 18 { "origin" "144 -352 -40" "angle" "90" "classname" "info_player_deathmatch" } -// entity 23 +// entity 19 { "classname" "item_health_25" "origin" "784 -224 144" } -// entity 24 +// entity 20 { "classname" "item_health_25" "origin" "-32 -96 -48" } -// entity 25 +// entity 21 { "classname" "info_player_deathmatch" "angle" "225" "origin" "776 12 384" } -// entity 26 +// entity 22 { "angle" "315" "classname" "info_player_deathmatch" "origin" "-116 356 152" } -// entity 27 +// entity 23 { "classname" "weapon_grenadelauncher" "origin" "928 -448 144" } -// entity 28 +// entity 24 { "classname" "item_ammo_rockets" "origin" "776 -520 376" } -// entity 29 +// entity 25 { "classname" "weapon_lightning" "origin" "-228 288 320" } -// entity 30 +// entity 26 { "origin" "368 120 152" "angle" "270" "targetname" "upper_sg" "classname" "info_teleport_destination" } -// entity 31 +// entity 27 { "origin" "160 -464 376" "classname" "weapon_stake" } -// entity 32 +// entity 28 { "origin" "448 32 376" "classname" "weapon_rocketlauncher" } -// entity 33 +// entity 29 { "origin" "148 312 448" "targetname" "jump1" "classname" "target_position" } -// entity 34 +// entity 30 { "origin" "980 -160 152" "angle" "180" "classname" "info_player_deathmatch" } -// entity 35 +// entity 31 { "origin" "-144 -184 144" "classname" "item_health_25" } -// entity 36 +// entity 32 { "spawnflags" "0" "classname" "item_health_25" "origin" "320 -464 376" } -// entity 37 +// entity 33 { "classname" "item_health_5" "origin" "928 -504 144" } -// entity 38 +// entity 34 { "classname" "item_health_5" "origin" "928 -344 144" } -// entity 39 +// entity 35 { "classname" "info_player_deathmatch" "angle" "90" "origin" "-384 -130 368" } -// entity 40 +// entity 36 { "origin" "928 -392 144" "classname" "item_health_5" } -// entity 41 +// entity 37 { "origin" "-324 -304 152" "targetname" "25armour" "classname" "info_teleport_destination" "angle" "180" } -// entity 42 +// entity 38 { "classname" "info_teleport_destination" "targetname" "rg" "origin" "16 -464 384" } -// entity 43 +// entity 39 { "classname" "item_ammo_stakes" "origin" "400 -128 144" } -// entity 44 -{ -"classname" "light" -"origin" "224 -464 220" -} -// entity 45 -{ -"classname" "light" -"origin" "32 -464 220" -} -// entity 46 -{ -"classname" "light" -"origin" "432 -464 196" -} -// entity 47 -{ -"classname" "light" -"origin" "816 -656 272" -} -// entity 48 -{ -"classname" "light" -"origin" "624 -48 288" -} -// entity 49 -{ -"classname" "light" -"origin" "656 -464 280" -} -// entity 50 -{ -"classname" "light" -"origin" "-560 -304 284" -} -// entity 51 -{ -"classname" "light" -"origin" "-160 304 388" -} -// entity 52 -{ -"classname" "light" -"origin" "96 312 492" -} -// entity 53 -{ -"classname" "light" -"origin" "448 0 492" -} -// entity 54 -{ -"classname" "light" -"origin" "712 -120 492" -} -// entity 55 -{ -"classname" "light" -"origin" "432 -464 486" -} -// entity 56 -{ -"classname" "light" -"origin" "256 -464 532" -} -// entity 57 -{ -"classname" "light" -"origin" "-384 -104 416" -} -// entity 58 -{ -"classname" "light" -"origin" "-384 -96 384" -"_color" "0.85 0.2 0.2" -"light" "200" -"radius" "32" -} -// entity 59 +// entity 40 { "classname" "light" "origin" "120 536 496" } -// entity 60 +// entity 41 +{ +"classname" "item_armor_green" +"origin" "944 0 144" +} +// entity 42 +{ +"classname" "item_health_50" +"origin" "128 544 376" +} +// entity 43 +{ +"classname" "target_position" +"origin" "160 40 504" +"targetname" "light_target1" +} +// entity 44 +{ +"classname" "info_player_deathmatch" +"origin" "-496 -304 152" +"angle" "180" +} +// entity 45 { "classname" "light" -"origin" "704 -448 492" +"origin" "-136 0 284" } -// entity 61 +// entity 46 { "classname" "light" -"origin" "64 -464 532" +"origin" "-384 -104 416" } -// entity 62 +// entity 47 { "classname" "light" -"origin" "-384 -304 284" +"origin" "-560 -304 284" } -// entity 63 -{ -"classname" "light" -"origin" "432 -464 96" -"_color" "0.752941 0.752941 0" -"light" "200" -"radius" "32" -} -// entity 64 -{ -"classname" "light" -"origin" "944 0 168" -"_color" "0 0.752941 0" -"light" "200" -"radius" "32" -} -// entity 65 +// entity 48 { "classname" "light" "origin" "-544 -304 168" @@ -4918,40 +4826,25 @@ "light" "200" "radius" "32" } -// entity 66 +// entity 49 { "classname" "light" -"origin" "424 0 252" +"origin" "-384 -304 284" } -// entity 67 +// entity 50 { "classname" "light" -"origin" "-136 0 284" -} -// entity 68 -{ -"classname" "light" -"origin" "64 -348 24" -} -// entity 69 -{ -"classname" "item_armor_green" -"origin" "944 0 144" -} -// entity 70 -{ -"classname" "light" -"origin" "128 -464 96" -"_color" "0 0 0.752941" +"origin" "-384 -96 384" +"_color" "0.85 0.2 0.2" "light" "200" "radius" "32" } -// entity 71 +// entity 51 { -"classname" "item_health_50" -"origin" "128 544 376" +"classname" "light" +"origin" "96 312 492" } -// entity 72 +// entity 52 { "classname" "light" "origin" "160 144 592" @@ -4959,15 +4852,122 @@ "radius" "0" "light" "600" } +// entity 53 +{ +"classname" "light" +"origin" "-160 304 388" +} +// entity 54 +{ +"origin" "612 -512 72" +"classname" "ammo_shells" +} +// entity 55 +{ +"origin" "668 -512 72" +"classname" "ammo_bullets" +} +// entity 56 +{ +"classname" "ammo_shells" +"origin" "492 -128 376" +} +// entity 57 +{ +"classname" "weapon_shotgun" +"origin" "128 0 -48" +} +// entity 58 +{ +"classname" "light" +"origin" "224 -464 220" +} +// entity 59 +{ +"classname" "light" +"origin" "32 -464 220" +} +// entity 60 +{ +"classname" "light" +"origin" "432 -464 196" +} +// entity 61 +{ +"classname" "light" +"origin" "816 -656 272" +} +// entity 62 +{ +"classname" "light" +"origin" "624 -48 288" +} +// entity 63 +{ +"classname" "light" +"origin" "656 -464 280" +} +// entity 64 +{ +"classname" "light" +"origin" "448 0 492" +} +// entity 65 +{ +"classname" "light" +"origin" "712 -120 492" +} +// entity 66 +{ +"classname" "light" +"origin" "432 -464 486" +} +// entity 67 +{ +"classname" "light" +"origin" "256 -464 532" +} +// entity 68 +{ +"classname" "light" +"origin" "704 -448 492" +} +// entity 69 +{ +"classname" "light" +"origin" "64 -464 532" +} +// entity 70 +{ +"classname" "light" +"origin" "432 -464 96" +"_color" "0.752941 0.752941 0" +"light" "200" +"radius" "32" +} +// entity 71 +{ +"classname" "light" +"origin" "944 0 168" +"_color" "0 0.752941 0" +"light" "200" +"radius" "32" +} +// entity 72 +{ +"classname" "light" +"origin" "424 0 252" +} // entity 73 { -"classname" "target_position" -"origin" "160 40 504" -"targetname" "light_target1" +"classname" "light" +"origin" "64 -348 24" } // entity 74 { -"classname" "info_player_deathmatch" -"origin" "-496 -304 152" -"angle" "180" +"classname" "light" +"origin" "128 -464 96" +"_color" "0 0 0.752941" +"light" "200" +"radius" "32" } diff --git a/Content/Materials/SimpleMapMaterial.flax b/Content/Materials/SimpleMapMaterial.flax index 57527c3..176b0bf 100644 Binary files a/Content/Materials/SimpleMapMaterial.flax and b/Content/Materials/SimpleMapMaterial.flax differ diff --git a/Content/Materials/SkyMaterial.flax b/Content/Materials/SkyMaterial.flax index 074c073..58378f5 100644 Binary files a/Content/Materials/SkyMaterial.flax and b/Content/Materials/SkyMaterial.flax differ diff --git a/Content/Materials/ViewModelMaterial.flax b/Content/Materials/ViewModelMaterial.flax index 41d18a4..dcd708e 100644 Binary files a/Content/Materials/ViewModelMaterial.flax and b/Content/Materials/ViewModelMaterial.flax differ diff --git a/Content/Materials/WeaponMaterial.flax b/Content/Materials/WeaponMaterial.flax index 127b3a7..c76639d 100644 Binary files a/Content/Materials/WeaponMaterial.flax and b/Content/Materials/WeaponMaterial.flax differ diff --git a/Content/Materials/interface/crosshairs/cross.flax b/Content/Materials/interface/crosshairs/cross.flax index 08708d0..9fbbb7f 100644 Binary files a/Content/Materials/interface/crosshairs/cross.flax and b/Content/Materials/interface/crosshairs/cross.flax differ diff --git a/Content/Materials/interface/testguimat.flax b/Content/Materials/interface/testguimat.flax index ed04e6b..9f50a80 100644 Binary files a/Content/Materials/interface/testguimat.flax and b/Content/Materials/interface/testguimat.flax differ diff --git a/Content/Materials/missing.flax b/Content/Materials/missing.flax index e6075db..cc87730 100644 Binary files a/Content/Materials/missing.flax and b/Content/Materials/missing.flax differ diff --git a/Content/Materials/testo.flax b/Content/Materials/testo.flax index 6d2deea..bca0f6d 100644 Binary files a/Content/Materials/testo.flax and b/Content/Materials/testo.flax differ diff --git a/Content/Scenes/MainScene.scene b/Content/Scenes/MainScene.scene index ed01883..82ee000 100644 --- a/Content/Scenes/MainScene.scene +++ b/Content/Scenes/MainScene.scene @@ -1,7 +1,7 @@ { "ID": "194e05f445ece24ec5448d886e1334df", "TypeName": "FlaxEngine.SceneAsset", - "EngineBuild": 6601, + "EngineBuild": 6605, "Data": [ { "ID": "194e05f445ece24ec5448d886e1334df", @@ -27,6 +27,12 @@ "X": 3.0, "Y": 0.0, "Z": 0.0 + }, + "Orientation": { + "X": 0.13052625954151154, + "Y": -1.6762705001838186e-7, + "Z": -1.6762705001838186e-7, + "W": 0.9914449453353882 } }, "V": {} @@ -51,7 +57,7 @@ } }, "Data": { - "Text": "0 tris\n 0 drawcalls\r\n262fps2\r\n285fps" + "Text": "0 tris\n 0 drawcalls\r\n10fps2\r\n10fps" } }, { @@ -68,6 +74,13 @@ "TypeName": "FlaxEngine.UIControl", "ParentID": "ff6b6db54b5aa08e7286ef86246149ef", "Name": "CrosshairWidget", + "Transform": { + "Translation": { + "X": 11.0, + "Y": 8.0, + "Z": 0.0 + } + }, "Control": "FlaxEngine.GUI.Image", "Data": { "Brush": { @@ -110,10 +123,10 @@ "Y": 1.0 }, "Offsets": { - "Left": 0.0, - "Right": 0.0, - "Top": 0.0, - "Bottom": 0.0 + "Left": 11.0, + "Right": -11.0, + "Top": 8.0, + "Bottom": -8.0 }, "Scale": { "X": 1.0, @@ -134,6 +147,7 @@ "B": 0.0, "A": 0.0 }, + "BackgroundBrush": null, "Enabled": true, "Visible": true, "AutoFocus": false diff --git a/Content/Settings/EngineSettings/BuildSettings.json b/Content/Settings/EngineSettings/BuildSettings.json index cc55101..4099356 100644 --- a/Content/Settings/EngineSettings/BuildSettings.json +++ b/Content/Settings/EngineSettings/BuildSettings.json @@ -1,8 +1,9 @@ { "ID": "af2e52554f7faed7b4937181dd22d166", "TypeName": "FlaxEditor.Content.Settings.BuildSettings", - "EngineBuild": 6340, + "EngineBuild": 6605, "Data": { + "OutputName": "${PROJECT_NAME}", "MaxAssetsPerPackage": 4096, "MaxPackageSizeMB": 1024, "ContentKey": 0, @@ -18,7 +19,9 @@ ], "ShadersNoOptimize": false, "ShadersGenerateDebugData": false, - "SkipDotnetPackaging": true, + "SkipDefaultFonts": false, + "SkipDotnetPackaging": false, + "SkipUnusedDotnetLibsPackaging": true, "Presets": [ { "Name": "Preset 3", diff --git a/Content/Visual Script.flax b/Content/Visual Script.flax deleted file mode 100644 index dbb7b46..0000000 Binary files a/Content/Visual Script.flax and /dev/null differ diff --git a/Content/config.cfg b/Content/config.cfg index 6279ea8..6961891 100644 --- a/Content/config.cfg +++ b/Content/config.cfg @@ -1,7 +1,7 @@ // comment r_shadows 1 r_lighting 1 -cl_maxfps 0 +cl_maxfps 240 r_upscaling 0 r_gi 0 r_staticbatch 1 \ No newline at end of file diff --git a/Source/Game/Console/ConsoleInputTextBox.cs b/Source/Game/Console/ConsoleInputTextBox.cs index 374a055..d7c1827 100644 --- a/Source/Game/Console/ConsoleInputTextBox.cs +++ b/Source/Game/Console/ConsoleInputTextBox.cs @@ -145,11 +145,18 @@ public class ConsoleInputTextBox : ConsoleTextBoxBase public override void OnLostFocus() { - // Prevent caret location getting reset back to beginning + // Prevent caret location getting reset back to beginning, + // and submitting the value when focus is lost. bool oldEditing = _isEditing; _isEditing = false; - base.OnLostFocus(); - _isEditing = oldEditing; + try + { + base.OnLostFocus(); + } + finally + { + _isEditing = oldEditing; + } } public override bool OnMouseDown(Float2 location, MouseButton button) diff --git a/Source/Game/GameMode/Messages/AcceptConnectionMessage.cs b/Source/Game/GameMode/Messages/AcceptConnectionMessage.cs new file mode 100644 index 0000000..70ee5d7 --- /dev/null +++ b/Source/Game/GameMode/Messages/AcceptConnectionMessage.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FlaxEngine.Networking; +using FlaxEngine; + +namespace Game; + +public struct AcceptConnectionMessage +{ + public struct PlayerInfo + { + public uint PlayerId; + public Float3 PlayerPosition; + } + public ulong Frame; + public float GameTime; + public PlayerInfo[] Players; + + public static AcceptConnectionMessage Read(ref NetworkMessage networkMessage) + { + AcceptConnectionMessage packet = new AcceptConnectionMessage(); + packet.Frame = networkMessage.ReadUInt64(); + packet.GameTime = networkMessage.ReadSingle(); + int numActors = (int)networkMessage.ReadUInt32(); + + packet.Players = new PlayerInfo[numActors]; + for (int i = 0; i < numActors; i++) + { + packet.Players[i].PlayerId = networkMessage.ReadUInt32(); + packet.Players[i].PlayerPosition.X = networkMessage.ReadSingle(); + packet.Players[i].PlayerPosition.Y = networkMessage.ReadSingle(); + packet.Players[i].PlayerPosition.Z = networkMessage.ReadSingle(); + } + return packet; + } + + public void Write(ref NetworkMessage networkMessage) + { + networkMessage.WriteByte((byte)GameModeMessageType2.AcceptConnection); + networkMessage.WriteUInt64(Frame); + networkMessage.WriteSingle(GameTime); + networkMessage.WriteUInt32((uint)Players.Length); + foreach (PlayerInfo player in Players) + { + networkMessage.WriteUInt32(player.PlayerId); + networkMessage.WriteSingle(player.PlayerPosition.X); + networkMessage.WriteSingle(player.PlayerPosition.Y); + networkMessage.WriteSingle(player.PlayerPosition.Z); + } + } +} diff --git a/Source/Game/GameMode/Messages/WelcomePlayerMessage.cs b/Source/Game/GameMode/Messages/WelcomePlayerMessage.cs deleted file mode 100644 index f938eee..0000000 --- a/Source/Game/GameMode/Messages/WelcomePlayerMessage.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using FlaxEngine.Networking; -using FlaxEngine; - -namespace Game; - -public struct WelcomePlayerMessage -{ - public struct PlayerInfo - { - public uint playerId; - public Float3 playerPosition; - } - public ulong frame; - public float time; - public PlayerInfo[] players; - - public static WelcomePlayerMessage Read(ref NetworkMessage networkMessage) - { - WelcomePlayerMessage packet = new WelcomePlayerMessage(); - packet.frame = networkMessage.ReadUInt64(); - packet.time = networkMessage.ReadSingle(); - int numActors = (int)networkMessage.ReadUInt32(); - - packet.players = new PlayerInfo[numActors]; - for (int i = 0; i < numActors; i++) - { - packet.players[i].playerId = networkMessage.ReadUInt32(); - packet.players[i].playerPosition.X = networkMessage.ReadSingle(); - packet.players[i].playerPosition.Y = networkMessage.ReadSingle(); - packet.players[i].playerPosition.Z = networkMessage.ReadSingle(); - } - return packet; - } - - public void Write(ref NetworkMessage networkMessage) - { - networkMessage.WriteByte((byte)GameModeMessageType.WelcomePlayer); - networkMessage.WriteUInt64(frame); - networkMessage.WriteSingle(time); - networkMessage.WriteUInt32((uint)players.Length); - foreach (PlayerInfo player in players) - { - networkMessage.WriteUInt32(player.playerId); - networkMessage.WriteSingle(player.playerPosition.X); - networkMessage.WriteSingle(player.playerPosition.Y); - networkMessage.WriteSingle(player.playerPosition.Z); - } - } -} diff --git a/Source/Game/GameMode/NetworkManager.cs b/Source/Game/GameMode/NetworkManager.cs index 9c63617..70d6ccf 100644 --- a/Source/Game/GameMode/NetworkManager.cs +++ b/Source/Game/GameMode/NetworkManager.cs @@ -33,6 +33,8 @@ public enum NetworkMessageType : byte public static partial class NetworkManager { public delegate bool OnMessageDecl(ref NetworkEvent networkEvent); + public delegate bool OnClientConnectingDecl(NetworkConnection sender); + public delegate void OnClientConnectedDecl(NetworkConnection sender); private static bool initialized; @@ -45,28 +47,76 @@ public static partial class NetworkManager private static readonly ushort MaximumClients = 32; private static List OnClientMessageDelegates = new(3); + private static List OnClientConnectingDelegates = new(3); + private static List OnClientConnectedDelegates = new(3); private static List OnServerMessageDelegates = new(3); + private static List OnServerConnectingDelegates = new(3); + private static List OnServerConnectedDelegates = new(3); - public static void RegisterClientMessageCallback(OnMessageDecl deleg) + public static void RegisterClientCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { - Assert.IsTrue(!OnClientMessageDelegates.Contains(deleg)); - OnClientMessageDelegates.Add(deleg); + Assert.IsTrue(!OnClientMessageDelegates.Contains(onMessage)); + OnClientMessageDelegates.Add(onMessage); + + if (onClientConnecting != null) + { + Assert.IsTrue(!OnClientConnectingDelegates.Contains(onClientConnecting)); + OnClientConnectingDelegates.Add(onClientConnecting); + } + if (onClientConnected != null) + { + Assert.IsTrue(!OnClientConnectedDelegates.Contains(onClientConnected)); + OnClientConnectedDelegates.Add(onClientConnected); + } } - public static void UnregisterClientMessageCallback(OnMessageDecl deleg) + public static void UnregisterClientCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { - Assert.IsTrue(OnClientMessageDelegates.Contains(deleg)); - OnClientMessageDelegates.Remove(deleg); + Assert.IsTrue(OnClientMessageDelegates.Contains(onMessage)); + OnClientMessageDelegates.Remove(onMessage); + + if (onClientConnecting != null) + { + Assert.IsTrue(OnClientConnectingDelegates.Contains(onClientConnecting)); + OnClientConnectingDelegates.Remove(onClientConnecting); + } + if (onClientConnected != null) + { + Assert.IsTrue(OnClientConnectedDelegates.Contains(onClientConnected)); + OnClientConnectedDelegates.Remove(onClientConnected); + } } - public static void RegisterServerMessageCallback(OnMessageDecl deleg) + public static void RegisterServerCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { - Assert.IsTrue(!OnServerMessageDelegates.Contains(deleg)); - OnServerMessageDelegates.Add(deleg); + Assert.IsTrue(!OnServerMessageDelegates.Contains(onMessage)); + OnServerMessageDelegates.Add(onMessage); + + if (onClientConnecting != null) + { + Assert.IsTrue(!OnServerConnectingDelegates.Contains(onClientConnecting)); + OnServerConnectingDelegates.Add(onClientConnecting); + } + if (onClientConnected != null) + { + Assert.IsTrue(!OnServerConnectedDelegates.Contains(onClientConnected)); + OnServerConnectedDelegates.Add(onClientConnected); + } } - public static void UnregisterServerMessageCallback(OnMessageDecl deleg) + public static void UnregisterServerCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { - Assert.IsTrue(OnServerMessageDelegates.Contains(deleg)); - OnServerMessageDelegates.Remove(deleg); + Assert.IsTrue(OnServerMessageDelegates.Contains(onMessage)); + OnServerMessageDelegates.Remove(onMessage); + + if (onClientConnecting != null) + { + Assert.IsTrue(OnServerConnectingDelegates.Contains(onClientConnecting)); + OnServerConnectingDelegates.Remove(onClientConnecting); + } + if (onClientConnected != null) + { + Assert.IsTrue(OnServerConnectedDelegates.Contains(onClientConnected)); + OnServerConnectedDelegates.Remove(onClientConnected); + } } public static string DebugLastHandledMessage = ""; @@ -129,22 +179,14 @@ public static partial class NetworkManager client = null; } - LocalPlayerClientId = 0; + //LocalPlayerClientId = 0; #if FLAX_EDITOR Editor.Instance.PlayModeEnd -= Cleanup; //GameModeManager.Cleanup(); // FIXME #endif - if (clientWorldStateManager != null) - { - clientWorldStateManager.Cleanup(); - clientWorldStateManager = null; - } - if (serverWorldStateManager != null) - { - serverWorldStateManager.Cleanup(); - serverWorldStateManager = null; - } + World.CleanupClient(); + World.CleanupServer(); StopRecording(); diff --git a/Source/Game/GameMode/NetworkManager_Client.cs b/Source/Game/GameMode/NetworkManager_Client.cs index 2a4cc4f..a4f30ad 100644 --- a/Source/Game/GameMode/NetworkManager_Client.cs +++ b/Source/Game/GameMode/NetworkManager_Client.cs @@ -14,7 +14,7 @@ public static partial class NetworkManager public static INetworkDriver ClientNetworkDriver { get; set; } - public static WorldStateManager clientWorldStateManager = null; + //public static WorldStateManager clientWorldStateManager = null; public static bool ConnectServer(string serverAddress = "localhost", bool listenServer = false) { @@ -50,7 +50,7 @@ public static partial class NetworkManager } Debug.Log("Connected..."); - if (!listenServer) + /*if (!listenServer) { //WorldStateManager.Init(); clientWorldStateManager = new WorldStateManager(isClient: true); @@ -58,7 +58,8 @@ public static partial class NetworkManager else { clientWorldStateManager = new WorldStateManager(isLocalClient: true); - } + }*/ + World.InitClient(); Scripting.FixedUpdate += OnClientNetworkUpdate; if (!listenServer) @@ -106,20 +107,20 @@ public static partial class NetworkManager { case NetworkEventType.Connected: { - LocalPlayerClientId = networkEvent.Sender.ConnectionId; - Console.Print("Connected to server, ConnectionId: " + networkEvent.Sender.ConnectionId); + foreach (var func in OnClientConnectedDelegates) + func(networkEvent.Sender); break; } case NetworkEventType.Disconnected: { Console.Print("Disconnected from server, timeout."); - LocalPlayerClientId = 0; + //LocalPlayerClientId = 0; break; } case NetworkEventType.Timeout: { Console.Print("Disconnected from server, connection closed."); - LocalPlayerClientId = 0; + //LocalPlayerClientId = 0; break; } case NetworkEventType.Message: diff --git a/Source/Game/GameMode/NetworkManager_Demo.cs b/Source/Game/GameMode/NetworkManager_Demo.cs index 642e55e..b434db1 100644 --- a/Source/Game/GameMode/NetworkManager_Demo.cs +++ b/Source/Game/GameMode/NetworkManager_Demo.cs @@ -38,7 +38,8 @@ public static unsafe partial class NetworkManager { { Cleanup(); - clientWorldStateManager = new WorldStateManager(isServer: true); + //clientWorldStateManager = new WorldStateManager(isServer: true); + World.InitServer(); } if (!ReadDemo(demoName)) diff --git a/Source/Game/GameMode/NetworkManager_Server.cs b/Source/Game/GameMode/NetworkManager_Server.cs index adc01f4..33514b2 100644 --- a/Source/Game/GameMode/NetworkManager_Server.cs +++ b/Source/Game/GameMode/NetworkManager_Server.cs @@ -18,7 +18,7 @@ public static partial class NetworkManager public static INetworkDriver ServerNetworkDriver { get; set; } - public static WorldStateManager serverWorldStateManager = null; + //public static WorldStateManager serverWorldStateManager = null; public static bool StartServer(bool listenServer = true) { @@ -82,8 +82,9 @@ public static partial class NetworkManager foreach (Type type in NetworkedTypes) Console.Print("tracking networked type: " + type.Name); #endif - serverWorldStateManager = new WorldStateManager(isServer: true); + //serverWorldStateManager = new WorldStateManager(isServer: true); //WorldStateManager.Init(); + World.InitServer(); if (listenServer) return ConnectServer(listenServer: true); @@ -139,13 +140,21 @@ public static partial class NetworkManager try { //IsServer = true; - if (serverWorldStateManager.OnClientConnecting(networkEvent.Sender)) + bool handled = false; + foreach (var func in OnServerConnectingDelegates) + { + handled = func(networkEvent.Sender); + if (handled) + break; + } + if (handled) { ConnectedClients.Add(networkEvent.Sender); Console.Print( $"Client({networkEvent.Sender.ConnectionId}) connected. Total clients: {ConnectedClients.Count}"); - serverWorldStateManager.OnClientConnected(networkEvent.Sender); + foreach (var func in OnServerConnectedDelegates) + func(networkEvent.Sender); } else Console.Print($"Client({networkEvent.Sender.ConnectionId}) connection refused"); diff --git a/Source/Game/GameMode/World.cs b/Source/Game/GameMode/World.cs new file mode 100644 index 0000000..e92805b --- /dev/null +++ b/Source/Game/GameMode/World.cs @@ -0,0 +1,549 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FlaxEngine; +using FlaxEngine.Assertions; +using FlaxEngine.Json; +using FlaxEngine.Networking; + +namespace Game; + +public enum GameModeMessageType2 : byte +{ + AcceptConnection, // AcceptConnectionMessage + SpawnPlayer, + PlayerInput, + PlayerPosition, // world update + PlayerSnapshot, // send world state delta to client since last client's acknowledged frame + + LastMessageType = 128, +} + +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; } + public ulong ServerFrame { get; protected set; } // Last received frame from server + public float GameTime { get; protected set; } // The join time + public float GetFrameTime(ulong frame) => GameTime + (frame * (1.0f / Time.PhysicsFPS)); + + protected Scene _scene; + protected Actor _worldSpawn; + protected Dictionary _clients = new(); + + public static void InitClient() + { + WorldStore.ClientWorld = new ClientWorld(); + WorldStore.ClientWorld.Init(); + } + public static void InitServer() + { + WorldStore.ServerWorld = new ServerWorld(); + WorldStore.ServerWorld.Init(); + } + public static void CleanupClient() + { + WorldStore.ClientWorld?.Cleanup(); + WorldStore.ClientWorld = null; + } + public static void CleanupServer() + { + WorldStore.ServerWorld?.Cleanup(); + WorldStore.ServerWorld = null; + } + + virtual protected void Init() + { + } + + virtual protected void Cleanup() + { + //Level.SceneLoaded -= OnLevelLoaded; + //Scripting.LateUpdate -= OnLateUpdatePre; + Scripting.LateFixedUpdate -= OnLateUpdate; + + if (_scene) + { + foreach (var player in _scene.GetChildren()) + { + FlaxEngine.Object.Destroy(player); + } + if (Level.UnloadScene(_scene)) + throw new Exception("Failed to unload scene"); + _scene = null; + } + } + + protected void CreateScene(string sceneNamePrefix, string sceneGuid) + { + string physicsSceneName = $"{sceneNamePrefix}PhysicsScene"; + PhysicsScene localPhysicsScene = Physics.FindOrCreateScene(physicsSceneName); + + Guid guid = JsonSerializer.ParseID(sceneGuid); + string guidStr = JsonSerializer.GetStringID(guid); + string sceneData = $@" +{{ + ""ID"": ""{guidStr}"", + ""TypeName"": ""FlaxEngine.SceneAsset"", + ""EngineBuild"": 65046, + ""Data"": [ + {{ + ""ID"": ""{guidStr}"", + ""TypeName"": ""FlaxEngine.Scene"", + ""LightmapSettings"": {{ + ""IndirectLightingIntensity"": 1.0, + ""GlobalObjectsScale"": 1.0, + ""ChartsPadding"": 3, + ""AtlasSize"": 1024, + ""BounceCount"": 1, + ""CompressLightmaps"": true, + ""UseGeometryWithNoMaterials"": true, + ""Quality"": 10 + }} + }} + ] +}}"; + + { + var onSceneLoaded = (Scene loadedScene, Guid id) => + { + if (guid == id) + { + loadedScene.PhysicsScene = localPhysicsScene; + //scene = loadedScene; + } + }; + + try + { + Level.SceneLoaded += onSceneLoaded; + //Level.LoadScene(new SceneReference(guid)); + _scene = Level.LoadSceneFromBytes(Encoding.ASCII.GetBytes(sceneData)); + } + finally + { + Level.SceneLoaded -= onSceneLoaded; + } + } + Assert.IsTrue(_scene); + + _scene.Name = $"{sceneNamePrefix}Scene"; + + //Level.SceneLoaded += OnLevelLoaded; + Scripting.LateFixedUpdate += OnLateUpdate; + + var importer = FlaxEngine.Object.New(); + importer.mapPath = @"C:\dev\GoakeFlax\Assets\Maps\aerowalk.map"; + importer.LoadCollidersOnly = sceneNamePrefix != "Client"; // FIXME + importer.Parent = _scene; + + //importer.Enabled = true; + _worldSpawn = _scene.FindActor("WorldSpawn");// ?? Level.FindActor("WorldSpawn"); + Assert.IsTrue(_worldSpawn); + } + + public void OnLateUpdate() + { + Frame++; + } + + 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) + { + 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 = SpawnActor(playerPrefab); + playerActor.Initialize(playerId, position, eulerAngles); + //playerActor.Teleport(position, eulerAngles); + + _clients[playerId].PlayerActor = playerActor; + IPlayerInput playerInput = playerActor.GetScript().Input; + playerInput.SetFrame(Frame); + /*if (IsServer) + { + serverWorldState.actors.Add(playerActor); + if (!playerLastReceivedFrames.ContainsKey(playerId)) + playerLastReceivedFrames.Add(playerId, 0); + }*/ + + return playerActor; + } + + protected T SpawnActor(Prefab prefab) where T : Actor + { + T actor = PrefabManager.SpawnPrefab(prefab, _scene).As(); + actor.PhysicsScene = _scene.PhysicsScene; + return actor; + } + + virtual public bool IsLocalPlayer(uint playerId) => false; + + public PlayerFrame GetPlayerFrame(uint playerId, ulong frame) + { + ClientInfo player = _clients[playerId]; + PlayerFrame playerFrame = player.FrameHistory[frame % 120]; + if (playerFrame.frame != frame) + return null; + + return playerFrame; + } + + public bool HasPlayerFrame(uint playerId, ulong frame) => GetPlayerFrame(playerId, frame) != null; + + public bool GetPlayerInputState(uint playerId, ulong frame, out PlayerInputState2 inputState) + { + PlayerFrame playerFrame = GetPlayerFrame(playerId, frame); + inputState = playerFrame?.inputState ?? default; + return playerFrame != null; + } +} + +file class ServerWorld : World +{ + protected override void Init() + { + NetworkManager.RegisterServerCallbacks(OnMessage, OnClientConnecting, OnClientConnected); + GameTime = Time.GameTime; + + CreateScene("Server", "59dd37cc444d5d7015759389c6153c4c"); + + base.Init(); + } + + protected override void Cleanup() + { + NetworkManager.UnregisterServerCallbacks(OnMessage, OnClientConnecting, OnClientConnected); + base.Cleanup(); + } + + public bool OnMessage(ref NetworkEvent networkEvent) + { + byte messageTypeByte = networkEvent.Message.ReadByte(); + if (!Enum.IsDefined(typeof(GameModeMessageType2), messageTypeByte)) + { + //Console.PrintError($"GameModeManager: Unsupported message type received from client: {messageTypeByte}"); + return false; + } + + GameModeMessageType2 messageType = (GameModeMessageType2)messageTypeByte; + NetworkManager.DebugLastHandledMessage = messageType.ToString(); + + //Console.Print($"GameModeManager: {messageType}"); + switch (messageType) + { + 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; + case GameModeMessageType2.PlayerPosition: + { + //uint playerId = networkEvent.Sender.ConnectionId; + PlayerInputState2 inputState; //? + PlayerActorState actorState; + + ulong receivedFrame = networkEvent.Message.ReadUInt64(); + uint reportedPlayerId = networkEvent.Message.ReadUInt32(); + actorState.position.X = networkEvent.Message.ReadSingle(); + actorState.position.Y = networkEvent.Message.ReadSingle(); + actorState.position.Z = networkEvent.Message.ReadSingle(); + actorState.velocity.X = networkEvent.Message.ReadSingle(); + actorState.velocity.Y = networkEvent.Message.ReadSingle(); + actorState.velocity.Z = networkEvent.Message.ReadSingle(); + /*actorState.orientation.X = networkEvent.Message.ReadSingle(); + actorState.orientation.Y = networkEvent.Message.ReadSingle(); + actorState.orientation.Z = networkEvent.Message.ReadSingle(); + actorState.orientation.W = networkEvent.Message.ReadSingle();*/ + actorState.viewAngles.X = networkEvent.Message.ReadSingle(); + actorState.viewAngles.Y = networkEvent.Message.ReadSingle(); + actorState.viewAngles.Z = networkEvent.Message.ReadSingle(); + actorState.lastJumpTime = networkEvent.Message.ReadSingle(); + actorState.numJumps = networkEvent.Message.ReadInt32(); + actorState.jumped = networkEvent.Message.ReadBoolean(); + + //inputState.frame = 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(); + + ServerFrame = receivedFrame; + } + break; + default: + break; + } + + return true; + } + + public bool OnClientConnecting(NetworkConnection connection) + { + AcceptConnectionMessage message = new AcceptConnectionMessage(); + message.Frame = Frame; + message.GameTime = GameTime; + message.Players = new AcceptConnectionMessage.PlayerInfo[_clients.Count]; + + int playerIndex = 0; + foreach (var player in _clients.Values) + { + ref AcceptConnectionMessage.PlayerInfo playerInfo = ref message.Players[playerIndex]; + playerInfo.PlayerId = player.PlayerActor.GetScript().PlayerId; + playerInfo.PlayerPosition = player.PlayerActor.Position; + playerIndex++; + } + NetworkMessage networkMessage = NetworkManager.ServerBeginSendMessage(); + message.Write(ref networkMessage); + NetworkManager.ServerEndSendMessage(ref networkMessage, connection); + return true; + } + + public new void OnClientConnected(NetworkConnection connection) + { + base.OnClientConnected(connection); + uint playerId = connection.ConnectionId; + + + var spawns = _worldSpawn.GetChildren().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray(); + Console.Print($"found {spawns.Length} spawns"); + + var randomSpawn = spawns.First(); + + Float3 position = randomSpawn.Position + new Float3(0f, 4.1f, 0f); + Float3 eulerAngles = randomSpawn.Orientation.EulerAngles; + + SpawnPlayer(playerId, position, eulerAngles); + { + NetworkMessage message = NetworkManager.ServerBeginSendMessage(); + message.WriteByte((byte)GameModeMessageType.SpawnPlayer); + message.WriteUInt32(playerId); + message.WriteUInt64(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); + } + } +} + +file class ClientWorld : World +{ + private uint _localPlayerId; + + protected override void Init() + { + NetworkManager.RegisterClientCallbacks(OnMessage, null, OnClientConnected); + + CreateScene("Client", "c095f9ac4989a46afd7fe3821f086e2e"); + + base.Init(); + } + + protected override void Cleanup() + { + NetworkManager.UnregisterClientCallbacks(OnMessage, null, OnClientConnected); + base.Cleanup(); + } + + public bool OnMessage(ref NetworkEvent networkEvent) + { + byte messageTypeByte = networkEvent.Message.ReadByte(); + if (!Enum.IsDefined(typeof(GameModeMessageType2), messageTypeByte)) + { + //Console.PrintError($"GameModeManager: Unsupported message type received from client: {messageTypeByte}"); + return false; + } + + GameModeMessageType2 messageType = (GameModeMessageType2)messageTypeByte; + NetworkManager.DebugLastHandledMessage = messageType.ToString(); + + //Console.Print($"GameModeManager: {messageType}"); + switch (messageType) + { + case GameModeMessageType2.AcceptConnection: + { + // Setup some initial state for newly connected client + AcceptConnectionMessage welcomePlayer = AcceptConnectionMessage.Read(ref networkEvent.Message); + ServerFrame = welcomePlayer.Frame; + Frame += welcomePlayer.Frame; + + GameTime = welcomePlayer.GameTime; + foreach (var playerInfo in welcomePlayer.Players) + { + _clients.Add(playerInfo.PlayerId, new ClientInfo()); + SpawnPlayer(playerInfo.PlayerId, playerInfo.PlayerPosition, new Float3(0)); + } + + Console.Print("received welcome: frame " + ServerFrame); + } + break; + case GameModeMessageType2.SpawnPlayer: + { + uint playerId = networkEvent.Message.ReadUInt32(); + ulong playerFrameIndex = networkEvent.Message.ReadUInt64(); + Float3 position = new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()); + Vector3 eulerAngles = new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()); + + /*if (!clientWorldState.playerFrameHistory.ContainsKey(playerId)) + { + var playerFrames = new PlayerFrame[120]; + for (int j = 0; j < playerFrames.Length; j++) + playerFrames[j] = new PlayerFrame(); + clientWorldState.playerFrameHistory.Add(playerId, playerFrames); + }*/ + + SpawnPlayer(playerId, position, eulerAngles); + + //if (NetworkManager.IsClient) + //players[playerId].GetScript().Input.SetFrame(ClientFrame); + } + break; +#if false + case GameModeMessageType2.PlayerPosition: + { + //uint playerId = networkEvent.Sender.ConnectionId; + PlayerInputState2 inputState; //? + PlayerActorState actorState; + + ulong receivedFrame = networkEvent.Message.ReadUInt64(); + uint reportedPlayerId = networkEvent.Message.ReadUInt32(); + actorState.position.X = networkEvent.Message.ReadSingle(); + actorState.position.Y = networkEvent.Message.ReadSingle(); + actorState.position.Z = networkEvent.Message.ReadSingle(); + actorState.velocity.X = networkEvent.Message.ReadSingle(); + actorState.velocity.Y = networkEvent.Message.ReadSingle(); + actorState.velocity.Z = networkEvent.Message.ReadSingle(); + /*actorState.orientation.X = networkEvent.Message.ReadSingle(); + actorState.orientation.Y = networkEvent.Message.ReadSingle(); + actorState.orientation.Z = networkEvent.Message.ReadSingle(); + actorState.orientation.W = networkEvent.Message.ReadSingle();*/ + actorState.viewAngles.X = networkEvent.Message.ReadSingle(); + actorState.viewAngles.Y = networkEvent.Message.ReadSingle(); + actorState.viewAngles.Z = networkEvent.Message.ReadSingle(); + actorState.lastJumpTime = networkEvent.Message.ReadSingle(); + actorState.numJumps = networkEvent.Message.ReadInt32(); + actorState.jumped = networkEvent.Message.ReadBoolean(); + + //inputState.frame = 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(); + + ServerFrame = receivedFrame; + } + break; +#endif + default: + break; + } + + return true; + } + + public override bool IsLocalPlayer(uint playerId) => playerId == _localPlayerId; + + public new void OnClientConnected(NetworkConnection connection) + { + base.OnClientConnected(connection); + _localPlayerId = connection.ConnectionId; + Console.Print($"ClientWorld: Connected, playerId: {_localPlayerId}"); + } + + protected override PlayerActor SpawnPlayer(uint playerId, Float3 position, Vector3 eulerAngles) + { + var playerActor = base.SpawnPlayer(playerId, position, eulerAngles); + if (playerId == _localPlayerId) + playerActor.Input = new PlayerInput2(); + else + playerActor.Input = new PlayerInputNetwork2(playerId, this); + return playerActor; + } +} + +public static class WorldExtensions +{ + public static World GetWorld(this Script script) => IsServerScene(script.Scene) ? WorldStore.ServerWorld : WorldStore.ClientWorld; + + public static World GetWorld(this Actor actor) => IsServerScene(actor.Scene) ? WorldStore.ServerWorld : WorldStore.ClientWorld; + + private static bool IsServerScene(Scene scene) => scene.Name == "ServerScene"; +} + +file static class WorldStore +{ + public static ServerWorld ServerWorld; + public static ClientWorld ClientWorld; +} + +/*file class WorldPlugin : GamePlugin +{ + public WorldPlugin() + { + } + + public override void Initialize() + { + + WorldStore.ClientWorld = new World(isServer: false); + } + + public override void Deinitialize() + { + WorldStore.ServerWorld = null; + WorldStore.ClientWorld = null; + } +}*/ \ No newline at end of file diff --git a/Source/Game/GameMode/WorldStateManager.cs b/Source/Game/GameMode/WorldStateManager.cs index 788f880..6d0f8a1 100644 --- a/Source/Game/GameMode/WorldStateManager.cs +++ b/Source/Game/GameMode/WorldStateManager.cs @@ -14,7 +14,7 @@ namespace Game; public enum GameModeMessageType : byte { - WelcomePlayer, // WelcomePlayerMessage + AcceptConnection, // WelcomePlayerMessage SpawnPlayer, PlayerInput, PlayerPosition, // world update @@ -97,10 +97,10 @@ public class WorldStateManager clientWorldState = new WorldState(); gameMode = new GameMode(); - if (IsServer) - NetworkManager.RegisterServerMessageCallback(OnMessage); + /*if (IsServer) + NetworkManager.RegisterServerCallbacks(OnMessage); else - NetworkManager.RegisterClientMessageCallback(OnMessage); + NetworkManager.RegisterClientCallbacks(OnMessage);*/ Debug.Log($"WorldStateManager Init is server: {IsServer}"); Debug.Log($"WorldStateManager Init is local client: {IsLocalClient}"); @@ -173,10 +173,10 @@ public class WorldStateManager public void Cleanup() { - if (IsServer) - NetworkManager.UnregisterServerMessageCallback(OnMessage); + /*if (IsServer) + NetworkManager.UnregisterServerCallbacks(OnMessage); else - NetworkManager.UnregisterClientMessageCallback(OnMessage); + NetworkManager.UnregisterClientCallbacks(OnMessage);*/ Level.SceneLoaded -= OnLevelLoaded; //Scripting.LateUpdate -= OnLateUpdatePre; @@ -454,26 +454,26 @@ public class WorldStateManager //Console.Print($"GameModeManager: {messageType}"); switch (messageType) { - case GameModeMessageType.WelcomePlayer: + case GameModeMessageType.AcceptConnection: { welcomed = true; if (IsClient || IsLocalClient) { - WelcomePlayerMessage welcomePlayer = WelcomePlayerMessage.Read(ref networkEvent.Message); + AcceptConnectionMessage welcomePlayer = AcceptConnectionMessage.Read(ref networkEvent.Message); if (!IsLocalClient) - serverWorldState.frame = welcomePlayer.frame; + serverWorldState.frame = welcomePlayer.Frame; //lastReceivedServerFrame = welcomePlayer.frame; - clientWorldState.frame += welcomePlayer.frame; + clientWorldState.frame += welcomePlayer.Frame; - ClientTime = welcomePlayer.time; - foreach (var playerInfo in welcomePlayer.players) + ClientTime = welcomePlayer.GameTime; + foreach (var playerInfo in welcomePlayer.Players) { - SpawnPlayer(playerInfo.playerId, playerInfo.playerPosition, new Float3(0)); + SpawnPlayer(playerInfo.PlayerId, playerInfo.PlayerPosition, new Float3(0)); var playerFrames = new PlayerFrame[120]; for (int j = 0; j < playerFrames.Length; j++) playerFrames[j] = new PlayerFrame(); - clientWorldState.playerFrameHistory.Add(playerInfo.playerId, playerFrames); + clientWorldState.playerFrameHistory.Add(playerInfo.PlayerId, playerFrames); } Console.Print("received welcome: frame " + serverWorldState.frame); @@ -614,17 +614,17 @@ public class WorldStateManager //if (connection.ConnectionId != NetworkManager.LocalPlayerClientId) { Console.Print("sending welcome: frame " + serverWorldState.frame); - WelcomePlayerMessage welcomeMessage = new WelcomePlayerMessage(); - welcomeMessage.frame = serverWorldState.frame; - welcomeMessage.time = ServerTime; - welcomeMessage.players = new WelcomePlayerMessage.PlayerInfo[serverWorldState.actors.Count]; + AcceptConnectionMessage welcomeMessage = new AcceptConnectionMessage(); + welcomeMessage.Frame = serverWorldState.frame; + welcomeMessage.GameTime = ServerTime; + welcomeMessage.Players = new AcceptConnectionMessage.PlayerInfo[serverWorldState.actors.Count]; for (int i = 0; i < serverWorldState.actors.Count; i++) { PlayerActor playerActor = serverWorldState.actors[i]; - ref WelcomePlayerMessage.PlayerInfo playerInfo = ref welcomeMessage.players[i]; - playerInfo.playerId = playerActor.GetScript().PlayerId; - playerInfo.playerPosition = playerActor.Position; + ref AcceptConnectionMessage.PlayerInfo playerInfo = ref welcomeMessage.Players[i]; + playerInfo.PlayerId = playerActor.GetScript().PlayerId; + playerInfo.PlayerPosition = playerActor.Position; /*playerActor.GetScript().Input.SetState(serverWorldState.frame, new PlayerInputState(), new PlayerActorState() { diff --git a/Source/Game/Player/PlayerActor.cs b/Source/Game/Player/PlayerActor.cs index 9c191ba..9aea85e 100644 --- a/Source/Game/Player/PlayerActor.cs +++ b/Source/Game/Player/PlayerActor.cs @@ -43,7 +43,12 @@ public class PlayerActor : RigidBody//, INetworkSerializable public MeshCollider meshCollider; //[NetworkReplicated] - public uint PlayerId = uint.MaxValue; + public uint PlayerId { get; private set; } = uint.MaxValue; + + public IPlayerInput Input { get; set; } = PlayerInputNone.Instance; + + private World _world; + private World World => _world ??= this.GetWorld(); /*public PlayerActor() { @@ -103,9 +108,9 @@ public class PlayerActor : RigidBody//, INetworkSerializable IsActive = true; PlayerId = playerId; - playerMovement.SetInput(playerId); + //playerMovement.SetInput(playerId); bool isServerScene = Scene.Name == "ServerScene"; - if (playerId == NetworkManager.LocalPlayerClientId && !isServerScene) + if (World.IsLocalPlayer(PlayerId)) { FindActor("CameraHolder").IsActive = true; //FindActor("ViewModelHolder").IsActive = true; diff --git a/Source/Game/Player/PlayerInput2.cs b/Source/Game/Player/PlayerInput2.cs index 238df95..9214644 100644 --- a/Source/Game/Player/PlayerInput2.cs +++ b/Source/Game/Player/PlayerInput2.cs @@ -84,21 +84,21 @@ public class PlayerInput2 : IPlayerInput public class PlayerInputNetwork2 : IPlayerInput { private uint _playerId; - private WorldStateManager _worldStateManager; + private World _world; private ulong _frame; private PlayerInputState2 _state; - public PlayerInputNetwork2(uint playerId, WorldStateManager worldStateManager) + public PlayerInputNetwork2(uint playerId, World world) { _playerId = playerId; - _worldStateManager = worldStateManager; + _world = world; } public void SetFrame(ulong frame) => _frame = frame; public void UpdateState() { - _worldStateManager.GetPlayerInputState(_playerId, _frame, out _state); + _world.GetPlayerInputState(_playerId, _frame, out _state); } public PlayerInputState2 GetState() => _state; @@ -107,4 +107,15 @@ public class PlayerInputNetwork2 : IPlayerInput { _state = new PlayerInputState2(); } +} + +public class PlayerInputNone : IPlayerInput +{ + public static PlayerInputNone Instance { get; } = new PlayerInputNone(); + private PlayerInputNone() { } + + public PlayerInputState2 GetState() => default; + public void ResetState() { } + public void SetFrame(ulong frame) { } + public void UpdateState() { } } \ No newline at end of file diff --git a/Source/Game/Player/PlayerMovement.cs b/Source/Game/Player/PlayerMovement.cs index e417d88..5885659 100644 --- a/Source/Game/Player/PlayerMovement.cs +++ b/Source/Game/Player/PlayerMovement.cs @@ -107,7 +107,10 @@ public class PlayerMovement : Script private readonly bool demoDeltasVerify = true; - private WorldStateManager worldStateManager; + //private WorldStateManager worldStateManager; + + private World _world; + private World World => _world ??= this.GetWorld(); private bool predicting = false; @@ -115,8 +118,8 @@ public class PlayerMovement : Script //public int currentInputFrame; //private int currentInputFrame2; - - public IPlayerInput Input; + [NoSerialize, HideInEditor] + public IPlayerInput Input => playerActor?.Input ?? PlayerInputNone.Instance; //private bool physicsInteractions = false; @@ -136,7 +139,7 @@ public class PlayerMovement : Script public Float3 viewAngles; private Float3 viewAnglesLastFrame; - public uint PlayerId = 0; + public uint PlayerId => playerActor ? playerActor.PlayerId : 0; [ReadOnly] public float CurrentVelocity @@ -161,7 +164,6 @@ public class PlayerMovement : Script { base.OnAwake(); - Console.Print("player awake, playerid: " + PlayerId); rootActor = Actor.GetChild("RootActor"); rigidBody = Actor.As(); playerActor = Actor.As(); @@ -176,36 +178,6 @@ public class PlayerMovement : Script //rigidBody.TriggerExit += OnTriggerExit; } - public void SetInput(uint playerId) - { - //if (playerId == 0) - // input = new PlayerInput(); - Assert.IsTrue(playerId != uint.MaxValue); - - PlayerId = playerId; - bool isServerScene = Scene.Name == "ServerScene"; - if (isServerScene) - worldStateManager = NetworkManager.serverWorldStateManager; - else - worldStateManager = NetworkManager.clientWorldStateManager; - - if (PlayerId == NetworkManager.LocalPlayerClientId && !isServerScene)//if (NetworkReplicator.GetObjectRole(this.Parent) == NetworkObjectRole.OwnedAuthoritative)// if (playerId == NetworkManager.LocalPlayerClientId) - { - Console.Print("local player?: " + playerId.ToString()); - //string demoPath = System.IO.Path.Combine(AssetManager.DemoPath, $"{DateTimeOffset.Now.UtcTicks}.gdem"); - //input = new PlayerInputLocal(playerActor, demoPath); // TODO: support recording - Input = new PlayerInput2();//new PlayerInputLocal(playerActor); - - } - else - { - Console.Print("network player: " + playerId.ToString()); - Input = new PlayerInputNetwork2(playerId, worldStateManager); - - } - Assert.IsTrue(worldStateManager != null); - } - #if false public void SetInput(string demoFile) { @@ -286,7 +258,7 @@ public class PlayerMovement : Script //if (Input is PlayerInputDemo /*&& currentInputFrame2 >= currentInputFrame*/) // return; - Input.SetFrame(worldStateManager.Frame); + Input.SetFrame(World.Frame); Input.UpdateState(); /*if (input.frame > 0) @@ -345,7 +317,107 @@ public class PlayerMovement : Script //Input.OnFixedUpdate(); PlayerInputState2 inputState = Input.GetState(); + bool predict = World.IsClient; + if (predict) + { + // Get the latest frame we have predicted + ulong lastFrame = World.ServerFrame; + for (; lastFrame < World.Frame; lastFrame++) + { + if (!World.HasPlayerFrame(PlayerId, lastFrame)) + //if (!Input.GetState(currentFrame, out var pastInputState, out var pastActorState)) + { + //Console.Print($"not predicting"); + predict = false; + break; + } + } + if (predict) + { + var oldAngles = viewAngles; + var oldPos = movementState.position; + var oldVel = movementState.currentVelocity; + ulong currentFrame = World.ServerFrame; + if (currentFrame == World.ServerFrame) + { + // Reset all state to latest confirmed state from server + var frameInfo = World.GetPlayerFrame(PlayerId, currentFrame); + 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); + //cameraHolder.Orientation = Quaternion.Euler(pastActorState.viewAngles.Y, pastActorState.viewAngles.X, pastActorState.viewAngles.Z); + //ApplyInputToCamera(pastInputState, true); + + //if (pastActorState.viewAngles != new Float3(90f, 0f, 0f)) + // Console.Print($"moved server frame: {currentFrame}, {pastActorState.viewAngles}"); + + currentFrame++; + //rootActor.Orientation = pastActorState.orientation; + //viewAngles = pastActorState.viewAngles; + //viewAngles = new Float3(pastActorState.viewAngles.Y, pastActorState.viewAngles.X, pastActorState.viewAngles.Z); + } + + // Predict the frames since last received frame from server up to latest client frame + predicting = true; + for (; currentFrame < lastFrame; currentFrame++) + { + var frameInfo = World.GetPlayerFrame(PlayerId, currentFrame); + if (frameInfo == null) + { + Console.Print($"unexpected predict failure: {currentFrame}"); + break; + } + + ApplyInputToCamera(frameInfo.inputState, true); + SimulatePlayerMovement(frameInfo.inputState, frameInfo.frame); + } + predicting = false; + + var posDelta = (movementState.position - oldPos); + var velDelta = (movementState.currentVelocity - oldVel); + if (posDelta.Length > 0.001) + Console.Print($"mispredicted final position"); + if (velDelta.Length > 0.001) + Console.Print($"mispredicted final velocity"); + + // Run simulation for the upcoming frame + //if (input is not PlayerInputNetwork) + { + /*if ((movementState.position - oldPos).Length > 0.001) + Console.Print($"mispredicted final position"); + if ((currentVelocity - oldVel).Length > 0.001) + Console.Print($"mispredicted final velocity");*/ + + ApplyInputToCamera(inputState, true); + + // Ensure orientation is always up-to-date after predicting + //rootActor.Orientation = Quaternion.Euler(0, viewAngles.X, 0); + cameraHolder.Orientation = Quaternion.Euler(viewAngles.Y, viewAngles.X, viewAngles.Z); + + SimulatePlayerMovement(inputState, World.Frame); // MAYBE? + } + + var viewDelta = (viewAngles - oldAngles); + if (viewDelta.Length > 0.001) + Console.Print($"mispredicted final viewangles: {viewAngles} <- {oldAngles}"); + } + } + + if (!predict) + { + SetCameraEulerAngles(viewAnglesLastFrame, true); + ApplyInputToCamera(inputState, true); + SimulatePlayerMovement(inputState, World.Frame); // MAYBE? + } + + viewAnglesLastFrame = viewAngles; + +#if false if (false && Input is PlayerInputNetwork2) { #if false @@ -456,12 +528,13 @@ public class PlayerMovement : Script //viewAngles = viewAnglesLastFrame; bool canpredict = true; - if (true /*&& Input.Predict*/ /*&& !NetworkManager.IsDemoPlaying*/ && worldStateManager.ClientFrame > 0 && worldStateManager.ServerFrame > 0) + if (true /*&& Input.Predict*/ /*&& !NetworkManager.IsDemoPlaying*/ && World.Frame > 0 && World.ServerFrame > 0) { - ulong currentFrame = worldStateManager.ServerFrame; - for (; currentFrame < worldStateManager.ClientFrame; currentFrame++) + // Get the latest frame we have predicted + ulong currentFrame = World.ServerFrame; + for (; currentFrame < World.Frame; currentFrame++) { - if (!worldStateManager.HasPlayerFrame(PlayerId, currentFrame)) + if (!World.HasPlayerFrame(PlayerId, currentFrame)) //if (!Input.GetState(currentFrame, out var pastInputState, out var pastActorState)) { //Console.Print($"not predicting"); @@ -471,22 +544,19 @@ public class PlayerMovement : Script } ulong lastFrame = currentFrame; - - if (canpredict) { var oldAngles = viewAngles; var oldPos = movementState.position; var oldVel = movementState.currentVelocity; - - + // Predict the frames since last received frame from server up to latest client frame predicting = true; - currentFrame = worldStateManager.ServerFrame; + currentFrame = World.ServerFrame; for (; currentFrame < lastFrame; currentFrame++) { //if (!Input.GetState(currentFrame, out var pastInputState, out var pastActorState)) - var frameInfo = worldStateManager.GetPlayerFrame(PlayerId, currentFrame); + var frameInfo = World.GetPlayerFrame(PlayerId, currentFrame); if (frameInfo == null) { Console.Print($"unexpected predict failure: {currentFrame}"); @@ -496,7 +566,7 @@ public class PlayerMovement : Script //if (currentFrame == inputState.frame) // movementState.jumped = movementState.jumped; - if (currentFrame == worldStateManager.ServerFrame) + if (currentFrame == World.ServerFrame) { movementState.position = frameInfo.movementState.position; movementState.currentVelocity = frameInfo.movementState.currentVelocity; @@ -563,7 +633,7 @@ public class PlayerMovement : Script //rootActor.Orientation = Quaternion.Euler(0, viewAngles.X, 0); cameraHolder.Orientation = Quaternion.Euler(viewAngles.Y, viewAngles.X, viewAngles.Z); - SimulatePlayerMovement(inputState, worldStateManager.Frame); // MAYBE? + SimulatePlayerMovement(inputState, World.Frame); // MAYBE? } var viewDelta = (viewAngles - oldAngles); @@ -585,7 +655,7 @@ public class PlayerMovement : Script { SetCameraEulerAngles(viewAnglesLastFrame, true); ApplyInputToCamera(inputState, true); - SimulatePlayerMovement(inputState, worldStateManager.Frame); // MAYBE? + SimulatePlayerMovement(inputState, World.Frame); // MAYBE? } if (movementState.position.Length < 0.1) @@ -625,7 +695,7 @@ public class PlayerMovement : Script });*/ //Console.Print($"recording frame {input.frame}, client: {GameModeManager.ClientFrame}, server: {GameModeManager.ServerFrame}"); - worldStateManager.RecordPlayerInput(PlayerId, worldStateManager.Frame, inputState, movementState); // MAYBE? + World.RecordPlayerInput(PlayerId, World.Frame, inputState, movementState); // MAYBE? Input.ResetState(); } @@ -649,6 +719,7 @@ public class PlayerMovement : Script currentVelocity = pastActorState2.velocity; SetCameraEulerAngles(pastActorState2.viewAngles, true); }*/ +#endif } public void ApplyInputToCamera(PlayerInputState2 inputState, bool wrapAround = true) @@ -925,10 +996,10 @@ public class PlayerMovement : Script } #endif } - else if (worldStateManager.IsClient) + else if (World.IsClient) { var serverBbox = boxCollider.OrientedBox.GetBoundingBox(); - var frameInfo = worldStateManager.GetPlayerFrame(PlayerId, worldStateManager.ServerFrame); + var frameInfo = World.GetPlayerFrame(PlayerId, World.ServerFrame); if (frameInfo != null) serverBbox.Center = frameInfo.movementState.position; @@ -1178,7 +1249,7 @@ public class PlayerMovement : Script public void SimulatePlayerMovement(PlayerInputState2 inputState, ulong frame) { - simulationTime = worldStateManager.ClientTime + (frame * (1.0f / Time.PhysicsFPS)); + simulationTime = World.GetFrameTime(frame); Vector3 inputDirection = new Float3(inputState.MoveRight, 0.0f, inputState.MoveForward);