Skip to content
This repository was archived by the owner on Oct 22, 2023. It is now read-only.

Commit c1eadfb

Browse files
committed
Add NetworkCommandBuffer
Simulation uses one Local and one Remote CommandBuffer First steps towards lag compensation (currently hardcoded 20 frames)
1 parent b61ec94 commit c1eadfb

File tree

14 files changed

+167
-90
lines changed

14 files changed

+167
-90
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,5 @@ Unity/Assembly-CSharp-Editor\.csproj
272272
/Unity/Assets/Integration/ECS.pdb.meta
273273
/Unity/Assets/Integration/BEPUutilities.pdb.meta
274274
/Unity/Assets/Editor Default Resources
275-
/Unity/Assets/Scripts/Debug.meta
276275
/Unity/Assets/Editor Default Resources.meta
276+
/Unity/Assets/Debug.meta

Engine/Client/CommandBuffer.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using Lockstep.Core.Data;
45
using Lockstep.Core.Interfaces;
@@ -9,6 +10,8 @@ public class CommandBuffer : ICommandBuffer
910
{
1011
private readonly Dictionary<long, List<ICommand>> _commands = new Dictionary<long, List<ICommand>>();
1112

13+
public event Action<long, ICommand> Inserted;
14+
1215
public long Count
1316
{
1417
get
@@ -24,7 +27,7 @@ public long Count
2427

2528
public long Remaining => Count - ItemIndex;
2629

27-
public void Insert(long frameNumber, ICommand command)
30+
public virtual void Insert(long frameNumber, ICommand command)
2831
{
2932
lock (_commands)
3033
{
@@ -34,6 +37,8 @@ public void Insert(long frameNumber, ICommand command)
3437
}
3538

3639
_commands[frameNumber].Add(command);
40+
41+
Inserted?.Invoke(frameNumber, command);
3742
}
3843
}
3944

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Lockstep.Client.Implementations;
5+
using Lockstep.Client.Interfaces;
6+
using Lockstep.Core.Data;
7+
using Lockstep.Network;
8+
using Lockstep.Network.Messages;
9+
using Lockstep.Network.Utils;
10+
11+
namespace Lockstep.Client
12+
{
13+
public class NetworkCommandBuffer : CommandBuffer
14+
{
15+
public event Action<Init> InitReceived;
16+
17+
private readonly INetwork _network;
18+
private readonly IDictionary<ushort, Func<ISerializableCommand>> _commandFactories = new Dictionary<ushort, Func<ISerializableCommand>>();
19+
20+
public NetworkCommandBuffer(INetwork network)
21+
{
22+
_network = network;
23+
_network.DataReceived += OnDataReceived;
24+
}
25+
26+
public void RegisterCommand(Func<ISerializableCommand> commandFactory)
27+
{
28+
var tag = commandFactory.Invoke().Tag;
29+
if (_commandFactories.ContainsKey(tag))
30+
{
31+
throw new InvalidDataException("The command tag " + tag + " is already registered. Every command tag must be unique.");
32+
}
33+
_commandFactories.Add(tag, commandFactory);
34+
}
35+
36+
public override void Insert(long frameNumber, ICommand command)
37+
{
38+
if (command is ISerializableCommand serializable)
39+
{
40+
//Tell the server
41+
var writer = new Serializer();
42+
writer.Put((byte)MessageTag.Input);
43+
writer.Put(frameNumber);
44+
writer.Put(serializable.Tag);
45+
serializable.Serialize(writer);
46+
47+
_network.Send(Compressor.Compress(writer));
48+
}
49+
}
50+
51+
private void OnDataReceived(byte[] data)
52+
{
53+
data = Compressor.Decompress(data);
54+
55+
var reader = new Deserializer(data);
56+
var messageTag = (MessageTag)reader.GetByte();
57+
switch (messageTag)
58+
{
59+
case MessageTag.StartSimulation:
60+
var init = new Init();
61+
init.Deserialize(reader);
62+
InitReceived?.Invoke(init);
63+
break;
64+
case MessageTag.Input:
65+
var frameNumber = reader.GetLong();
66+
var tag = reader.GetUShort();
67+
68+
if (_commandFactories.ContainsKey(tag))
69+
{
70+
var newCommand = _commandFactories[tag].Invoke();
71+
newCommand.Deserialize(reader);
72+
73+
74+
base.Insert(frameNumber, newCommand);
75+
}
76+
77+
break;
78+
}
79+
}
80+
}
81+
}

Engine/Client/Simulation.cs

Lines changed: 34 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.IO;
1+
using System;
42
using Lockstep.Client.Implementations;
5-
using Lockstep.Client.Interfaces;
3+
using Lockstep.Client.Interfaces;
4+
using Lockstep.Core.Data;
65
using Lockstep.Core.Interfaces;
7-
using Lockstep.Network;
86
using Lockstep.Network.Messages;
9-
using Lockstep.Network.Utils;
107

118
namespace Lockstep.Client
129
{
@@ -16,54 +13,49 @@ public class Simulation
1613

1714
public bool Running { get; set; }
1815

19-
private readonly ISystems _systems;
20-
private readonly INetwork _network;
16+
private readonly ISystems _systems;
2117

2218
public float _tickDt;
2319
public float _accumulatedTime;
2420

2521
public long CurrentTick { get; private set; }
2622

2723
public ICommandBuffer LocalCommandBuffer => _systems.CommandBuffer;
28-
public readonly CommandBuffer NetworkCommandBuffer;
24+
public readonly ICommandBuffer RemoteCommandBuffer;
2925

30-
private readonly IDictionary<ushort, Func<ISerializableCommand>> _commandFactories = new Dictionary<ushort, Func<ISerializableCommand>>();
26+
private readonly object _currentTickLock = new object();
3127

32-
public Simulation(ISystems systems, INetwork network)
28+
29+
public Simulation(ISystems systems, ICommandBuffer remoteCommandBuffer)
3330
{
3431
_systems = systems;
3532
_systems.CommandBuffer = new CommandBuffer();
3633

37-
NetworkCommandBuffer = new CommandBuffer();
38-
39-
_network = network;
40-
_network.DataReceived += OnDataReceived;
34+
RemoteCommandBuffer = remoteCommandBuffer;
35+
RemoteCommandBuffer.Inserted += (l, command) =>
36+
{
37+
LocalCommandBuffer.Insert(l, command);
38+
};
4139
}
4240

41+
public void Start(Init init)
42+
{
43+
_tickDt = 1000f / init.TargetFPS;
4344

44-
public void RegisterCommand(Func<ISerializableCommand> commandFactory)
45-
{
46-
var tag = commandFactory.Invoke().Tag;
47-
if (_commandFactories.ContainsKey(tag))
45+
_systems.Initialize();
46+
47+
Running = true;
48+
}
49+
50+
public void Execute(ICommand command)
51+
{
52+
lock (_currentTickLock)
4853
{
49-
throw new InvalidDataException("The command tag " + tag + " is already registered. Every command tag must be unique.");
50-
}
51-
_commandFactories.Add(tag, commandFactory);
52-
}
54+
var executionTick = CurrentTick + 20;
5355

54-
public void Execute(ISerializableCommand command)
55-
{
56-
//Execute the command locally on the next tick
57-
_systems.CommandBuffer.Insert(CurrentTick + 1, command);
58-
59-
//Tell the server
60-
var writer = new Serializer();
61-
writer.Put((byte)MessageTag.Input);
62-
writer.Put(CurrentTick + 1);
63-
writer.Put(command.Tag);
64-
command.Serialize(writer);
65-
66-
_network.Send(Compressor.Compress(writer));
56+
//LocalCommandBuffer.Insert(nextTick, command);
57+
RemoteCommandBuffer.Insert(executionTick, command);
58+
}
6759
}
6860

6961
public void Update(float deltaTime)
@@ -76,21 +68,16 @@ public void Update(float deltaTime)
7668
_accumulatedTime += deltaTime;
7769

7870
while (_accumulatedTime >= _tickDt)
79-
{
80-
Tick();
71+
{
72+
lock (_currentTickLock)
73+
{
74+
Tick();
75+
}
8176

8277
_accumulatedTime -= _tickDt;
8378
}
8479
}
85-
86-
private void StartSimulation(int targetFps)
87-
{
88-
_tickDt = 1000f / targetFps;
89-
90-
_systems.Initialize();
91-
92-
Running = true;
93-
}
80+
9481

9582
private void Tick()
9683
{
@@ -100,33 +87,5 @@ private void Tick()
10087
CurrentTick++;
10188
}
10289

103-
private void OnDataReceived(byte[] data)
104-
{
105-
data = Compressor.Decompress(data);
106-
107-
var reader = new Deserializer(data);
108-
var messageTag = (MessageTag)reader.GetByte();
109-
switch (messageTag)
110-
{
111-
case MessageTag.StartSimulation:
112-
var init = new Init();
113-
init.Deserialize(reader);
114-
StartSimulation(init.TargetFPS);
115-
break;
116-
case MessageTag.Input:
117-
var frameNumber = reader.GetLong();
118-
var tag = reader.GetUShort();
119-
120-
if (_commandFactories.ContainsKey(tag))
121-
{
122-
var newCommand = _commandFactories[tag].Invoke();
123-
newCommand.Deserialize(reader);
124-
125-
NetworkCommandBuffer.Insert(frameNumber, newCommand);
126-
}
127-
128-
break;
129-
}
130-
}
13190
}
13291
}

