record demos as packets
This commit is contained in:
@@ -550,19 +550,25 @@ namespace Game
|
||||
[ConsoleCommand("playdemo")]
|
||||
public static void PlayDemoCommand(string[] text)
|
||||
{
|
||||
string demoName;
|
||||
if (text.Length < 1)
|
||||
return;
|
||||
string demoName = text[0];
|
||||
demoName = "638201307621505588_v2";//return;
|
||||
else
|
||||
demoName = text[0];
|
||||
|
||||
if (!demoName.EndsWith(".gdem"))
|
||||
demoName += ".gdem";
|
||||
|
||||
string demoPath = Path.Combine(AssetManager.DemoPath, demoName);
|
||||
NetworkManager.PlayDemo(demoPath);
|
||||
#if false
|
||||
PlayerActor playerActor = Level.GetActors<PlayerActor>().First(/*x =>
|
||||
x.GetScript<PlayerMovement>().PlayerId == NetworkManager.LocalPlayerClientId*/);
|
||||
|
||||
string demoPath = Path.Combine(AssetManager.DemoPath, demoName);
|
||||
if (File.Exists(demoPath))
|
||||
playerActor.GetScript<PlayerMovement>().SetInput(demoPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Action timeDemoUpdate = null;
|
||||
@@ -575,51 +581,97 @@ namespace Game
|
||||
[ConsoleCommand("timedemo")]
|
||||
public static void TimeDemoCommand(string[] text)
|
||||
{
|
||||
string demoName;
|
||||
if (text.Length < 1)
|
||||
return;
|
||||
string demoName = text[0];
|
||||
demoName = "638201307621505588_v2";//return;
|
||||
else
|
||||
demoName = text[0];
|
||||
|
||||
if (!demoName.EndsWith(".gdem"))
|
||||
demoName += ".gdem";
|
||||
|
||||
PlayerActor playerActor = Level.GetActors<PlayerActor>().First(/*x =>
|
||||
x.GetScript<PlayerMovement>().PlayerId == NetworkManager.LocalPlayerClientId*/);
|
||||
|
||||
var playerMovement = playerActor.GetScript<PlayerMovement>();
|
||||
string demoPath = Path.Combine(AssetManager.DemoPath, demoName);
|
||||
if (File.Exists(demoPath))
|
||||
playerMovement.SetInput(demoPath);
|
||||
NetworkManager.PlayDemo(demoPath);
|
||||
|
||||
float oldPhysicsFps = Time.PhysicsFPS;
|
||||
float oldFps = Time.UpdateFPS;
|
||||
Time.PhysicsFPS = 0f;
|
||||
Time.UpdateFPS = 0f;
|
||||
Time.DrawFPS = 0f;
|
||||
|
||||
float accumTime = 0f;
|
||||
int accumTimes = 0;
|
||||
timeDemoUpdate = () =>
|
||||
{
|
||||
if (playerMovement)
|
||||
if (!NetworkManager.IsDemoPlaying)
|
||||
{
|
||||
var input = playerMovement.input as PlayerInputDemo;
|
||||
if (input != null)
|
||||
{
|
||||
|
||||
if (!input.IsPlaying)
|
||||
{
|
||||
Console.Print($"demo ended, time: {accumTimes} frames, avg: {(accumTime/(float)accumTimes)*1000.0f}");
|
||||
timeDemoUpdate = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (accumTimes == 0)
|
||||
Console.Print($"timedemo started");
|
||||
|
||||
accumTime += Time.DeltaTime;
|
||||
accumTimes++;
|
||||
}
|
||||
Console.Print($"timedemo ended, time: {accumTimes} frames, avg: {(accumTime/(float)accumTimes)*1000.0f}");
|
||||
timeDemoUpdate = null;
|
||||
Time.PhysicsFPS = oldPhysicsFps;
|
||||
Time.UpdateFPS = oldFps;
|
||||
Time.DrawFPS = oldFps;
|
||||
return;
|
||||
}
|
||||
|
||||
if (accumTimes == 0)
|
||||
Console.Print($"timedemo started");
|
||||
|
||||
accumTime += Time.DeltaTime;
|
||||
accumTimes++;
|
||||
|
||||
};
|
||||
|
||||
Scripting.Update -= TimeDemoOnUpdate;
|
||||
Scripting.Update += TimeDemoOnUpdate;
|
||||
}
|
||||
|
||||
[ConsoleCommand("timedemo2")]
|
||||
public static void TimeDemo2Command(string[] text)
|
||||
{
|
||||
string demoName;
|
||||
if (text.Length < 1)
|
||||
demoName = "638201307621505588_v2";//return;
|
||||
else
|
||||
demoName = text[0];
|
||||
|
||||
if (!demoName.EndsWith(".gdem"))
|
||||
demoName += ".gdem";
|
||||
|
||||
string demoPath = Path.Combine(AssetManager.DemoPath, demoName);
|
||||
NetworkManager.PlayDemo(demoPath);
|
||||
|
||||
float oldPhysicsFps = Time.PhysicsFPS;
|
||||
float oldFps = Time.UpdateFPS;
|
||||
Time.PhysicsFPS = 0f;
|
||||
Time.UpdateFPS = 5f;
|
||||
Time.DrawFPS = 5f;
|
||||
|
||||
float accumTime = 0f;
|
||||
int accumTimes = 0;
|
||||
timeDemoUpdate = () =>
|
||||
{
|
||||
if (!NetworkManager.IsDemoPlaying)
|
||||
{
|
||||
Console.Print($"timedemo ended, time: {accumTimes} frames, avg: {(accumTime/(float)accumTimes)*1000.0f}");
|
||||
timeDemoUpdate = null;
|
||||
Time.PhysicsFPS = oldPhysicsFps;
|
||||
Time.UpdateFPS = oldFps;
|
||||
Time.DrawFPS = oldFps;
|
||||
return;
|
||||
}
|
||||
|
||||
if (accumTimes == 0)
|
||||
Console.Print($"timedemo started");
|
||||
|
||||
accumTime += Time.DeltaTime;
|
||||
accumTimes++;
|
||||
|
||||
};
|
||||
|
||||
Scripting.FixedUpdate -= TimeDemoOnUpdate;
|
||||
Scripting.FixedUpdate += TimeDemoOnUpdate;
|
||||
}
|
||||
|
||||
[ConsoleVariable("net_fakelag")]
|
||||
public static string NetFakeLag
|
||||
{
|
||||
|
||||
@@ -84,6 +84,11 @@ namespace Game
|
||||
Level.SceneLoaded -= OnLevelLoaded;
|
||||
//Scripting.LateUpdate -= OnLateUpdatePre;
|
||||
Scripting.LateFixedUpdate -= OnLateUpdatePre;
|
||||
|
||||
foreach (var player in Level.GetActors<PlayerActor>())
|
||||
{
|
||||
FlaxEngine.Object.Destroy(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static PlayerFrame GetPlayerFrame(uint playerIndex, ulong playerFrameIndex)
|
||||
@@ -109,8 +114,8 @@ namespace Game
|
||||
try
|
||||
{
|
||||
NetworkManager.IsServer = NetworkManager.server != null;
|
||||
NetworkManager.IsClient = NetworkManager.client != null && NetworkManager.server == null;
|
||||
NetworkManager.IsLocalClient = NetworkManager.client != null && NetworkManager.server != null;
|
||||
NetworkManager.IsClient = (NetworkManager.client != null || NetworkManager.IsDemoPlaying) && NetworkManager.server == null;
|
||||
NetworkManager.IsLocalClient = (NetworkManager.client != null || NetworkManager.IsDemoPlaying) && NetworkManager.server != null;
|
||||
OnLateUpdate();
|
||||
}
|
||||
finally
|
||||
@@ -213,9 +218,9 @@ namespace Game
|
||||
clientWorldState.playerFrameHistory.Add(playerId, playerFrames);
|
||||
}
|
||||
var playerFrameHistory = clientWorldState.playerFrameHistory[playerId];
|
||||
var playerFrame = playerFrameHistory[clientWorldState.frame % 120];
|
||||
var playerFrame = playerFrameHistory[ClientFrame % 120];
|
||||
|
||||
playerFrame.frame = clientWorldState.frame;
|
||||
playerFrame.frame = ClientFrame;
|
||||
playerFrame.position = playerActor.Position;
|
||||
}
|
||||
|
||||
@@ -341,7 +346,7 @@ namespace Game
|
||||
new Float3(networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle(), networkEvent.Message.ReadSingle()));
|
||||
|
||||
//if (NetworkManager.IsClient)
|
||||
players[playerId].GetScript<PlayerMovement>().input.frame = clientWorldState.frame;
|
||||
players[playerId].GetScript<PlayerMovement>().input.frame = ClientFrame;
|
||||
break;
|
||||
}
|
||||
case GameModeMessageType.PlayerInput:
|
||||
@@ -547,7 +552,7 @@ namespace Game
|
||||
players.Add(playerId, null);
|
||||
players[playerId] = playerActor;
|
||||
PlayerInput playerInput = playerActor.GetScript<PlayerMovement>().input;
|
||||
playerInput.frame = (NetworkManager.IsServer) ? serverWorldState.frame : clientWorldState.frame;
|
||||
playerInput.frame = (NetworkManager.IsServer) ? serverWorldState.frame : ClientFrame;
|
||||
if (NetworkManager.IsServer)
|
||||
{
|
||||
serverWorldState.actors.Add(playerActor);
|
||||
|
||||
@@ -82,23 +82,25 @@ namespace Game
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
Scripting.Exit -= Cleanup;
|
||||
Scripting.FixedUpdate -= OnDemoUpdate;
|
||||
Scripting.FixedUpdate -= OnServerNetworkUpdate;
|
||||
Level.ActorSpawned -= OnServerActorSpawned;
|
||||
Scripting.FixedUpdate -= OnClientNetworkUpdate;
|
||||
Level.ActorSpawned -= OnClientActorSpawned;
|
||||
|
||||
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
|
||||
@@ -107,6 +109,8 @@ namespace Game
|
||||
#endif
|
||||
GameModeManager.Cleanup(); // FIXME
|
||||
|
||||
StopRecording();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@@ -147,5 +151,7 @@ namespace Game
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -49,79 +49,96 @@ namespace Game
|
||||
return false;
|
||||
}
|
||||
|
||||
Scripting.FixedUpdate += OnClientUpdate;
|
||||
Scripting.FixedUpdate += OnClientNetworkUpdate;
|
||||
if (!listenServer)
|
||||
{
|
||||
Scripting.Exit += Cleanup;
|
||||
Level.ActorSpawned += OnClientActorSpawned;
|
||||
//Level.ActorSpawned += OnClientActorSpawned;
|
||||
}
|
||||
|
||||
string demoPath = System.IO.Path.Combine(AssetManager.DemoPath, $"{DateTimeOffset.Now.UtcTicks}_v2.gdem");
|
||||
RecordDemo(demoPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void OnClientUpdate()
|
||||
private static void OnClientNetworkUpdate()
|
||||
{
|
||||
using Utilities.ScopeProfiler _ = Utilities.ProfileScope("NetworkManager_OnClientUpdate");
|
||||
using var _ = Utilities.ProfileScope("NetworkManager_OnClientNetworkUpdate");
|
||||
|
||||
while (client.PopEvent(out NetworkEvent networkEvent))
|
||||
switch (networkEvent.EventType)
|
||||
{
|
||||
RecordMessage(ref networkEvent);
|
||||
|
||||
try
|
||||
{
|
||||
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();
|
||||
IsLocalClient = server != null;
|
||||
IsClient = true;
|
||||
OnClientReadMessage(ref networkEvent);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLocalClient = false;
|
||||
IsClient = false;
|
||||
if (networkEvent.EventType == NetworkEventType.Message)
|
||||
client.RecycleMessage(networkEvent.Message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnClientReadMessage(ref NetworkEvent networkEvent)
|
||||
{
|
||||
using var _ = Utilities.ProfileScope("NetworkManager_OnClientReadMessage");
|
||||
|
||||
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:
|
||||
{
|
||||
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}.\n";
|
||||
|
||||
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")));
|
||||
string messageBytesStr = string.Join("", messageBytes.Select(x => ((int)x).ToString("X2")));
|
||||
|
||||
Console.PrintError(err + $"Message dump: {messageBytesStr}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnClientActorSpawned(Actor actor)
|
||||
|
||||
278
Source/Game/GameMode/NetworkManager_Demo.cs
Normal file
278
Source/Game/GameMode/NetworkManager_Demo.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
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 FlaxEngine;
|
||||
using FlaxEngine.Networking;
|
||||
using Console = Game.Console;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public static unsafe partial class NetworkManager
|
||||
{
|
||||
public const byte DemoVer = 2;
|
||||
private struct DemoNetworkEventHeader
|
||||
{
|
||||
public NetworkEventType EventType;
|
||||
public uint SenderId;
|
||||
public uint MessageId;
|
||||
public uint Length;
|
||||
}
|
||||
|
||||
private static List<NetworkEvent> buffer;
|
||||
private static IEnumerator<NetworkEvent> bufferEnumerable;
|
||||
private static GZipStream demoStream; // record
|
||||
private static FileStream demoFileStream;
|
||||
|
||||
public static bool IsRecording => demoStream != null;
|
||||
private static long flushedFrames = 0;
|
||||
|
||||
public static bool IsDemoPlaying => bufferEnumerable != null;
|
||||
|
||||
public static bool PlayDemo(string demoName)
|
||||
{
|
||||
{
|
||||
Cleanup();
|
||||
GameModeManager.Init();
|
||||
}
|
||||
|
||||
if (!ReadDemo(demoName))
|
||||
{
|
||||
Console.Print("Failed to read demo.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*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 += OnDemoUpdate;
|
||||
//if (!listenServer)
|
||||
{
|
||||
Scripting.Exit += Cleanup;
|
||||
//Level.ActorSpawned += OnClientActorSpawned;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void RecordDemo(string demoPath)
|
||||
{
|
||||
if (IsRecording)
|
||||
StopRecording();
|
||||
|
||||
buffer = new List<NetworkEvent>();
|
||||
var demoFolder = Directory.GetParent(demoPath);
|
||||
if (!demoFolder.Exists)
|
||||
Directory.CreateDirectory(demoFolder.FullName);
|
||||
|
||||
demoFileStream = File.Open(demoPath, FileMode.Create, FileAccess.Write);
|
||||
demoStream = new GZipStream(demoFileStream, CompressionMode.Compress);
|
||||
demoStream.WriteByte(DemoVer);
|
||||
}
|
||||
|
||||
private static void RecordMessage(ref NetworkEvent networkEvent)
|
||||
{
|
||||
if (!IsRecording)
|
||||
return;
|
||||
|
||||
NetworkEvent recordedEvent = networkEvent;
|
||||
recordedEvent.Message.Buffer = (byte*)NativeMemory.Alloc(recordedEvent.Message.Length);
|
||||
recordedEvent.Sender.ConnectionId = networkEvent.Sender.ConnectionId;
|
||||
|
||||
NativeMemory.Copy(networkEvent.Message.Buffer, recordedEvent.Message.Buffer, recordedEvent.Message.Length);
|
||||
buffer.Add(recordedEvent);
|
||||
}
|
||||
|
||||
public static void FlushDemo()
|
||||
{
|
||||
if (!IsRecording)
|
||||
return;
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
Span<byte> bytes = stackalloc byte[Unsafe.SizeOf<DemoNetworkEventHeader>()];
|
||||
foreach (ref NetworkEvent networkEvent in CollectionsMarshal.AsSpan(buffer))
|
||||
{
|
||||
DemoNetworkEventHeader header = new DemoNetworkEventHeader()
|
||||
{
|
||||
EventType = networkEvent.EventType,
|
||||
SenderId = networkEvent.Sender.ConnectionId,
|
||||
Length = networkEvent.Message.Length,
|
||||
MessageId = networkEvent.Message.MessageId,
|
||||
};
|
||||
MemoryMarshal.Write(bytes, ref header);
|
||||
demoStream.Write(bytes);
|
||||
|
||||
Span<byte> messageBytes = new Span<byte>(networkEvent.Message.Buffer, (int)networkEvent.Message.Length);
|
||||
demoStream.Write(messageBytes);
|
||||
|
||||
// TODO: free networkEvent buffer
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
|
||||
flushedFrames += buffer.Count;
|
||||
buffer.Clear();
|
||||
|
||||
FlaxEngine.Debug.Write(LogType.Info, $"Wrote demo in {sw.Elapsed.TotalMilliseconds}ms, frames: {flushedFrames}");
|
||||
}
|
||||
|
||||
public static void StopRecording()
|
||||
{
|
||||
if (!IsRecording)
|
||||
return;
|
||||
|
||||
FlushDemo();
|
||||
demoStream.Close();
|
||||
demoStream = null;
|
||||
demoFileStream.Close();
|
||||
demoFileStream = null;
|
||||
}
|
||||
|
||||
private static unsafe bool ReadDemo(string demoPath)
|
||||
{
|
||||
if (!File.Exists(demoPath))
|
||||
return false;
|
||||
|
||||
buffer = new List<NetworkEvent>();
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
using FileStream fileStream = File.OpenRead(demoPath);
|
||||
using GZipStream stream = new GZipStream(fileStream, CompressionMode.Decompress);
|
||||
|
||||
int ver = stream.ReadByte();
|
||||
if (ver != DemoVer)
|
||||
{
|
||||
Console.Print($"Demo version mismatch, expected {DemoVer}, got {ver}");
|
||||
stream.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
int headerSize = Unsafe.SizeOf<DemoNetworkEventHeader>();
|
||||
Span<byte> headerBuffer = stackalloc byte[headerSize];
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
int bytesLeftInBuffer = headerSize;
|
||||
do
|
||||
{
|
||||
int readBytes = stream.Read(headerBuffer.Slice(headerSize - bytesLeftInBuffer, bytesLeftInBuffer));
|
||||
if (readBytes == 0)
|
||||
break;
|
||||
bytesLeftInBuffer -= readBytes;
|
||||
} while (bytesLeftInBuffer > 0);
|
||||
|
||||
if (bytesLeftInBuffer > 0)
|
||||
break; // EOF;
|
||||
}
|
||||
|
||||
DemoNetworkEventHeader header = MemoryMarshal.Read<DemoNetworkEventHeader>(headerBuffer);
|
||||
|
||||
//buffer.Add(new NetworkEvent());
|
||||
//ref NetworkEvent networkEvent = ref buffer[buffer.Length]; // collectionmarshal
|
||||
NetworkEvent networkEvent = new NetworkEvent();
|
||||
networkEvent.EventType = header.EventType;
|
||||
networkEvent.Message.Position = 0;
|
||||
networkEvent.Message.MessageId = header.MessageId;
|
||||
networkEvent.Message.Length = networkEvent.Message.BufferSize = header.Length;
|
||||
networkEvent.Sender.ConnectionId = header.SenderId;
|
||||
|
||||
if (header.Length > 0)
|
||||
{
|
||||
networkEvent.Message.Buffer = (byte*)NativeMemory.Alloc(header.Length);
|
||||
|
||||
Span<byte> messageBufferSpan = new Span<byte>(networkEvent.Message.Buffer, (int)networkEvent.Message.BufferSize);
|
||||
{
|
||||
int bytesLeftInBuffer = (int)header.Length;
|
||||
do
|
||||
{
|
||||
int readBytes = stream.Read(messageBufferSpan.Slice((int)header.Length - bytesLeftInBuffer, bytesLeftInBuffer));
|
||||
if (readBytes == 0)
|
||||
break;
|
||||
bytesLeftInBuffer -= readBytes;
|
||||
} while (bytesLeftInBuffer > 0);
|
||||
|
||||
if (bytesLeftInBuffer > 0)
|
||||
break; // EOF;
|
||||
}
|
||||
}
|
||||
buffer.Add(networkEvent);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
|
||||
bufferEnumerable = buffer.GetEnumerator();
|
||||
|
||||
Console.Print($"Demo parse time {sw.Elapsed.TotalMilliseconds}ms, frames: {buffer.Count} ");
|
||||
|
||||
DemoEndFrame(); // advances to first frame
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void DemoEndFrame()
|
||||
{
|
||||
// TODO: check if the current state frame matches the current frame number before advancing
|
||||
|
||||
/*asdf++;
|
||||
if (asdf < 8)
|
||||
return;*/
|
||||
|
||||
if (bufferEnumerable == null || !bufferEnumerable.MoveNext())
|
||||
{
|
||||
if (buffer.Any())
|
||||
{
|
||||
bufferEnumerable.Dispose();
|
||||
bufferEnumerable = null;
|
||||
buffer.Clear();
|
||||
Console.Print("Demo ended");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//var actorState = currentState.actor;
|
||||
//currentState.input = bufferEnumerable.Current;
|
||||
//frame++;
|
||||
//currentState.actor = actorState;
|
||||
}
|
||||
|
||||
private static void OnDemoUpdate()
|
||||
{
|
||||
if (!IsDemoPlaying)
|
||||
return;
|
||||
|
||||
using Utilities.ScopeProfiler _ = Utilities.ProfileScope("NetworkManager_OnDemoUpdate");
|
||||
|
||||
NetworkEvent demoEvent = bufferEnumerable.Current; // ref?
|
||||
|
||||
// TODO: change/randomize Sender.ConnectionId?
|
||||
try
|
||||
{
|
||||
IsLocalClient = server != null;
|
||||
IsClient = true;
|
||||
OnClientReadMessage(ref demoEvent);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLocalClient = false;
|
||||
IsClient = false;
|
||||
//TODO: recycle event?
|
||||
}
|
||||
|
||||
DemoEndFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace Game
|
||||
return false;
|
||||
}
|
||||
|
||||
Scripting.FixedUpdate += OnServerUpdate;
|
||||
Scripting.FixedUpdate += OnServerNetworkUpdate;
|
||||
Scripting.Exit += Cleanup;
|
||||
Level.ActorSpawned += OnServerActorSpawned;
|
||||
|
||||
@@ -116,84 +116,91 @@ namespace Game
|
||||
client.EndSendMessage(channelType, message);
|
||||
}
|
||||
|
||||
private static void OnServerUpdate()
|
||||
private static void OnServerNetworkUpdate()
|
||||
{
|
||||
using Utilities.ScopeProfiler _ = Utilities.ProfileScope("NetworkManager_OnServerUpdate");
|
||||
using var _ = Utilities.ProfileScope("NetworkManager_OnServerNetworkUpdate");
|
||||
|
||||
while (server.PopEvent(out NetworkEvent networkEvent))
|
||||
switch (networkEvent.EventType)
|
||||
OnServerReadMessage(ref networkEvent);
|
||||
}
|
||||
|
||||
private static void OnServerReadMessage(ref NetworkEvent networkEvent)
|
||||
{
|
||||
using var _ = Utilities.ProfileScope("NetworkManager_OnServerReadMessage");
|
||||
|
||||
switch (networkEvent.EventType)
|
||||
{
|
||||
case NetworkEventType.Connected:
|
||||
{
|
||||
case NetworkEventType.Connected:
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) is trying to connect");
|
||||
|
||||
try
|
||||
{
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) is trying to connect");
|
||||
|
||||
try
|
||||
IsServer = true;
|
||||
if (GameModeManager.OnClientConnecting(networkEvent.Sender))
|
||||
{
|
||||
IsServer = true;
|
||||
if (GameModeManager.OnClientConnecting(networkEvent.Sender))
|
||||
{
|
||||
ConnectedClients.Add(networkEvent.Sender);
|
||||
Console.Print(
|
||||
$"Client({networkEvent.Sender.ConnectionId}) connected. Total clients: {ConnectedClients.Count}");
|
||||
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");
|
||||
GameModeManager.OnClientConnected(networkEvent.Sender);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsServer = false;
|
||||
}
|
||||
|
||||
break;
|
||||
else
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) connection refused");
|
||||
}
|
||||
case NetworkEventType.Disconnected:
|
||||
case NetworkEventType.Timeout:
|
||||
finally
|
||||
{
|
||||
Console.Print($"Client({networkEvent.Sender.ConnectionId}) disconnected!");
|
||||
|
||||
ConnectedClients.Remove(networkEvent.Sender);
|
||||
Console.Print("Connected clients: " + ConnectedClients.Count);
|
||||
break;
|
||||
IsServer = false;
|
||||
}
|
||||
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();
|
||||
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)
|
||||
|
||||
@@ -1286,7 +1286,7 @@ namespace Game
|
||||
LightWithShadow light;
|
||||
Float3? lightTargetPosition = null;
|
||||
|
||||
int preset = 3;
|
||||
|
||||
|
||||
if (entity.properties.TryGetValue("target", out string targetName))
|
||||
{
|
||||
@@ -1353,6 +1353,7 @@ namespace Game
|
||||
light.ShadowsDistance = 500f;
|
||||
light.ShadowsDepthBias = 0.027f;//0.005f;
|
||||
|
||||
int preset = 3;
|
||||
if (preset == 0) // most accurate?, huge radius and low performance
|
||||
{
|
||||
light.Brightness = lightamm / 93f;
|
||||
@@ -1394,16 +1395,9 @@ namespace Game
|
||||
}
|
||||
else //if (preset == 3)
|
||||
{
|
||||
if (pointLight != null)
|
||||
{
|
||||
pointLight.Radius = radamm * LightRadiusMultiplier;
|
||||
pointLight.FallOffExponent = FallOffExponent;
|
||||
}
|
||||
if (spotLight != null)
|
||||
{
|
||||
spotLight.Radius = radamm * LightRadiusMultiplier;
|
||||
spotLight.FallOffExponent = FallOffExponent;
|
||||
}
|
||||
bool inverse = false;
|
||||
float finalRadius = radamm * LightRadiusMultiplier;
|
||||
|
||||
|
||||
light.Brightness = (lightamm / 128f) * BrightnessMultiplier;
|
||||
|
||||
@@ -1427,6 +1421,25 @@ namespace Game
|
||||
// huge aliasing with spot lights for some reason?
|
||||
light.ShadowsDepthBias = 0.7f;
|
||||
}
|
||||
|
||||
if (inverse)
|
||||
{
|
||||
light.Brightness *= 20000f;
|
||||
finalRadius *= 0.7f;
|
||||
}
|
||||
|
||||
if (pointLight != null)
|
||||
{
|
||||
pointLight.UseInverseSquaredFalloff = inverse;
|
||||
pointLight.Radius = finalRadius;
|
||||
pointLight.FallOffExponent = FallOffExponent;
|
||||
}
|
||||
if (spotLight != null)
|
||||
{
|
||||
spotLight.UseInverseSquaredFalloff = inverse;
|
||||
spotLight.Radius = finalRadius;
|
||||
spotLight.FallOffExponent = FallOffExponent;
|
||||
}
|
||||
}
|
||||
|
||||
lightIndex++;
|
||||
|
||||
@@ -90,11 +90,13 @@ namespace Game
|
||||
public class PlayerInput
|
||||
{
|
||||
public const byte DemoVer = 1;
|
||||
public const int MaxPlayerStates = 120;
|
||||
|
||||
public PlayerState currentState;
|
||||
public ulong frame;
|
||||
//public ulong oldestFrame;
|
||||
|
||||
private PlayerState[] states = new PlayerState[120];
|
||||
private PlayerState[] states = new PlayerState[MaxPlayerStates];
|
||||
|
||||
public virtual bool Predict => false;
|
||||
|
||||
@@ -110,7 +112,7 @@ namespace Game
|
||||
{
|
||||
//Console.Print("recorded frame " + frame);
|
||||
|
||||
states[frame % 120] = currentState;
|
||||
states[frame % MaxPlayerStates] = currentState;
|
||||
|
||||
/*ulong oldest = ulong.MaxValue;
|
||||
for (int i = 0; i < 120; i++)
|
||||
@@ -130,7 +132,7 @@ namespace Game
|
||||
|
||||
public bool GetState(ulong frame, out PlayerInputState inputState, out PlayerActorState actorState)
|
||||
{
|
||||
int frameIndex = (int)frame % 120;
|
||||
int frameIndex = (int)frame % MaxPlayerStates;
|
||||
if (states[frameIndex].input.frame != frame)
|
||||
{
|
||||
inputState = default;
|
||||
@@ -145,7 +147,7 @@ namespace Game
|
||||
|
||||
public void SetState(ulong frame, PlayerInputState inputState)
|
||||
{
|
||||
int frameIndex = (int)frame % 120;
|
||||
int frameIndex = (int)frame % MaxPlayerStates;
|
||||
states[frameIndex].input = inputState;
|
||||
states[frameIndex].input.frame = frame;
|
||||
states[frameIndex].actor = new PlayerActorState();
|
||||
@@ -153,7 +155,7 @@ namespace Game
|
||||
|
||||
public void SetState(ulong frame, PlayerInputState inputState, PlayerActorState actorState)
|
||||
{
|
||||
int frameIndex = (int)frame % 120;
|
||||
int frameIndex = (int)frame % MaxPlayerStates;
|
||||
states[frameIndex].input = inputState;
|
||||
states[frameIndex].input.frame = frame;
|
||||
states[frameIndex].actor = actorState;
|
||||
|
||||
@@ -27,9 +27,11 @@ namespace Game
|
||||
|
||||
public override bool Predict => true;
|
||||
|
||||
public PlayerInputLocal(PlayerActor playerActor, string demoPath)
|
||||
public PlayerInputLocal(PlayerActor playerActor, string demoPath = null)
|
||||
{
|
||||
this.playerActor = playerActor;
|
||||
if (demoPath == null)
|
||||
return;
|
||||
var demoFolder = Directory.GetParent(demoPath);
|
||||
if (!demoFolder.Exists)
|
||||
Directory.CreateDirectory(demoFolder.FullName);
|
||||
@@ -72,6 +74,7 @@ namespace Game
|
||||
|
||||
public override void OnEndFrame()
|
||||
{
|
||||
currentState.input.frame = frame;
|
||||
if (IsRecording)
|
||||
{
|
||||
currentState.input.verificationPosition = currentState.actor.position;
|
||||
@@ -79,7 +82,6 @@ namespace Game
|
||||
currentState.input.verificationViewAngles = currentState.actor.viewAngles;
|
||||
currentState.input.verificationOrientation = currentState.actor.orientation;
|
||||
|
||||
currentState.input.frame = frame;
|
||||
buffer.Add(currentState.input);
|
||||
}
|
||||
|
||||
@@ -111,14 +113,6 @@ namespace Game
|
||||
currentState.input.viewDeltaY = 0;
|
||||
}
|
||||
|
||||
public override void RecordCurrentActorState(PlayerActorState actorState)
|
||||
{
|
||||
if (!IsRecording)
|
||||
return;
|
||||
|
||||
base.RecordCurrentActorState(actorState);
|
||||
}
|
||||
|
||||
public void FlushDemo()
|
||||
{
|
||||
if (!IsRecording)
|
||||
|
||||
@@ -135,7 +135,6 @@ namespace Game
|
||||
public Float3 viewAngles;
|
||||
private Float3 viewAnglesLastFrame;
|
||||
|
||||
[NetworkReplicated]
|
||||
public uint PlayerId = 0;
|
||||
|
||||
[ReadOnly]
|
||||
@@ -186,8 +185,9 @@ namespace Game
|
||||
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");
|
||||
input = new PlayerInputLocal(playerActor, demoPath); // TODO: support recording
|
||||
//string demoPath = System.IO.Path.Combine(AssetManager.DemoPath, $"{DateTimeOffset.Now.UtcTicks}.gdem");
|
||||
//input = new PlayerInputLocal(playerActor, demoPath); // TODO: support recording
|
||||
input = new PlayerInputLocal(playerActor);
|
||||
|
||||
}
|
||||
else
|
||||
@@ -327,67 +327,14 @@ namespace Game
|
||||
|
||||
public override void OnFixedUpdate()
|
||||
{
|
||||
PlayerInputDemo demoInput = input as PlayerInputDemo;
|
||||
if (demoInput != null)
|
||||
demoInput.OnUpdate();
|
||||
|
||||
float timeDeltaDiff = Time.DeltaTime - 1.0f / Time.PhysicsFPS;
|
||||
if (Math.Abs(timeDeltaDiff) > 0.0001f)
|
||||
if (Time.PhysicsFPS > 0 && Math.Abs(timeDeltaDiff) > 0.0001f)
|
||||
Console.Print("Time.DeltaTime is not stable: " + timeDeltaDiff);
|
||||
|
||||
input.OnFixedUpdate();
|
||||
PlayerInputState inputState = input.GetCurrentInputState();
|
||||
|
||||
if (demoInput != null && demoInput.IsPlaying)
|
||||
{
|
||||
ApplyInputToCamera(inputState, true);
|
||||
|
||||
// Verify view angles first
|
||||
if (demoDeltasVerify)
|
||||
{
|
||||
Float3 verifAngles = new Float3(inputState.verificationViewAngles.Y, inputState.verificationViewAngles.X, inputState.verificationViewAngles.Z);
|
||||
float viewAnglesDelta = (viewAngles - verifAngles).Length;
|
||||
if (viewAnglesDelta > 0.00001)
|
||||
{
|
||||
Console.Print($"Demo verification failed, view angles delta: {viewAnglesDelta}, viewAngles:{viewAngles}, verif:{verifAngles}");
|
||||
if (demoDeltasCorrect)
|
||||
SetCameraEulerAngles(verifAngles, false);
|
||||
}
|
||||
}
|
||||
|
||||
SimulatePlayerMovement(inputState);
|
||||
|
||||
if (demoDeltasVerify)
|
||||
{
|
||||
// verify
|
||||
float positionDelta = (movementState.position - inputState.verificationPosition).Length;
|
||||
if (positionDelta > 0.00001)
|
||||
Console.Print("Demo verification failed, position delta: " + positionDelta);
|
||||
|
||||
float velocityDelta = (movementState.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);
|
||||
movementState.position = inputState.verificationPosition;
|
||||
currentVelocity = inputState.verificationVelocity;
|
||||
rootActor.Orientation = inputState.verificationOrientation;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
else if (input is PlayerInputNetwork)
|
||||
if (input is PlayerInputNetwork)
|
||||
{
|
||||
#if false
|
||||
bool canpredict = true;
|
||||
@@ -497,7 +444,7 @@ namespace Game
|
||||
|
||||
//viewAngles = viewAnglesLastFrame;
|
||||
bool canpredict = true;
|
||||
if (true && input.Predict && GameModeManager.ClientFrame > 0 && GameModeManager.ServerFrame > 0)
|
||||
if (true && input.Predict /*&& !NetworkManager.IsDemoPlaying*/ && GameModeManager.ClientFrame > 0 && GameModeManager.ServerFrame > 0)
|
||||
{
|
||||
ulong currentFrame = GameModeManager.ServerFrame;
|
||||
for (; currentFrame < GameModeManager.ClientFrame; currentFrame++)
|
||||
@@ -580,9 +527,11 @@ namespace Game
|
||||
|
||||
predicting = false;
|
||||
|
||||
if ((movementState.position - oldPos).Length > 0.001)
|
||||
var posDelta = (movementState.position - oldPos);
|
||||
var velDelta = (movementState.currentVelocity - oldVel);
|
||||
if (posDelta.Length > 0.001)
|
||||
Console.Print($"mispredicted final position");
|
||||
if ((movementState.currentVelocity - oldVel).Length > 0.001)
|
||||
if (velDelta.Length > 0.001)
|
||||
Console.Print($"mispredicted final velocity");
|
||||
|
||||
|
||||
@@ -602,7 +551,8 @@ namespace Game
|
||||
SimulatePlayerMovement(inputState);
|
||||
}
|
||||
|
||||
if ((viewAngles - oldAngles).Length > 0.001)
|
||||
var viewDelta = (viewAngles - oldAngles);
|
||||
if (viewDelta.Length > 0.001)
|
||||
Console.Print($"mispredicted final viewangles: {viewAngles} <- {oldAngles}");
|
||||
|
||||
//if (viewAngles != new Float3(90f, 0f, 0f))
|
||||
|
||||
Reference in New Issue
Block a user