networking progress
This commit is contained in:
@@ -4965,3 +4965,9 @@
|
||||
"origin" "160 40 504"
|
||||
"targetname" "light_target1"
|
||||
}
|
||||
// entity 74
|
||||
{
|
||||
"classname" "info_player_deathmatch"
|
||||
"origin" "-496 -304 152"
|
||||
"angle" "180"
|
||||
}
|
||||
|
||||
@@ -9,19 +9,6 @@
|
||||
"V": {},
|
||||
"IsActive": false,
|
||||
"Name": "PlayerPrefab",
|
||||
"Transform": {
|
||||
"Translation": {
|
||||
"X": 73.19660949707031,
|
||||
"Y": -33.01502227783203,
|
||||
"Z": -139.43125915527345
|
||||
},
|
||||
"Orientation": {
|
||||
"X": 0.0,
|
||||
"Y": 1.0,
|
||||
"Z": 0.0,
|
||||
"W": -4.371138828673793e-8
|
||||
}
|
||||
},
|
||||
"StaticFlags": 0,
|
||||
"OverrideMass": true,
|
||||
"Mass": 10.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"ID": "3c7bc3854d42f9b1b0fea9ba0d7fa8e9",
|
||||
"TypeName": "FlaxEditor.Content.Settings.GameSettings",
|
||||
"EngineBuild": 6335,
|
||||
"EngineBuild": 6340,
|
||||
"Data": {
|
||||
"ProductName": "Goake",
|
||||
"CompanyName": "GoaLitiuM",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -49,13 +49,13 @@
|
||||
"ParentID": "ff6b6db54b5aa08e7286ef86246149ef",
|
||||
"Transform": {
|
||||
"Translation": {
|
||||
"X": 1841.0,
|
||||
"Y": 972.0,
|
||||
"X": 1355.0,
|
||||
"Y": 791.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
},
|
||||
"Data": {
|
||||
"Text": "1302 tris\n 131 drawcalls\n434fps2\n1921fps"
|
||||
"Text": "13022 tris\n 102 drawcalls\n238fps2\n240fps"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"ID": "af2e52554f7faed7b4937181dd22d166",
|
||||
"TypeName": "FlaxEditor.Content.Settings.BuildSettings",
|
||||
"EngineBuild": 6332,
|
||||
"EngineBuild": 6340,
|
||||
"Data": {
|
||||
"MaxAssetsPerPackage": 4096,
|
||||
"MaxPackageSizeMB": 1024,
|
||||
@@ -9,6 +9,7 @@
|
||||
"ForDistribution": false,
|
||||
"SkipPackaging": false,
|
||||
"AdditionalAssets": [],
|
||||
"AdditionalScenes": null,
|
||||
"AdditionalAssetFolders": [
|
||||
"Content/Materials",
|
||||
"Content/Textures",
|
||||
@@ -17,6 +18,7 @@
|
||||
],
|
||||
"ShadersNoOptimize": false,
|
||||
"ShadersGenerateDebugData": false,
|
||||
"SkipDotnetPackaging": true,
|
||||
"Presets": [
|
||||
{
|
||||
"Name": "Preset 3",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,6 +7,6 @@ StaticallyLinkedBinaryModuleInitializer StaticallyLinkedBinaryModuleGame(GetBina
|
||||
|
||||
extern "C" BinaryModule* GetBinaryModuleGame()
|
||||
{
|
||||
static NativeBinaryModule module("Game", MAssemblyOptions());
|
||||
static NativeBinaryModule module("Game");
|
||||
return &module;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using FlaxEditor;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public class ConsoleLine
|
||||
public struct ConsoleLine
|
||||
{
|
||||
public string content;
|
||||
|
||||
@@ -75,7 +76,7 @@ namespace Game
|
||||
|
||||
public static string LinePrefix => instance.LinePrefix;
|
||||
|
||||
public static IReadOnlyCollection<ConsoleLine> Lines => instance.Lines;
|
||||
public static ReadOnlySpan<ConsoleLine> Lines => instance.Lines;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
@@ -220,7 +221,7 @@ namespace Game
|
||||
public int DebugVerbosity { get; set; } = 1;
|
||||
public string LinePrefix { get; internal set; } = "]";
|
||||
|
||||
public IReadOnlyCollection<ConsoleLine> Lines => consoleLines.AsReadOnly();
|
||||
public ReadOnlySpan<ConsoleLine> Lines => CollectionsMarshal.AsSpan(consoleLines);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -380,23 +381,14 @@ namespace Game
|
||||
{
|
||||
debugLastLine = text;
|
||||
|
||||
foreach (string line in text.Split('\n'))
|
||||
{
|
||||
ConsoleLine lineEntry = new ConsoleLine(line);
|
||||
consoleLines.Add(lineEntry);
|
||||
OnPrint?.Invoke(text);
|
||||
}
|
||||
PrintLine(text);
|
||||
}
|
||||
|
||||
|
||||
// Echoes warning text to Console
|
||||
public void PrintWarning(string text)
|
||||
{
|
||||
foreach (string line in text.Split('\n'))
|
||||
{
|
||||
ConsoleLine lineEntry = new ConsoleLine(line);
|
||||
consoleLines.Add(lineEntry);
|
||||
OnPrint?.Invoke(text);
|
||||
}
|
||||
PrintLine(text);
|
||||
}
|
||||
|
||||
// Echoes error text to Console
|
||||
@@ -404,12 +396,7 @@ namespace Game
|
||||
[DebuggerHidden]
|
||||
public void PrintError(string text)
|
||||
{
|
||||
foreach (string line in text.Split('\n'))
|
||||
{
|
||||
ConsoleLine lineEntry = new ConsoleLine(line);
|
||||
consoleLines.Add(lineEntry);
|
||||
OnPrint?.Invoke(text);
|
||||
}
|
||||
PrintLine(text);
|
||||
|
||||
/*if (Debugger.IsAttached)
|
||||
Debugger.Break();
|
||||
@@ -417,6 +404,26 @@ namespace Game
|
||||
throw new Exception(text);
|
||||
}
|
||||
|
||||
private void PrintLine(string text)
|
||||
{
|
||||
if (text.IndexOf('\n') != -1)
|
||||
{
|
||||
// Avoid generating extra garbage in single-line cases
|
||||
foreach (string line in text.Split('\n'))
|
||||
{
|
||||
ConsoleLine lineEntry = new ConsoleLine(line);
|
||||
consoleLines.Add(lineEntry);
|
||||
OnPrint?.Invoke(text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleLine lineEntry = new ConsoleLine(text);
|
||||
consoleLines.Add(lineEntry);
|
||||
OnPrint?.Invoke(text);
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintDebug(int verbosity, bool noRepeat, string text)
|
||||
{
|
||||
if (DebugVerbosity < verbosity)
|
||||
@@ -428,12 +435,7 @@ namespace Game
|
||||
|
||||
debugLastLine = text;
|
||||
|
||||
foreach (string line in text.Split('\n'))
|
||||
{
|
||||
ConsoleLine lineEntry = new ConsoleLine(line);
|
||||
consoleLines.Add(lineEntry);
|
||||
OnPrint?.Invoke(text);
|
||||
}
|
||||
PrintLine(text);
|
||||
}
|
||||
|
||||
// Opens the Console
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Game
|
||||
}
|
||||
|
||||
|
||||
private void CalculateVisibleLines(IReadOnlyCollection<ConsoleLine> lines, out int firstVisibleLine,
|
||||
private void CalculateVisibleLines(ReadOnlySpan<ConsoleLine> lines, out int firstVisibleLine,
|
||||
out int lastVisibleLine, out LineInfo[] wrappedLines)
|
||||
{
|
||||
wrappedLines = null;
|
||||
@@ -142,18 +142,21 @@ namespace Game
|
||||
int lineMaxChars = (int)(Width / fontWidth);
|
||||
int lineMaxLines = GetHeightInLines();
|
||||
int numLines = 0;
|
||||
int lineIndex = lines.Count;
|
||||
var lineInfos = new List<LineInfo>(lineMaxLines + 1);
|
||||
int linesSkipped = 0;
|
||||
foreach (string line in lines.Reverse())
|
||||
//foreach (string line in lines.Reverse())
|
||||
int lineIndex = lines.Length - 1;
|
||||
for (; lineIndex >= 0; lineIndex--)
|
||||
{
|
||||
lineIndex--;
|
||||
//lineIndex--;
|
||||
if (linesSkipped < ScrollOffset)
|
||||
{
|
||||
linesSkipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
string line = lines[lineIndex];
|
||||
|
||||
int numChars = 0;
|
||||
int startIndex = lineInfos.Count;
|
||||
while (numChars < line.Length)
|
||||
@@ -207,7 +210,7 @@ namespace Game
|
||||
Profiler.BeginEvent("ConsoleContentTextBoxDraw_FetchLines");
|
||||
var lines = Console.Lines;
|
||||
Profiler.EndEvent();
|
||||
if (lines.Count > 0)
|
||||
if (lines.Length > 0)
|
||||
{
|
||||
Profiler.BeginEvent("ConsoleContentTextBoxDraw_Lines");
|
||||
|
||||
@@ -253,7 +256,7 @@ namespace Game
|
||||
foreach (LineInfo li in wrappedLines)
|
||||
{
|
||||
int lineIndex = li.lineIndex;
|
||||
string fullLine = lines.ElementAt(lineIndex);
|
||||
string fullLine = lines[lineIndex];
|
||||
string line = fullLine.Substring(li.lineOffset, li.lineLength);
|
||||
|
||||
int leftChar = selectionLeftChar;
|
||||
@@ -330,7 +333,7 @@ namespace Game
|
||||
foreach (LineInfo li in wrappedLines)
|
||||
{
|
||||
int lineIndex = li.lineIndex;
|
||||
string line = lines.ElementAt(lineIndex).content.Substring(li.lineOffset, li.lineLength);
|
||||
string line = lines[lineIndex].content.Substring(li.lineOffset, li.lineLength);
|
||||
Render2D.DrawText(font, line, TextColor, ref layout);
|
||||
layout.Bounds.Y += lineHeight;
|
||||
}
|
||||
@@ -405,7 +408,7 @@ namespace Game
|
||||
return false;
|
||||
|
||||
hitLine = wrappedLines[hitWrappedLine].lineIndex;
|
||||
string line = lines.ElementAt(hitLine).content.Substring(wrappedLines[hitWrappedLine].lineOffset,
|
||||
string line = lines[hitLine].content.Substring(wrappedLines[hitWrappedLine].lineOffset,
|
||||
wrappedLines[hitWrappedLine].lineLength);
|
||||
|
||||
layout.Bounds.Y = top + hitWrappedLine * lineHeight;
|
||||
@@ -447,7 +450,7 @@ namespace Game
|
||||
ScrollOffset += GetHeightInLines() / 2;
|
||||
// should count the wrapped line count here over Console.Lines.Count
|
||||
//var maxOffset = Console.Lines.Count - GetHeightInLines();
|
||||
int maxOffset = Console.Lines.Count - 1;
|
||||
int maxOffset = Console.Lines.Length - 1;
|
||||
if (ScrollOffset > maxOffset)
|
||||
ScrollOffset = maxOffset;
|
||||
}
|
||||
@@ -477,7 +480,7 @@ namespace Game
|
||||
else if (delta > 0)
|
||||
{
|
||||
ScrollOffset += ScrollMouseLines;
|
||||
int maxOffset = Console.Lines.Count - GetHeightInLines();
|
||||
int maxOffset = Console.Lines.Length - GetHeightInLines();
|
||||
if (ScrollOffset > maxOffset)
|
||||
ScrollOffset = maxOffset;
|
||||
}
|
||||
@@ -497,7 +500,7 @@ namespace Game
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (button == MouseButton.Left && Console.Lines.Count > 0)
|
||||
if (button == MouseButton.Left && Console.Lines.Length > 0)
|
||||
{
|
||||
bool selectionStarted = !selectionActive;
|
||||
Focus();
|
||||
@@ -614,7 +617,7 @@ namespace Game
|
||||
selectedText.AppendLine();
|
||||
lastLineIndex = lineIndex;
|
||||
|
||||
string fullLine = lines.ElementAt(lineIndex);
|
||||
string fullLine = lines[lineIndex];
|
||||
string line = fullLine.Substring(li.lineOffset, li.lineLength);
|
||||
|
||||
int leftChar = selectionLeftChar;
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Game
|
||||
//AssetManager.Init(); // TODO: move these elsewhere
|
||||
#if !FLAX_EDITOR
|
||||
Level.SceneLoading += OnSceneLoading;
|
||||
Level.SceneLoaded += OnSceneLoaded;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -32,6 +33,7 @@ namespace Game
|
||||
{
|
||||
#if !FLAX_EDITOR
|
||||
Level.SceneLoading -= OnSceneLoading;
|
||||
Level.SceneLoaded -= OnSceneLoaded;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -41,6 +43,19 @@ namespace Game
|
||||
LoadConfig();
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, Guid guid)
|
||||
{
|
||||
Level.SceneLoaded -= OnSceneLoaded;
|
||||
#if !FLAX_EDITOR
|
||||
|
||||
//GameMode.Connect();
|
||||
//GameMode.StartServer(true);
|
||||
//NetworkManager.StartServer();
|
||||
GameModeManager.Init();
|
||||
NetworkManager.ConnectServer();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void LoadConfig()
|
||||
{
|
||||
Console.Print("Loading config file (GamePlugin)");
|
||||
@@ -101,12 +116,16 @@ namespace Game
|
||||
{
|
||||
//FlaxEditor.Editor.Instance.PlayModeBegin -= Instance_PlayModeBegin;
|
||||
LoadConfig();
|
||||
GameMode.StartServer(true);
|
||||
//GameMode.Connect();
|
||||
GameModeManager.Init();
|
||||
NetworkManager.StartServer();
|
||||
//GameMode.StartServer(true);
|
||||
}
|
||||
|
||||
private void OnPlayModeEnd()
|
||||
{
|
||||
GameMode.StopServer();
|
||||
//GameMode.StopServer();
|
||||
NetworkManager.Cleanup();
|
||||
}
|
||||
|
||||
public override void Deinitialize()
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace Game
|
||||
|
||||
private UIControl rootControl;
|
||||
|
||||
private int fontHeight;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
consoleInputEvent = new InputEvent("Console");
|
||||
@@ -37,8 +39,7 @@ namespace Game
|
||||
|
||||
FontReference fontReference = new FontReference(ConsoleFont, ConsoleFontSize);
|
||||
Font fontRaw = fontReference.GetFont();
|
||||
int fontHeight = (int)(fontRaw.Height / Platform.DpiScale);
|
||||
|
||||
fontHeight = (int)(fontRaw.Height / Platform.DpiScale);
|
||||
// root actor which holds all the elements
|
||||
//var rootContainerControl = new ContainerControl(new Rectangle(0, 0, screenSize.X, screenSize.Y));
|
||||
ContainerControl rootContainerControl = new ContainerControl(new Rectangle());
|
||||
@@ -222,6 +223,7 @@ namespace Game
|
||||
Debug.Logger.LogHandler.SendLog += OnSendLog;
|
||||
Debug.Logger.LogHandler.SendExceptionLog += OnSendExceptionLog;
|
||||
|
||||
|
||||
Console.OnOpen += OnConsoleOpen;
|
||||
Console.OnClose += OnConsoleClose;
|
||||
Console.OnPrint += OnPrint;
|
||||
@@ -254,7 +256,7 @@ namespace Game
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Print("[EXCEP] " + exception.Message);
|
||||
Console.Print("[EXCEP] " + exception.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +376,6 @@ namespace Game
|
||||
}
|
||||
else if (!Console.IsOpen)
|
||||
{
|
||||
int fontHeight = (int)(consoleNotifyBox.FontHeight / Platform.DpiScale);
|
||||
if (location.Y < -conHeight * ConsoleHeight + fontHeight)
|
||||
{
|
||||
consoleNotifyBox.Visible = true;
|
||||
@@ -386,8 +387,7 @@ namespace Game
|
||||
|
||||
public void OnPrint(string text)
|
||||
{
|
||||
int fontHeight = (int)(consoleNotifyBox.FontHeight / Platform.DpiScale);
|
||||
consoleNotifyBox.Height = Math.Min(ConsoleNotifyLines, Console.Lines.Count) * fontHeight;
|
||||
consoleNotifyBox.Height = Math.Min(ConsoleNotifyLines, Console.Lines.Length) * fontHeight;
|
||||
}
|
||||
|
||||
public void SetInput(string text)
|
||||
|
||||
@@ -7,6 +7,7 @@ using NVIDIA;
|
||||
#endif
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
@@ -353,11 +354,12 @@ namespace Game
|
||||
/*aoSettings.OverrideFlags = (aoSettings.OverrideFlags & ~AmbientOcclusionSettingsOverride.Enabled) |
|
||||
(boolValue
|
||||
? AmbientOcclusionSettingsOverride.Enabled
|
||||
: 0 & AmbientOcclusionSettingsOverride.Enabled);
|
||||
*/
|
||||
: 0 & AmbientOcclusionSettingsOverride.Enabled);*/
|
||||
|
||||
|
||||
aoSettings.Enabled = boolValue;
|
||||
postProcessSettings.AmbientOcclusion = aoSettings;
|
||||
Graphics.PostProcessSettings = postProcessSettings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,17 +610,45 @@ namespace Game
|
||||
Scripting.Update += TimeDemoOnUpdate;
|
||||
}
|
||||
|
||||
[ConsoleVariable("net_fakelag")]
|
||||
public static string NetFakeLag
|
||||
{
|
||||
get
|
||||
{
|
||||
var driver = NetworkManager.server != null ? (NetworkManager.ServerNetworkDriver as NetworkLagDriver) : (NetworkManager.ClientNetworkDriver as NetworkLagDriver);
|
||||
if (driver == null)
|
||||
return 0.ToString();
|
||||
return ((int)driver.Lag).ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
var driver = NetworkManager.server != null ? (NetworkManager.ServerNetworkDriver as NetworkLagDriver) : (NetworkManager.ClientNetworkDriver as NetworkLagDriver);
|
||||
if (driver == null)
|
||||
return;
|
||||
|
||||
int intValue = 0;
|
||||
if (int.TryParse(value, out intValue))
|
||||
{ }
|
||||
else if (float.TryParse(value, out float valueFloat))
|
||||
intValue = (int)valueFloat;
|
||||
intValue = Math.Clamp(intValue, 0, 2000);
|
||||
|
||||
driver.Lag = intValue;
|
||||
}
|
||||
}
|
||||
|
||||
[ConsoleCommand("map")]
|
||||
public static void MapCommand()
|
||||
{
|
||||
//NetworkManager.StartServer(true);
|
||||
GameMode.StartServer(true);
|
||||
NetworkManager.StartServer();
|
||||
}
|
||||
|
||||
[ConsoleCommand("connect")]
|
||||
public static void ConnectCommand()
|
||||
{
|
||||
GameMode.Connect();
|
||||
//GameMode.Connect();
|
||||
NetworkManager.ConnectServer();
|
||||
}
|
||||
|
||||
[ConsoleSubsystemInitializer]
|
||||
|
||||
@@ -35,8 +35,8 @@ public class Game : GameModule
|
||||
options.PublicDependencies.Add("Networking");
|
||||
|
||||
options.PrivateDependencies.Add("FidelityFXFSR");
|
||||
|
||||
//options.ScriptingAPI.FileReferences.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", "Newtonsoft.Json.dll"));
|
||||
|
||||
//options.ScriptingAPI.FileReferences.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", "Newtonsoft.Json.dll"));
|
||||
|
||||
#if COMPILE_WITH_DLSS
|
||||
DLSS.ConditionalImport(options, options.PrivateDependencies);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#if false
|
||||
#if true
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.Networking;
|
||||
using Console = Game.Console;
|
||||
|
||||
@@ -14,7 +15,8 @@ namespace Game
|
||||
WelcomePlayer,
|
||||
SpawnPlayer,
|
||||
PlayerInput,
|
||||
PlayerPosition, // debug
|
||||
PlayerPosition, // world update
|
||||
PlayerSnapshot, // send world state delta to client since last client's acknowledged frame
|
||||
}
|
||||
|
||||
|
||||
@@ -43,32 +45,42 @@ namespace Game
|
||||
|
||||
private static Dictionary<uint, PlayerActor> players;
|
||||
private static Dictionary<uint, NetworkConnection> playerConnections;
|
||||
private static bool spawned = false;
|
||||
private static bool welcomed = false;
|
||||
|
||||
private static WorldState worldState;
|
||||
private static PlayerFrame[] localPlayerFrameHistory;
|
||||
private static WorldState serverWorldState;
|
||||
private static WorldState clientWorldState;
|
||||
|
||||
private static ulong lastReceivedServerFrame = 0;
|
||||
public static ulong ServerFrame => /*NetworkManager.server != null ? serverWorldState.frame :*/ lastReceivedServerFrame;
|
||||
public static ulong ClientFrame => clientWorldState.frame;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
welcomed = false;
|
||||
lastReceivedServerFrame = 0;
|
||||
|
||||
players = new Dictionary<uint, PlayerActor>();
|
||||
playerConnections = new Dictionary<uint, NetworkConnection>();
|
||||
localPlayerFrameHistory = new PlayerFrame[120];
|
||||
worldState = new WorldState();
|
||||
serverWorldState = new WorldState();
|
||||
clientWorldState = new WorldState();
|
||||
|
||||
NetworkManager.OnMessage += OnMessage;
|
||||
Level.SceneLoaded += OnLevelLoaded;
|
||||
Scripting.LateUpdate += OnLateUpdatePre;
|
||||
//Scripting.LateUpdate += OnLateUpdatePre;
|
||||
Scripting.LateFixedUpdate += OnLateUpdatePre;
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
NetworkManager.OnMessage -= OnMessage;
|
||||
Level.SceneLoaded -= OnLevelLoaded;
|
||||
Scripting.LateUpdate -= OnLateUpdatePre;
|
||||
//Scripting.LateUpdate -= OnLateUpdatePre;
|
||||
Scripting.LateFixedUpdate -= OnLateUpdatePre;
|
||||
}
|
||||
|
||||
private static PlayerFrame GetPlayerFrame(uint playerIndex, ulong playerFrameIndex)
|
||||
{
|
||||
WorldState worldState = NetworkManager.server != null ? serverWorldState : clientWorldState;
|
||||
PlayerFrame[] playerFrames = worldState.playerFrameHistory[playerIndex];
|
||||
PlayerFrame playerFrame = playerFrames[playerFrameIndex % 120];
|
||||
|
||||
@@ -80,7 +92,7 @@ namespace Game
|
||||
|
||||
public static void OnLevelLoaded(Scene scene, Guid assetGuid)
|
||||
{
|
||||
worldState.frame = 0;
|
||||
serverWorldState.frame = 0;
|
||||
Console.Print("level loaded");
|
||||
}
|
||||
|
||||
@@ -90,12 +102,14 @@ namespace Game
|
||||
{
|
||||
NetworkManager.IsServer = NetworkManager.server != null;
|
||||
NetworkManager.IsClient = NetworkManager.client != null && NetworkManager.server == null;
|
||||
NetworkManager.IsLocalClient = NetworkManager.client != null && NetworkManager.server != null;
|
||||
OnLateUpdate();
|
||||
}
|
||||
finally
|
||||
{
|
||||
NetworkManager.IsServer = false;
|
||||
NetworkManager.IsClient = false;
|
||||
NetworkManager.IsLocalClient = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,10 +122,10 @@ namespace Game
|
||||
var playerId = kv.Key;
|
||||
var playerActor = kv.Value;
|
||||
|
||||
var playerFrames = worldState.playerFrameHistory[playerId];
|
||||
var playerFrame = playerFrames[worldState.frame % 120];
|
||||
var playerFrames = serverWorldState.playerFrameHistory[playerId];
|
||||
var playerFrame = playerFrames[serverWorldState.frame % 120];
|
||||
|
||||
playerFrame.frame = worldState.frame;
|
||||
playerFrame.frame = serverWorldState.frame;
|
||||
playerFrame.position = playerActor.Position;
|
||||
}
|
||||
|
||||
@@ -120,41 +134,79 @@ namespace Game
|
||||
var playerId = kv.Key;
|
||||
foreach (KeyValuePair<uint, PlayerActor> kv2 in players)
|
||||
{
|
||||
if (kv2.Key == playerId)
|
||||
continue;
|
||||
//if (kv2.Key == playerId)
|
||||
// continue;
|
||||
|
||||
var otherPlayerActor = kv2.Value;
|
||||
|
||||
// TODO: relevancy checks here etc.
|
||||
|
||||
PlayerMovement playerMovement = otherPlayerActor.GetScript<PlayerMovement>();
|
||||
PlayerActorState actorState = playerMovement.input.GetCurrentActorState();
|
||||
PlayerInputState inputState = playerMovement.input.GetCurrentInputState();
|
||||
{
|
||||
NetworkMessage message = NetworkManager.ServerBeginSendMessage();
|
||||
message.WriteByte((byte)GameModeMessageType.PlayerPosition);
|
||||
message.WriteUInt64(worldState.frame);
|
||||
message.WriteUInt64(serverWorldState.frame);
|
||||
message.WriteUInt32(kv2.Key);
|
||||
message.WriteSingle(otherPlayerActor.Position.X);
|
||||
message.WriteSingle(otherPlayerActor.Position.Y);
|
||||
message.WriteSingle(otherPlayerActor.Position.Z);
|
||||
message.WriteSingle(actorState.position.X);
|
||||
message.WriteSingle(actorState.position.Y);
|
||||
message.WriteSingle(actorState.position.Z);
|
||||
message.WriteSingle(actorState.velocity.X);
|
||||
message.WriteSingle(actorState.velocity.Y);
|
||||
message.WriteSingle(actorState.velocity.Z);
|
||||
message.WriteSingle(actorState.orientation.X);
|
||||
message.WriteSingle(actorState.orientation.Y);
|
||||
message.WriteSingle(actorState.orientation.Z);
|
||||
message.WriteSingle(actorState.orientation.W);
|
||||
message.WriteSingle(actorState.viewAngles.X);
|
||||
message.WriteSingle(actorState.viewAngles.Y);
|
||||
message.WriteSingle(actorState.viewAngles.Z);
|
||||
|
||||
//inputState.frame
|
||||
message.WriteSingle(inputState.viewDeltaX);
|
||||
message.WriteSingle(inputState.viewDeltaY);
|
||||
message.WriteSingle(inputState.moveForward);
|
||||
message.WriteSingle(inputState.moveRight);
|
||||
message.WriteBoolean(inputState.attacking);
|
||||
message.WriteBoolean(inputState.jumping);
|
||||
|
||||
NetworkManager.ServerEndSendMessage(ref message, playerConnections[playerId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NetworkManager.IsClient)
|
||||
if (NetworkManager.IsClient || NetworkManager.IsLocalClient)
|
||||
{
|
||||
if (!spawned)
|
||||
return;
|
||||
//if (!welcomed)
|
||||
// return;
|
||||
|
||||
PlayerActor playerActor = Level.GetActors<PlayerActor>().First(x =>
|
||||
if (welcomed)
|
||||
foreach (PlayerActor playerActor in Level.GetActors<PlayerActor>())
|
||||
{
|
||||
var playerId = playerActor.PlayerId;
|
||||
var playerFrameHistory = clientWorldState.playerFrameHistory[playerId];
|
||||
var playerFrame = playerFrameHistory[clientWorldState.frame % 120];
|
||||
|
||||
playerFrame.frame = clientWorldState.frame;
|
||||
playerFrame.position = playerActor.Position;
|
||||
}
|
||||
|
||||
clientWorldState.frame++;
|
||||
|
||||
/*PlayerActor playerActor = Level.GetActors<PlayerActor>().FirstOrDefault(x =>
|
||||
x.GetScript<PlayerMovement>().PlayerId == NetworkManager.LocalPlayerClientId);
|
||||
|
||||
var playerFrame = localPlayerFrameHistory[worldState.frame % 120];
|
||||
if (playerActor == null)
|
||||
return;*/
|
||||
|
||||
playerFrame.frame = worldState.frame;
|
||||
playerFrame.position = playerActor.Position;
|
||||
//var playerFrame = localPlayerFrameHistory[serverWorldState.frame % 120];
|
||||
|
||||
{
|
||||
//playerFrame.frame = serverWorldState.frame;
|
||||
//playerFrame.position = playerActor.Position;
|
||||
|
||||
/*{
|
||||
NetworkMessage message = NetworkManager.ClientBeginSendMessage();
|
||||
message.WriteByte((byte)GameModeMessageType.PlayerPosition);
|
||||
message.WriteUInt64(worldState.frame);
|
||||
@@ -163,10 +215,14 @@ namespace Game
|
||||
message.WriteSingle(playerActor.Position.Y);
|
||||
message.WriteSingle(playerActor.Position.Z);
|
||||
NetworkManager.ClientEndSendMessage(ref message);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
else if (NetworkManager.IsLocalClient)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
worldState.frame++;
|
||||
serverWorldState.frame++;
|
||||
|
||||
/*foreach (KeyValuePair<uint,PlayerActor> kv in players)
|
||||
{
|
||||
@@ -205,11 +261,12 @@ namespace Game
|
||||
{
|
||||
case GameModeMessageType.WelcomePlayer:
|
||||
{
|
||||
if (NetworkManager.IsClient)
|
||||
welcomed = true;
|
||||
if (NetworkManager.IsClient || NetworkManager.IsLocalClient)
|
||||
{
|
||||
worldState.frame = networkEvent.Message.ReadUInt64();
|
||||
var serverFrame = networkEvent.Message.ReadUInt64();
|
||||
int numActors = (int)networkEvent.Message.ReadUInt32();
|
||||
for (int i=0; i<numActors; i++)
|
||||
for (int i = 0; i < numActors; i++)
|
||||
{
|
||||
uint playerId = networkEvent.Message.ReadUInt32();
|
||||
Float3 playerPosition;
|
||||
@@ -218,15 +275,24 @@ namespace Game
|
||||
playerPosition.Z = networkEvent.Message.ReadSingle();
|
||||
|
||||
SpawnPlayer(playerId, playerPosition, new Float3(0));
|
||||
|
||||
var playerFrames = new PlayerFrame[120];
|
||||
for (int j = 0; j < playerFrames.Length; j++)
|
||||
playerFrames[j] = new PlayerFrame();
|
||||
clientWorldState.playerFrameHistory.Add(playerId, playerFrames);
|
||||
}
|
||||
|
||||
Console.Print("received welcome: frame " + worldState.frame);
|
||||
Console.Print("received welcome: frame " + serverWorldState.frame);
|
||||
|
||||
players.Add(NetworkManager.LocalPlayerClientId, null);
|
||||
if (!NetworkManager.IsLocalClient)
|
||||
serverWorldState.frame = serverFrame;
|
||||
//lastReceivedServerFrame = serverFrame;
|
||||
clientWorldState.frame += serverFrame;
|
||||
|
||||
if (!players.ContainsKey(NetworkManager.LocalPlayerClientId)) // listen server
|
||||
players.Add(NetworkManager.LocalPlayerClientId, null);
|
||||
//playerConnections.Add(NetworkManager.LocalPlayerClientId, connection);
|
||||
localPlayerFrameHistory = new PlayerFrame[120];
|
||||
for (int i = 0; i < localPlayerFrameHistory.Length; i++)
|
||||
localPlayerFrameHistory[i] = new PlayerFrame();
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -234,9 +300,21 @@ namespace Game
|
||||
{
|
||||
uint playerId = networkEvent.Message.ReadUInt32();
|
||||
ulong playerFrameIndex = networkEvent.Message.ReadUInt64();
|
||||
|
||||
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,
|
||||
new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()),
|
||||
new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()));
|
||||
|
||||
//if (NetworkManager.IsClient)
|
||||
players[playerId].GetScript<PlayerMovement>().input.frame = clientWorldState.frame;
|
||||
break;
|
||||
}
|
||||
case GameModeMessageType.PlayerInput:
|
||||
@@ -257,21 +335,49 @@ namespace Game
|
||||
case GameModeMessageType.PlayerPosition:
|
||||
{
|
||||
uint playerId = networkEvent.Sender.ConnectionId;
|
||||
Float3 reportedPosition;
|
||||
PlayerInputState inputState = default; //?
|
||||
PlayerActorState actorState;
|
||||
|
||||
ulong reportedFrame = networkEvent.Message.ReadUInt64();
|
||||
uint reportedPlayerId = networkEvent.Message.ReadUInt32();
|
||||
reportedPosition.X = networkEvent.Message.ReadSingle();
|
||||
reportedPosition.Y = networkEvent.Message.ReadSingle();
|
||||
reportedPosition.Z = networkEvent.Message.ReadSingle();
|
||||
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();
|
||||
|
||||
if (NetworkManager.IsLocalClient && !NetworkManager.IsServer)
|
||||
inputState.frame = reportedFrame;
|
||||
inputState.viewDeltaX = networkEvent.Message.ReadSingle();
|
||||
inputState.viewDeltaY = networkEvent.Message.ReadSingle();
|
||||
inputState.moveForward = networkEvent.Message.ReadSingle();
|
||||
inputState.moveRight = networkEvent.Message.ReadSingle();
|
||||
inputState.attacking = networkEvent.Message.ReadBoolean();
|
||||
inputState.jumping = networkEvent.Message.ReadBoolean();
|
||||
|
||||
//Assert.IsTrue(reportedFrame >= lastReceivedServerFrame);
|
||||
if (reportedFrame < lastReceivedServerFrame)
|
||||
{
|
||||
Console.Print($"packet wrong order, received {lastReceivedServerFrame}, new {reportedFrame}");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/*if (NetworkManager.IsLocalClient && !NetworkManager.IsServer)
|
||||
{
|
||||
|
||||
}
|
||||
else if (NetworkManager.IsServer)
|
||||
else*/ if (NetworkManager.IsServer)
|
||||
{
|
||||
PlayerFrame playerFrame = GetPlayerFrame(playerId, reportedFrame);
|
||||
Assert.Fail();
|
||||
/*PlayerFrame playerFrame = GetPlayerFrame(playerId, reportedFrame);
|
||||
PlayerActor playerActor = players[playerId];
|
||||
if (playerFrame == null)
|
||||
Console.Print("frame is in the past, unable to verify frame");
|
||||
@@ -285,12 +391,8 @@ namespace Game
|
||||
{
|
||||
NetworkMessage message = NetworkManager.ServerBeginSendMessage();
|
||||
message.WriteByte((byte)GameModeMessageType.PlayerPosition);
|
||||
/*message.WriteUInt64(reportedFrame);
|
||||
message.WriteUInt32(playerId);
|
||||
message.WriteSingle(playerFramePosition.X);
|
||||
message.WriteSingle(playerFramePosition.Y);
|
||||
message.WriteSingle(playerFramePosition.Z);*/
|
||||
message.WriteUInt64(worldState.frame);
|
||||
|
||||
message.WriteUInt64(serverWorldState.frame);
|
||||
message.WriteUInt32(playerId);
|
||||
message.WriteSingle(playerActor.Position.X);
|
||||
message.WriteSingle(playerActor.Position.Y);
|
||||
@@ -298,16 +400,26 @@ namespace Game
|
||||
NetworkManager.ServerEndSendMessage(ref message, playerConnections[playerId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
else if (NetworkManager.IsClient)
|
||||
{
|
||||
Console.Print($"we drifted, corrected. client frame: {worldState.frame}, server frame: {reportedFrame}");
|
||||
lastReceivedServerFrame = reportedFrame;
|
||||
//Console.Print($"we drifted, corrected. client frame: {serverWorldState.frame}, server frame: {reportedFrame}");
|
||||
PlayerActor playerActor = Level.GetActors<PlayerActor>().FirstOrDefault(x =>
|
||||
x.GetScript<PlayerMovement>().PlayerId == reportedPlayerId);
|
||||
|
||||
|
||||
if (playerActor != null)
|
||||
playerActor.SetPosition(reportedPosition);
|
||||
{
|
||||
PlayerInput playerInput = playerActor.GetScript<PlayerMovement>().input;
|
||||
|
||||
playerInput.SetState(reportedFrame, ref inputState, ref actorState);
|
||||
{
|
||||
|
||||
}
|
||||
//playerActor.SetPosition(reportedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -322,14 +434,14 @@ namespace Game
|
||||
|
||||
public static bool OnClientConnecting(NetworkConnection connection)
|
||||
{
|
||||
if (connection.ConnectionId != NetworkManager.LocalPlayerClientId)
|
||||
//if (connection.ConnectionId != NetworkManager.LocalPlayerClientId)
|
||||
{
|
||||
Console.Print("sending welcome: frame " + worldState.frame);
|
||||
Console.Print("sending welcome: frame " + serverWorldState.frame);
|
||||
NetworkMessage message = NetworkManager.ServerBeginSendMessage();
|
||||
message.WriteByte((byte)GameModeMessageType.WelcomePlayer);
|
||||
message.WriteUInt64(worldState.frame);
|
||||
message.WriteUInt32((uint)worldState.actors.Count);
|
||||
foreach (PlayerActor playerActor in worldState.actors)
|
||||
message.WriteUInt64(serverWorldState.frame);
|
||||
message.WriteUInt32((uint)serverWorldState.actors.Count);
|
||||
foreach (PlayerActor playerActor in serverWorldState.actors)
|
||||
{
|
||||
message.WriteUInt32(playerActor.GetScript<PlayerMovement>().PlayerId);
|
||||
message.WriteSingle(playerActor.Position.X);
|
||||
@@ -343,12 +455,16 @@ namespace Game
|
||||
|
||||
public static bool OnClientConnected(NetworkConnection connection)
|
||||
{
|
||||
uint playerId = connection.ConnectionId;
|
||||
if (NetworkManager.LocalPlayerClientId == 0)
|
||||
NetworkManager.LocalPlayerClientId = playerId;
|
||||
|
||||
var spawns = Level.GetActors<Actor>().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray();
|
||||
Console.Print($"found {spawns.Length} spawns");
|
||||
|
||||
var randomSpawn = spawns.First();
|
||||
|
||||
uint playerId = connection.ConnectionId;
|
||||
|
||||
Float3 position = randomSpawn.Position + new Float3(0f, 4.1f, 0f);
|
||||
Float3 eulerAngles = randomSpawn.Orientation.EulerAngles;
|
||||
|
||||
@@ -357,7 +473,7 @@ namespace Game
|
||||
var playerFrames = new PlayerFrame[120];
|
||||
for (int i = 0; i < playerFrames.Length; i++)
|
||||
playerFrames[i] = new PlayerFrame();
|
||||
worldState.playerFrameHistory.Add(playerId, playerFrames);
|
||||
serverWorldState.playerFrameHistory.Add(playerId, playerFrames);
|
||||
|
||||
|
||||
SpawnPlayer(playerId, position, eulerAngles);
|
||||
@@ -365,7 +481,7 @@ namespace Game
|
||||
NetworkMessage message = NetworkManager.ServerBeginSendMessage();
|
||||
message.WriteByte((byte)GameModeMessageType.SpawnPlayer);
|
||||
message.WriteUInt32(playerId);
|
||||
message.WriteUInt64(worldState.frame);
|
||||
message.WriteUInt64(serverWorldState.frame);
|
||||
message.WriteSingle(position.X);
|
||||
message.WriteSingle(position.Y);
|
||||
message.WriteSingle(position.Z);
|
||||
@@ -383,7 +499,7 @@ namespace Game
|
||||
if (NetworkManager.IsLocalClient)
|
||||
return; // Handled by listenserver
|
||||
|
||||
spawned = true;
|
||||
//spawned = true;
|
||||
|
||||
string prefabPath = Path.Combine(AssetManager.ContentPath, "Common");
|
||||
var playerPrefab = Content.Load<Prefab>(Path.Combine(prefabPath, "PlayerPrefab.prefab"));
|
||||
@@ -391,15 +507,16 @@ namespace Game
|
||||
Console.PrintError("GameModeManager: Failed to find PlayerPrefab");
|
||||
|
||||
PlayerActor playerActor = PrefabManager.SpawnPrefab(playerPrefab).As<PlayerActor>();
|
||||
playerActor.Initialize(playerId);
|
||||
playerActor.Teleport(position, eulerAngles);
|
||||
playerActor.Initialize(playerId, position, eulerAngles);
|
||||
//playerActor.Teleport(position, eulerAngles);
|
||||
|
||||
if (!players.ContainsKey(playerId))
|
||||
players.Add(playerId, null);
|
||||
players[playerId] = playerActor;
|
||||
PlayerInput playerInput = playerActor.GetScript<PlayerMovement>().input;
|
||||
|
||||
if (NetworkManager.IsServer)
|
||||
worldState.actors.Add(playerActor);
|
||||
serverWorldState.actors.Add(playerActor);
|
||||
}
|
||||
|
||||
private static void UpdatePlayerInput(uint playerId, PlayerInputState inputState)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FlaxEditor.Content.Settings;
|
||||
#if false
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
using System;
|
||||
@@ -88,6 +89,8 @@ namespace Game
|
||||
|
||||
currentGameMode.Start();
|
||||
|
||||
Console.Open();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -122,9 +125,23 @@ namespace Game
|
||||
|
||||
private static void NetworkManager_ClientConnected(NetworkClient networkClient)
|
||||
{
|
||||
Console.Print("new client connected");
|
||||
try
|
||||
{
|
||||
Console.Print("new client connected");
|
||||
|
||||
currentGameMode.OnPlayerSpawn(networkClient.ClientId);
|
||||
foreach (var (playerId, playerActor) in currentGameMode.players)
|
||||
{
|
||||
NetworkReplicator.SpawnObject(playerActor, new[] { networkClient.ClientId });
|
||||
playerActor.Initialize(playerId, playerActor.Position, playerActor.GetRotation());
|
||||
}
|
||||
|
||||
currentGameMode.OnPlayerSpawn(networkClient.ClientId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Print(e.ToString());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static void NetworkManager_ClientConnecting(ref NetworkClientConnectionData arg0)
|
||||
@@ -184,11 +201,18 @@ namespace Game
|
||||
|
||||
public void OnPlayerSpawn(uint clientId)
|
||||
{
|
||||
// Get random spawn
|
||||
// Get a random spawn
|
||||
var spawns = Level.GetActors<Actor>().Where(x => x.Name.StartsWith("PlayerSpawn_")).ToArray();
|
||||
Console.Print($"found {spawns.Length} spawns");
|
||||
|
||||
var randomSpawn = spawns.First();
|
||||
var randomSpawn = spawns.FirstOrDefault();
|
||||
if (players.Count > 0)
|
||||
randomSpawn = spawns.LastOrDefault();
|
||||
if (randomSpawn == null)
|
||||
{
|
||||
Console.Print("No spawns found for player");
|
||||
return;
|
||||
}
|
||||
Console.Print($"found {spawns.Length} spawns");
|
||||
|
||||
Float3 spawnPosition = randomSpawn.Position + new Float3(0f, 4.1f, 0f);
|
||||
Float3 spawnAngles = randomSpawn.Orientation.EulerAngles;
|
||||
@@ -201,12 +225,13 @@ namespace Game
|
||||
|
||||
PlayerActor playerActor = PrefabManager.SpawnPrefab(playerPrefab).As<PlayerActor>();
|
||||
NetworkReplicator.AddObject(playerActor);
|
||||
playerActor.Initialize(clientId);
|
||||
playerActor.Initialize(clientId, spawnPosition, spawnAngles);
|
||||
|
||||
playerActor.Teleport(spawnPosition, spawnAngles);
|
||||
//playerActor.Teleport(spawnPosition, spawnAngles);
|
||||
//NetworkReplicator.SetObjectOwnership(playerActor, clientId);
|
||||
|
||||
NetworkReplicator.SpawnObject(playerActor);
|
||||
players.Add(clientId, playerActor);
|
||||
//playerActor.Initialize(clientId);
|
||||
//playerActor.hai = 345;
|
||||
|
||||
@@ -232,4 +257,5 @@ namespace Game
|
||||
return Float3.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
150
Source/Game/GameMode/NetworkManager.cs
Normal file
150
Source/Game/GameMode/NetworkManager.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
using Console = Game.Console;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class NetworkPredictedAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: insert code to update variables with this attribute?
|
||||
// rename to NetworkReplicatedAttribute?
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class NetworkedAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
// NetworkMulticastAttribute: calls methods marked with this in all clients
|
||||
|
||||
public enum NetworkMessageType : byte
|
||||
{
|
||||
Handshake = 1,
|
||||
Message
|
||||
}
|
||||
|
||||
|
||||
public static partial class NetworkManager
|
||||
{
|
||||
public delegate bool OnMessageDecl(ref NetworkEvent networkEvent);
|
||||
|
||||
private static bool initialized;
|
||||
|
||||
public static NetworkPeer server;
|
||||
public static NetworkPeer client;
|
||||
|
||||
private static readonly ushort ServerPort = 59183;
|
||||
private static string ServerAddress;
|
||||
private static readonly ushort MTU = 1500;
|
||||
private static readonly ushort MaximumClients = 32;
|
||||
public static OnMessageDecl OnMessage;
|
||||
|
||||
public static bool IsServer = false;
|
||||
public static bool IsClient = false;
|
||||
public static bool IsLocalClient = false; // Context dependant, true when message is handled by local client
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
/*if (Engine.CommandLine.Contains("-server"))
|
||||
{
|
||||
StartServer();
|
||||
ServerAddress = "localhost";
|
||||
ConnectServer();
|
||||
}
|
||||
else if (Engine.CommandLine.Contains("-client"))
|
||||
{
|
||||
ServerAddress = "localhost";
|
||||
ConnectServer();
|
||||
}
|
||||
//#if FLAX_EDITOR
|
||||
else
|
||||
{
|
||||
StartServer();
|
||||
ServerAddress = "localhost";
|
||||
ConnectServer();
|
||||
}*/
|
||||
//#endif
|
||||
|
||||
initialized = true;
|
||||
#if FLAX_EDITOR
|
||||
Editor.Instance.PlayModeEnd += Cleanup;
|
||||
#endif
|
||||
|
||||
GameModeManager.Init(); // FIXME
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
if (server != null)
|
||||
{
|
||||
Scripting.FixedUpdate -= OnServerUpdate;
|
||||
Scripting.Exit -= Cleanup;
|
||||
Level.ActorSpawned -= OnServerActorSpawned;
|
||||
NetworkPeer.ShutdownPeer(server);
|
||||
server = null;
|
||||
}
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
Scripting.FixedUpdate -= OnClientUpdate;
|
||||
Scripting.Exit -= Cleanup;
|
||||
Level.ActorSpawned -= OnClientActorSpawned;
|
||||
NetworkPeer.ShutdownPeer(client);
|
||||
client = null;
|
||||
}
|
||||
LocalPlayerClientId = 0;
|
||||
|
||||
#if FLAX_EDITOR
|
||||
Editor.Instance.PlayModeEnd -= Cleanup;
|
||||
GameModeManager.Cleanup(); // FIXME
|
||||
#endif
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private static void OnNetworkMessage(ref NetworkEvent networkEvent)
|
||||
{
|
||||
byte messageTypeByte = networkEvent.Message.ReadByte();
|
||||
if (!Enum.IsDefined(typeof(NetworkMessageType), messageTypeByte))
|
||||
{
|
||||
Console.PrintError($"Unsupported message type received from client: {messageTypeByte}");
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkMessageType messageType = (NetworkMessageType)messageTypeByte;
|
||||
|
||||
switch (messageType)
|
||||
{
|
||||
case NetworkMessageType.Handshake:
|
||||
{
|
||||
string message = networkEvent.Message.ReadString();
|
||||
Console.Print($"Received handshake from {networkEvent.Sender.ConnectionId}, msg: " + message);
|
||||
break;
|
||||
}
|
||||
case NetworkMessageType.Message:
|
||||
{
|
||||
if (OnMessage != null)
|
||||
foreach (OnMessageDecl func in OnMessage.GetInvocationList()
|
||||
.Cast<OnMessageDecl>().ToArray())
|
||||
{
|
||||
bool ret = func.Invoke(ref networkEvent);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Console.PrintError($"Unsupported message type received from client: {messageTypeByte}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Source/Game/GameMode/NetworkManager_Client.cs
Normal file
128
Source/Game/GameMode/NetworkManager_Client.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
using Console = Game.Console;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public static partial class NetworkManager
|
||||
{
|
||||
public static uint LocalPlayerClientId { get; /*private*/ set; }
|
||||
|
||||
public static INetworkDriver ClientNetworkDriver { get; set; }
|
||||
|
||||
public static bool ConnectServer(string serverAddress = "localhost", bool listenServer = false)
|
||||
{
|
||||
if (!listenServer)
|
||||
Cleanup();
|
||||
ServerAddress = serverAddress;
|
||||
|
||||
//var driver = Object.New(typeof(ENetDriver));
|
||||
//ClientNetworkDriver = null;
|
||||
NetworkLagDriver driver = Object.New<NetworkLagDriver>();
|
||||
driver.Lag = 0f;
|
||||
ClientNetworkDriver = driver;
|
||||
|
||||
|
||||
client = NetworkPeer.CreatePeer(new NetworkConfig
|
||||
{
|
||||
NetworkDriver = driver,
|
||||
ConnectionsLimit = MaximumClients,
|
||||
MessagePoolSize = 2048,
|
||||
MessageSize = MTU,
|
||||
Address = ServerAddress == "localhost" ? "127.0.0.1" : ServerAddress,
|
||||
Port = ServerPort
|
||||
});
|
||||
if (client == null)
|
||||
{
|
||||
Console.Print("Failed to create NetworkPeer.");
|
||||
return false;
|
||||
}
|
||||
if (!client.Connect())
|
||||
{
|
||||
Console.Print("Failed to connect to the server.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Scripting.FixedUpdate += OnClientUpdate;
|
||||
if (!listenServer)
|
||||
{
|
||||
Scripting.Exit += Cleanup;
|
||||
Level.ActorSpawned += OnClientActorSpawned;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void OnClientUpdate()
|
||||
{
|
||||
using Utilities.ScopeProfiler _ = Utilities.ProfileScope("NetworkManager_OnClientUpdate");
|
||||
|
||||
while (client.PopEvent(out NetworkEvent networkEvent))
|
||||
switch (networkEvent.EventType)
|
||||
{
|
||||
case NetworkEventType.Connected:
|
||||
{
|
||||
LocalPlayerClientId = networkEvent.Sender.ConnectionId;
|
||||
Console.Print("Connected to server, ConnectionId: " + networkEvent.Sender.ConnectionId);
|
||||
break;
|
||||
}
|
||||
case NetworkEventType.Disconnected:
|
||||
{
|
||||
Console.Print("Disconnected from server, timeout.");
|
||||
LocalPlayerClientId = 0;
|
||||
break;
|
||||
}
|
||||
case NetworkEventType.Timeout:
|
||||
{
|
||||
Console.Print("Disconnected from server, connection closed.");
|
||||
LocalPlayerClientId = 0;
|
||||
break;
|
||||
}
|
||||
case NetworkEventType.Message:
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLocalClient = server != null;
|
||||
IsClient = true;
|
||||
OnNetworkMessage(ref networkEvent);
|
||||
|
||||
if (networkEvent.Message.Position > 0 &&
|
||||
networkEvent.Message.Position < networkEvent.Message.Length)
|
||||
{
|
||||
string err =
|
||||
$"Network message was not fully read: {networkEvent.Message.Position} / {networkEvent.Message.Length}.";
|
||||
|
||||
networkEvent.Message.Position = 0;
|
||||
byte[] messageBytes = new byte[networkEvent.Message.Length];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* messageBytePtr = &messageBytes[0])
|
||||
networkEvent.Message.ReadBytes(messageBytePtr, (int)networkEvent.Message.Length);
|
||||
}
|
||||
|
||||
string messageBytesStr = string.Join(", ",
|
||||
messageBytes.Select(x => "0x" + ((int)x).ToString("X2")));
|
||||
|
||||
Console.PrintError(err + $"Message dump: {messageBytesStr}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLocalClient = false;
|
||||
IsClient = false;
|
||||
client.RecycleMessage(networkEvent.Message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnClientActorSpawned(Actor actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
196
Source/Game/GameMode/NetworkManager_Server.cs
Normal file
196
Source/Game/GameMode/NetworkManager_Server.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
using Console = Game.Console;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public static partial class NetworkManager
|
||||
{
|
||||
private static List<NetworkConnection> ConnectedClients;
|
||||
|
||||
private static List<Type> NetworkedTypes;
|
||||
|
||||
public static INetworkDriver ServerNetworkDriver { get; set; }
|
||||
|
||||
public static bool StartServer(bool listenServer = true)
|
||||
{
|
||||
ConnectedClients = new List<NetworkConnection>(MaximumClients);
|
||||
|
||||
|
||||
//var driver = Object.New(typeof(ENetDriver));
|
||||
//ServerNetworkDriver = null;
|
||||
NetworkLagDriver driver = Object.New<NetworkLagDriver>();
|
||||
driver.Lag = 200f;
|
||||
ServerNetworkDriver = driver;
|
||||
|
||||
server = NetworkPeer.CreatePeer(new NetworkConfig
|
||||
{
|
||||
NetworkDriver = driver,
|
||||
ConnectionsLimit = MaximumClients,
|
||||
MessagePoolSize = 2048,
|
||||
MessageSize = MTU,
|
||||
Address = "any",
|
||||
Port = ServerPort
|
||||
});
|
||||
if (!server.Listen())
|
||||
{
|
||||
Console.PrintError("Failed to start the server.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Scripting.FixedUpdate += OnServerUpdate;
|
||||
Scripting.Exit += Cleanup;
|
||||
Level.ActorSpawned += OnServerActorSpawned;
|
||||
|
||||
NetworkedTypes = new List<Type>();
|
||||
|
||||
#if false
|
||||
var assemblies = Utils.GetAssemblies();
|
||||
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
// Skip common assemblies
|
||||
string assemblyName = assembly.GetName().Name;
|
||||
if (assemblyName == "System" ||
|
||||
assemblyName.StartsWith("System.") ||
|
||||
assemblyName.StartsWith("Mono.") ||
|
||||
assemblyName == "mscorlib" ||
|
||||
assemblyName == "Newtonsoft.Json" ||
|
||||
assemblyName == "Snippets" ||
|
||||
assemblyName == "netstandard" ||
|
||||
assemblyName == "Anonymously Hosted DynamicMethods Assembly" ||
|
||||
assemblyName.StartsWith("FlaxEngine.") ||
|
||||
assemblyName.StartsWith("JetBrains.") ||
|
||||
assemblyName.StartsWith("Microsoft.") ||
|
||||
assemblyName.StartsWith("nunit."))
|
||||
continue;
|
||||
|
||||
foreach (Type type in assembly.GetTypes())
|
||||
if (type.GetCustomAttributes().Any(x => x is NetworkedAttribute))
|
||||
NetworkedTypes.Add(type);
|
||||
}
|
||||
|
||||
foreach (Type type in NetworkedTypes)
|
||||
Console.Print("tracking networked type: " + type.Name);
|
||||
#endif
|
||||
|
||||
if (listenServer)
|
||||
return ConnectServer(listenServer: true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static NetworkMessage ServerBeginSendMessage()
|
||||
{
|
||||
NetworkMessage message = server.BeginSendMessage();
|
||||
message.WriteByte((byte)NetworkMessageType.Message);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static void ServerEndSendMessage(ref NetworkMessage message, NetworkConnection connection, NetworkChannelType channelType = NetworkChannelType.Reliable)
|
||||
{
|
||||
server.EndSendMessage(channelType, message, connection);
|
||||
}
|
||||
|
||||
public static NetworkMessage ClientBeginSendMessage()
|
||||
{
|
||||
NetworkMessage message = client.BeginSendMessage();
|
||||
message.WriteByte((byte)NetworkMessageType.Message);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static void ClientEndSendMessage(ref NetworkMessage message, NetworkChannelType channelType = NetworkChannelType.Reliable)
|
||||
{
|
||||
client.EndSendMessage(channelType, message);
|
||||
}
|
||||
|
||||
private static void OnServerUpdate()
|
||||
{
|
||||
using Utilities.ScopeProfiler _ = Utilities.ProfileScope("NetworkManager_OnServerUpdate");
|
||||
|
||||
while (server.PopEvent(out NetworkEvent networkEvent))
|
||||
switch (networkEvent.EventType)
|
||||
{
|
||||
case NetworkEventType.Connected:
|
||||
{
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) is trying to connect");
|
||||
|
||||
try
|
||||
{
|
||||
IsServer = true;
|
||||
if (GameModeManager.OnClientConnecting(networkEvent.Sender))
|
||||
{
|
||||
ConnectedClients.Add(networkEvent.Sender);
|
||||
Console.Print(
|
||||
$"Client({networkEvent.Sender.ConnectionId}) connected. Total clients: {ConnectedClients.Count}");
|
||||
|
||||
GameModeManager.OnClientConnected(networkEvent.Sender);
|
||||
}
|
||||
else
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) connection refused");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsServer = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case NetworkEventType.Disconnected:
|
||||
case NetworkEventType.Timeout:
|
||||
{
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) disconnected!");
|
||||
|
||||
ConnectedClients.Remove(networkEvent.Sender);
|
||||
Console.Print("Connected clients: " + ConnectedClients.Count);
|
||||
break;
|
||||
}
|
||||
case NetworkEventType.Message:
|
||||
{
|
||||
try
|
||||
{
|
||||
IsServer = true;
|
||||
OnNetworkMessage(ref networkEvent);
|
||||
|
||||
if (networkEvent.Message.Position > 0 &&
|
||||
networkEvent.Message.Position < networkEvent.Message.Length)
|
||||
{
|
||||
string err =
|
||||
$"Network message was not fully read: {networkEvent.Message.Position} / {networkEvent.Message.Length}.";
|
||||
|
||||
networkEvent.Message.Position = 0;
|
||||
byte[] messageBytes = new byte[networkEvent.Message.Length];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* messageBytePtr = &messageBytes[0])
|
||||
networkEvent.Message.ReadBytes(messageBytePtr, (int)networkEvent.Message.Length);
|
||||
}
|
||||
|
||||
string messageBytesStr = string.Join(", ",
|
||||
messageBytes.Select(x => "0x" + ((int)x).ToString("X2")));
|
||||
|
||||
Console.PrintError(err + $"Message dump: {messageBytesStr}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsServer = false;
|
||||
server.RecycleMessage(networkEvent.Message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnServerActorSpawned(Actor actor)
|
||||
{
|
||||
//Console.Print($"actor spawned: {actor.Name} ({actor.TypeName})");
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Source/Game/GameMode/NetworkWorld.cs
Normal file
50
Source/Game/GameMode/NetworkWorld.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
|
||||
namespace Game;
|
||||
|
||||
public static class NetworkWorld
|
||||
{
|
||||
private class WorldState
|
||||
{
|
||||
public ulong frame;
|
||||
public List<PlayerActor> actors;
|
||||
public Dictionary<PlayerActor, PlayerLagCompStates> playerFrameHistory;
|
||||
|
||||
public WorldState()
|
||||
{
|
||||
actors = new List<PlayerActor>();
|
||||
playerFrameHistory = new Dictionary<PlayerActor, PlayerLagCompStates>();
|
||||
}
|
||||
}
|
||||
|
||||
private struct PlayerLagCompState
|
||||
{
|
||||
public Float3 position;
|
||||
}
|
||||
|
||||
private class PlayerLagCompStates
|
||||
{
|
||||
public ulong oldestFrame;
|
||||
private PlayerLagCompState[] states;
|
||||
|
||||
public PlayerLagCompStates(int frames)
|
||||
{
|
||||
states = new PlayerLagCompState[frames];
|
||||
}
|
||||
|
||||
public PlayerLagCompState GetState(ulong frame)
|
||||
{
|
||||
Assert.IsTrue(frame >= oldestFrame);
|
||||
ulong index = frame % (ulong)states.Length;
|
||||
return states[index];
|
||||
}
|
||||
|
||||
public void SetState(PlayerLagCompState state, ulong frame)
|
||||
{
|
||||
ulong index = frame % (ulong)states.Length;
|
||||
states[index] = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace Game
|
||||
outVertices = finalPoints.ToArray();
|
||||
|
||||
//verts = new QuickHull().QuickHull2(points);
|
||||
//outVertices = verts.ToArray();
|
||||
//outVertices = verts.ToArray(); frf f
|
||||
}
|
||||
|
||||
private MapEntity root;
|
||||
|
||||
@@ -35,12 +35,16 @@ namespace Game
|
||||
}
|
||||
#endif
|
||||
|
||||
public class PlayerActor : RigidBody
|
||||
public class SomeActor : EmptyActor
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class PlayerActor : RigidBody//, INetworkSerializable
|
||||
{
|
||||
private PlayerMovement playerMovement;
|
||||
private RigidBody playerRigidBody;
|
||||
|
||||
[NetworkReplicated]
|
||||
//[NetworkReplicated]
|
||||
public uint PlayerId = uint.MaxValue;
|
||||
|
||||
/*public PlayerActor()
|
||||
@@ -65,12 +69,17 @@ namespace Game
|
||||
base.OnBeginPlay();
|
||||
|
||||
playerMovement = FindScript<PlayerMovement>();
|
||||
playerRigidBody = FindActor<RigidBody>();
|
||||
//playerRigidBody = FindActor<RigidBody>();
|
||||
|
||||
|
||||
|
||||
//Console.Print("OnBeginPlay playerid: " + PlayerId.ToString());
|
||||
//playerMovement.input = new PlayerInputNetwork();
|
||||
}
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
// Trigger OnEnable manually, does not seem 8to propagate when parent gets enabled/disabled
|
||||
// Trigger OnEnable manually, does not seem to propagate when parent gets enabled/disabled
|
||||
playerMovement.Enabled = true;
|
||||
//NetworkReplicator.AddObject(this);
|
||||
}
|
||||
@@ -81,29 +90,37 @@ namespace Game
|
||||
//NetworkReplicator.RemoveObject(this);
|
||||
}
|
||||
|
||||
[NetworkRpc(client: true)]
|
||||
public void Initialize(uint playerId)
|
||||
//[NetworkRpc(client: true)]
|
||||
public void Initialize(uint playerId, Float3 newPosition, Float3 eulerAngles)
|
||||
{
|
||||
if (PlayerId == playerId) // FIXME
|
||||
return;
|
||||
|
||||
FindActor("PlayerModel").IsActive = true;
|
||||
IsActive = true;
|
||||
|
||||
PlayerId = playerId;
|
||||
playerMovement.SetInput(playerId);
|
||||
if (playerId == NetworkManager.LocalClientId)
|
||||
if (playerId == NetworkManager.LocalPlayerClientId)
|
||||
{
|
||||
FindActor("CameraHolder").IsActive = true;
|
||||
//FindActor("ViewModelHolder").IsActive = true;
|
||||
FindActor("PlayerModel").IsActive = false;
|
||||
}
|
||||
else
|
||||
FindActor("PlayerModel").IsActive = true;
|
||||
IsActive = true;
|
||||
SetPosition(newPosition);
|
||||
SetRotation(eulerAngles);
|
||||
//else
|
||||
// FindActor("PlayerModel").IsActive = true;
|
||||
//IsActive = true;
|
||||
}
|
||||
|
||||
[NetworkRpc(server: true)]
|
||||
public void UpdateNetworkInput(ulong frame, Float4 viewDeltaXYMoveForwardRight, bool attacking, bool jumping)
|
||||
//[NetworkRpc(server: true)]
|
||||
public void UpdateNetworkInput(PlayerInputState inputState/*, Float4 viewDeltaXYMoveForwardRight, bool attacking, bool jumping*/)
|
||||
{
|
||||
if (playerMovement.input is not PlayerInputNetwork playerInputNetwork)
|
||||
return;
|
||||
|
||||
PlayerInputState inputState = new PlayerInputState(frame, viewDeltaXYMoveForwardRight.X, viewDeltaXYMoveForwardRight.Y, viewDeltaXYMoveForwardRight.Z, viewDeltaXYMoveForwardRight.W, attacking, jumping);
|
||||
//PlayerInputState inputState = new PlayerInputState(frame, viewDeltaXYMoveForwardRight.X, viewDeltaXYMoveForwardRight.Y, viewDeltaXYMoveForwardRight.Z, viewDeltaXYMoveForwardRight.W, attacking, jumping);
|
||||
playerInputNetwork.currentState.input = inputState;
|
||||
}
|
||||
|
||||
@@ -117,7 +134,12 @@ namespace Game
|
||||
playerMovement.ResetRotation(eulerAngles);
|
||||
}
|
||||
|
||||
[NetworkRpc(client: true)]
|
||||
public Float3 GetRotation()
|
||||
{
|
||||
return playerMovement.viewAngles;
|
||||
}
|
||||
|
||||
//[NetworkRpc(client: true)]
|
||||
public void Teleport(Float3 newPosition, Float3 eulerAngles)
|
||||
{
|
||||
SetPosition(newPosition);
|
||||
|
||||
@@ -87,6 +87,11 @@ namespace Game
|
||||
public const byte DemoVer = 1;
|
||||
public PlayerState currentState;
|
||||
public ulong frame;
|
||||
//public ulong oldestFrame;
|
||||
|
||||
private PlayerState[] states = new PlayerState[120];
|
||||
|
||||
public virtual bool Predict => false;
|
||||
|
||||
public virtual void OnUpdate()
|
||||
{
|
||||
@@ -98,12 +103,43 @@ namespace Game
|
||||
|
||||
public virtual void OnEndFrame()
|
||||
{
|
||||
states[frame % 120] = currentState;
|
||||
|
||||
/*ulong oldest = ulong.MaxValue;
|
||||
for (int i = 0; i < 120; i++)
|
||||
oldest = states[i].input.frame < oldest ? states[i].input.frame : oldest;
|
||||
oldestFrame = oldest;*/
|
||||
|
||||
frame++;
|
||||
}
|
||||
|
||||
public virtual void RecordCurrentActorState(PlayerActorState actorState)
|
||||
{
|
||||
}
|
||||
|
||||
public bool GetState(ulong frame, out PlayerInputState inputState, out PlayerActorState actorState)
|
||||
{
|
||||
int frameIndex = (int)frame % 120;
|
||||
if (states[frameIndex].input.frame != frame)
|
||||
{
|
||||
inputState = default;
|
||||
actorState = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
inputState = states[frameIndex].input;
|
||||
actorState = states[frameIndex].actor;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetState(ulong frame, ref PlayerInputState inputState, ref PlayerActorState actorState)
|
||||
{
|
||||
int frameIndex = (int)frame % 120;
|
||||
states[frameIndex].input = inputState;
|
||||
states[frameIndex].input.frame = frame;
|
||||
states[frameIndex].actor = actorState;
|
||||
}
|
||||
|
||||
public PlayerInputState GetCurrentInputState()
|
||||
{
|
||||
return currentState.input;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Console = Game.Console;
|
||||
|
||||
@@ -26,49 +28,46 @@ namespace Game
|
||||
if (!File.Exists(demoPath))
|
||||
return;
|
||||
|
||||
int expectedPlayerInputStateSize = Marshal.SizeOf(typeof(PlayerInputState));
|
||||
int expectedPlayerInputStateSize = Unsafe.SizeOf<PlayerInputState>();
|
||||
|
||||
FileStream fileStream = File.OpenRead(demoPath);
|
||||
GZipStream stream = new GZipStream(fileStream, CompressionMode.Decompress);
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
using FileStream fileStream = File.OpenRead(demoPath);
|
||||
using GZipStream stream = new GZipStream(fileStream, CompressionMode.Decompress);
|
||||
|
||||
int ver = stream.ReadByte();
|
||||
int inputStateSize = stream.ReadByte();
|
||||
if (ver != DemoVer && inputStateSize != expectedPlayerInputStateSize)
|
||||
if (ver != DemoVer || inputStateSize != expectedPlayerInputStateSize)
|
||||
{
|
||||
Console.Print("demover mismatch: version " + ver + " != " + DemoVer + ", inputStateSize " +
|
||||
inputStateSize + " != " + Marshal.SizeOf(typeof(PlayerInputState)));
|
||||
inputStateSize + " != " + Unsafe.SizeOf<PlayerInputState>());
|
||||
stream.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: bench and compare to unsafe ptr version of this:
|
||||
// https://stackoverflow.com/questions/17549123/c-sharp-performance-using-unsafe-pointers-instead-of-intptr-and-marshal/29836312#29836312
|
||||
T RawDeserialize<T>(byte[] rawData, int position)
|
||||
{
|
||||
int rawsize = Marshal.SizeOf(typeof(T));
|
||||
if (rawsize > rawData.Length - position)
|
||||
throw new ArgumentException("Not enough data to fill struct. Array length from position: " +
|
||||
(rawData.Length - position) + ", Struct length: " + rawsize);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
|
||||
Marshal.Copy(rawData, position, buffer, rawsize);
|
||||
T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T));
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
return retobj;
|
||||
}
|
||||
|
||||
Span<byte> b = stackalloc byte[expectedPlayerInputStateSize];
|
||||
while (true)
|
||||
{
|
||||
byte[] b = new byte[expectedPlayerInputStateSize];
|
||||
int readBytes = stream.Read(b, 0, b.Length);
|
||||
if (readBytes < expectedPlayerInputStateSize)
|
||||
break;
|
||||
int bytesLeftInBuffer = expectedPlayerInputStateSize;
|
||||
do
|
||||
{
|
||||
int readBytes = stream.Read(b.Slice(expectedPlayerInputStateSize - bytesLeftInBuffer, bytesLeftInBuffer));
|
||||
if (readBytes == 0)
|
||||
break;
|
||||
bytesLeftInBuffer -= readBytes;
|
||||
} while (bytesLeftInBuffer > 0);
|
||||
|
||||
buffer.Add(RawDeserialize<PlayerInputState>(b, 0));
|
||||
if (bytesLeftInBuffer > 0)
|
||||
break; // EOF;
|
||||
|
||||
buffer.Add(MemoryMarshal.Read<PlayerInputState>(b));
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
|
||||
bufferEnumerable = buffer.GetEnumerator();
|
||||
|
||||
Console.Print("demo numstates: " + buffer.Count);
|
||||
Console.Print($"Demo parse time {sw.Elapsed.TotalMilliseconds}ms, frames: {buffer.Count} ");
|
||||
|
||||
OnEndFrame(); // advances to first frame
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
@@ -12,16 +14,19 @@ namespace Game
|
||||
public class PlayerInputLocal : PlayerInput
|
||||
{
|
||||
protected List<PlayerInputState> buffer = new List<PlayerInputState>();
|
||||
protected GZipStream demoFileStream;
|
||||
protected FileStream demoFileStream2;
|
||||
protected GZipStream demoStream;
|
||||
protected FileStream demoFileStream;
|
||||
|
||||
private PlayerActor playerActor;
|
||||
//public bool IsNetworked => NetworkManager.client != null;
|
||||
public bool IsNetworked => NetworkManager.client != null;
|
||||
private long flushedFrames = 0;
|
||||
|
||||
/*public PlayerInputLocal()
|
||||
{
|
||||
}*/
|
||||
|
||||
public override bool Predict => true;
|
||||
|
||||
public PlayerInputLocal(PlayerActor playerActor, string demoPath)
|
||||
{
|
||||
this.playerActor = playerActor;
|
||||
@@ -29,15 +34,13 @@ namespace Game
|
||||
if (!demoFolder.Exists)
|
||||
Directory.CreateDirectory(demoFolder.FullName);
|
||||
|
||||
demoFileStream2 = File.Open(demoPath, FileMode.Create, FileAccess.Write);
|
||||
demoFileStream = new GZipStream(demoFileStream2, CompressionMode.Compress);
|
||||
//stream.Position = 0;
|
||||
//stream.SetLength(0);
|
||||
demoFileStream.WriteByte(DemoVer);
|
||||
demoFileStream.WriteByte((byte)Marshal.SizeOf(typeof(PlayerInputState)));
|
||||
demoFileStream = File.Open(demoPath, FileMode.Create, FileAccess.Write);
|
||||
demoStream = new GZipStream(demoFileStream, CompressionMode.Compress);
|
||||
demoStream.WriteByte(DemoVer);
|
||||
demoStream.WriteByte((byte)Unsafe.SizeOf<PlayerInputState>());
|
||||
}
|
||||
|
||||
public bool IsRecording => demoFileStream != null;
|
||||
public bool IsRecording => demoStream != null;
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
@@ -82,11 +85,12 @@ namespace Game
|
||||
|
||||
if (playerActor != null)
|
||||
{
|
||||
playerActor.UpdateNetworkInput(currentState.input.frame, new Float4(currentState.input.viewDeltaX, currentState.input.viewDeltaY, currentState.input.moveForward, currentState.input.moveRight), currentState.input.attacking, currentState.input.jumping);
|
||||
//playerActor.UpdateNetworkInput(currentState.input.frame, new Float4(currentState.input.viewDeltaX, currentState.input.viewDeltaY, currentState.input.moveForward, currentState.input.moveRight), currentState.input.attacking, currentState.input.jumping);
|
||||
|
||||
}
|
||||
//playerActor.UpdateNetworkInput(currentState.input.frame, currentState.input.viewDeltaX, currentState.input.viewDeltaY, currentState.input.moveForward, currentState.input.moveRight, currentState.input.attacking, currentState.input.jumping, currentState.input.verificationPosition, currentState.input.verificationVelocity, currentState.input.verificationViewAngles, currentState.input.verificationOrientation);
|
||||
|
||||
/*if (IsNetworked)
|
||||
if (IsNetworked)
|
||||
{
|
||||
var message = NetworkManager.ClientBeginSendMessage();
|
||||
message.WriteByte((byte)GameModeMessageType.PlayerInput);
|
||||
@@ -98,13 +102,13 @@ namespace Game
|
||||
message.WriteBoolean(currentState.input.attacking);
|
||||
message.WriteBoolean(currentState.input.jumping);
|
||||
NetworkManager.ClientEndSendMessage(ref message);
|
||||
}*/
|
||||
}
|
||||
|
||||
// Reset anything accumulatable here
|
||||
currentState.input.viewDeltaX = 0;
|
||||
currentState.input.viewDeltaY = 0;
|
||||
|
||||
frame++;
|
||||
base.OnEndFrame();
|
||||
}
|
||||
|
||||
public override void RecordCurrentActorState(PlayerActorState actorState)
|
||||
@@ -122,24 +126,21 @@ namespace Game
|
||||
if (!IsRecording)
|
||||
return;
|
||||
|
||||
byte[] RawSerialize(object anything)
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
Span<byte> bytes = stackalloc byte[Unsafe.SizeOf<PlayerInputState>()];
|
||||
foreach (ref PlayerInputState state in CollectionsMarshal.AsSpan(buffer))
|
||||
{
|
||||
int rawSize = Marshal.SizeOf(anything);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(rawSize);
|
||||
Marshal.StructureToPtr(anything, buffer, false);
|
||||
byte[] rawDatas = new byte[rawSize];
|
||||
Marshal.Copy(buffer, rawDatas, 0, rawSize);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
return rawDatas;
|
||||
}
|
||||
|
||||
foreach (PlayerInputState state in buffer)
|
||||
{
|
||||
byte[] bytes = RawSerialize(state);
|
||||
demoFileStream.Write(bytes, 0, bytes.Length * sizeof(byte));
|
||||
MemoryMarshal.Write(bytes, ref state);
|
||||
demoStream.Write(bytes);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
|
||||
flushedFrames += buffer.Count;
|
||||
buffer.Clear();
|
||||
|
||||
FlaxEngine.Debug.Write(LogType.Info, $"Wrote demo in {sw.Elapsed.TotalMilliseconds}ms, frames: {flushedFrames}");
|
||||
}
|
||||
|
||||
public void StopRecording()
|
||||
@@ -148,10 +149,10 @@ namespace Game
|
||||
return;
|
||||
|
||||
FlushDemo();
|
||||
demoStream.Close();
|
||||
demoStream = null;
|
||||
demoFileStream.Close();
|
||||
demoFileStream = null;
|
||||
|
||||
Debug.Write(LogType.Info, "demo, wrote states: " + buffer.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,12 @@
|
||||
{
|
||||
public class PlayerInputNetwork : PlayerInput
|
||||
{
|
||||
public override bool Predict => true;
|
||||
|
||||
public override void OnEndFrame()
|
||||
{
|
||||
currentState.input.frame = frame;
|
||||
base.OnEndFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.Networking;
|
||||
@@ -79,11 +80,11 @@ namespace Game
|
||||
|
||||
private readonly InputEvent onExit = new InputEvent("Exit");
|
||||
|
||||
private readonly bool predicting = false;
|
||||
private bool predicting = false;
|
||||
|
||||
private readonly List<PhysicsColliderActor> touchingActors = new List<PhysicsColliderActor>();
|
||||
public int currentInputFrame;
|
||||
private int currentInputFrame2;
|
||||
//public int currentInputFrame;
|
||||
//private int currentInputFrame2;
|
||||
|
||||
private Float3 currentVelocity;
|
||||
public PlayerInput input;
|
||||
@@ -92,7 +93,7 @@ namespace Game
|
||||
|
||||
private bool jumped;
|
||||
|
||||
private int lastInputFrame;
|
||||
//private int lastInputFrame;
|
||||
private float lastJumped = -1f;
|
||||
private float lastLanded = -1f;
|
||||
private int numJumps;
|
||||
@@ -105,7 +106,7 @@ namespace Game
|
||||
private Actor rootActor;
|
||||
private float startupTime;
|
||||
|
||||
private Float3 viewAngles;
|
||||
public Float3 viewAngles;
|
||||
private Float3 viewAnglesLastFrame;
|
||||
|
||||
[Limit(0, 9000)]
|
||||
@@ -114,7 +115,7 @@ namespace Game
|
||||
|
||||
private static Float3 Gravity { get; } = new Float3(0, -800.0f, 0f);
|
||||
|
||||
//private Float3 safePosition;
|
||||
//private Float3 safePosition;444 rg
|
||||
|
||||
[NetworkReplicated]
|
||||
public uint PlayerId = 0;
|
||||
@@ -173,7 +174,7 @@ namespace Game
|
||||
Assert.IsTrue(playerId != uint.MaxValue);
|
||||
|
||||
PlayerId = playerId;
|
||||
if (PlayerId == NetworkManager.LocalClientId)//if (NetworkReplicator.GetObjectRole(this.Parent) == NetworkObjectRole.OwnedAuthoritative)// if (playerId == NetworkManager.LocalPlayerClientId)
|
||||
if (PlayerId == NetworkManager.LocalPlayerClientId)//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");
|
||||
@@ -290,24 +291,26 @@ namespace Game
|
||||
viewPitch = viewPitch,
|
||||
viewRoll = viewRoll
|
||||
});*/
|
||||
currentInputFrame2++;
|
||||
//currentInputFrame2++;
|
||||
}
|
||||
|
||||
private ulong lastPredictedFrame = 0;
|
||||
public override void OnFixedUpdate()
|
||||
{
|
||||
if (input is PlayerInputDemo)
|
||||
input.OnUpdate();
|
||||
PlayerInputDemo demoInput = input as PlayerInputDemo;
|
||||
if (demoInput != null)
|
||||
demoInput.OnUpdate();
|
||||
|
||||
float deltadif = Time.DeltaTime - 1.0f / Time.PhysicsFPS;
|
||||
//if (Math.Abs(deltadif) > 0.0001f)
|
||||
// Console.Print("drift: " + deltadif);
|
||||
float timeDeltaDiff = Time.DeltaTime - 1.0f / Time.PhysicsFPS;
|
||||
if (Math.Abs(timeDeltaDiff) > 0.0001f)
|
||||
Console.Print("Time.DeltaTime is not stable: " + timeDeltaDiff);
|
||||
|
||||
input.OnFixedUpdate();
|
||||
PlayerInputState inputState = input.GetCurrentInputState();
|
||||
|
||||
if (input is PlayerInputDemo)
|
||||
if (demoInput != null && demoInput.IsPlaying)
|
||||
{
|
||||
ApplyInputToCamera(inputState, false);
|
||||
ApplyInputToCamera(inputState, true);
|
||||
|
||||
// Verify view angles first
|
||||
if (demoDeltasVerify)
|
||||
@@ -316,43 +319,75 @@ namespace Game
|
||||
float viewAnglesDelta = (viewAngles - verifAngles).Length;
|
||||
if (viewAnglesDelta > 0.00001)
|
||||
{
|
||||
Console.PrintError($"Demo verification failed, view angles delta: {viewAnglesDelta}, viewAngles:{viewAngles}, verif:{verifAngles}");
|
||||
Console.Print($"Demo verification failed, view angles delta: {viewAnglesDelta}, viewAngles:{viewAngles}, verif:{verifAngles}");
|
||||
if (demoDeltasCorrect)
|
||||
SetCameraEulerAngles(verifAngles, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimulatePlayerMovement(inputState);
|
||||
SimulatePlayerMovement(inputState);
|
||||
|
||||
if (input is PlayerInputDemo && demoDeltasVerify)
|
||||
{
|
||||
// verify
|
||||
float positionDelta = (Actor.Position - inputState.verificationPosition).Length;
|
||||
if (positionDelta > 0.00001)
|
||||
Console.PrintError("Demo verification failed, position delta: " + positionDelta);
|
||||
|
||||
float velocityDelta = (currentVelocity - inputState.verificationVelocity).Length;
|
||||
if (velocityDelta > 0.00001)
|
||||
Console.PrintError("Demo verification failed, velocity delta: " + velocityDelta);
|
||||
|
||||
float orientationDelta = (rootActor.Orientation - inputState.verificationOrientation).Length;
|
||||
if (orientationDelta > 0.00001)
|
||||
if (demoDeltasVerify)
|
||||
{
|
||||
Console.PrintError("Demo verification failed, orientation delta: " + orientationDelta);
|
||||
if (demoDeltasCorrect)
|
||||
// verify
|
||||
float positionDelta = (Actor.Position - inputState.verificationPosition).Length;
|
||||
if (positionDelta > 0.00001)
|
||||
Console.Print("Demo verification failed, position delta: " + positionDelta);
|
||||
|
||||
float velocityDelta = (currentVelocity - inputState.verificationVelocity).Length;
|
||||
if (velocityDelta > 0.00001)
|
||||
Console.Print("Demo verification failed, velocity delta: " + velocityDelta);
|
||||
|
||||
float orientationDelta = (rootActor.Orientation - inputState.verificationOrientation).Length;
|
||||
if (orientationDelta > 0.00001)
|
||||
{
|
||||
Console.Print("Demo verification failed, orientation delta: " + orientationDelta);
|
||||
if (demoDeltasCorrect)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if (currentInputFrame == 0)
|
||||
/*{
|
||||
//Console.Print("repos: " + inputState.verificationPosition);
|
||||
Actor.Position = inputState.verificationPosition;
|
||||
currentVelocity = inputState.verificationVelocity;
|
||||
rootActor.Orientation = inputState.verificationOrientation;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input.Predict && GameModeManager.ClientFrame > 0 && GameModeManager.ServerFrame > 0)
|
||||
{
|
||||
predicting = true;
|
||||
for (ulong currentFrame = GameModeManager.ServerFrame; currentFrame < GameModeManager.ClientFrame - 1; currentFrame++)
|
||||
{
|
||||
if (!input.GetState(currentFrame, out var pastInputState, out var pastActorState))
|
||||
{
|
||||
Console.Print($"predict failure: {currentFrame}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentFrame == GameModeManager.ServerFrame)
|
||||
{
|
||||
Actor.Position = pastActorState.position;
|
||||
currentVelocity = pastActorState.velocity;
|
||||
rootActor.Orientation = pastActorState.orientation;
|
||||
viewAngles = new Float3(pastActorState.viewAngles.Y, pastActorState.viewAngles.X, pastActorState.viewAngles.Z);
|
||||
}
|
||||
SimulatePlayerMovement(pastInputState);
|
||||
|
||||
//Console.Print($"predicted: {currentFrame}");
|
||||
}
|
||||
predicting = false;
|
||||
//Console.Print($"current: {inputState.frame}");
|
||||
//if (GameModeManager.ClientFrame - GameModeManager.ServerFrame > 0)
|
||||
// Console.Print($"current diff: {GameModeManager.ClientFrame - GameModeManager.ServerFrame}");
|
||||
}
|
||||
|
||||
|
||||
//if (currentInputFrame == 0)
|
||||
/*{
|
||||
//Console.Print("repos: " + inputState.verificationPosition);
|
||||
Actor.Position = inputState.verificationPosition;
|
||||
currentVelocity = inputState.verificationVelocity;
|
||||
rootActor.Orientation = inputState.verificationOrientation;
|
||||
}*/
|
||||
SimulatePlayerMovement(inputState);
|
||||
}
|
||||
|
||||
input.RecordCurrentActorState(new PlayerActorState
|
||||
@@ -365,8 +400,8 @@ namespace Game
|
||||
input.OnEndFrame();
|
||||
|
||||
|
||||
lastInputFrame = currentInputFrame;
|
||||
currentInputFrame++;
|
||||
//lastInputFrame = currentInputFrame;
|
||||
//currentInputFrame++;
|
||||
|
||||
viewAnglesLastFrame = viewAngles;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@ public class GameTarget : GameProjectTarget
|
||||
Modules.Add("Game");
|
||||
}
|
||||
|
||||
public override void SetupTargetEnvironment(BuildOptions options)
|
||||
{
|
||||
base.SetupTargetEnvironment(options);
|
||||
|
||||
options.LinkEnv.UseFastPDBLinking = true;
|
||||
}
|
||||
|
||||
public override string GetOutputFilePath(BuildOptions options, TargetOutputType? outputType = null)
|
||||
{
|
||||
if (!Environment.CommandLine.Contains("Cooker")) // Hacky way to detect if this is run during cooking
|
||||
|
||||
Reference in New Issue
Block a user