Separate game rule enabling and game rule starting (#6168)

This commit is contained in:
mirrorcult
2022-02-15 20:06:28 -07:00
committed by GitHub
parent e427381be6
commit 3abc7a443e
13 changed files with 125 additions and 52 deletions

View File

@@ -38,7 +38,7 @@ namespace Content.IntegrationTests.Tests.GameRules
{ {
Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
sGameTicker.AddGameRule(IoCManager.Resolve<IPrototypeManager>().Index<GameRulePrototype>(maxTimeMaxTimeRestartRuleSystem.Prototype)); sGameTicker.StartGameRule(IoCManager.Resolve<IPrototypeManager>().Index<GameRulePrototype>(maxTimeMaxTimeRestartRuleSystem.Prototype));
maxTimeMaxTimeRestartRuleSystem.RoundMaxTime = TimeSpan.FromSeconds(3); maxTimeMaxTimeRestartRuleSystem.RoundMaxTime = TimeSpan.FromSeconds(3);
sGameTicker.StartRound(); sGameTicker.StartRound();

View File

@@ -85,6 +85,14 @@ namespace Content.Server.GameTicking
return true; return true;
} }
private void StartGamePresetRules()
{
foreach (var rule in _addedGameRules)
{
StartGameRule(rule);
}
}
public bool OnGhostAttempt(Mind.Mind mind, bool canReturnGlobal) public bool OnGhostAttempt(Mind.Mind mind, bool canReturnGlobal)
{ {
var handleEv = new GhostAttemptHandleEvent(mind, canReturnGlobal); var handleEv = new GhostAttemptHandleEvent(mind, canReturnGlobal);

View File

@@ -8,35 +8,80 @@ namespace Content.Server.GameTicking
public partial class GameTicker public partial class GameTicker
{ {
// No duplicates. // No duplicates.
[ViewVariables] private readonly HashSet<GameRulePrototype> _gameRules = new(); [ViewVariables] private readonly HashSet<GameRulePrototype> _addedGameRules = new();
public IEnumerable<GameRulePrototype> ActiveGameRules => _gameRules; public IEnumerable<GameRulePrototype> AddedGameRules => _addedGameRules;
[ViewVariables] private readonly HashSet<GameRulePrototype> _startedGameRules = new();
public IEnumerable<GameRulePrototype> StartedGameRules => _startedGameRules;
/// <summary>
/// Game rules can be 'started' separately from being added. 'Starting' them usually
/// happens at round start while they can be added and removed before then.
/// </summary>
public void StartGameRule(GameRulePrototype rule)
{
if (!GameRuleAdded(rule))
AddGameRule(rule);
if (_startedGameRules.Add(rule))
RaiseLocalEvent(new GameRuleStartedEvent(rule));
}
/// <summary>
/// Ends a game rule.
/// This always includes removing it (removing it from added game rules) so that behavior
/// is not separate from this.
/// </summary>
/// <param name="rule"></param>
public void EndGameRule(GameRulePrototype rule)
{
if (!GameRuleAdded(rule))
return;
_addedGameRules.Remove(rule);
if (GameRuleStarted(rule))
_startedGameRules.Remove(rule);
RaiseLocalEvent(new GameRuleEndedEvent(rule));
}
/// <summary>
/// Adds a game rule to the list, but does not
/// start it yet, instead waiting until roundstart.
/// </summary>
public bool AddGameRule(GameRulePrototype rule) public bool AddGameRule(GameRulePrototype rule)
{ {
if (!_gameRules.Add(rule)) if (!_addedGameRules.Add(rule))
return false; return false;
RaiseLocalEvent(new GameRuleAddedEvent(rule)); RaiseLocalEvent(new GameRuleAddedEvent(rule));
return true; return true;
} }
public bool RemoveGameRule(GameRulePrototype rule) public bool GameRuleAdded(GameRulePrototype rule)
{ {
if (!_gameRules.Remove(rule)) return _addedGameRules.Contains(rule);
return false;
RaiseLocalEvent(new GameRuleRemovedEvent(rule));
return true;
} }
public bool HasGameRule(GameRulePrototype rule) public bool GameRuleAdded(string rule)
{ {
return _gameRules.Contains(rule); foreach (var ruleProto in _addedGameRules)
{
if (ruleProto.ID.Equals(rule))
return true;
}
return false;
} }
public bool HasGameRule(string rule) public bool GameRuleStarted(GameRulePrototype rule)
{ {
foreach (var ruleProto in _gameRules) return _startedGameRules.Contains(rule);
}
public bool GameRuleStarted(string rule)
{
foreach (var ruleProto in _startedGameRules)
{ {
if (ruleProto.ID.Equals(rule)) if (ruleProto.ID.Equals(rule))
return true; return true;
@@ -47,14 +92,17 @@ namespace Content.Server.GameTicking
public void ClearGameRules() public void ClearGameRules()
{ {
foreach (var rule in _gameRules.ToArray()) foreach (var rule in _addedGameRules.ToArray())
{ {
RemoveGameRule(rule); EndGameRule(rule);
} }
} }
} }
public class GameRuleAddedEvent /// <summary>
/// Raised broadcast when a game rule is selected, but not started yet.
/// </summary>
public sealed class GameRuleAddedEvent
{ {
public GameRulePrototype Rule { get; } public GameRulePrototype Rule { get; }
@@ -64,11 +112,21 @@ namespace Content.Server.GameTicking
} }
} }
public class GameRuleRemovedEvent public sealed class GameRuleStartedEvent
{ {
public GameRulePrototype Rule { get; } public GameRulePrototype Rule { get; }
public GameRuleRemovedEvent(GameRulePrototype rule) public GameRuleStartedEvent(GameRulePrototype rule)
{
Rule = rule;
}
}
public sealed class GameRuleEndedEvent
{
public GameRulePrototype Rule { get; }
public GameRuleEndedEvent(GameRulePrototype rule)
{ {
Rule = rule; Rule = rule;
} }

View File

@@ -71,6 +71,8 @@ namespace Content.Server.GameTicking
private void PreRoundSetup() private void PreRoundSetup()
{ {
AddGamePresetRules();
DefaultMap = _mapManager.CreateMap(); DefaultMap = _mapManager.CreateMap();
_pauseManager.AddUninitializedMap(DefaultMap); _pauseManager.AddUninitializedMap(DefaultMap);
_startingRound = false; _startingRound = false;
@@ -188,7 +190,7 @@ namespace Content.Server.GameTicking
SendServerMessage(Loc.GetString("game-ticker-start-round")); SendServerMessage(Loc.GetString("game-ticker-start-round"));
AddGamePresetRules(); StartGamePresetRules();
List<IPlayerSession> readyPlayers; List<IPlayerSession> readyPlayers;
if (LobbyEnabled) if (LobbyEnabled)
@@ -246,6 +248,7 @@ namespace Content.Server.GameTicking
ClearGameRules(); ClearGameRules();
SetGamePreset(_configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset)); SetGamePreset(_configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset));
AddGamePresetRules(); AddGamePresetRules();
StartGamePresetRules();
startAttempt.Uncancel(); startAttempt.Uncancel();
RaiseLocalEvent(startAttempt); RaiseLocalEvent(startAttempt);
@@ -508,7 +511,7 @@ namespace Content.Server.GameTicking
// Clear up any game rules. // Clear up any game rules.
ClearGameRules(); ClearGameRules();
_gameRules.Clear(); _addedGameRules.Clear();
// Round restart cleanup event, so entity systems can reset. // Round restart cleanup event, so entity systems can reset.
var ev = new RoundRestartCleanupEvent(); var ev = new RoundRestartCleanupEvent();

View File

@@ -35,14 +35,14 @@ public sealed class DeathMatchRuleSystem : GameRuleSystem
SubscribeLocalEvent<DamageChangedEvent>(OnHealthChanged); SubscribeLocalEvent<DamageChangedEvent>(OnHealthChanged);
} }
public override void Added() public override void Started()
{ {
_chatManager.DispatchServerAnnouncement(Loc.GetString("rule-death-match-added-announcement")); _chatManager.DispatchServerAnnouncement(Loc.GetString("rule-death-match-added-announcement"));
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged; _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
} }
public override void Removed() public override void Ended()
{ {
_deadCheckTimer = null; _deadCheckTimer = null;
_restartTimer = null; _restartTimer = null;

View File

@@ -27,8 +27,9 @@ public abstract class GameRuleSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<GameRuleAddedEvent>(OnGameRuleAdded); SubscribeLocalEvent<GameRuleAddedEvent>(OnGameRuleAdded);
SubscribeLocalEvent<GameRuleRemovedEvent>(OnGameRuleRemoved);
SubscribeLocalEvent<GameRuleStartedEvent>(OnGameRuleStarted);
SubscribeLocalEvent<GameRuleEndedEvent>(OnGameRuleEnded);
} }
private void OnGameRuleAdded(GameRuleAddedEvent ev) private void OnGameRuleAdded(GameRuleAddedEvent ev)
@@ -37,25 +38,32 @@ public abstract class GameRuleSystem : EntitySystem
return; return;
Enabled = true; Enabled = true;
Added();
} }
private void OnGameRuleRemoved(GameRuleRemovedEvent ev) private void OnGameRuleStarted(GameRuleStartedEvent ev)
{
if (ev.Rule.ID != Prototype)
return;
Started();
}
private void OnGameRuleEnded(GameRuleEndedEvent ev)
{ {
if (ev.Rule.ID != Prototype) if (ev.Rule.ID != Prototype)
return; return;
Enabled = false; Enabled = false;
Removed(); Ended();
} }
/// <summary> /// <summary>
/// Called when the game rule has been added and this system has been enabled. /// Called when the game rule has been started..
/// </summary> /// </summary>
public abstract void Added(); public abstract void Started();
/// <summary> /// <summary>
/// Called when the game rule has been removed and this system has been disabled. /// Called when the game rule has ended..
/// </summary> /// </summary>
public abstract void Removed(); public abstract void Ended();
} }

View File

@@ -28,12 +28,12 @@ public class InactivityTimeRestartRuleSystem : GameRuleSystem
SubscribeLocalEvent<GameRunLevelChangedEvent>(RunLevelChanged); SubscribeLocalEvent<GameRunLevelChangedEvent>(RunLevelChanged);
} }
public override void Added() public override void Started()
{ {
_playerManager.PlayerStatusChanged += PlayerStatusChanged; _playerManager.PlayerStatusChanged += PlayerStatusChanged;
} }
public override void Removed() public override void Ended()
{ {
_playerManager.PlayerStatusChanged -= PlayerStatusChanged; _playerManager.PlayerStatusChanged -= PlayerStatusChanged;

View File

@@ -26,13 +26,13 @@ public sealed class MaxTimeRestartRuleSystem : GameRuleSystem
SubscribeLocalEvent<GameRunLevelChangedEvent>(RunLevelChanged); SubscribeLocalEvent<GameRunLevelChangedEvent>(RunLevelChanged);
} }
public override void Added() public override void Started()
{ {
if(GameTicker.RunLevel == GameRunLevel.InRound) if(GameTicker.RunLevel == GameRunLevel.InRound)
RestartTimer(); RestartTimer();
} }
public override void Removed() public override void Ended()
{ {
RoundMaxTime = TimeSpan.FromMinutes(5); RoundMaxTime = TimeSpan.FromMinutes(5);
RoundEndDelay = TimeSpan.FromMinutes(10); RoundEndDelay = TimeSpan.FromMinutes(10);

View File

@@ -9,12 +9,12 @@ public class SandboxRuleSystem : GameRuleSystem
public override string Prototype => "Sandbox"; public override string Prototype => "Sandbox";
public override void Added() public override void Started()
{ {
_sandbox.IsSandboxEnabled = true; _sandbox.IsSandboxEnabled = true;
} }
public override void Removed() public override void Ended()
{ {
_sandbox.IsSandboxEnabled = false; _sandbox.IsSandboxEnabled = false;
} }

View File

@@ -202,7 +202,7 @@ public sealed class SuspicionRuleSystem : GameRuleSystem
} }
} }
public override void Added() public override void Started()
{ {
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged; _playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
@@ -223,7 +223,7 @@ public sealed class SuspicionRuleSystem : GameRuleSystem
Timer.SpawnRepeating(DeadCheckDelay, CheckWinConditions, _checkTimerCancel.Token); Timer.SpawnRepeating(DeadCheckDelay, CheckWinConditions, _checkTimerCancel.Token);
} }
public override void Removed() public override void Ended()
{ {
_doorSystem.AccessType = SharedDoorSystem.AccessTypes.Id; _doorSystem.AccessType = SharedDoorSystem.AccessTypes.Id;
EndTime = null; EndTime = null;

View File

@@ -202,14 +202,14 @@ public class TraitorDeathMatchRuleSystem : GameRuleSystem
ev.AddLine(string.Join('\n', lines)); ev.AddLine(string.Join('\n', lines));
} }
public override void Added() public override void Started()
{ {
_restarter.RoundMaxTime = TimeSpan.FromMinutes(30); _restarter.RoundMaxTime = TimeSpan.FromMinutes(30);
_restarter.RestartTimer(); _restarter.RestartTimer();
_safeToEndRound = true; _safeToEndRound = true;
} }
public override void Removed() public override void Ended()
{ {
} }

