This commit is contained in:
SepComet
2026-04-06 11:58:36 +08:00
parent b1b38b485e
commit aebc4011c7
84 changed files with 5796 additions and 238 deletions
+38 -1
View File
@@ -63,7 +63,7 @@ namespace Network.Defines {
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.MoveInput), global::Network.Defines.MoveInput.Parser, new[]{ "PlayerId", "Tick", "TurnInput", "ThrottleInput" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.ShootInput), global::Network.Defines.ShootInput.Parser, new[]{ "PlayerId", "Tick", "DirX", "DirY", "TargetId" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.CombatEvent), global::Network.Defines.CombatEvent.Parser, new[]{ "Tick", "EventType", "AttackerId", "TargetId", "Damage", "HitPosition" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.PlayerState), global::Network.Defines.PlayerState.Parser, new[]{ "PlayerId", "Position", "Velocity", "Rotation", "Tick", "Hp" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.PlayerState), global::Network.Defines.PlayerState.Parser, new[]{ "PlayerId", "Position", "Velocity", "Rotation", "Tick", "Hp", "AcknowledgedMoveTick" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.Heartbeat), global::Network.Defines.Heartbeat.Parser, null, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Network.Defines.HeartbeatResponse), global::Network.Defines.HeartbeatResponse.Parser, new[]{ "ServerTick" }, null, null, null, null)
}));
@@ -2679,6 +2679,7 @@ namespace Network.Defines {
rotation_ = other.rotation_;
tick_ = other.tick_;
hp_ = other.hp_;
acknowledgedMoveTick_ = other.acknowledgedMoveTick_;
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}
@@ -2760,6 +2761,18 @@ namespace Network.Defines {
}
}
/// <summary>Field number for the "acknowledged_move_tick" field.</summary>
public const int AcknowledgedMoveTickFieldNumber = 7;
private long acknowledgedMoveTick_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public long AcknowledgedMoveTick {
get { return acknowledgedMoveTick_; }
set {
acknowledgedMoveTick_ = value;
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public override bool Equals(object other) {
@@ -2781,6 +2794,7 @@ namespace Network.Defines {
if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Rotation, other.Rotation)) return false;
if (Tick != other.Tick) return false;
if (Hp != other.Hp) return false;
if (AcknowledgedMoveTick != other.AcknowledgedMoveTick) return false;
return Equals(_unknownFields, other._unknownFields);
}
@@ -2794,6 +2808,7 @@ namespace Network.Defines {
if (Rotation != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Rotation);
if (Tick != 0L) hash ^= Tick.GetHashCode();
if (Hp != 0) hash ^= Hp.GetHashCode();
if (AcknowledgedMoveTick != 0L) hash ^= AcknowledgedMoveTick.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
@@ -2836,6 +2851,10 @@ namespace Network.Defines {
output.WriteRawTag(48);
output.WriteInt32(Hp);
}
if (AcknowledgedMoveTick != 0L) {
output.WriteRawTag(56);
output.WriteInt64(AcknowledgedMoveTick);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
@@ -2870,6 +2889,10 @@ namespace Network.Defines {
output.WriteRawTag(48);
output.WriteInt32(Hp);
}
if (AcknowledgedMoveTick != 0L) {
output.WriteRawTag(56);
output.WriteInt64(AcknowledgedMoveTick);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(ref output);
}
@@ -2898,6 +2921,9 @@ namespace Network.Defines {
if (Hp != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Hp);
}
if (AcknowledgedMoveTick != 0L) {
size += 1 + pb::CodedOutputStream.ComputeInt64Size(AcknowledgedMoveTick);
}
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
@@ -2934,6 +2960,9 @@ namespace Network.Defines {
if (other.Hp != 0) {
Hp = other.Hp;
}
if (other.AcknowledgedMoveTick != 0L) {
AcknowledgedMoveTick = other.AcknowledgedMoveTick;
}
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -2983,6 +3012,10 @@ namespace Network.Defines {
Hp = input.ReadInt32();
break;
}
case 56: {
AcknowledgedMoveTick = input.ReadInt64();
break;
}
}
}
#endif
@@ -3032,6 +3065,10 @@ namespace Network.Defines {
Hp = input.ReadInt32();
break;
}
case 56: {
AcknowledgedMoveTick = input.ReadInt64();
break;
}
}
}
}
@@ -75,6 +75,7 @@ message PlayerState {
float rotation = 4;
int64 tick = 5;
int32 hp = 6;
int64 acknowledged_move_tick = 7;
}
message Heartbeat {
@@ -0,0 +1,28 @@
using System;
using Network.Defines;
namespace Network.NetworkApplication
{
public readonly struct ClientMovementBootstrap
{
public ClientMovementBootstrap(int authoritativeMoveSpeed, long serverTick)
{
AuthoritativeMoveSpeed = authoritativeMoveSpeed;
ServerTick = serverTick;
}
public int AuthoritativeMoveSpeed { get; }
public long ServerTick { get; }
public static ClientMovementBootstrap FromLoginResponse(LoginResponse response)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
return new ClientMovementBootstrap(response.Speed, response.ServerTick);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f82d57bd033abb439004c7ad1b201b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -24,6 +24,8 @@ namespace Network.NetworkApplication
public long? LastAuthoritativeTick { get; private set; }
public long? LastAcknowledgedMoveTick { get; private set; }
public IReadOnlyList<PredictedMoveStep> PendingInputs => pendingInputs;
public void Record(MoveInput input)
@@ -66,9 +68,10 @@ namespace Network.NetworkApplication
}
LastAuthoritativeTick = state.Tick;
pendingInputs.RemoveAll(input => input.Input.Tick <= state.Tick);
LastAcknowledgedMoveTick = state.AcknowledgedMoveTick;
pendingInputs.RemoveAll(input => input.Input.Tick <= state.AcknowledgedMoveTick);
replayInputs = pendingInputs.ToArray();
return true;
}
}
}
}
@@ -8,6 +8,8 @@ namespace Network.NetworkHost
public float TurnSpeedDegreesPerSecond { get; set; } = 180f;
public TimeSpan SimulationInterval { get; set; } = TimeSpan.FromMilliseconds(50);
public TimeSpan BroadcastInterval { get; set; } = TimeSpan.FromMilliseconds(50);
public int DefaultHp { get; set; } = 100;
@@ -24,6 +26,11 @@ namespace Network.NetworkHost
throw new ArgumentOutOfRangeException(nameof(TurnSpeedDegreesPerSecond), "Turn speed must be finite and non-negative.");
}
if (SimulationInterval <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(SimulationInterval), "Simulation interval must be positive.");
}
if (BroadcastInterval <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(BroadcastInterval), "Broadcast interval must be positive.");
@@ -17,6 +17,7 @@ namespace Network.NetworkHost
private readonly IAuthoritativeMovementWorldValidator worldValidator;
private readonly Dictionary<string, ServerAuthoritativeMovementState> statesByPeer = new();
private long nextBroadcastTick = 1;
private TimeSpan accumulatedSimulationTime;
private TimeSpan accumulatedBroadcastTime;
public ServerAuthoritativeMovementCoordinator(
@@ -31,6 +32,8 @@ namespace Network.NetworkHost
this.worldValidator = worldValidator ?? throw new ArgumentNullException(nameof(worldValidator));
}
public TimeSpan SimulationInterval => configuration.SimulationInterval;
public IReadOnlyList<ServerAuthoritativeMovementState> States
{
get
@@ -153,25 +156,30 @@ namespace Network.NetworkHost
lock (gate)
{
foreach (var state in statesByPeer.Values)
accumulatedSimulationTime += elapsed;
while (accumulatedSimulationTime >= configuration.SimulationInterval)
{
IntegrateState(state, elapsed);
}
accumulatedBroadcastTime += elapsed;
while (accumulatedBroadcastTime >= configuration.BroadcastInterval)
{
accumulatedBroadcastTime -= configuration.BroadcastInterval;
pendingBroadcasts ??= new List<PendingBroadcast>();
accumulatedSimulationTime -= configuration.SimulationInterval;
foreach (var state in statesByPeer.Values)
{
state.LastBroadcastTick = nextBroadcastTick;
pendingBroadcasts.Add(new PendingBroadcast(
state.RemoteEndPoint,
BuildPlayerState(state, nextBroadcastTick)));
IntegrateState(state, configuration.SimulationInterval);
}
nextBroadcastTick++;
accumulatedBroadcastTime += configuration.SimulationInterval;
while (accumulatedBroadcastTime >= configuration.BroadcastInterval)
{
accumulatedBroadcastTime -= configuration.BroadcastInterval;
pendingBroadcasts ??= new List<PendingBroadcast>();
foreach (var state in statesByPeer.Values)
{
state.LastBroadcastTick = nextBroadcastTick;
pendingBroadcasts.Add(new PendingBroadcast(
state.RemoteEndPoint,
BuildPlayerState(state, nextBroadcastTick)));
}
nextBroadcastTick++;
}
}
}
@@ -299,6 +307,7 @@ namespace Network.NetworkHost
lock (gate)
{
statesByPeer.Clear();
accumulatedSimulationTime = TimeSpan.Zero;
accumulatedBroadcastTime = TimeSpan.Zero;
}
}
@@ -460,7 +469,8 @@ namespace Network.NetworkHost
Z = state.VelocityZ
},
Rotation = state.Rotation,
Hp = state.Hp
Hp = state.Hp,
AcknowledgedMoveTick = state.LastAcceptedMoveTick
};
}
@@ -71,6 +71,8 @@ namespace Network.NetworkHost
public MultiSessionManager SessionCoordinator { get; }
public TimeSpan AuthoritativeMovementCadence => authoritativeMovementCoordinator.SimulationInterval;
public IReadOnlyList<ManagedNetworkSession> ManagedSessions => SessionCoordinator.Sessions;
public IReadOnlyList<ServerAuthoritativeMovementState> AuthoritativeMovementStates => authoritativeMovementCoordinator.States;
@@ -21,6 +21,8 @@ namespace Network.NetworkHost
public bool IsRunning { get; private set; }
public TimeSpan AuthoritativeMovementCadence => host.AuthoritativeMovementCadence;
public IReadOnlyList<ManagedNetworkSession> ManagedSessions => host.ManagedSessions;
public IReadOnlyList<ServerAuthoritativeMovementState> AuthoritativeMovementStates => host.AuthoritativeMovementStates;