From f15d0d6c00520e852ce0b646ee8205d00436424e Mon Sep 17 00:00:00 2001
From: tornac1234 <24827220+tornac1234@users.noreply.github.com>
Date: Wed, 27 Dec 2023 11:03:46 +0100
Subject: [PATCH] Fix time incrementing on client-side while it's paused on
server-side (for first player joining during queue)
---
.../StoryGoalInitialSyncProcessor.cs | 2 +-
NitroxClient/GameLogic/TimeManager.cs | 42 +++++++++++++++++--
.../DataStructures/GameLogic/TimeData.cs | 4 +-
NitroxModel/Packets/InitialPlayerSync.cs | 5 +++
NitroxModel/Packets/TimeChange.cs | 7 +++-
.../Dynamic/FPSCounter_UpdateDisplay_Patch.cs | 3 +-
...layerJoiningMultiplayerSessionProcessor.cs | 2 +
NitroxServer/GameLogic/StoryManager.cs | 2 +-
NitroxServer/GameLogic/TimeKeeper.cs | 2 +-
9 files changed, 57 insertions(+), 12 deletions(-)
diff --git a/NitroxClient/GameLogic/InitialSync/StoryGoalInitialSyncProcessor.cs b/NitroxClient/GameLogic/InitialSync/StoryGoalInitialSyncProcessor.cs
index 3e2e349193..a1c6d71585 100644
--- a/NitroxClient/GameLogic/InitialSync/StoryGoalInitialSyncProcessor.cs
+++ b/NitroxClient/GameLogic/InitialSync/StoryGoalInitialSyncProcessor.cs
@@ -163,7 +163,7 @@ private static IEnumerator RefreshStoryWithLatestData()
private void SetTimeData(InitialPlayerSync packet)
{
timeManager.ProcessUpdate(packet.TimeData.TimePacket);
- timeManager.InitRealTimeElapsed(packet.TimeData.RealTimeElapsed, packet.TimeData.TimePacket.UpdateTime);
+ timeManager.InitRealTimeElapsed(packet.TimeData.TimePacket.RealTimeElapsed, packet.TimeData.TimePacket.UpdateTime, packet.IsFirstPlayer);
timeManager.AuroraRealExplosionTime = packet.TimeData.AuroraEventData.AuroraRealExplosionTime;
}
}
diff --git a/NitroxClient/GameLogic/TimeManager.cs b/NitroxClient/GameLogic/TimeManager.cs
index f080b095d5..1ca0eb334e 100644
--- a/NitroxClient/GameLogic/TimeManager.cs
+++ b/NitroxClient/GameLogic/TimeManager.cs
@@ -1,4 +1,5 @@
using System;
+using NitroxClient.MonoBehaviours;
using NitroxModel.Packets;
using UnityEngine;
@@ -6,6 +7,12 @@ namespace NitroxClient.GameLogic;
public class TimeManager
{
+ ///
+ /// When first player connects to the server, time will resume when time will be resumed on server-side.
+ /// According to this, we need to freeze time on first player connecting before it has fully loaded.
+ ///
+ private bool freezeTime = true;
+
///
/// Latest moment at which we updated the time
///
@@ -26,16 +33,31 @@ public class TimeManager
public float AuroraRealExplosionTime { get; set; }
+ private const double DEFAULT_REAL_TIME = 0;
+
///
/// Calculates the exact real time elapsed from an offset () and the delta time between
/// and the offset's exact ().
///
public double RealTimeElapsed
{
- get => (DateTimeOffset.UtcNow - realTimeElapsedRegistrationTime).TotalMilliseconds * 0.001 + realTimeElapsed;
+ get
+ {
+ // Unitialized state
+ if (realTimeElapsedRegistrationTime == default)
+ {
+ return DEFAULT_REAL_TIME;
+ }
+ if (freezeTime)
+ {
+ return realTimeElapsed;
+ }
+
+ return (DateTimeOffset.UtcNow - realTimeElapsedRegistrationTime).TotalMilliseconds * 0.001 + realTimeElapsed;
+ }
}
- private const double DEFAULT_TIME = 480;
+ private const double DEFAULT_SUBNAUTICA_TIME = 480;
///
/// Calculates the current exact time from an offset () and the delta time between
@@ -52,7 +74,11 @@ public double CurrentTime
// Unitialized state
if (latestRegisteredTime == 0)
{
- return DEFAULT_TIME;
+ return DEFAULT_SUBNAUTICA_TIME;
+ }
+ if (freezeTime)
+ {
+ return latestRegisteredTime;
}
return (DateTimeOffset.UtcNow - latestRegistrationTime).TotalMilliseconds * 0.001 + latestRegisteredTime;
}
@@ -76,6 +102,13 @@ public double CurrentTime
public void ProcessUpdate(TimeChange packet)
{
+ if (freezeTime && Multiplayer.Main && Multiplayer.Main.InitialSyncCompleted)
+ {
+ freezeTime = false;
+ }
+ realTimeElapsedRegistrationTime = DateTimeOffset.FromUnixTimeMilliseconds(packet.UpdateTime);
+ realTimeElapsed = packet.RealTimeElapsed;
+
latestRegistrationTime = DateTimeOffset.FromUnixTimeMilliseconds(packet.UpdateTime);
latestRegisteredTime = packet.CurrentTime;
@@ -97,9 +130,10 @@ public double CalculateCurrentTime()
return currentTime;
}
- public void InitRealTimeElapsed(double realTimeElapsed, long registrationTime)
+ public void InitRealTimeElapsed(double realTimeElapsed, long registrationTime, bool isFirstPlayer)
{
this.realTimeElapsed = realTimeElapsed;
realTimeElapsedRegistrationTime = DateTimeOffset.FromUnixTimeMilliseconds(registrationTime);
+ freezeTime = isFirstPlayer;
}
}
diff --git a/NitroxModel/DataStructures/GameLogic/TimeData.cs b/NitroxModel/DataStructures/GameLogic/TimeData.cs
index 85fe5ab454..824c118843 100644
--- a/NitroxModel/DataStructures/GameLogic/TimeData.cs
+++ b/NitroxModel/DataStructures/GameLogic/TimeData.cs
@@ -8,12 +8,10 @@ public class TimeData
{
public TimeChange TimePacket;
public AuroraEventData AuroraEventData;
- public double RealTimeElapsed;
- public TimeData(TimeChange timePacket, AuroraEventData auroraEventData, double realTimeElapsed)
+ public TimeData(TimeChange timePacket, AuroraEventData auroraEventData)
{
TimePacket = timePacket;
AuroraEventData = auroraEventData;
- RealTimeElapsed = realTimeElapsed;
}
}
diff --git a/NitroxModel/Packets/InitialPlayerSync.cs b/NitroxModel/Packets/InitialPlayerSync.cs
index 6a7d6fc739..030b6cc4f9 100644
--- a/NitroxModel/Packets/InitialPlayerSync.cs
+++ b/NitroxModel/Packets/InitialPlayerSync.cs
@@ -32,6 +32,7 @@ public class InitialPlayerSync : Packet
public Perms Permissions { get; }
public SubnauticaPlayerPreferences Preferences { get; }
public TimeData TimeData { get; }
+ public bool IsFirstPlayer { get; }
public Dictionary BuildOperationIds { get; }
public InitialPlayerSync(NitroxId playerGameObjectId,
@@ -53,6 +54,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId,
Perms perms,
SubnauticaPlayerPreferences preferences,
TimeData timeData,
+ bool isFirstPlayer,
Dictionary buildOperationIds)
{
AssignedEscapePodId = assignedEscapePodId;
@@ -74,6 +76,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId,
Permissions = perms;
Preferences = preferences;
TimeData = timeData;
+ IsFirstPlayer = isFirstPlayer;
BuildOperationIds = buildOperationIds;
}
@@ -98,6 +101,7 @@ public InitialPlayerSync(
Perms permissions,
SubnauticaPlayerPreferences preferences,
TimeData timeData,
+ bool isFirstPlayer,
Dictionary buildOperationIds)
{
AssignedEscapePodId = assignedEscapePodId;
@@ -119,6 +123,7 @@ public InitialPlayerSync(
Permissions = permissions;
Preferences = preferences;
TimeData = timeData;
+ IsFirstPlayer = isFirstPlayer;
BuildOperationIds = buildOperationIds;
}
}
diff --git a/NitroxModel/Packets/TimeChange.cs b/NitroxModel/Packets/TimeChange.cs
index b8ff7a1d8f..89904388f6 100644
--- a/NitroxModel/Packets/TimeChange.cs
+++ b/NitroxModel/Packets/TimeChange.cs
@@ -13,10 +13,15 @@ public class TimeChange : Packet
/// Real time at which the CurrentTime was observed
///
public long UpdateTime { get; }
+ ///
+ /// Real time elapsed in seconds
+ ///
+ public double RealTimeElapsed;
- public TimeChange(double currentTime, long updateTime)
+ public TimeChange(double currentTime, long updateTime, double realTimeElapsed)
{
CurrentTime = currentTime;
UpdateTime = updateTime;
+ RealTimeElapsed = realTimeElapsed;
}
}
diff --git a/NitroxPatcher/Patches/Dynamic/FPSCounter_UpdateDisplay_Patch.cs b/NitroxPatcher/Patches/Dynamic/FPSCounter_UpdateDisplay_Patch.cs
index ce27fa6a5f..33b51f6e15 100644
--- a/NitroxPatcher/Patches/Dynamic/FPSCounter_UpdateDisplay_Patch.cs
+++ b/NitroxPatcher/Patches/Dynamic/FPSCounter_UpdateDisplay_Patch.cs
@@ -12,11 +12,12 @@ public sealed partial class FPSCounter_UpdateDisplay_Patch : NitroxPatch, IDynam
public static void Postfix(FPSCounter __instance)
{
- if (!Multiplayer.Main.InitialSyncCompleted)
+ if (!Multiplayer.Active)
{
return;
}
__instance.strBuffer.Append("Loading entities: ").AppendLine(Resolve().EntitiesToSpawn.Count.ToString());
+ __instance.strBuffer.Append("Real time elapsed: ").AppendLine(Resolve().RealTimeElapsed.ToString());
__instance.text.SetText(__instance.strBuffer);
}
}
diff --git a/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs b/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
index 2257ea095e..c1f015c759 100644
--- a/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
+++ b/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
@@ -70,6 +70,7 @@ public override void Process(PlayerJoiningMultiplayerSession packet, NitroxConne
RespawnExistingEntity(player);
}
List globalRootEntities = world.WorldEntityManager.GetGlobalRootEntities(true);
+ bool isFirstPlayer = playerManager.GetConnectedPlayers().Count == 1;
InitialPlayerSync initialPlayerSync = new(player.GameObjectId,
wasBrandNewPlayer,
@@ -90,6 +91,7 @@ public override void Process(PlayerJoiningMultiplayerSession packet, NitroxConne
player.Permissions,
new(new(player.PingInstancePreferences), player.PinnedRecipePreferences.ToList()),
storyManager.GetTimeData(),
+ isFirstPlayer,
BuildingManager.GetEntitiesOperations(globalRootEntities)
);
diff --git a/NitroxServer/GameLogic/StoryManager.cs b/NitroxServer/GameLogic/StoryManager.cs
index 3cb9beb9d6..6705341b4e 100644
--- a/NitroxServer/GameLogic/StoryManager.cs
+++ b/NitroxServer/GameLogic/StoryManager.cs
@@ -192,7 +192,7 @@ public AuroraEventData MakeAuroraData()
public TimeData GetTimeData()
{
- return new(timeKeeper.MakeTimePacket(), MakeAuroraData(), timeKeeper.RealTimeElapsed);
+ return new(timeKeeper.MakeTimePacket(), MakeAuroraData());
}
public enum TimeModification
diff --git a/NitroxServer/GameLogic/TimeKeeper.cs b/NitroxServer/GameLogic/TimeKeeper.cs
index 20593545d2..309b2ef3c3 100644
--- a/NitroxServer/GameLogic/TimeKeeper.cs
+++ b/NitroxServer/GameLogic/TimeKeeper.cs
@@ -142,7 +142,7 @@ public void ChangeTime(TimeModification type)
public TimeChange MakeTimePacket()
{
- return new(ElapsedSeconds, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
+ return new(ElapsedSeconds, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), RealTimeElapsed);
}
public delegate void TimeSkipped(double skipAmount);