View File

@@ -53,13 +53,13 @@ public class TraitorRuleSystem : GameRuleSystem
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText); SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText);
} }
public override void Added() public override void Started()
{ {
// This seems silly, but I'll leave it. // This seems silly, but I'll leave it.
_chatManager.DispatchServerAnnouncement(Loc.GetString("rule-traitor-added-announcement")); _chatManager.DispatchServerAnnouncement(Loc.GetString("rule-traitor-added-announcement"));
} }
public override void Removed() public override void Ended()
{ {
_traitors.Clear(); _traitors.Clear();
} }

View File

@@ -1,10 +1,6 @@
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Spawners.Components; using Content.Server.Spawners.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.Spawners.EntitySystems namespace Content.Server.Spawners.EntitySystems
@@ -19,7 +15,7 @@ namespace Content.Server.Spawners.EntitySystems
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<GameRuleAddedEvent>(OnRuleAdded); SubscribeLocalEvent<GameRuleStartedEvent>(OnRuleStarted);
SubscribeLocalEvent<ConditionalSpawnerComponent, MapInitEvent>(OnCondSpawnMapInit); SubscribeLocalEvent<ConditionalSpawnerComponent, MapInitEvent>(OnCondSpawnMapInit);
SubscribeLocalEvent<RandomSpawnerComponent, MapInitEvent>(OnRandSpawnMapInit); SubscribeLocalEvent<RandomSpawnerComponent, MapInitEvent>(OnRandSpawnMapInit);
} }
@@ -35,15 +31,15 @@ namespace Content.Server.Spawners.EntitySystems
EntityManager.QueueDeleteEntity(uid); EntityManager.QueueDeleteEntity(uid);
} }
private void OnRuleAdded(GameRuleAddedEvent args) private void OnRuleStarted(GameRuleStartedEvent args)
{ {
foreach (var spawner in EntityManager.EntityQuery<ConditionalSpawnerComponent>()) foreach (var spawner in EntityManager.EntityQuery<ConditionalSpawnerComponent>())
{ {
RuleAdded(spawner, args); RuleStarted(spawner, args);
} }
} }
public void RuleAdded(ConditionalSpawnerComponent component, GameRuleAddedEvent obj) public void RuleStarted(ConditionalSpawnerComponent component, GameRuleStartedEvent obj)
{ {
if(component.GameRules.Contains(obj.Rule.ID)) if(component.GameRules.Contains(obj.Rule.ID))
Spawn(component); Spawn(component);
@@ -59,7 +55,7 @@ namespace Content.Server.Spawners.EntitySystems
foreach (var rule in component.GameRules) foreach (var rule in component.GameRules)
{ {
if (!_ticker.HasGameRule(rule)) continue; if (!_ticker.GameRuleStarted(rule)) continue;
Spawn(component); Spawn(component);
return; return;
} }