Skip to content

Commit 6ffb360

Browse files
committed
Reworked CustomGamemode management
1 parent 0fb912f commit 6ffb360

2 files changed

Lines changed: 198 additions & 144 deletions

File tree

LabExtended/API/Custom/Gamemodes/CustomGamemode.cs

Lines changed: 115 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
using LabExtended.Events.Round;
66

77
using LabExtended.Core;
8+
using LabExtended.Extensions;
89
using LabExtended.Utilities.Update;
910

1011
using System.Diagnostics;
1112
using System.ComponentModel;
1213

1314
using YamlDotNet.Serialization;
15+
using System.Text;
1416

1517
namespace LabExtended.API.Custom.Gamemodes
1618
{
@@ -22,132 +24,143 @@ public abstract class CustomGamemode : CustomObject<CustomGamemode>
2224
private static PlayerUpdateComponent updateComponent = PlayerUpdateComponent.Create();
2325

2426
/// <summary>
25-
/// Gets a queue of gamemodes to be activated next.
27+
/// Gets the curently enabled gamemode.
2628
/// </summary>
27-
public static Queue<CustomGamemode> Queue { get; } = new();
29+
public static List<CustomGamemode> Active { get; } = new();
30+
31+
private Stopwatch runTimeWatch = new();
2832

2933
/// <summary>
30-
/// Gets the curently enabled gamemode.
34+
/// Whether or not the gamemode is currently active.
35+
/// </summary>
36+
[YamlIgnore]
37+
public bool IsActive => runTimeWatch.IsRunning;
38+
39+
/// <summary>
40+
/// Gets or sets a value indicating whether the game mode can be activated after a round has already started.
3141
/// </summary>
32-
public static CustomGamemode? Current { get; private set; }
42+
[Description("Whether or not the gamemode can be started in the middle of the round.")]
43+
public virtual bool CanActivateMidRound { get; set; } = true;
3344

3445
/// <summary>
35-
/// Enables the specified gamemode as the current active gamemode.
46+
/// Gets or sets a value indicating whether the gamemode is automatically disabled when the round ends.
3647
/// </summary>
37-
/// <remarks>
38-
/// If the specified gamemode is already active or cannot be activated mid-round, the method returns without enabling the gamemode.
39-
/// If an override is not allowed and a gamemode is already active, the method does not enable the new gamemode.
40-
/// </remarks>
41-
/// <param name="gamemode">The gamemode instance to enable.</param>
42-
/// <param name="overrideCurrent">Indicates whether to allow overriding the currently active gamemode if one is active.</param>
43-
/// <returns>
44-
/// true if the gamemode is successfully enabled; otherwise, false.
45-
/// </returns>
46-
/// <exception cref="ArgumentNullException">Thrown when the provided gamemode instance is null.</exception>
47-
public static bool Enable(CustomGamemode gamemode, bool overrideCurrent = false)
48-
{
49-
if (gamemode is null)
50-
throw new ArgumentNullException(nameof(gamemode));
51-
52-
if (Current != null && Current == gamemode)
48+
[Description("Whether or not the gamemode should be automatically disabled when the round ends.")]
49+
public virtual bool ShouldDisableOnRoundEnd { get; set; } = true;
50+
51+
/// <summary>
52+
/// Gets or sets a value indicating whether the gamemode prevents the default wave spawns from occurring.
53+
/// </summary>
54+
[Description("Whether or not the gamemode prevents the default wave spawns from occurring.")]
55+
public virtual bool PreventWaveSpawns { get; set; }
56+
57+
/// <summary>
58+
/// Gets or sets the list of gamemode identifiers that are incompatible with this gamemode.
59+
/// </summary>
60+
[Description("A list of gamemode IDs that are incompatible with this gamemode.")]
61+
public virtual string[] IncompatibleGamemodes { get; set; } = [];
62+
63+
/// <summary>
64+
/// Gets the amount of time that has elapsed since the gamemode has started.
65+
/// </summary>
66+
[YamlIgnore]
67+
public TimeSpan RunTime => runTimeWatch.Elapsed;
68+
69+
/// <summary>
70+
/// Gets the date and time at which the current run started.
71+
/// </summary>
72+
[YamlIgnore]
73+
public DateTime StartTime => DateTime.Now - runTimeWatch.Elapsed;
74+
75+
/// <summary>
76+
/// Enables the current instance if it is not already active and can be enabled.
77+
/// </summary>
78+
/// <returns>true if the instance was successfully enabled; otherwise, false.</returns>
79+
public bool Enable()
80+
{
81+
if (IsActive)
5382
return false;
5483

55-
if (!gamemode.CanActivateMidRound && !ExRound.IsWaitingForPlayers)
56-
{
57-
var list = Queue.ToList();
58-
59-
list.Insert(0, gamemode);
60-
61-
Queue.Clear();
62-
63-
list.ForEach(Queue.Enqueue);
64-
list.Clear();
65-
84+
if (!CanBeEnabled(Active))
6685
return false;
67-
}
6886

69-
if (Current != null)
87+
if (!Active.AddUnique(this))
7088
{
71-
if (!overrideCurrent)
72-
return false;
73-
74-
if (!Disable())
75-
return false;
89+
runTimeWatch.Restart();
90+
return false;
7691
}
7792

78-
Current = gamemode;
79-
Current.OnEnabled();
93+
runTimeWatch.Restart();
8094

81-
Current.IsActive = true;
95+
ApiLog.Info("Custom Gamemodes", $"Enabled custom gamemode &3{Id}&r!");
96+
97+
OnEnabled();
8298
return true;
8399
}
84100

85101
/// <summary>
86-
/// Disables the current gamemode if one is active.
102+
/// Disables the current instance if it is active.
87103
/// </summary>
88-
/// <remarks>If no current gamemode is active, the method performs no action and returns false.
89-
/// After disabling, the current gamemode is set to null and will no longer be considered active.</remarks>
90-
/// <returns>true if an active gamemode was disabled; otherwise, false.</returns>
91-
public static bool Disable()
104+
/// <returns>true if the instance was active and is now disabled; otherwise, false.</returns>
105+
public bool Disable()
92106
{
93-
if (Current == null)
107+
if (!IsActive)
94108
return false;
95109

96-
var current = Current;
110+
Active.Remove(this);
97111

98-
Current = null;
112+
runTimeWatch.Stop();
113+
runTimeWatch.Reset();
99114

100-
current.IsActive = false;
101-
current.OnDisabled();
115+
ApiLog.Info("Custom Gamemodes", $"Disabled custom gamemode &3{Id}&r!");
102116

117+
OnDisabled();
103118
return true;
104119
}
105120

106-
private Stopwatch runTimeWatch = new();
107-
108121
/// <summary>
109-
/// Whether or not the gamemode is currently active.
122+
/// Determines whether or not the gamemode can be enabled given the other currently active gamemodes.
110123
/// </summary>
111-
[YamlIgnore]
112-
public bool IsActive { get; private set; }
124+
/// <param name="otherModes">List of other active modes.</param>
125+
/// <returns><c>true</c> if the gamemode can be enabled; otherwise, <c>false</c>.</returns>
126+
public virtual bool CanBeEnabled(List<CustomGamemode> otherModes)
127+
{
128+
if (IsActive)
129+
return false;
113130

114-
/// <summary>
115-
/// Gets or sets a value indicating whether the game mode can be activated after a round has already started.
116-
/// </summary>
117-
[Description("Whether or not the gamemode can be started in the middle of the round.")]
118-
public virtual bool CanActivateMidRound { get; set; } = true;
131+
if (!CanActivateMidRound && !ExRound.IsWaitingForPlayers)
132+
return false;
119133

120-
/// <summary>
121-
/// Gets or sets a value indicating whether the gamemode prevents the default wave spawns from occurring.
122-
/// </summary>
123-
[Description("Whether or not the gamemode prevents the default wave spawns from occurring.")]
124-
public virtual bool PreventWaveSpawns { get; set; } = true;
134+
if (IncompatibleGamemodes?.Length > 0 && otherModes.Any(x => IncompatibleGamemodes.Contains(x.Id)))
135+
return false;
125136

126-
/// <summary>
127-
/// Gets the amount of time that has elapsed since the gamemode has started.
128-
/// </summary>
129-
[YamlIgnore]
130-
public TimeSpan RunTime => runTimeWatch.Elapsed;
137+
return true;
138+
}
131139

132140
/// <summary>
133141
/// Gets called when the gamemode is enabled.
134142
/// </summary>
135143
public virtual void OnEnabled()
136144
{
137-
runTimeWatch.Restart();
138-
139-
ApiLog.Info("Custom Gamemodes", $"Enabled custom gamemode &3{Id}&r!");
145+
140146
}
141147

142148
/// <summary>
143149
/// Gets called when the gamemode is disabled.
144150
/// </summary>
145151
public virtual void OnDisabled()
146152
{
147-
runTimeWatch.Stop();
148-
runTimeWatch.Reset();
149-
150-
ApiLog.Info("Custom Gamemodes", $"Disabled custom gamemode &3{Id}&r!");
153+
154+
}
155+
156+
/// <summary>
157+
/// Appends a textual representation of the current gamemode's state to the specified <see cref="StringBuilder"/>
158+
/// instance.
159+
/// </summary>
160+
/// <param name="builder">The <see cref="StringBuilder"/> instance to which the gamemode's state will be appended. Cannot be null.</param>
161+
public virtual void PrintState(StringBuilder builder)
162+
{
163+
151164
}
152165

153166
/// <inheritdoc/>
@@ -163,7 +176,7 @@ public override void OnUnregistered()
163176
{
164177
base.OnUnregistered();
165178

166-
if (Current != null && Current == this)
179+
if (IsActive)
167180
Disable();
168181

169182
ApiLog.Info("Custom Gamemodes", $"&1Unregistered&r custom gamemode &3{Id}&r");
@@ -210,7 +223,10 @@ public virtual void OnRoundEnded(RoundEndedEventArgs args)
210223
/// </summary>
211224
public virtual void OnRoundRestarting()
212225
{
213-
226+
if (ShouldDisableOnRoundEnd && IsActive)
227+
{
228+
Disable();
229+
}
214230
}
215231

216232
/// <summary>
@@ -322,99 +338,87 @@ public virtual void OnWaveSpawned(WaveRespawnedEventArgs args)
322338

323339
private static void _WaitingForPlayers()
324340
{
325-
while (Current == null && Queue.TryDequeue(out var gamemode))
326-
{
327-
if (!Enable(gamemode, true))
328-
{
329-
ApiLog.Warn("Custom Gamemodes", $"Queued gamemode &1{gamemode.Id}&r could not be enabled!");
330-
}
331-
else
332-
{
333-
break;
334-
}
335-
}
336-
337-
Current?.OnWaitingForPlayers();
341+
Active.ForEach(x => x.OnWaitingForPlayers());
338342
}
339343

340344
private static void _Update()
341345
{
342-
Current?.OnUpdate();
346+
Active.ForEach(x => x.OnUpdate());
343347
}
344348

345349
private static void _RoundCheckingEndConditions(RoundEndingConditionsCheckEventArgs args)
346350
{
347-
Current?.OnRoundCheckingEndConditions(args);
351+
Active.ForEach(x => x.OnRoundCheckingEndConditions(args));
348352
}
349353

350354
private static void _RoundEnding(RoundEndingEventArgs args)
351355
{
352-
Current?.OnRoundEnding(args);
356+
Active.ForEach(x => x.OnRoundEnding(args));
353357
}
354358

355359
private static void _RoundEnded(RoundEndedEventArgs args)
356360
{
357-
Current?.OnRoundEnded(args);
361+
Active.ForEach(x => x.OnRoundEnded(args));
358362
}
359363

360364
private static void _RoundRestarting()
361365
{
362-
Current?.OnRoundRestarting();
366+
Active.ForEach(x => x.OnRoundRestarting());
363367
}
364368

365369
private static void _RoundStarted()
366370
{
367-
Current?.OnRoundStarted();
371+
Active.ForEach(x => x.OnRoundStarted());
368372
}
369373

370374
private static void _AssigningRoles(AssigningRolesEventArgs args)
371375
{
372-
Current?.OnAssigningRoles(args);
376+
Active.ForEach(x => x.OnAssigningRoles(args));
373377
}
374378

375379
private static void _AssignedRoles(AssignedRolesEventArgs args)
376380
{
377-
Current?.OnAssignedRoles(args);
381+
Active.ForEach(x => x.OnAssignedRoles(args));
378382
}
379383

380384
private static void _LateJoinSettingRole(LateJoinSettingRoleEventArgs args)
381385
{
382-
Current?.OnLateJoinSettingRole(args);
386+
Active.ForEach(x => x.OnLateJoinSettingRole(args));
383387
}
384388

385389
private static void _LateJoinSetRole(LateJoinSetRoleEventArgs args)
386390
{
387-
Current?.OnLateJoinSetRole(args);
391+
Active.ForEach(x => x.OnLateJoinSetRole(args));
388392
}
389393

390394
private static void _PlayerJoined(ExPlayer player)
391395
{
392-
Current?.OnPlayerJoined(player);
396+
Active.ForEach(x => x.OnPlayerJoined(player));
393397
}
394398

395399
private static void _PlayerLeft(ExPlayer player)
396400
{
397-
Current?.OnPlayerLeft(player);
401+
Active.ForEach(x => x.OnPlayerLeft(player));
398402
}
399403

400404
private static void _WaveSelecting(WaveTeamSelectingEventArgs args)
401405
{
402-
Current?.OnWaveSelecting(args);
406+
Active.ForEach(x => x.OnWaveSelecting(args));
403407
}
404408

405409
private static void _WaveSelected(WaveTeamSelectedEventArgs args)
406-
{
407-
Current?.OnWaveSelected(args);
410+
{
411+
Active.ForEach(x => x.OnWaveSelected(args));
408412
}
409413

410414
private static void _WaveSpawning(WaveRespawningEventArgs args)
411415
{
412-
Current?.OnWaveSpawning(args);
416+
Active.ForEach(x => x.OnWaveSpawning(args));
413417
}
414418

415419
private static void _WaveSpawned(WaveRespawnedEventArgs args)
416420
{
417-
Current?.OnWaveSpawned(args);
421+
Active.ForEach(x => x.OnWaveSpawned(args));
418422
}
419423

420424
internal static void _Init()

0 commit comments

Comments
 (0)