Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce allocations, Remove GetNode(), Consolidate ImGui #10

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Godot 4+ specific ignores
.godot/
.idea/
27 changes: 12 additions & 15 deletions client/ClientClock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ public override void _Ready()
_minLatencyInTicks = PhysicsUtils.MsecToTick(_minLatency);
}

public override void _Process(double delta)
{
DisplayDebugInformation();
}

// Called every Physics Tick, same as in the server
public void ProcessTick()
{
Expand Down Expand Up @@ -118,7 +113,8 @@ private static int SimpleAverage(List<int> samples)
}

int count = 0;
samples.ForEach(s => count += s);
for (int i = 0; i < samples.Count; i++)
count += samples[i];
return count / samples.Count;
}

Expand Down Expand Up @@ -165,15 +161,16 @@ private void SendSyncPacket(NetMessage.Sync sync)
_multiplayer.SendBytes(data, 1, MultiplayerPeer.TransferModeEnum.Unreliable, 1);
}

private void DisplayDebugInformation()
public void DrawGui()
{
ImGui.Begin("Network Clock Information");
ImGui.Text($"Synced Tick {GetCurrentRemoteTick()}");
ImGui.Text($"Local Tick {GetCurrentTick()}");
ImGui.Text($"Immediate Latency {_immediateLatencyMsec}ms");
ImGui.Text($"Average Latency {_averageLatencyInTicks} ticks");
ImGui.Text($"Latency Jitter {_jitterInTicks} ticks");
ImGui.Text($"Average Offset {_averageOffsetInTicks} ticks");
ImGui.End();
if (ImGui.CollapsingHeader("Network Clock Information"))
{
ImGui.Text($"Synced Tick {GetCurrentRemoteTick()}");
ImGui.Text($"Local Tick {GetCurrentTick()}");
ImGui.Text($"Immediate Latency {_immediateLatencyMsec}ms");
ImGui.Text($"Average Latency {_averageLatencyInTicks} ticks");
ImGui.Text($"Latency Jitter {_jitterInTicks} ticks");
ImGui.Text($"Average Offset {_averageOffsetInTicks} ticks");
}
}
}
25 changes: 19 additions & 6 deletions client/ClientManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Godot;
using ImGuiNET;
using MemoryPack;
using Vector2 = System.Numerics.Vector2;

/*
Network manager for the client, handles server connection and routes packages.
Expand All @@ -13,13 +14,16 @@ public partial class ClientManager : Node
private SceneMultiplayer _multiplayer = new();
private SnapshotInterpolator _snapshotInterpolator;
private ClientClock _clock;
private NetworkDebug _networkDebug;
private Node _entityArray;

public override void _EnterTree()
{
// Connects to the server
ConnectClient();

_networkDebug = GetNode<NetworkDebug>("Debug");

_entityArray = GetNode("/root/Main/EntityArray");

// Stores NetworkClock node instance
Expand All @@ -33,15 +37,15 @@ public override void _EnterTree()
public override void _Process(double delta)
{
_snapshotInterpolator.InterpolateStates(_entityArray);
DisplayDebugInformation();
DrawGui();
}

public override void _PhysicsProcess(double delta)
{
_clock.ProcessTick();
int currentTick = _clock.GetCurrentTick();
int currentRemoteTick = _clock.GetCurrentRemoteTick();
CustomSpawner.LocalPlayer.ProcessTick(currentRemoteTick);
CustomSpawner.LocalPlayer?.ProcessTick(currentRemoteTick);
_snapshotInterpolator.ProcessTick(currentTick);
}

Expand Down Expand Up @@ -84,11 +88,20 @@ private void ConnectClient()
GD.Print("Client connected to ", _address, ":", _port);
}

private void DisplayDebugInformation()
private void DrawGui()
{
ImGui.Begin("Client Information");
ImGui.Text($"Framerate {Engine.GetFramesPerSecond()}fps");
ImGui.Text($"Physics Tick {Engine.PhysicsTicksPerSecond}hz");
ImGui.SetNextWindowPos(Vector2.Zero);
if (ImGui.Begin("Client Information",
ImGuiWindowFlags.NoMove
| ImGuiWindowFlags.NoResize
| ImGuiWindowFlags.AlwaysAutoResize))
{
ImGui.Text($"Framerate {Engine.GetFramesPerSecond()}fps");
ImGui.Text($"Physics Tick {Engine.PhysicsTicksPerSecond}hz");
_clock.DrawGui();
_networkDebug.DrawGui();
_snapshotInterpolator.DrawGui();
}
ImGui.End();
}
}
36 changes: 13 additions & 23 deletions client/ClientPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using Godot;
using System.Collections.Generic;
using NetMessage;
using System;
using System.Linq;
using Godot;
using ImGuiNET;
using MemoryPack;
using System.Linq;
using NetMessage;
using Vector2 = System.Numerics.Vector2;

/*
Main player script, send movement packets to the server, does CSP, and reconciliation.
Main player script, send movement packets to the server, does CSP, and reconciliation.
*/
public partial class ClientPlayer : CharacterBody3D
{
Expand All @@ -25,11 +25,6 @@ public override void _Ready()
_networkId = Multiplayer.GetUniqueId();
}

