From 08dc95313dd636a19696a8b08f6acf83b84d9aa1 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:00:41 +1200 Subject: [PATCH 01/10] Prevent SecretRule from picking invalid presets (#27456) * Prevent SecretRule from picking invalid presets * remove lonely semicolon --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- Content.Server/GameTicking/GameTicker.RoundFlow.cs | 3 +++ Content.Server/GameTicking/Rules/SecretRuleSystem.cs | 1 + 2 files changed, 4 insertions(+) diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 8b4798e7966..e022e5af645 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -186,6 +186,9 @@ public int ReadyPlayerCount() if (!_playerManager.TryGetSessionById(userId, out _)) continue; + if (_banManager.GetRoleBans(userId) == null) + continue; + total++; } diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index 320f9d197aa..3542b2e0864 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -46,6 +46,7 @@ protected override void Added(EntityUid uid, SecretRuleComponent component, Game Log.Info($"Selected {preset.ID} as the secret preset."); _adminLogger.Add(LogType.EventStarted, $"Selected {preset.ID} as the secret preset."); + _chatManager.SendAdminAnnouncement(Loc.GetString("rule-secret-selected-preset", ("preset", preset.ID))); foreach (var rule in preset.Rules) { From d029614becfc995b20fafa6ff3c6657195ddbf7a Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 16 Feb 2025 13:58:11 -0400 Subject: [PATCH 02/10] update gamerule.cs --- .../GameTicking/GameTicker.GameRule.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index 2a33d01bd09..9e263998a6f 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -9,6 +9,7 @@ using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Localization; namespace Content.Server.GameTicking; @@ -71,6 +72,16 @@ public EntityUid AddGameRule(string ruleId) var ruleEntity = Spawn(ruleId, MapCoordinates.Nullspace); _sawmill.Info($"Added game rule {ToPrettyString(ruleEntity)}"); _adminLogger.Add(LogType.EventStarted, $"Added game rule {ToPrettyString(ruleEntity)}"); + var str = Loc.GetString("station-event-system-run-event", ("eventName", ToPrettyString(ruleEntity))); +#if DEBUG + _chatManager.SendAdminAlert(str); +#else + if (RunLevel == GameRunLevel.InRound) // avoids telling admins the round type before it starts so that can be handled elsewhere. + { + _chatManager.SendAdminAlert(str); + } +#endif + Log.Info(str); var ev = new GameRuleAddedEvent(ruleEntity, ruleId); RaiseLocalEvent(ruleEntity, ref ev, true); @@ -324,6 +335,13 @@ private void AddGameRuleCommand(IConsoleShell shell, string argstr, string[] arg foreach (var rule in args) { + if (!_prototypeManager.HasIndex(rule)) + { + shell.WriteError($"Invalid game rule {rule} was skipped."); + + continue; + } + if (shell.Player != null) { _adminLogger.Add(LogType.EventStarted, $"{shell.Player} tried to add game rule [{rule}] via command"); @@ -420,4 +438,4 @@ private string GetGameRulesListMessage(bool forChatWindow) } #endregion -} +} \ No newline at end of file From ad646f8a1990eb44c6b18deb853e9ffc60a4ab6d Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 16 Jun 2024 15:37:43 -0400 Subject: [PATCH 03/10] Missing meteor presets (#29044) * add meteors into missing game presets * changes for real * implement code * this too --- .../Tests/GameRules/FailAndStartPresetTest.cs | 14 ++++++++------ .../GameTicking/Rules/GameRuleSystem.cs | 17 ++++++++++++----- .../GameTicking/Rules/SecretRuleSystem.cs | 2 +- .../GameTicking/Components/GameRuleComponent.cs | 7 +++++++ Resources/Prototypes/game_presets.yml | 3 +++ 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs index b0039144c9c..b3f9dcee28c 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -136,12 +136,14 @@ // if (args.Forced || args.Cancelled) // return; -// var query = EntityQueryEnumerator(); -// while (query.MoveNext(out _, out _, out var gameRule)) -// { -// var minPlayers = gameRule.MinPlayers; -// if (args.Players.Length >= minPlayers) -// continue; + // var query = EntityQueryEnumerator(); + // while (query.MoveNext(out _, out _, out var gameRule)) + // { + // var minPlayers = gameRule.MinPlayers; + // if (!gameRule.CancelPresetOnTooFewPlayers) + // continue; + // if (args.Players.Length >= minPlayers) + // continue; // args.Cancel(); // } diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index 730748ce6b9..cb5b1175495 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -41,11 +41,18 @@ private void OnStartAttempt(RoundStartAttemptEvent args) if (args.Players.Length >= minPlayers) continue; - ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", - ("readyPlayersCount", args.Players.Length), - ("minimumPlayers", minPlayers), - ("presetName", ToPrettyString(uid)))); - args.Cancel(); + if (gameRule.CancelPresetOnTooFewPlayers) + { + ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", + ("readyPlayersCount", args.Players.Length), + ("minimumPlayers", minPlayers), + ("presetName", ToPrettyString(uid)))); + args.Cancel(); + } + else + { + ForceEndSelf(uid, gameRule); + } } } diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index 3542b2e0864..61725ccfa3d 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -165,7 +165,7 @@ private bool CanPick([NotNullWhen(true)] GamePresetPrototype? selected, int play return false; } - if (ruleComp.MinPlayers > players) + if (ruleComp.MinPlayers > players && ruleComp.CancelPresetOnTooFewPlayers) return false; } diff --git a/Content.Shared/GameTicking/Components/GameRuleComponent.cs b/Content.Shared/GameTicking/Components/GameRuleComponent.cs index 28ea435f1b7..10f23a149c8 100644 --- a/Content.Shared/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/GameRuleComponent.cs @@ -23,6 +23,13 @@ public sealed partial class GameRuleComponent : Component [DataField] public int MinPlayers; + /// + /// If true, this rule not having enough players will cancel the preset selection. + /// If false, it will simply not run silently. + /// + [DataField] + public bool CancelPresetOnTooFewPlayers = false; + /// /// A delay for when the rule the is started and when the starting logic actually runs. /// diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index bd1cc45f352..8993f2aee8d 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -7,6 +7,7 @@ description: survival-description rules: - RampingStationEventScheduler + - GameRuleMeteorScheduler - BasicRoundstartVariation - SubGamemodesRule @@ -53,6 +54,7 @@ - Revolutionary - Zombie - RampingStationEventScheduler + - GameRuleMeteorScheduler - type: gamePreset id: Extended @@ -77,6 +79,7 @@ description: greenshift-description rules: - BasicRoundstartVariation + - GameRuleMeteorScheduler - type: gamePreset id: Secret From 3ed9ce248313fa052061ba2441e839b62615eab3 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 4 Jun 2024 00:04:19 +0000 Subject: [PATCH 04/10] LoadMapRule grid storage rework (#28210) * --- .../Tests/GameRules/NukeOpsTest.cs | 12 +-- .../Rules/Components/LoadMapRuleComponent.cs | 26 +++--- .../Rules/Components/RuleGridsComponent.cs | 30 ++++++ .../Rules/GameRuleSystem.Utility.cs | 4 + .../GameTicking/Rules/LoadMapRuleSystem.cs | 92 +++++++------------ .../GameTicking/Rules/NukeopsRuleSystem.cs | 12 +-- .../GameTicking/Rules/RuleGridsSystem.cs | 78 ++++++++++++++++ .../GridPreloader/GridPreloaderSystem.cs | 9 +- .../Events/StationEventSystem.cs | 9 -- Resources/Prototypes/GameRules/events.yml | 1 + Resources/Prototypes/GameRules/midround.yml | 18 ++++ Resources/Prototypes/GameRules/roundstart.yml | 1 + .../Prototypes/GameRules/unknown_shuttles.yml | 40 +++----- 13 files changed, 209 insertions(+), 123 deletions(-) create mode 100644 Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs create mode 100644 Content.Server/GameTicking/Rules/RuleGridsSystem.cs diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index 71f3a0988a8..da94b345c26 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -118,8 +118,8 @@ public async Task TryStopNukeOpsFromConstantlyFailing() // The game rule exists, and all the stations/shuttles/maps are properly initialized var rule = entMan.AllComponents().Single().Component; - var mapRule = entMan.AllComponents().Single().Component; - foreach (var grid in mapRule.MapGrids) + var gridsRule = entMan.AllComponents().Single().Component; + foreach (var grid in gridsRule.MapGrids) { Assert.That(entMan.EntityExists(grid)); Assert.That(entMan.HasComponent(grid)); @@ -139,7 +139,7 @@ public async Task TryStopNukeOpsFromConstantlyFailing() Assert.That(entMan.EntityExists(nukieShuttlEnt)); EntityUid? nukieStationEnt = null; - foreach (var grid in mapRule.MapGrids) + foreach (var grid in gridsRule.MapGrids) { if (entMan.HasComponent(grid)) { @@ -155,10 +155,8 @@ public async Task TryStopNukeOpsFromConstantlyFailing() Assert.That(entMan.EntityExists(nukieStation.Station)); Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation)); - Assert.That(server.MapMan.MapExists(mapRule.Map)); - var nukieMap = mapSys.GetMap(mapRule.Map!.Value); - Assert.That(server.MapMan.MapExists(rule.NukiePlanet)); - var nukieMap = mapSys.GetMap(rule.NukiePlanet!.Value); + Assert.That(server.MapMan.MapExists(gridsRule.Map)); + var nukieMap = mapSys.GetMap(gridsRule.Map!.Value); var targetStation = entMan.GetComponent(rule.TargetStation!.Value); var targetGrid = targetStation.Grids.First(); diff --git a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs index b7aef0c61dc..1f0505c60fc 100644 --- a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs @@ -1,8 +1,6 @@ +using Content.Server.GameTicking.Rules; using Content.Server.Maps; using Content.Shared.GridPreloader.Prototypes; -using Content.Shared.Storage; -using Content.Shared.Whitelist; -using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -10,25 +8,27 @@ namespace Content.Server.GameTicking.Rules.Components; /// /// This is used for a game rule that loads a map when activated. +/// Works with . /// -[RegisterComponent] +[RegisterComponent, Access(typeof(LoadMapRuleSystem))] public sealed partial class LoadMapRuleComponent : Component { - [DataField] - public MapId? Map; - + /// + /// A to load on a new map. + /// [DataField] public ProtoId? GameMap; + /// + /// A map path to load on a new map. + /// [DataField] public ResPath? MapPath; + /// + /// A to move to a new map. + /// If there are no instances left nothing is done. + /// [DataField] public ProtoId? PreloadedGrid; - - [DataField] - public List MapGrids = new(); - - [DataField] - public EntityWhitelist? SpawnerWhitelist; } diff --git a/Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs b/Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs new file mode 100644 index 00000000000..eec6f888156 --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs @@ -0,0 +1,30 @@ +using Content.Server.GameTicking.Rules; +using Content.Shared.Whitelist; +using Robust.Shared.Map; + +/// +/// Stores grids created by another gamerule component. +/// With AntagSelection, spawners on these grids can be used for its antags. +/// +[RegisterComponent, Access(typeof(RuleGridsSystem))] +public sealed partial class RuleGridsComponent : Component +{ + /// + /// The map that was loaded. + /// + [DataField] + public MapId? Map; + + /// + /// The grid entities that have been loaded. + /// + [DataField] + public List MapGrids = new(); + + /// + /// Whitelist for a spawner to be considered for an antag. + /// All spawners must have SpawnPointComponent regardless to be found. + /// + [DataField] + public EntityWhitelist? SpawnerWhitelist; +} diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index b72ba59ca27..5a5eb720cfb 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -126,4 +126,8 @@ protected bool TryFindRandomTileOnStation(Entity station, return found; } + protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null) + { + GameTicker.EndGameRule(uid, component); + } } diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index f2287219fbe..1c09d6e86e1 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -1,9 +1,6 @@ -using Content.Server.Antag; +using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; -using Content.Server.Spawners.Components; using Content.Server.GridPreloader; -using Content.Shared.GameTicking.Components; -using Content.Shared.Whitelist; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Map; @@ -14,97 +11,70 @@ namespace Content.Server.GameTicking.Rules; public sealed class LoadMapRuleSystem : GameRuleSystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly MapSystem _map = default!; [Dependency] private readonly MapLoaderSystem _mapLoader = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly GridPreloaderSystem _gridPreloader = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectLocation); - SubscribeLocalEvent(OnGridSplit); - } - - private void OnGridSplit(ref GridSplitEvent args) - { - var rule = QueryActiveRules(); - while (rule.MoveNext(out _, out var mapComp, out _)) - { - if (!mapComp.MapGrids.Contains(args.Grid)) - continue; - - mapComp.MapGrids.AddRange(args.NewGrids); - break; - } - } protected override void Added(EntityUid uid, LoadMapRuleComponent comp, GameRuleComponent rule, GameRuleAddedEvent args) { - if (comp.Map != null) + if (comp.PreloadedGrid != null && !_gridPreloader.PreloadingEnabled) + { + // Preloading will never work if it's disabled, duh + Log.Debug($"Immediately ending {ToPrettyString(uid):rule} as preloading grids is disabled by cvar."); + ForceEndSelf(uid, rule); return; + } // grid preloading needs map to init after moving it - var mapUid = comp.PreloadedGrid != null ? _map.CreateMap(out var mapId, false) : _map.CreateMap(out mapId); - _metaData.SetEntityName(mapUid, $"LoadMapRule destination for rule {ToPrettyString(uid)}"); - comp.Map = mapId; + var mapUid = _map.CreateMap(out var mapId, runMapInit: comp.PreloadedGrid == null); + + Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}"); + IReadOnlyList grids; if (comp.GameMap != null) { var gameMap = _prototypeManager.Index(comp.GameMap.Value); - comp.MapGrids.AddRange(GameTicker.LoadGameMap(gameMap, comp.Map.Value, new MapLoadOptions())); + grids = GameTicker.LoadGameMap(gameMap, mapId, new MapLoadOptions()); } - else if (comp.MapPath != null) + else if (comp.MapPath is {} path) { - if (!_mapLoader.TryLoad(comp.Map.Value, - comp.MapPath.Value.ToString(), - out var roots, - new MapLoadOptions { LoadMap = true })) + var options = new MapLoadOptions { LoadMap = true }; + if (!_mapLoader.TryLoad(mapId, path.ToString(), out var roots, options)) { - _mapManager.DeleteMap(mapId); + Log.Error($"Failed to load map from {path}!"); + Del(mapUid); + ForceEndSelf(uid, rule); return; } - comp.MapGrids.AddRange(roots); + grids = roots; } - else if (comp.PreloadedGrid != null) + else if (comp.PreloadedGrid is {} preloaded) { // TODO: If there are no preloaded grids left, any rule announcements will still go off! - if (!_gridPreloader.TryGetPreloadedGrid(comp.PreloadedGrid.Value, out var loadedShuttle)) + if (!_gridPreloader.TryGetPreloadedGrid(preloaded, out var loadedShuttle)) { - _mapManager.DeleteMap(mapId); + Log.Error($"Failed to get a preloaded grid with {preloaded}!"); + Del(mapUid); + ForceEndSelf(uid, rule); return; } _transform.SetParent(loadedShuttle.Value, mapUid); - comp.MapGrids.Add(loadedShuttle.Value); - _map.InitializeMap(mapId); + grids = new List() { loadedShuttle.Value }; + _map.InitializeMap(mapUid); } else { Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}"); + Del(mapUid); + ForceEndSelf(uid, rule); + return; } - } - - private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var xform)) - { - if (xform.MapID != ent.Comp.Map) - continue; - - if (xform.GridUid == null || !ent.Comp.MapGrids.Contains(xform.GridUid.Value)) - continue; - if (_whitelistSystem.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid)) - continue; - - args.Coordinates.Add(_transform.GetMapCoordinates(xform)); - } + var ev = new RuleLoadedGridsEvent(mapId, grids); + RaiseLocalEvent(uid, ref ev); } } diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 4d02e2311d1..d6e0cb9290f 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -266,10 +266,10 @@ private void OnMapInit(Entity ent, ref MapInitEvent arg { var map = Transform(ent).MapID; - var rules = EntityQueryEnumerator(); - while (rules.MoveNext(out var uid, out _, out var mapRule)) + var rules = EntityQueryEnumerator(); + while (rules.MoveNext(out var uid, out _, out var grids)) { - if (map != mapRule.Map) + if (map != grids.Map) continue; ent.Comp.AssociatedRule = uid; break; @@ -330,7 +330,7 @@ private void OnWarDeclared(ref WarDeclaredEvent ev) if (nukeops.WarDeclaredTime != null) continue; - if (TryComp(uid, out var mapComp) && Transform(ev.DeclaratorEntity).MapID != mapComp.Map) + if (TryComp(uid, out var grids) && Transform(ev.DeclaratorEntity).MapID != grids.Map) continue; var newStatus = GetWarCondition(nukeops, ev.Status); @@ -451,7 +451,7 @@ private void CheckRoundShouldEnd(Entity ent) // Check that there are spawns available and that they can access the shuttle. var spawnsAvailable = EntityQuery(true).Any(); - if (spawnsAvailable && CompOrNull(ent)?.Map == shuttleMapId) + if (spawnsAvailable && CompOrNull(ent)?.Map == shuttleMapId) return; // Ghost spawns can still access the shuttle. Continue the round. // The shuttle is inaccessible to both living nuke operatives and yet to spawn nuke operatives, @@ -484,7 +484,7 @@ private void OnAfterAntagEntSelected(Entity ent, ref After /// Is this method the shitty glue holding together the last of my sanity? yes. /// Do i have a better solution? not presently. /// - private EntityUid? GetOutpost(Entity ent) + private EntityUid? GetOutpost(Entity ent) { if (!Resolve(ent, ref ent.Comp, false)) return null; diff --git a/Content.Server/GameTicking/Rules/RuleGridsSystem.cs b/Content.Server/GameTicking/Rules/RuleGridsSystem.cs new file mode 100644 index 00000000000..9eae9e3c95f --- /dev/null +++ b/Content.Server/GameTicking/Rules/RuleGridsSystem.cs @@ -0,0 +1,78 @@ +using Content.Server.Antag; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Spawners.Components; +using Content.Shared.Whitelist; +using Robust.Server.Physics; +using Robust.Shared.Map; + +namespace Content.Server.GameTicking.Rules; + +/// +/// Handles storing grids from and antags spawning on their spawners. +/// +public sealed class RuleGridsSystem : GameRuleSystem +{ + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGridSplit); + + SubscribeLocalEvent(OnLoadedGrids); + SubscribeLocalEvent(OnSelectLocation); + } + + private void OnGridSplit(ref GridSplitEvent args) + { + var rule = QueryActiveRules(); + while (rule.MoveNext(out _, out var comp, out _)) + { + if (!comp.MapGrids.Contains(args.Grid)) + continue; + + comp.MapGrids.AddRange(args.NewGrids); + break; // only 1 rule can own a grid, not multiple + } + } + + private void OnLoadedGrids(Entity ent, ref RuleLoadedGridsEvent args) + { + var (uid, comp) = ent; + if (comp.Map != null && args.Map != comp.Map) + { + Log.Warning($"{ToPrettyString(uid):rule} loaded grids on multiple maps {comp.Map} and {args.Map}, the second will be ignored."); + return; + } + + comp.Map = args.Map; + comp.MapGrids.AddRange(args.Grids); + } + + private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var xform)) + { + if (xform.MapID != ent.Comp.Map) + continue; + + if (xform.GridUid is not {} grid || !ent.Comp.MapGrids.Contains(grid)) + continue; + + if (_whitelist.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid)) + continue; + + args.Coordinates.Add(_transform.GetMapCoordinates(xform)); + } + } +} + +/// +/// Raised by another gamerule system to store loaded grids, and have other systems work with it. +/// A single rule can only load grids for a single map, attempts to load more are ignored. +/// +[ByRefEvent] +public record struct RuleLoadedGridsEvent(MapId Map, IReadOnlyList Grids); diff --git a/Content.Server/GridPreloader/GridPreloaderSystem.cs b/Content.Server/GridPreloader/GridPreloaderSystem.cs index 569fe54141c..e12ce41a31e 100644 --- a/Content.Server/GridPreloader/GridPreloaderSystem.cs +++ b/Content.Server/GridPreloader/GridPreloaderSystem.cs @@ -24,12 +24,19 @@ public sealed class GridPreloaderSystem : SharedGridPreloaderSystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + /// + /// Whether the preloading CVar is set or not. + /// + public bool PreloadingEnabled; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRoundRestart); SubscribeLocalEvent(OnPostGameMapLoad); + + Subs.CVar(_cfg, CCVars.PreloadGrids, value => PreloadingEnabled = value, true); } private void OnRoundRestart(RoundRestartCleanupEvent ev) @@ -52,7 +59,7 @@ private void EnsurePreloadedGridMap() if (GetPreloaderEntity() != null) return; - if (!_cfg.GetCVar(CCVars.PreloadGrids)) + if (!PreloadingEnabled) return; var mapUid = _map.CreateMap(out var mapId, false); diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index a88258b9ad9..85739ccde58 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -128,13 +128,4 @@ public override void Update(float frameTime) } } } - - #region Helper Functions - - protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null) - { - GameTicker.EndGameRule(uid, component); - } - - #endregion } diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index ca4ba9942dc..e0c05d0874f 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -486,6 +486,7 @@ weight: 5.5 minimumPlayers: 20 duration: 1 + - type: RuleGrids - type: LoadMapRule preloadedGrid: ShuttleStriker - type: NukeopsRule diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index 39fbab580fe..476c7b10711 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -1,3 +1,21 @@ +# doesnt spawn a ninja or anything, just stores configuration for it +# see NinjaSpawn event for spawning +- type: entity + id: Ninja + parent: BaseGameRule + components: + - type: GenericAntagRule + agentName: ninja-round-end-agent-name + objectives: + - StealResearchObjective + - DoorjackObjective + - SpiderChargeObjective + - TerrorObjective + - MassArrestObjective + - NinjaSurviveObjective + - type: NinjaRule + threats: NinjaThreats + - type: entity categories: [ HideSpawnMenu ] parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 70ccd06f394..7e63db3d618 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -73,6 +73,7 @@ - operationPrefix - operationSuffix - type: NukeopsRule + - type: RuleGrids - type: AntagSelection - type: AntagLoadProfileRule diff --git a/Resources/Prototypes/GameRules/unknown_shuttles.yml b/Resources/Prototypes/GameRules/unknown_shuttles.yml index db9d4756a89..886b02b4cc4 100644 --- a/Resources/Prototypes/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/GameRules/unknown_shuttles.yml @@ -1,64 +1,52 @@ -- type: entity - id: UnknownShuttleCargoLost +- type: entity + abstract: true parent: BaseGameRule categories: [ HideSpawnMenu ] + id: BaseUnknownShuttleRule components: - type: StationEvent startAnnouncement: false weight: 5 reoccurrenceDelay: 30 duration: 1 + - type: RuleGrids + - type: LoadMapRule + +- type: entity + parent: BaseUnknownShuttleRule + id: UnknownShuttleCargoLost + components: - type: LoadMapRule preloadedGrid: ShuttleCargoLost - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleTravelingCuisine - parent: BaseGameRule - categories: [ HideSpawnMenu ] components: - - type: StationEvent - startAnnouncement: false - weight: 5 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: TravelingCuisine - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleDisasterEvacPod - parent: BaseGameRule - categories: [ HideSpawnMenu ] components: - - type: StationEvent - startAnnouncement: false - weight: 5 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: DisasterEvacPod - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleHonki - parent: BaseGameRule - categories: [ HideSpawnMenu ] components: - type: StationEvent - startAnnouncement: false weight: 2 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: Honki - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleSyndieEvacPod - parent: BaseGameRule - categories: [ HideSpawnMenu ] components: - type: StationEvent - startAnnouncement: false weight: 2 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: SyndieEvacPod From fe56df518cb64b9ee47eeac8d7033069d66f18eb Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 16 Feb 2025 14:15:27 -0400 Subject: [PATCH 05/10] wahoo --- Resources/Prototypes/GameRules/unknown_shuttles.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Prototypes/GameRules/unknown_shuttles.yml b/Resources/Prototypes/GameRules/unknown_shuttles.yml index 886b02b4cc4..a28396799cb 100644 --- a/Resources/Prototypes/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/GameRules/unknown_shuttles.yml @@ -1,7 +1,6 @@ - type: entity abstract: true parent: BaseGameRule - categories: [ HideSpawnMenu ] id: BaseUnknownShuttleRule components: - type: StationEvent From 32d03f9c20e6ba28dd32a25e765e73ce765edc31 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 16 Feb 2025 17:04:09 -0400 Subject: [PATCH 06/10] wahoooo! --- Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index 1c09d6e86e1..3594242bcde 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.GridPreloader; +using Content.Shared.GameTicking.Components; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Map; From 8837b1fa7c6d4882ce28e5a061cc6d2fc49b049a Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 16 Feb 2025 18:25:14 -0400 Subject: [PATCH 07/10] fix --- Resources/Prototypes/GameRules/midround.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index 476c7b10711..39fbab580fe 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -1,21 +1,3 @@ -# doesnt spawn a ninja or anything, just stores configuration for it -# see NinjaSpawn event for spawning -- type: entity - id: Ninja - parent: BaseGameRule - components: - - type: GenericAntagRule - agentName: ninja-round-end-agent-name - objectives: - - StealResearchObjective - - DoorjackObjective - - SpiderChargeObjective - - TerrorObjective - - MassArrestObjective - - NinjaSurviveObjective - - type: NinjaRule - threats: NinjaThreats - - type: entity categories: [ HideSpawnMenu ] parent: BaseGameRule From a19d6249848d0b8a93cd016707ea57194f2ec0f8 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Mon, 17 Feb 2025 15:12:37 -0400 Subject: [PATCH 08/10] Revert "Missing meteor presets (#29044)" This reverts commit ad646f8a1990eb44c6b18deb853e9ffc60a4ab6d. --- .../Tests/GameRules/FailAndStartPresetTest.cs | 14 ++++++-------- .../GameTicking/Rules/GameRuleSystem.cs | 17 +++++------------ .../GameTicking/Rules/SecretRuleSystem.cs | 2 +- .../GameTicking/Components/GameRuleComponent.cs | 7 ------- Resources/Prototypes/game_presets.yml | 3 --- 5 files changed, 12 insertions(+), 31 deletions(-) diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs index b3f9dcee28c..b0039144c9c 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -136,14 +136,12 @@ // if (args.Forced || args.Cancelled) // return; - // var query = EntityQueryEnumerator(); - // while (query.MoveNext(out _, out _, out var gameRule)) - // { - // var minPlayers = gameRule.MinPlayers; - // if (!gameRule.CancelPresetOnTooFewPlayers) - // continue; - // if (args.Players.Length >= minPlayers) - // continue; +// var query = EntityQueryEnumerator(); +// while (query.MoveNext(out _, out _, out var gameRule)) +// { +// var minPlayers = gameRule.MinPlayers; +// if (args.Players.Length >= minPlayers) +// continue; // args.Cancel(); // } diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index cb5b1175495..730748ce6b9 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -41,18 +41,11 @@ private void OnStartAttempt(RoundStartAttemptEvent args) if (args.Players.Length >= minPlayers) continue; - if (gameRule.CancelPresetOnTooFewPlayers) - { - ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", - ("readyPlayersCount", args.Players.Length), - ("minimumPlayers", minPlayers), - ("presetName", ToPrettyString(uid)))); - args.Cancel(); - } - else - { - ForceEndSelf(uid, gameRule); - } + ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", + ("readyPlayersCount", args.Players.Length), + ("minimumPlayers", minPlayers), + ("presetName", ToPrettyString(uid)))); + args.Cancel(); } } diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index 61725ccfa3d..3542b2e0864 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -165,7 +165,7 @@ private bool CanPick([NotNullWhen(true)] GamePresetPrototype? selected, int play return false; } - if (ruleComp.MinPlayers > players && ruleComp.CancelPresetOnTooFewPlayers) + if (ruleComp.MinPlayers > players) return false; } diff --git a/Content.Shared/GameTicking/Components/GameRuleComponent.cs b/Content.Shared/GameTicking/Components/GameRuleComponent.cs index 10f23a149c8..28ea435f1b7 100644 --- a/Content.Shared/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/GameRuleComponent.cs @@ -23,13 +23,6 @@ public sealed partial class GameRuleComponent : Component [DataField] public int MinPlayers; - /// - /// If true, this rule not having enough players will cancel the preset selection. - /// If false, it will simply not run silently. - /// - [DataField] - public bool CancelPresetOnTooFewPlayers = false; - /// /// A delay for when the rule the is started and when the starting logic actually runs. /// diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index 8993f2aee8d..bd1cc45f352 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -7,7 +7,6 @@ description: survival-description rules: - RampingStationEventScheduler - - GameRuleMeteorScheduler - BasicRoundstartVariation - SubGamemodesRule @@ -54,7 +53,6 @@ - Revolutionary - Zombie - RampingStationEventScheduler - - GameRuleMeteorScheduler - type: gamePreset id: Extended @@ -79,7 +77,6 @@ description: greenshift-description rules: - BasicRoundstartVariation - - GameRuleMeteorScheduler - type: gamePreset id: Secret From 024d1ef4f41c8aa328d341834286b7d81b6c636b Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Mon, 17 Feb 2025 15:17:59 -0400 Subject: [PATCH 09/10] revert meteor, use custom --- .../GameTicking/Rules/GameRuleSystem.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index 730748ce6b9..f75a09a78f3 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -41,11 +41,20 @@ private void OnStartAttempt(RoundStartAttemptEvent args) if (args.Players.Length >= minPlayers) continue; - ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", - ("readyPlayersCount", args.Players.Length), - ("minimumPlayers", minPlayers), - ("presetName", ToPrettyString(uid)))); - args.Cancel(); + // if (gameRule.CancelPresetOnTooFewPlayers) + // { + // ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", + // ("readyPlayersCount", args.Players.Length), + // ("minimumPlayers", minPlayers), + // ("presetName", ToPrettyString(uid)))); + // args.Cancel(); + // } + // else + // { + // GameTicker.EndGameRule(uid, component); + // } + + GameTicker.EndGameRule(uid, component); } } @@ -135,4 +144,4 @@ public override void Update(float frameTime) ActiveTick(uid, comp1, comp2, frameTime); } } -} +} \ No newline at end of file From 1ae2bfcfcc5a7dd372bc7190d8f7f2fb3775f2be Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Mon, 17 Feb 2025 15:22:47 -0400 Subject: [PATCH 10/10] mhm --- Content.Server/GameTicking/Rules/GameRuleSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index f75a09a78f3..febde3919f2 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -54,7 +54,7 @@ private void OnStartAttempt(RoundStartAttemptEvent args) // GameTicker.EndGameRule(uid, component); // } - GameTicker.EndGameRule(uid, component); + GameTicker.EndGameRule(uid, gameRule); } }