From e3be84b5f89c7aac4fead1e168724cc7380ac114 Mon Sep 17 00:00:00 2001 From: mirrorcult Date: Sat, 15 Jan 2022 15:43:14 -0700 Subject: [PATCH] Multi-map and multi-station gameticker loading (#6167) --- .../GameTicking/GameTicker.RoundFlow.cs | 102 +++++++++++++++--- .../Components/BecomesStationComponent.cs | 21 ++++ .../Components/PartOfStationComponent.cs | 18 ++++ .../{ => Components}/StationComponent.cs | 0 Resources/Maps/dart.yml | 2 + Resources/Maps/knightship.yml | 2 + Resources/Maps/packedstation.yml | 2 + Resources/Maps/packedstationxmas.yml | 2 + Resources/Maps/saltern.yml | 2 + Resources/Maps/ssreach.yml | 2 + 10 files changed, 137 insertions(+), 16 deletions(-) create mode 100644 Content.Server/Station/Components/BecomesStationComponent.cs create mode 100644 Content.Server/Station/Components/PartOfStationComponent.cs rename Content.Server/Station/{ => Components}/StationComponent.cs (100%) diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 6a6651af48..137b193237 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -4,8 +4,10 @@ using System.Linq; using Content.Server.Database; using Content.Server.GameTicking.Events; using Content.Server.Ghost; +using Content.Server.Maps; using Content.Server.Mind; using Content.Server.Players; +using Content.Server.Station; using Content.Shared.CCVar; using Content.Shared.Coordinates; using Content.Shared.GameTicking; @@ -72,29 +74,82 @@ namespace Content.Server.GameTicking _pauseManager.AddUninitializedMap(DefaultMap); _startingRound = false; var startTime = _gameTiming.RealTime; - var map = _gameMapManager.GetSelectedMapChecked(true); - _mapLoader.LoadMap(DefaultMap, map.MapPath); + var maps = new List() { _gameMapManager.GetSelectedMapChecked(true) }; - var grids = _mapManager.GetAllMapGrids(DefaultMap).ToList(); - StationId stationId = default; + // Let game rules dictate what maps we should load. + RaiseLocalEvent(new LoadingMapsEvent(maps)); - if (grids.Count > 0) + foreach (var map in maps) { - var grid = grids[0]; - stationId = _stationSystem.InitialSetupStationGrid(grid.GridEntityId, map); - SetupGridStation(grid); - _spawnPoint = grid.ToCoordinates(); - } + var toLoad = DefaultMap; + if (maps[0] != map) + { + // Create other maps for the others since we need to. + toLoad = _mapManager.CreateMap(); + _pauseManager.AddUninitializedMap(toLoad); + } - for (var i = 1; i < grids.Count; i++) - { - var grid = grids[i]; - SetupGridStation(grid); - _stationSystem.AddGridToStation(grid.GridEntityId, stationId); + _mapLoader.LoadMap(toLoad, map.MapPath); + + var grids = _mapManager.GetAllMapGrids(toLoad).ToList(); + var dict = new Dictionary(); + + StationId SetupInitialStation(IMapGrid grid, GameMapPrototype map) + { + var stationId = _stationSystem.InitialSetupStationGrid(grid.GridEntityId, map); + SetupGridStation(grid); + + // ass! + _spawnPoint = grid.ToCoordinates(); + return stationId; + } + + // Iterate over all BecomesStation + for (var i = 0; i < grids.Count; i++) + { + var grid = grids[i]; + + // We still setup the grid + if (!TryComp(grid.GridEntityId, out var becomesStation)) + continue; + + var stationId = SetupInitialStation(grid, map); + + dict.Add(becomesStation.Id, stationId); + } + + if (!dict.Any()) + { + // Oh jeez, no stations got loaded. + // We'll just take the first grid and setup that, then. + + var grid = grids[0]; + var stationId = SetupInitialStation(grid, map); + + dict.Add("Station", stationId); + } + + // Iterate over all PartOfStation + for (var i = 0; i < grids.Count; i++) + { + var grid = grids[i]; + if (!TryComp(grid.GridEntityId, out var partOfStation)) + continue; + SetupGridStation(grid); + + if (dict.TryGetValue(partOfStation.Id, out var stationId)) + { + _stationSystem.AddGridToStation(grid.GridEntityId, stationId); + } + else + { + Logger.Error($"Grid {grid.Index} ({grid.GridEntityId}) specified that it was part of station {partOfStation.Id} which does not exist"); + } + } } var timeSpan = _gameTiming.RealTime - startTime; - Logger.InfoS("ticker", $"Loaded map in {timeSpan.TotalMilliseconds:N2}ms."); + Logger.InfoS("ticker", $"Loaded maps in {timeSpan.TotalMilliseconds:N2}ms."); } private void SetupGridStation(IMapGrid grid) @@ -525,6 +580,21 @@ namespace Content.Server.GameTicking } } + /// + /// Event raised before maps are loaded in pre-round setup. + /// Contains a list of game map prototypes to load; modify it if you want to load different maps, + /// for example as part of a game rule. + /// + public class LoadingMapsEvent : EntityEventArgs + { + public List Maps; + + public LoadingMapsEvent(List maps) + { + Maps = maps; + } + } + /// /// Event raised to refresh the late join status. /// If you want to disallow late joins, listen to this and call Disallow. diff --git a/Content.Server/Station/Components/BecomesStationComponent.cs b/Content.Server/Station/Components/BecomesStationComponent.cs new file mode 100644 index 0000000000..93cb818529 --- /dev/null +++ b/Content.Server/Station/Components/BecomesStationComponent.cs @@ -0,0 +1,21 @@ +using Content.Server.GameTicking; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Station; + +/// +/// Added to grids saved in maps to designate that they are the 'main station' grid. +/// +[RegisterComponent, ComponentProtoName("BecomesStation")] +[Friend(typeof(GameTicker))] +public class BecomesStationComponent : Component +{ + /// + /// Mapping only. Should use StationIds in all other + /// scenarios. + /// + [DataField("id", required: true)] + public string Id = default!; +} diff --git a/Content.Server/Station/Components/PartOfStationComponent.cs b/Content.Server/Station/Components/PartOfStationComponent.cs new file mode 100644 index 0000000000..f4bc7b3b6b --- /dev/null +++ b/Content.Server/Station/Components/PartOfStationComponent.cs @@ -0,0 +1,18 @@ +using Content.Server.GameTicking; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Station; + +/// +/// Added to grids saved in maps to designate them as 'part of a station' and not main grids. I.e. ancillary +/// shuttles for multi-grid stations. +/// +[RegisterComponent, ComponentProtoName("PartOfStation")] +[Friend(typeof(GameTicker))] +public class PartOfStationComponent : Component +{ + [DataField("id", required: true)] // does yamllinter even lint maps for required fields? + public string Id = default!; +} diff --git a/Content.Server/Station/StationComponent.cs b/Content.Server/Station/Components/StationComponent.cs similarity index 100% rename from Content.Server/Station/StationComponent.cs rename to Content.Server/Station/Components/StationComponent.cs diff --git a/Resources/Maps/dart.yml b/Resources/Maps/dart.yml index 7fe3260c74..d03f1b1fb0 100644 --- a/Resources/Maps/dart.yml +++ b/Resources/Maps/dart.yml @@ -87,6 +87,8 @@ entities: type: Transform - index: 0 type: MapGrid + - type: BecomesStation + id: Dart - linearDamping: 0.1 fixedRotation: False bodyType: Dynamic diff --git a/Resources/Maps/knightship.yml b/Resources/Maps/knightship.yml index b38eb7ef95..142898a80c 100644 --- a/Resources/Maps/knightship.yml +++ b/Resources/Maps/knightship.yml @@ -61,6 +61,8 @@ entities: type: Transform - index: 0 type: MapGrid + - type: BecomesStation + id: Knightship - angularDamping: 0.3 fixedRotation: False fixtures: diff --git a/Resources/Maps/packedstation.yml b/Resources/Maps/packedstation.yml index cfc837649e..b9510e429e 100644 --- a/Resources/Maps/packedstation.yml +++ b/Resources/Maps/packedstation.yml @@ -167,6 +167,8 @@ entities: type: Transform - index: 0 type: MapGrid + - type: BecomesStation + id: Packed - linearDamping: 0.1 fixedRotation: False bodyType: Dynamic diff --git a/Resources/Maps/packedstationxmas.yml b/Resources/Maps/packedstationxmas.yml index fccf435d2d..0dc5468137 100644 --- a/Resources/Maps/packedstationxmas.yml +++ b/Resources/Maps/packedstationxmas.yml @@ -167,6 +167,8 @@ entities: type: Transform - index: 0 type: MapGrid + - type: BecomesStation + id: Packed - linearDamping: 0.1 fixedRotation: False bodyType: Dynamic diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index bb34891dfd..8ead2c9f39 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -8786,6 +8786,8 @@ entities: type: Transform - index: 0 type: MapGrid + - type: BecomesStation + id: Saltern - tiles: -16,-16: 0 -16,-15: 0 diff --git a/Resources/Maps/ssreach.yml b/Resources/Maps/ssreach.yml index 03440e215a..973945f787 100644 --- a/Resources/Maps/ssreach.yml +++ b/Resources/Maps/ssreach.yml @@ -115,6 +115,8 @@ entities: type: Transform - index: 0 type: MapGrid + - type: BecomesStation + id: Reach - linearDamping: 0.1 fixedRotation: False bodyType: Dynamic