public override void _Process(double delta)
{
DisplayDebugInformation();
}

public void ProcessTick(int currentTick)
{
var userInput = GenerateUserInput(currentTick);
Expand Down Expand Up @@ -57,14 +52,17 @@ private void AdvancePhysics(byte input)

// Called when a UserState is received from the server
// Here we validate that our prediction was correct
public void ReceiveState(NetMessage.EntityState state, int forTick)
public void ReceiveState(EntityState state, int forTick)
{
// Ignore any stamp that should have been received in the past
if (forTick > _lastStampReceived)
_lastStampReceived = forTick;
else return;

_userInputs.RemoveAll(input => input.Tick <= forTick); // Delete all stored inputs up to that point, we don't need them anymore
// Delete all stored inputs up to that point, we don't need them anymore
for (int i = _userInputs.Count-1; i >= 0; i--)
if (_userInputs[i].Tick <= forTick)
_userInputs.RemoveAt(i);

// Re-apply all inputs that haven't been processed by the server starting from the last acked state (the one just received)
Transform3D expectedTransform = this.GlobalTransform;
Expand Down Expand Up @@ -111,15 +109,15 @@ private void SolveAutoMove()
// Sends all non-processed inputs to the server
private void SendInputs(int currentTick)
{
var userCmd = new NetMessage.UserCommand
var userCmd = new UserCommand
{
Tick = currentTick,
Inputs = _userInputs.Select(i => i.Input).ToArray()
};

if (this.IsMultiplayerAuthority() && Multiplayer.GetUniqueId() != 1)
{
byte[] data = MemoryPackSerializer.Serialize<NetMessage.ICommand>(userCmd);
byte[] data = MemoryPackSerializer.Serialize<ICommand>(userCmd);

(Multiplayer as SceneMultiplayer).SendBytes(data, 1,
MultiplayerPeer.TransferModeEnum.Unreliable, 0);
Expand All @@ -146,21 +144,13 @@ private static LocalInput GenerateUserInput(int tick)
return userInput;
}

private void DisplayDebugInformation()
public void DrawGui()
{
ImGui.Begin("Player Network Information");
ImGui.Text($"Network Id {_networkId}");
ImGui.Text($"Position {Position.Snapped(Vector3.One * 0.01f)}");
ImGui.Text($"Redundant Inputs {_userInputs.Count}");
ImGui.Text($"Last Stamp Rec. {_lastStampReceived}");
ImGui.Text($"Misspredictions {_misspredictionCounter}");
ImGui.Checkbox("Automove?", ref _autoMoveEnabled);
ImGui.End();
}

private struct LocalInput
{
public int Tick;
public byte Input;
}
}
24 changes: 11 additions & 13 deletions client/SnapshotInterpolator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public override void _Process(double delta)
smooth out the interpolation, I still believe that it is not as smooth as it was when using
Time.GetTicksMsec() so this is still pending, but is good enough for now.*/
_currentTick += delta / PhysicsUtils.FrameTime;
DisplayDebugInformation();
}

public void ProcessTick(int currentTick)
Expand Down Expand Up @@ -61,9 +60,9 @@ public void InterpolateStates(Node playersArray)
{
//TODO: check if the Entity is available in both states
NetMessage.EntityState futureState = nextSnapshot.States[i];
NetMessage.EntityState pastState = prevSnapshot.States[i];
NetMessage.EntityState pastState = prevSnapshot.States.Length < i ? prevSnapshot.States[i] : futureState;

var dummy = playersArray.GetNode<Node3D>(futureState.Id.ToString()); //FIXME: remove GetNode for the love of god
var dummy = CustomSpawner.GetSpawnedNode(futureState.Id);

if (dummy != null && dummy.IsMultiplayerAuthority() == false)
{
Expand All @@ -86,17 +85,16 @@ public void SetBufferTime(int bufferTime)
_bufferTime = bufferTime + _minBufferTime;
}

private void DisplayDebugInformation()
public void DrawGui()
{
ImGui.Begin("Snapshot Interpolator Information");

if (_interpolationFactor > 1) ImGui.PushStyleColor(ImGuiCol.Text, 0xFF0000FF);
ImGui.Text($"Interp. Factor {_interpolationFactor:0.00}");
ImGui.PopStyleColor();
if (ImGui.CollapsingHeader("Snapshot Interpolator Information"))
{
if (_interpolationFactor > 1) ImGui.PushStyleColor(ImGuiCol.Text, 0xFF0000FF);
ImGui.Text($"Interp. Factor {_interpolationFactor:0.00}");
ImGui.PopStyleColor();

ImGui.Text($"Buffer Size {_snapshotBuffer.Count} snapshots");
ImGui.Text($"Buffer Time {_bufferTime} ticks");
ImGui.End();
ImGui.Text($"Buffer Size {_snapshotBuffer.Count} snapshots");
ImGui.Text($"Buffer Time {_bufferTime} ticks");
}
}

}
2 changes: 1 addition & 1 deletion multiplayer-base.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.2.1">
<Project Sdk="Godot.NET.Sdk/4.2.2">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
Expand Down
20 changes: 10 additions & 10 deletions server/ServerClock.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Godot;
using ImGuiNET;
using MemoryPack;
using System;
using NetMessage;

public partial class ServerClock : Node
{
Expand All @@ -21,7 +21,6 @@ public override void _Ready()

public override void _Process(double delta)
{
DisplayDebugInformation();
SolveSendNetworkTickEvent(delta);
}

Expand Down Expand Up @@ -49,20 +48,21 @@ private void SolveSendNetworkTickEvent(double delta)
// When we receive a sync packet from a Client, we return it with the current Clock data
private void OnPacketReceived(long id, byte[] data)
{
var command = MemoryPackSerializer.Deserialize<NetMessage.ICommand>(data);
var command = MemoryPackSerializer.Deserialize<ICommand>(data);

if (command is NetMessage.Sync sync)
if (command is Sync sync)
{
sync.ServerTime = _currentTick;
_multiplayer.SendBytes(MemoryPackSerializer.Serialize<NetMessage.ICommand>(sync), (int)id, MultiplayerPeer.TransferModeEnum.Unreliable, 1);
_multiplayer.SendBytes(MemoryPackSerializer.Serialize<ICommand>(sync), (int)id, MultiplayerPeer.TransferModeEnum.Unreliable, 1);
}
}

private void DisplayDebugInformation()
public void DrawGui()
{
ImGui.Begin($"Clock Information");
ImGui.Text($"Network Tickrate {GetNetworkTickRate()}hz");
ImGui.Text($"Current Tick {_currentTick}");
ImGui.End();
if (ImGui.CollapsingHeader("Clock Information"))
{
ImGui.Text($"Network Tickrate {GetNetworkTickRate()}hz");
ImGui.Text($"Current Tick {_currentTick}");
}
}
}
24 changes: 17 additions & 7 deletions server/ServerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ public partial class ServerManager : Node
[Export] private int _port = 9999;

private SceneMultiplayer _multiplayer = new();
private Godot.Collections.Array<Godot.Node> entityArray;
private Godot.Collections.Array<Godot.Node> entityArray = new();
private ServerClock _serverClock;
private NetworkDebug _networkDebug;

private int _currentTick = 0;
public override void _EnterTree()
{
StartListening();
_networkDebug = GetNode<NetworkDebug>("Debug");
_serverClock = GetNode<ServerClock>("ServerClock");
_serverClock.NetworkProcessTick += NetworkProcess;
}

public override void _Process(double delta)
{
DisplayDebugInformation();
DrawGui();
}

public override void _PhysicsProcess(double delta)
Expand Down Expand Up @@ -68,7 +70,7 @@ private void OnPacketReceived(long id, byte[] data)
var command = MemoryPackSerializer.Deserialize<NetMessage.ICommand>(data);
if (command is NetMessage.UserCommand userCommand)
{
ServerPlayer player = GetNode($"/root/Main/EntityArray/{id}") as ServerPlayer; //FIXME: do not use GetNode here
ServerPlayer player = CustomSpawner.GetSpawnedNode((int)id) as ServerPlayer;
player.PushCommand(userCommand);
}

Expand Down Expand Up @@ -105,11 +107,19 @@ private void StartListening()
GD.Print("Server listening on ", _port);
}

private void DisplayDebugInformation()
private void DrawGui()
{
ImGui.Begin("Server Information");
ImGui.Text($"Framerate {Engine.GetFramesPerSecond()}fps");
ImGui.Text($"Physics Tick {Engine.PhysicsTicksPerSecond}hz");
ImGui.SetNextWindowPos(System.Numerics.Vector2.Zero);
if(ImGui.Begin("Server Information",
ImGuiWindowFlags.NoMove
| ImGuiWindowFlags.NoResize
| ImGuiWindowFlags.AlwaysAutoResize))
{
ImGui.Text($"Framerate {Engine.GetFramesPerSecond()}fps");
ImGui.Text($"Physics Tick {Engine.PhysicsTicksPerSecond}hz");
_serverClock.DrawGui();
_networkDebug.DrawGui();
}
ImGui.End();
}
}
Loading