Files
Nitrox/NitroxServer/GameLogic/ScheduleKeeper.cs
2025-07-06 00:23:46 +02:00

100 lines
4.0 KiB
C#

using System.Collections.Generic;
using System.Linq;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.Packets;
using NitroxServer.GameLogic.Unlockables;
namespace NitroxServer.GameLogic
{
public class ScheduleKeeper
{
private readonly ThreadSafeDictionary<string, NitroxScheduledGoal> scheduledGoals = new();
private readonly PDAStateData pdaStateData;
private readonly StoryGoalData storyGoalData;
private readonly TimeKeeper timeKeeper;
private readonly PlayerManager playerManager;
private float ElapsedSecondsFloat => (float)timeKeeper.ElapsedSeconds;
public ScheduleKeeper(PDAStateData pdaStateData, StoryGoalData storyGoalData, TimeKeeper timeKeeper, PlayerManager playerManager)
{
this.pdaStateData = pdaStateData;
this.storyGoalData = storyGoalData;
this.timeKeeper = timeKeeper;
this.playerManager = playerManager;
// We still want to get a "replicated" list in memory
for (int i = storyGoalData.ScheduledGoals.Count - 1; i >= 0; i--)
{
NitroxScheduledGoal scheduledGoal = storyGoalData.ScheduledGoals[i];
// In the unlikely case that there's a duplicated entry
if (scheduledGoals.TryGetValue(scheduledGoal.GoalKey, out NitroxScheduledGoal alreadyInGoal))
{
// We remove the goal that's already in if it's planned for later than the first one
if (scheduledGoal.TimeExecute <= alreadyInGoal.TimeExecute)
{
UnScheduleGoal(alreadyInGoal.GoalKey);
}
continue;
}
scheduledGoals.Add(scheduledGoal.GoalKey, scheduledGoal);
}
}
public List<NitroxScheduledGoal> GetScheduledGoals()
{
return scheduledGoals.Values.ToList();
}
public bool ContainsScheduledGoal(string goalKey)
{
return scheduledGoals.ContainsKey(goalKey);
}
public void ScheduleGoal(NitroxScheduledGoal scheduledGoal)
{
// Only add if it's not in already
if (!scheduledGoals.ContainsKey(scheduledGoal.GoalKey))
{
// If it's not already in any PDA stuff (completed goals or PDALog)
if (!IsAlreadyRegistered(scheduledGoal.GoalKey))
{
if (scheduledGoal.TimeExecute > ElapsedSecondsFloat)
{
scheduledGoals.Add(scheduledGoal.GoalKey, scheduledGoal);
}
}
}
}
/// <param name="becauseOfTime">
/// When the server starts, it happens that there are still some goals that were supposed to happen
/// but didn't, so to make sure that they happen on at least one client, we postpone its execution
/// </param>
public void UnScheduleGoal(string goalKey, bool becauseOfTime = false)
{
if (!scheduledGoals.TryGetValue(goalKey, out NitroxScheduledGoal scheduledGoal))
{
return;
}
// The best solution, to ensure any bad simulation of client side, is to postpone the execution
// If the goal is already done, no need to check anything
if (becauseOfTime && !IsAlreadyRegistered(goalKey))
{
scheduledGoal.TimeExecute = ElapsedSecondsFloat + 15;
playerManager.SendPacketToAllPlayers(new Schedule(scheduledGoal.TimeExecute, goalKey, scheduledGoal.GoalType));
return;
}
scheduledGoals.Remove(goalKey);
}
public bool IsAlreadyRegistered(string goalKey)
{
return pdaStateData.PdaLog.Any(entry => entry.Key == goalKey)
|| storyGoalData.CompletedGoals.Contains(goalKey);
}
}
}