Skip to content

Commit

Permalink
Fix time incrementing on client-side while it's paused on server-side…
Browse files Browse the repository at this point in the history
… (for first player joining during queue)
  • Loading branch information
tornac1234 committed Jan 7, 2024
1 parent b8eb8c4 commit f15d0d6
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
42 changes: 38 additions & 4 deletions NitroxClient/GameLogic/TimeManager.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
using System;
using NitroxClient.MonoBehaviours;
using NitroxModel.Packets;
using UnityEngine;

namespace NitroxClient.GameLogic;

public class TimeManager
{
/// <summary>
/// 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.
/// </summary>
private bool freezeTime = true;

/// <summary>
/// Latest moment at which we updated the time
/// </summary>
Expand All @@ -26,16 +33,31 @@ public class TimeManager

public float AuroraRealExplosionTime { get; set; }

private const double DEFAULT_REAL_TIME = 0;

/// <summary>
/// Calculates the exact real time elapsed from an offset (<see cref="realTimeElapsedRegistrationTime"/>) and the delta time between
/// <see cref="DateTimeOffset.UtcNow"/> and the offset's exact <see cref="DateTimeOffset"/> (<see cref="latestRegistrationTime"/>).
/// </summary>
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;

/// <summary>
/// Calculates the current exact time from an offset (<see cref="latestRegisteredTime"/>) and the delta time between
Expand All @@ -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;
}
Expand All @@ -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;

Expand All @@ -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;
}
}
4 changes: 1 addition & 3 deletions NitroxModel/DataStructures/GameLogic/TimeData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
5 changes: 5 additions & 0 deletions NitroxModel/Packets/InitialPlayerSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<NitroxId, int> BuildOperationIds { get; }

public InitialPlayerSync(NitroxId playerGameObjectId,
Expand All @@ -53,6 +54,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId,
Perms perms,
SubnauticaPlayerPreferences preferences,
TimeData timeData,
bool isFirstPlayer,
Dictionary<NitroxId, int> buildOperationIds)
{
AssignedEscapePodId = assignedEscapePodId;
Expand All @@ -74,6 +76,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId,
Permissions = perms;
Preferences = preferences;
TimeData = timeData;
IsFirstPlayer = isFirstPlayer;
BuildOperationIds = buildOperationIds;
}

Expand All @@ -98,6 +101,7 @@ public InitialPlayerSync(
Perms permissions,
SubnauticaPlayerPreferences preferences,
TimeData timeData,
bool isFirstPlayer,
Dictionary<NitroxId, int> buildOperationIds)
{
AssignedEscapePodId = assignedEscapePodId;
Expand All @@ -119,6 +123,7 @@ public InitialPlayerSync(
Permissions = permissions;
Preferences = preferences;
TimeData = timeData;
IsFirstPlayer = isFirstPlayer;
BuildOperationIds = buildOperationIds;
}
}
Expand Down
7 changes: 6 additions & 1 deletion NitroxModel/Packets/TimeChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ public class TimeChange : Packet
/// Real time at which the CurrentTime was observed
/// </summary>
public long UpdateTime { get; }
/// <summary>
/// Real time elapsed in seconds
/// </summary>
public double RealTimeElapsed;

public TimeChange(double currentTime, long updateTime)
public TimeChange(double currentTime, long updateTime, double realTimeElapsed)
{
CurrentTime = currentTime;
UpdateTime = updateTime;
RealTimeElapsed = realTimeElapsed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Entities>().EntitiesToSpawn.Count.ToString());
__instance.strBuffer.Append("Real time elapsed: ").AppendLine(Resolve<TimeManager>().RealTimeElapsed.ToString());
__instance.text.SetText(__instance.strBuffer);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public override void Process(PlayerJoiningMultiplayerSession packet, NitroxConne
RespawnExistingEntity(player);
}
List<GlobalRootEntity> globalRootEntities = world.WorldEntityManager.GetGlobalRootEntities(true);
bool isFirstPlayer = playerManager.GetConnectedPlayers().Count == 1;

InitialPlayerSync initialPlayerSync = new(player.GameObjectId,
wasBrandNewPlayer,
Expand All @@ -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)
);

Expand Down
2 changes: 1 addition & 1 deletion NitroxServer/GameLogic/StoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion NitroxServer/GameLogic/TimeKeeper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit f15d0d6

Please sign in to comment.