diff --git a/Content.Server/Commands/GameTicking/ForcePresetCommand.cs b/Content.Server/Commands/GameTicking/ForcePresetCommand.cs
index ed959d0e7a..7f98c52fb1 100644
--- a/Content.Server/Commands/GameTicking/ForcePresetCommand.cs
+++ b/Content.Server/Commands/GameTicking/ForcePresetCommand.cs
@@ -40,4 +40,4 @@ namespace Content.Server.Commands.GameTicking
shell.WriteLine($"Forced the game to start with preset {name}.");
}
}
-}
\ No newline at end of file
+}
diff --git a/Content.Server/GameTicking/GamePresets/GamePresetAttribute.cs b/Content.Server/GameTicking/GamePresets/GamePresetAttribute.cs
new file mode 100644
index 0000000000..5f9b2fa397
--- /dev/null
+++ b/Content.Server/GameTicking/GamePresets/GamePresetAttribute.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Immutable;
+using JetBrains.Annotations;
+
+namespace Content.Server.GameTicking.GamePresets
+{
+ ///
+ /// Attribute that marks a game preset.
+ /// The id and aliases are registered in lowercase in .
+ /// A duplicate id or alias will throw an exception.
+ ///
+ [AttributeUsage(AttributeTargets.Class, Inherited = false)]
+ [BaseTypeRequired(typeof(GamePreset))]
+ [MeansImplicitUse]
+ public class GamePresetAttribute : Attribute
+ {
+ public string Id { get; }
+
+ public ImmutableList Aliases { get; }
+
+ public GamePresetAttribute(string id, params string[] aliases)
+ {
+ Id = id;
+ Aliases = aliases.ToImmutableList();
+ }
+ }
+}
diff --git a/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs b/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs
index 87368f6834..a434661c61 100644
--- a/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs
@@ -6,6 +6,7 @@ using Robust.Shared.IoC;
namespace Content.Server.GameTicking.GamePresets
{
+ [GamePreset("deathmatch")]
public sealed class PresetDeathMatch : GamePreset
{
[Dependency] private readonly IGameTicker _gameTicker = default!;
diff --git a/Content.Server/GameTicking/GamePresets/PresetExtended.cs b/Content.Server/GameTicking/GamePresets/PresetExtended.cs
index 60b8f099e5..a469cec3c5 100644
--- a/Content.Server/GameTicking/GamePresets/PresetExtended.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetExtended.cs
@@ -3,6 +3,7 @@ using Robust.Server.Player;
namespace Content.Server.GameTicking.GamePresets
{
+ [GamePreset("extended")]
public class PresetExtended : GamePreset
{
public override string Description => "No antagonists, have fun!";
diff --git a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs
index 4f370a9159..3288002a2f 100644
--- a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs
@@ -5,6 +5,7 @@ using Robust.Shared.IoC;
namespace Content.Server.GameTicking.GamePresets
{
+ [GamePreset("sandbox")]
public sealed class PresetSandbox : GamePreset
{
[Dependency] private readonly ISandboxManager _sandboxManager = default!;
diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
index 045ac396e3..f373a4571d 100644
--- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
@@ -24,6 +24,7 @@ using Robust.Shared.Random;
namespace Content.Server.GameTicking.GamePresets
{
+ [GamePreset("suspicion")]
public class PresetSuspicion : GamePreset
{
[Dependency] private readonly IChatManager _chatManager = default!;
diff --git a/Content.Server/GameTicking/GamePresets/PresetTraitor.cs b/Content.Server/GameTicking/GamePresets/PresetTraitor.cs
index 6ee10df25c..cca5e0cdc2 100644
--- a/Content.Server/GameTicking/GamePresets/PresetTraitor.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetTraitor.cs
@@ -25,9 +25,10 @@ using Robust.Shared.Random;
namespace Content.Server.GameTicking.GamePresets
{
+ [GamePreset("traitor")]
public class PresetTraitor : GamePreset
{
- [Dependency] private readonly IGameTicker _gameticker = default!;
+ [Dependency] private readonly IGameTicker _gameTicker = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
@@ -154,7 +155,7 @@ namespace Content.Server.GameTicking.GamePresets
traitor.GreetTraitor(codewords);
}
- _gameticker.AddGameRule();
+ _gameTicker.AddGameRule();
return true;
}
diff --git a/Content.Server/GameTicking/GamePresets/PresetTraitorDeathMatch.cs b/Content.Server/GameTicking/GamePresets/PresetTraitorDeathMatch.cs
index 6ae9e41904..4cdce1a01e 100644
--- a/Content.Server/GameTicking/GamePresets/PresetTraitorDeathMatch.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetTraitorDeathMatch.cs
@@ -29,6 +29,7 @@ using Robust.Shared.Random;
namespace Content.Server.GameTicking.GamePresets
{
+ [GamePreset("traitordm", "traitordeathmatch")]
public sealed class PresetTraitorDeathMatch : GamePreset
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs
index 6876d1872e..25fc426ef4 100644
--- a/Content.Server/GameTicking/GameTicker.cs
+++ b/Content.Server/GameTicking/GameTicker.cs
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Reflection;
using System.Threading;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.GUI;
@@ -41,6 +44,7 @@ using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Robust.Shared.Reflection;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -118,6 +122,8 @@ namespace Content.Server.GameTicking
set => _preset = value;
}
+ public ImmutableDictionary Presets { get; private set; }
+
private GamePreset _preset;
public event Action OnRunLevelChanged;
@@ -132,6 +138,22 @@ namespace Content.Server.GameTicking
DebugTools.Assert(!_initialized);
+ var presets = new Dictionary();
+
+ foreach (var type in _reflectionManager.FindTypesWithAttribute())
+ {
+ var attribute = type.GetCustomAttribute();
+
+ presets.Add(attribute!.Id.ToLowerInvariant(), type);
+
+ foreach (var alias in attribute.Aliases)
+ {
+ presets.Add(alias.ToLowerInvariant(), type);
+ }
+ }
+
+ Presets = presets.ToImmutableDictionary();
+
_netManager.RegisterNetMessage(nameof(MsgTickerJoinLobby));
_netManager.RegisterNetMessage(nameof(MsgTickerJoinGame));
_netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus));
@@ -479,21 +501,10 @@ namespace Content.Server.GameTicking
public IEnumerable ActiveGameRules => _gameRules;
- public bool TryGetPreset(string name, out Type type)
+ public bool TryGetPreset(string name, [NotNullWhen(true)] out Type type)
{
- type = name.ToLower() switch
- {
- "sandbox" => typeof(PresetSandbox),
- "deathmatch" => typeof(PresetDeathMatch),
- "suspicion" => typeof(PresetSuspicion),
- "traitor" => typeof(PresetTraitor),
- "traitordm" => typeof(PresetTraitorDeathMatch),
- "traitordeathmatch" => typeof(PresetTraitorDeathMatch),
- "extended" => typeof(PresetExtended),
- _ => default
- };
-
- return type != default;
+ name = name.ToLowerInvariant();
+ return Presets.TryGetValue(name, out type);
}
public void SetStartPreset(Type type, bool force = false)
@@ -513,7 +524,7 @@ namespace Content.Server.GameTicking
{
if (!TryGetPreset(name, out var type))
{
- throw new NotSupportedException();
+ throw new NotSupportedException($"No preset found with name {name}");
}
SetStartPreset(type, force);
@@ -576,7 +587,7 @@ namespace Content.Server.GameTicking
private IEntity _spawnPlayerMob(Job job, HumanoidCharacterProfile profile, bool lateJoin = true)
{
- EntityCoordinates coordinates = lateJoin ? GetLateJoinSpawnPoint() : GetJobSpawnPoint(job.Prototype.ID);
+ var coordinates = lateJoin ? GetLateJoinSpawnPoint() : GetJobSpawnPoint(job.Prototype.ID);
var entity = _entityManager.SpawnEntity(PlayerPrototypeName, coordinates);
var startingGear = _prototypeManager.Index(job.StartingGear);
EquipStartingGear(entity, startingGear, profile);
@@ -1071,6 +1082,7 @@ The current game mode is: [color=white]{0}[/color].
[Dependency] private readonly IBaseServer _baseServer = default!;
[Dependency] private readonly IWatchdogApi _watchdogApi = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
+ [Dependency] private readonly IReflectionManager _reflectionManager = default!;
}
public enum GameRunLevel
diff --git a/Content.Server/Interfaces/GameTicking/IGameTicker.cs b/Content.Server/Interfaces/GameTicking/IGameTicker.cs
index 404070aba9..e719f55020 100644
--- a/Content.Server/Interfaces/GameTicking/IGameTicker.cs
+++ b/Content.Server/Interfaces/GameTicking/IGameTicker.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using Content.Server.GameTicking;
using Content.Server.Mobs;
using Content.Shared.Roles;
@@ -17,12 +18,12 @@ namespace Content.Server.Interfaces.GameTicking
public interface IGameTicker
{
GameRunLevel RunLevel { get; }
-
+
///
/// The map loaded by the GameTicker on round start.
///
MapId DefaultMap { get; }
-
+
///
/// The GridId loaded by the GameTicker on round start.
///
@@ -59,7 +60,7 @@ namespace Content.Server.Interfaces.GameTicking
void RemoveGameRule(GameRule rule);
IEnumerable ActiveGameRules { get; }
- bool TryGetPreset(string name, out Type type);
+ bool TryGetPreset(string name, [NotNullWhen(true)] out Type type);
void SetStartPreset(Type type, bool force = false);
void SetStartPreset(string name, bool force = false);