Engine/Core/DefaultServices/DefaultHashService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
using Lockstep.Core.Interfaces;
1+
using System.Collections.Generic;
2+
using Lockstep.Core.Interfaces;
23

34
namespace Lockstep.Core.DefaultServices
45
{
56
class DefaultHashService : IHashService
67
{
7-
public long CalculateHashCode(GameEntity[] entities)
8+
public long CalculateHashCode(IEnumerable<GameEntity> entities)
89
{
910
long hashCode = 0;
1011
foreach (var entity in entities)

Engine/Core/Interfaces/ICommandBuffer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
using Lockstep.Core.Data;
1+
using System;
2+
using Lockstep.Core.Data;
23

34
namespace Lockstep.Core.Interfaces
45
{
56
public interface ICommandBuffer
67
{
8+
event Action<long, ICommand> Inserted;
9+
710
long Count { get; }
811
long ItemIndex { get; }
912
long Remaining { get; }
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
namespace Lockstep.Core.Interfaces
1+
using System.Collections.Generic;
2+
3+
namespace Lockstep.Core.Interfaces
24
{
35
public interface IHashService : IService
46
{
5-
long CalculateHashCode(GameEntity[] hashableEntities);
7+
long CalculateHashCode(IEnumerable<GameEntity> hashableEntities);
68
}
79
}
1 KB
Binary file not shown.
428 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)