diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml b/Content.Client/Administration/UI/AdminMenuWindow.xaml
index 311d67b826..d3d3df02d9 100644
--- a/Content.Client/Administration/UI/AdminMenuWindow.xaml
+++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml
@@ -6,7 +6,8 @@
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
- xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab">
+ xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"
+ xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab">
@@ -14,6 +15,7 @@
+
diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
index c3ea67a3ed..f3aa2572f2 100644
--- a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
+++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
@@ -21,8 +21,12 @@ namespace Content.Client.Administration.UI
MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab"));
MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab"));
MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-panic-bunker-tab"));
- MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-players-tab"));
- MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-objects-tab"));
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+ MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-baby-jail-tab"));
+ MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-players-tab"));
+ MasterTabContainer.SetTabTitle(8, Loc.GetString("admin-menu-objects-tab"));
}
protected override void Dispose(bool disposing)
diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml
new file mode 100644
index 0000000000..3432f11669
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs
new file mode 100644
index 0000000000..4e5f679b45
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs
@@ -0,0 +1,18 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
+
+/*
+ * TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+[GenerateTypedNameReferences]
+public sealed partial class BabyJailStatusWindow : DefaultWindow
+{
+ public BabyJailStatusWindow()
+ {
+ RobustXamlLoader.Load(this);
+ }
+}
diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml
new file mode 100644
index 0000000000..dd770c2be5
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs
new file mode 100644
index 0000000000..aa9d6ced95
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs
@@ -0,0 +1,75 @@
+using Content.Shared.Administration.Events;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Console;
+
+/*
+ * TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
+
+[GenerateTypedNameReferences]
+public sealed partial class BabyJailTab : Control
+{
+ [Dependency] private readonly IConsoleHost _console = default!;
+
+ private string _maxAccountAge;
+ private string _maxOverallMinutes;
+
+ public BabyJailTab()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ MaxAccountAge.OnTextEntered += args => SendMaxAccountAge(args.Text);
+ MaxAccountAge.OnFocusExit += args => SendMaxAccountAge(args.Text);
+ _maxAccountAge = MaxAccountAge.Text;
+
+ MaxOverallMinutes.OnTextEntered += args => SendMaxOverallMinutes(args.Text);
+ MaxOverallMinutes.OnFocusExit += args => SendMaxOverallMinutes(args.Text);
+ _maxOverallMinutes = MaxOverallMinutes.Text;
+ }
+
+ private void SendMaxAccountAge(string text)
+ {
+ if (string.IsNullOrWhiteSpace(text) ||
+ text == _maxAccountAge ||
+ !int.TryParse(text, out var minutes))
+ {
+ return;
+ }
+
+ _console.ExecuteCommand($"babyjail_max_account_age {minutes}");
+ }
+
+ private void SendMaxOverallMinutes(string text)
+ {
+ if (string.IsNullOrWhiteSpace(text) ||
+ text == _maxOverallMinutes ||
+ !int.TryParse(text, out var minutes))
+ {
+ return;
+ }
+
+ _console.ExecuteCommand($"babyjail_max_overall_minutes {minutes}");
+ }
+
+ public void UpdateStatus(BabyJailStatus status)
+ {
+ EnabledButton.Pressed = status.Enabled;
+ EnabledButton.Text = Loc.GetString(status.Enabled
+ ? "admin-ui-baby-jail-enabled"
+ : "admin-ui-baby-jail-disabled"
+ );
+ EnabledButton.ModulateSelfOverride = status.Enabled ? Color.Red : null;
+ ShowReasonButton.Pressed = status.ShowReason;
+
+ MaxAccountAge.Text = status.MaxAccountAgeMinutes.ToString();
+ _maxAccountAge = MaxAccountAge.Text;
+
+ MaxOverallMinutes.Text = status.MaxOverallMinutes.ToString();
+ _maxOverallMinutes = MaxOverallMinutes.Text;
+ }
+}
diff --git a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml
index b52eacfa72..69534af7f6 100644
--- a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml
+++ b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml
@@ -2,7 +2,7 @@
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:fancyTree="clr-namespace:Content.Client.UserInterface.Controls.FancyTree"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
- SetSize="850 700"
+ SetSize="900 700"
MinSize="100 200"
Resizable="True"
Title="{Loc 'guidebook-window-title'}">
diff --git a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs
index f5ee1200cc..c904a9c789 100644
--- a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs
+++ b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs
@@ -4,10 +4,12 @@ using Content.Client.UserInterface.ControlExtensions;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Controls.FancyTree;
using Content.Client.UserInterface.Systems.Info;
+using Content.Shared.CCVar;
using Content.Shared.Guidebook;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
diff --git a/Content.Client/MapText/MapTextComponent.cs b/Content.Client/MapText/MapTextComponent.cs
new file mode 100644
index 0000000000..f70b3c8dd4
--- /dev/null
+++ b/Content.Client/MapText/MapTextComponent.cs
@@ -0,0 +1,20 @@
+using Content.Shared.MapText;
+using Robust.Client.Graphics;
+
+namespace Content.Client.MapText;
+
+[RegisterComponent]
+public sealed partial class MapTextComponent : SharedMapTextComponent
+{
+ ///
+ /// The font that gets cached on component init or state changes
+ ///
+ [ViewVariables]
+ public VectorFont? CachedFont;
+
+ ///
+ /// The text currently being displayed. This is either or the
+ /// localized text or
+ ///
+ public string CachedText = string.Empty;
+}
diff --git a/Content.Client/MapText/MapTextOverlay.cs b/Content.Client/MapText/MapTextOverlay.cs
new file mode 100644
index 0000000000..70e708bba8
--- /dev/null
+++ b/Content.Client/MapText/MapTextOverlay.cs
@@ -0,0 +1,85 @@
+using System.Numerics;
+using Content.Shared.MapText;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.RichText;
+using Robust.Shared;
+using Robust.Shared.Configuration;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.MapText;
+
+///
+/// Draws map text as an overlay
+///
+public sealed class MapTextOverlay : Overlay
+{
+ private readonly IConfigurationManager _configManager;
+ private readonly IEntityManager _entManager;
+ private readonly IUserInterfaceManager _uiManager;
+ private readonly SharedTransformSystem _transform;
+ public override OverlaySpace Space => OverlaySpace.ScreenSpace;
+
+ public MapTextOverlay(
+ IConfigurationManager configManager,
+ IEntityManager entManager,
+ IUserInterfaceManager uiManager,
+ SharedTransformSystem transform,
+ IResourceCache resourceCache,
+ IPrototypeManager prototypeManager)
+ {
+ _configManager = configManager;
+ _entManager = entManager;
+ _uiManager = uiManager;
+ _transform = transform;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (args.ViewportControl == null)
+ return;
+
+ args.DrawingHandle.SetTransform(Matrix3x2.Identity);
+
+ var scale = _configManager.GetCVar(CVars.DisplayUIScale);
+
+ if (scale == 0f)
+ scale = _uiManager.DefaultUIScale;
+
+ DrawWorld(args.ScreenHandle, args, scale);
+
+ args.DrawingHandle.UseShader(null);
+ }
+
+ private void DrawWorld(DrawingHandleScreen handle, OverlayDrawArgs args, float scale)
+ {
+ if ( args.ViewportControl == null)
+ return;
+
+ var matrix = args.ViewportControl.GetWorldToScreenMatrix();
+ var query = _entManager.AllEntityQueryEnumerator();
+
+ // Enlarge bounds to try prevent pop-in due to large text.
+ var bounds = args.WorldBounds.Enlarged(2);
+
+ while(query.MoveNext(out var uid, out var mapText))
+ {
+ var mapPos = _transform.GetMapCoordinates(uid);
+
+ if (mapPos.MapId != args.MapId)
+ continue;
+
+ if (!bounds.Contains(mapPos.Position))
+ continue;
+
+ if (mapText.CachedFont == null)
+ continue;
+
+ var pos = Vector2.Transform(mapPos.Position, matrix) + mapText.Offset;
+ var dimensions = handle.GetDimensions(mapText.CachedFont, mapText.CachedText, scale);
+ handle.DrawString(mapText.CachedFont, pos - dimensions / 2f, mapText.CachedText, scale, mapText.Color);
+ }
+ }
+}
diff --git a/Content.Client/MapText/MapTextSystem.cs b/Content.Client/MapText/MapTextSystem.cs
new file mode 100644
index 0000000000..96ce8f93c2
--- /dev/null
+++ b/Content.Client/MapText/MapTextSystem.cs
@@ -0,0 +1,81 @@
+using Content.Shared.MapText;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.RichText;
+using Robust.Shared.Configuration;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.Client.MapText;
+
+///
+public sealed class MapTextSystem : SharedMapTextSystem
+{
+ [Dependency] private readonly IConfigurationManager _configManager = default!;
+ [Dependency] private readonly IUserInterfaceManager _uiManager = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly IResourceCache _resourceCache = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IOverlayManager _overlayManager = default!;
+
+ private MapTextOverlay _overlay = default!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnComponentStartup);
+ SubscribeLocalEvent(HandleCompState);
+
+ _overlay = new MapTextOverlay(_configManager, EntityManager, _uiManager, _transform, _resourceCache, _prototypeManager);
+ _overlayManager.AddOverlay(_overlay);
+
+ // TODO move font prototype to robust.shared, then use ProtoId
+ DebugTools.Assert(_prototypeManager.HasIndex(SharedMapTextComponent.DefaultFont));
+ }
+
+ private void OnComponentStartup(Entity ent, ref ComponentStartup args)
+ {
+ CacheText(ent.Comp);
+ // TODO move font prototype to robust.shared, then use ProtoId
+ DebugTools.Assert(_prototypeManager.HasIndex(ent.Comp.FontId));
+ }
+
+ private void HandleCompState(Entity ent, ref ComponentHandleState args)
+ {
+ if (args.Current is not MapTextComponentState state)
+ return;
+
+ ent.Comp.Text = state.Text;
+ ent.Comp.LocText = state.LocText;
+ ent.Comp.Color = state.Color;
+ ent.Comp.FontId = state.FontId;
+ ent.Comp.FontSize = state.FontSize;
+ ent.Comp.Offset = state.Offset;
+
+ CacheText(ent.Comp);
+ }
+
+ private void CacheText(MapTextComponent component)
+ {
+ component.CachedFont = null;
+
+ component.CachedText = string.IsNullOrWhiteSpace(component.Text)
+ ? Loc.GetString(component.LocText)
+ : component.Text;
+
+ if (!_prototypeManager.TryIndex(component.FontId, out var fontPrototype))
+ {
+ component.CachedText = Loc.GetString("map-text-font-error");
+ component.Color = Color.Red;
+
+ if(_prototypeManager.TryIndex(SharedMapTextComponent.DefaultFont, out var @default))
+ component.CachedFont = new VectorFont(_resourceCache.GetResource(@default.Path), 14);
+ return;
+ }
+
+ var fontResource = _resourceCache.GetResource(fontPrototype.Path);
+ component.CachedFont = new VectorFont(fontResource, component.FontSize);
+ }
+}
diff --git a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
index 3d8235591a..e12ce0ab01 100644
--- a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
+++ b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
@@ -3,6 +3,7 @@ using Content.Client.Administration.Systems;
using Content.Client.Administration.UI;
using Content.Client.Administration.UI.Tabs.ObjectsTab;
using Content.Client.Administration.UI.Tabs.PanicBunkerTab;
+using Content.Client.Administration.UI.Tabs.BabyJailTab;
using Content.Client.Administration.UI.Tabs.PlayerTab;
using Content.Client.Gameplay;
using Content.Client.Lobby;
@@ -37,11 +38,13 @@ public sealed class AdminUIController : UIController,
private AdminMenuWindow? _window;
private MenuButton? AdminButton => UIManager.GetActiveUIWidgetOrNull()?.AdminButton;
private PanicBunkerStatus? _panicBunker;
+ private BabyJailStatus? _babyJail;
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent(OnPanicBunkerUpdated);
+ SubscribeNetworkEvent(OnBabyJailUpdated);
}
private void OnPanicBunkerUpdated(PanicBunkerChangedEvent msg, EntitySessionEventArgs args)
@@ -56,6 +59,18 @@ public sealed class AdminUIController : UIController,
}
}
+ private void OnBabyJailUpdated(BabyJailChangedEvent msg, EntitySessionEventArgs args)
+ {
+ var showDialog = _babyJail == null && msg.Status.Enabled;
+ _babyJail = msg.Status;
+ _window?.BabyJailControl.UpdateStatus(msg.Status);
+
+ if (showDialog)
+ {
+ UIManager.CreateWindow().OpenCentered();
+ }
+ }
+
public void OnStateEntered(GameplayState state)
{
EnsureWindow();
@@ -101,6 +116,13 @@ public sealed class AdminUIController : UIController,
if (_panicBunker != null)
_window.PanicBunkerControl.UpdateStatus(_panicBunker);
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+ if (_babyJail != null)
+ _window.BabyJailControl.UpdateStatus(_babyJail);
+
_window.PlayerTabControl.OnEntryKeyBindDown += PlayerTabEntryKeyBindDown;
_window.ObjectsTabControl.OnEntryKeyBindDown += ObjectsTabEntryKeyBindDown;
_window.OnOpen += OnWindowOpen;
diff --git a/Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs b/Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs
index 61195f3a9a..03ea47827f 100644
--- a/Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs
+++ b/Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs
@@ -3,11 +3,15 @@ using Content.Client.Gameplay;
using Content.Client.Guidebook;
using Content.Client.Guidebook.Controls;
using Content.Client.Lobby;
+using Content.Client.Players.PlayTimeTracking;
using Content.Client.UserInterface.Controls;
+using Content.Shared.CCVar;
using Content.Shared.Guidebook;
using Content.Shared.Input;
+using Robust.Client.State;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
+using Robust.Shared.Configuration;
using static Robust.Client.UserInterface.Controls.BaseButton;
using Robust.Shared.Input.Binding;
using Robust.Shared.Prototypes;
@@ -19,21 +23,25 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered UIManager.GetActiveUIWidgetOrNull()?.GuidebookButton;
public void OnStateEntered(LobbyState state)
{
- HandleStateEntered();
+ HandleStateEntered(state);
}
public void OnStateEntered(GameplayState state)
{
- HandleStateEntered();
+ HandleStateEntered(state);
}
- private void HandleStateEntered()
+ private void HandleStateEntered(State state)
{
DebugTools.Assert(_guideWindow == null);
@@ -42,6 +50,14 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered()
diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs
index 2cb7b77e82..f4f402a2dc 100644
--- a/Content.Server.Database/Model.cs
+++ b/Content.Server.Database/Model.cs
@@ -890,6 +890,10 @@ namespace Content.Server.Database
Whitelist = 1,
Full = 2,
Panic = 3,
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+ BabyJail = 4,
}
public class ServerBanHit
diff --git a/Content.Server/Administration/Commands/BabyJailCommand.cs b/Content.Server/Administration/Commands/BabyJailCommand.cs
new file mode 100644
index 0000000000..058b67ca52
--- /dev/null
+++ b/Content.Server/Administration/Commands/BabyJailCommand.cs
@@ -0,0 +1,139 @@
+using Content.Shared.Administration;
+using Content.Shared.CCVar;
+using Robust.Shared.Configuration;
+using Robust.Shared.Console;
+
+/*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+namespace Content.Server.Administration.Commands;
+
+[AdminCommand(AdminFlags.Server)]
+public sealed class BabyJailCommand : LocalizedCommands
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ public override string Command => "babyjail";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ var toggle = Toggle(CCVars.BabyJailEnabled, shell, args, _cfg);
+ if (toggle == null)
+ return;
+
+ shell.WriteLine(Loc.GetString(toggle.Value ? "babyjail-command-enabled" : "babyjail-command-disabled"));
+ }
+
+ public static bool? Toggle(CVarDef cvar, IConsoleShell shell, string[] args, IConfigurationManager config)
+ {
+ if (args.Length > 1)
+ {
+ shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1)));
+ return null;
+ }
+
+ var enabled = config.GetCVar(cvar);
+
+ switch (args.Length)
+ {
+ case 0:
+ enabled = !enabled;
+ break;
+ case 1 when !bool.TryParse(args[0], out enabled):
+ shell.WriteError(Loc.GetString("shell-argument-must-be-boolean"));
+ return null;
+ }
+
+ config.SetCVar(cvar, enabled);
+
+ return enabled;
+ }
+}
+
+
+[AdminCommand(AdminFlags.Server)]
+public sealed class BabyJailShowReasonCommand : LocalizedCommands
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ public override string Command => "babyjail_show_reason";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ var toggle = BabyJailCommand.Toggle(CCVars.BabyJailShowReason, shell, args, _cfg);
+ if (toggle == null)
+ return;
+
+ shell.WriteLine(Loc.GetString(toggle.Value
+ ? "babyjail-command-show-reason-enabled"
+ : "babyjail-command-show-reason-disabled"
+ ));
+ }
+}
+
+[AdminCommand(AdminFlags.Server)]
+public sealed class BabyJailMinAccountAgeCommand : LocalizedCommands
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ public override string Command => "babyjail_max_account_age";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ switch (args.Length)
+ {
+ case 0:
+ {
+ var current = _cfg.GetCVar(CCVars.BabyJailMaxAccountAge);
+ shell.WriteLine(Loc.GetString("babyjail-command-max-account-age-is", ("minutes", current)));
+ break;
+ }
+ case > 1:
+ shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1)));
+ return;
+ }
+
+ if (!int.TryParse(args[0], out var minutes))
+ {
+ shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
+ return;
+ }
+
+ _cfg.SetCVar(CCVars.BabyJailMaxAccountAge, minutes);
+ shell.WriteLine(Loc.GetString("babyjail-command-max-account-age-set", ("minutes", minutes)));
+ }
+}
+
+[AdminCommand(AdminFlags.Server)]
+public sealed class BabyJailMinOverallHoursCommand : LocalizedCommands
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ public override string Command => "babyjail_max_overall_minutes";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ switch (args.Length)
+ {
+ case 0:
+ {
+ var current = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes);
+ shell.WriteLine(Loc.GetString("babyjail-command-max-overall-minutes-is", ("minutes", current)));
+ break;
+ }
+ case > 1:
+ shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1)));
+ return;
+ }
+
+ if (!int.TryParse(args[0], out var hours))
+ {
+ shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
+ return;
+ }
+
+ _cfg.SetCVar(CCVars.BabyJailMaxOverallMinutes, hours);
+ shell.WriteLine(Loc.GetString("babyjail-command-overall-minutes-set", ("hours", hours)));
+ }
+}
diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs
index a1a6b33ebc..a12d106880 100644
--- a/Content.Server/Administration/Systems/AdminSystem.cs
+++ b/Content.Server/Administration/Systems/AdminSystem.cs
@@ -62,6 +62,7 @@ namespace Content.Server.Administration.Systems
private readonly HashSet _roundActivePlayers = new();
public readonly PanicBunkerStatus PanicBunker = new();
+ public readonly BabyJailStatus BabyJail = new();
public override void Initialize()
{
@@ -70,14 +71,25 @@ namespace Content.Server.Administration.Systems
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_adminManager.OnPermsChanged += OnAdminPermsChanged;
+ // Panic Bunker Settings
Subs.CVar(_config, CCVars.PanicBunkerEnabled, OnPanicBunkerChanged, true);
Subs.CVar(_config, CCVars.PanicBunkerDisableWithAdmins, OnPanicBunkerDisableWithAdminsChanged, true);
Subs.CVar(_config, CCVars.PanicBunkerEnableWithoutAdmins, OnPanicBunkerEnableWithoutAdminsChanged, true);
Subs.CVar(_config, CCVars.PanicBunkerCountDeadminnedAdmins, OnPanicBunkerCountDeadminnedAdminsChanged, true);
- Subs.CVar(_config, CCVars.PanicBunkerShowReason, OnShowReasonChanged, true);
+ Subs.CVar(_config, CCVars.PanicBunkerShowReason, OnPanicBunkerShowReasonChanged, true);
Subs.CVar(_config, CCVars.PanicBunkerMinAccountAge, OnPanicBunkerMinAccountAgeChanged, true);
Subs.CVar(_config, CCVars.PanicBunkerMinOverallHours, OnPanicBunkerMinOverallHoursChanged, true);
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+ // Baby Jail Settings
+ Subs.CVar(_config, CCVars.BabyJailEnabled, OnBabyJailChanged, true);
+ Subs.CVar(_config, CCVars.BabyJailShowReason, OnBabyJailShowReasonChanged, true);
+ Subs.CVar(_config, CCVars.BabyJailMaxAccountAge, OnBabyJailMaxAccountAgeChanged, true);
+ Subs.CVar(_config, CCVars.BabyJailMaxOverallMinutes, OnBabyJailMaxOverallMinutesChanged, true);
+
SubscribeLocalEvent(OnIdentityChanged);
SubscribeLocalEvent(OnPlayerAttached);
SubscribeLocalEvent(OnPlayerDetached);
@@ -249,6 +261,17 @@ namespace Content.Server.Administration.Systems
SendPanicBunkerStatusAll();
}
+ private void OnBabyJailChanged(bool enabled)
+ {
+ BabyJail.Enabled = enabled;
+ _chat.SendAdminAlert(Loc.GetString(enabled
+ ? "admin-ui-baby-jail-enabled-admin-alert"
+ : "admin-ui-baby-jail-disabled-admin-alert"
+ ));
+
+ SendBabyJailStatusAll();
+ }
+
private void OnPanicBunkerDisableWithAdminsChanged(bool enabled)
{
PanicBunker.DisableWithAdmins = enabled;
@@ -267,24 +290,42 @@ namespace Content.Server.Administration.Systems
UpdatePanicBunker();
}
- private void OnShowReasonChanged(bool enabled)
+ private void OnPanicBunkerShowReasonChanged(bool enabled)
{
PanicBunker.ShowReason = enabled;
SendPanicBunkerStatusAll();
}
+ private void OnBabyJailShowReasonChanged(bool enabled)
+ {
+ BabyJail.ShowReason = enabled;
+ SendBabyJailStatusAll();
+ }
+
private void OnPanicBunkerMinAccountAgeChanged(int minutes)
{
PanicBunker.MinAccountAgeHours = minutes / 60;
SendPanicBunkerStatusAll();
}
+ private void OnBabyJailMaxAccountAgeChanged(int minutes)
+ {
+ BabyJail.MaxAccountAgeMinutes = minutes;
+ SendBabyJailStatusAll();
+ }
+
private void OnPanicBunkerMinOverallHoursChanged(int hours)
{
PanicBunker.MinOverallHours = hours;
SendPanicBunkerStatusAll();
}
+ private void OnBabyJailMaxOverallMinutesChanged(int minutes)
+ {
+ BabyJail.MaxOverallMinutes = minutes;
+ SendBabyJailStatusAll();
+ }
+
private void UpdatePanicBunker()
{
var admins = PanicBunker.CountDeadminnedAdmins
@@ -326,6 +367,15 @@ namespace Content.Server.Administration.Systems
}
}
+ private void SendBabyJailStatusAll()
+ {
+ var ev = new BabyJailChangedEvent(BabyJail);
+ foreach (var admin in _adminManager.AllAdmins)
+ {
+ RaiseNetworkEvent(ev, admin);
+ }
+ }
+
///
/// Erases a player from the round.
/// This removes them and any trace of them from the round, deleting their
diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs
index e6bed6a84a..5754cd9eca 100644
--- a/Content.Server/Connection/ConnectionManager.cs
+++ b/Content.Server/Connection/ConnectionManager.cs
@@ -14,6 +14,9 @@ using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Timing;
+/*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
namespace Content.Server.Connection
{
@@ -129,6 +132,10 @@ namespace Content.Server.Connection
}
}
+ /*
+ * TODO: Jesus H Christ what is this utter mess of a function
+ * TODO: Break this apart into is constituent steps.
+ */
private async Task<(ConnectionDenyReason, string, List? bansHit)?> ShouldDeny(
NetConnectingArgs e)
{
@@ -209,10 +216,21 @@ namespace Content.Server.Connection
}
}
+ if (_cfg.GetCVar(CCVars.BabyJailEnabled) && adminData == null)
+ {
+ var result = await IsInvalidConnectionDueToBabyJail(userId, e);
+
+ if (result.IsInvalid)
+ return (ConnectionDenyReason.BabyJail, result.Reason, null);
+ }
+
+ var wasInGame = EntitySystem.TryGet(out var ticker) &&
+ ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
+ status == PlayerGameStatus.JoinedGame;
// Sunrise-Queue-Start
var isQueueEnabled = IoCManager.Instance!.TryResolveType(out var mgr) && mgr.IsEnabled;
- if (_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && !isPrivileged && !isQueueEnabled)
- // Sunrise-Queue-End
+ if (_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && !isPrivileged && !isQueueEnabled && !wasInGame)
+ // Sunrise-Queue-End
{
return (ConnectionDenyReason.Full, Loc.GetString("soft-player-cap-full"), null);
}
@@ -237,6 +255,57 @@ namespace Content.Server.Connection
return null;
}
+ private async Task<(bool IsInvalid, string Reason)> IsInvalidConnectionDueToBabyJail(NetUserId userId, NetConnectingArgs e)
+ {
+ // If you're whitelisted then bypass this whole thing
+ if (await _db.GetWhitelistStatusAsync(userId))
+ return (false, "");
+
+ // Initial cvar retrieval
+ var showReason = _cfg.GetCVar(CCVars.BabyJailShowReason);
+ var reason = _cfg.GetCVar(CCVars.BabyJailCustomReason);
+ var maxAccountAgeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxAccountAge);
+ var maxPlaytimeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes);
+
+ // Wait some time to lookup data
+ var record = await _dbManager.GetPlayerRecordByUserId(userId);
+
+ var isAccountAgeInvalid = record == null || record.FirstSeenTime.CompareTo(DateTimeOffset.Now - TimeSpan.FromMinutes(maxAccountAgeMinutes)) <= 0;
+ if (isAccountAgeInvalid && showReason)
+ {
+ var locAccountReason = reason != string.Empty
+ ? reason
+ : Loc.GetString("baby-jail-account-denied-reason",
+ ("reason",
+ Loc.GetString(
+ "baby-jail-account-reason-account",
+ ("minutes", maxAccountAgeMinutes))));
+
+ return (true, locAccountReason);
+ }
+
+ var overallTime = ( await _db.GetPlayTimes(e.UserId)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall);
+ var isTotalPlaytimeInvalid = overallTime == null || overallTime.TimeSpent.TotalMinutes >= maxPlaytimeMinutes;
+
+ if (isTotalPlaytimeInvalid && showReason)
+ {
+ var locPlaytimeReason = reason != string.Empty
+ ? reason
+ : Loc.GetString("baby-jail-account-denied-reason",
+ ("reason",
+ Loc.GetString(
+ "baby-jail-account-reason-overall",
+ ("minutes", maxPlaytimeMinutes))));
+
+ return (true, locPlaytimeReason);
+ }
+
+ if (!showReason && isTotalPlaytimeInvalid || isAccountAgeInvalid)
+ return (true, Loc.GetString("baby-jail-account-denied"));
+
+ return (false, "");
+ }
+
private bool HasTemporaryBypass(NetUserId user)
{
return _temporaryBypasses.TryGetValue(user, out var time) && time > _gameTiming.RealTime;
diff --git a/Content.Server/GameTicking/GameTicker.StatusShell.cs b/Content.Server/GameTicking/GameTicker.StatusShell.cs
index af79145aae..62a2feb794 100644
--- a/Content.Server/GameTicking/GameTicker.StatusShell.cs
+++ b/Content.Server/GameTicking/GameTicker.StatusShell.cs
@@ -53,6 +53,12 @@ namespace Content.Server.GameTicking
jObject["players"] = players; // Sunrise-Queue
jObject["soft_max_players"] = _cfg.GetCVar(CCVars.SoftMaxPlayers);
jObject["panic_bunker"] = _cfg.GetCVar(CCVars.PanicBunkerEnabled);
+
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+ jObject["baby_jail"] = _cfg.GetCVar(CCVars.BabyJailEnabled);
jObject["run_level"] = (int) _runLevel;
if (preset != null)
jObject["preset"] = Loc.GetString(preset.ModeTitle);
diff --git a/Content.Server/MapText/MapTextComponent.cs b/Content.Server/MapText/MapTextComponent.cs
new file mode 100644
index 0000000000..2af9583484
--- /dev/null
+++ b/Content.Server/MapText/MapTextComponent.cs
@@ -0,0 +1,6 @@
+using Content.Shared.MapText;
+
+namespace Content.Server.MapText;
+
+[RegisterComponent]
+public sealed partial class MapTextComponent : SharedMapTextComponent;
diff --git a/Content.Server/MapText/MapTextSystem.cs b/Content.Server/MapText/MapTextSystem.cs
new file mode 100644
index 0000000000..5632c06e1a
--- /dev/null
+++ b/Content.Server/MapText/MapTextSystem.cs
@@ -0,0 +1,28 @@
+using Content.Shared.MapText;
+using Robust.Shared.GameStates;
+
+namespace Content.Server.MapText;
+
+///
+public sealed class MapTextSystem : SharedMapTextSystem
+{
+ ///
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(GetCompState);
+ }
+
+ private void GetCompState(Entity ent, ref ComponentGetState args)
+ {
+ args.State = new MapTextComponentState
+ {
+ Text = ent.Comp.Text,
+ LocText = ent.Comp.LocText,
+ Color = ent.Comp.Color,
+ FontId = ent.Comp.FontId,
+ FontSize = ent.Comp.FontSize,
+ Offset = ent.Comp.Offset
+ };
+ }
+}
diff --git a/Content.Shared/Administration/Events/BabyJailChangedEvent.cs b/Content.Shared/Administration/Events/BabyJailChangedEvent.cs
new file mode 100644
index 0000000000..56d5ce5162
--- /dev/null
+++ b/Content.Shared/Administration/Events/BabyJailChangedEvent.cs
@@ -0,0 +1,22 @@
+using Robust.Shared.Serialization;
+
+/*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+namespace Content.Shared.Administration.Events;
+
+[Serializable, NetSerializable]
+public sealed class BabyJailStatus
+{
+ public bool Enabled;
+ public bool ShowReason;
+ public int MaxAccountAgeMinutes;
+ public int MaxOverallMinutes;
+}
+
+[Serializable, NetSerializable]
+public sealed class BabyJailChangedEvent(BabyJailStatus status) : EntityEventArgs
+{
+ public BabyJailStatus Status = status;
+}
diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs
index ed0c96ebea..f9fcc82448 100644
--- a/Content.Shared/CCVar/CCVars.cs
+++ b/Content.Shared/CCVar/CCVars.cs
@@ -26,6 +26,12 @@ namespace Content.Shared.CCVar
public static readonly CVarDef RulesFile =
CVarDef.Create("server.rules_file", "DefaultRuleset", CVar.REPLICATED | CVar.SERVER);
+ ///
+ /// Guide entry that is displayed by default when a guide is opened.
+ ///
+ public static readonly CVarDef DefaultGuide =
+ CVarDef.Create("server.default_guide", "NewPlayer", CVar.REPLICATED | CVar.SERVER);
+
/*
* Ambience
*/
@@ -314,6 +320,48 @@ namespace Content.Shared.CCVar
public static readonly CVarDef BypassBunkerWhitelist =
CVarDef.Create("game.panic_bunker.whitelisted_can_bypass", true, CVar.SERVERONLY);
+ /*
+ * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
+ */
+
+ ///
+ /// Whether the baby jail is currently enabled.
+ ///
+ public static readonly CVarDef BabyJailEnabled =
+ CVarDef.Create("game.baby_jail.enabled", false, CVar.NOTIFY | CVar.REPLICATED | CVar.SERVER);
+
+ ///
+ /// Show reason of disconnect for user or not.
+ ///
+ public static readonly CVarDef BabyJailShowReason =
+ CVarDef.Create("game.baby_jail.show_reason", false, CVar.SERVERONLY);
+
+ ///
+ /// Maximum age of the account (from server's PoV, so from first-seen date) in minutes that can access baby
+ /// jailed servers.
+ ///
+ public static readonly CVarDef BabyJailMaxAccountAge =
+ CVarDef.Create("game.baby_jail.max_account_age", 1440, CVar.SERVERONLY);
+
+ ///
+ /// Maximum overall played time allowed to access baby jailed servers.
+ ///
+ public static readonly CVarDef BabyJailMaxOverallMinutes =
+ CVarDef.Create("game.baby_jail.max_overall_minutes", 120, CVar.SERVERONLY);
+
+ ///
+ /// A custom message that will be used for connections denied due to the baby jail.
+ /// If not empty, then will overwrite
+ ///
+ public static readonly CVarDef BabyJailCustomReason =
+ CVarDef.Create("game.baby_jail.custom_reason", string.Empty, CVar.SERVERONLY);
+
+ ///
+ /// Allow bypassing the baby jail if the user is whitelisted.
+ ///
+ public static readonly CVarDef BypassBabyJailWhitelist =
+ CVarDef.Create("game.baby_jail.whitelisted_can_bypass", true, CVar.SERVERONLY);
+
///
/// Make people bonk when trying to climb certain objects like tables.
///
diff --git a/Content.Shared/MapText/SharedMapTextComponent.cs b/Content.Shared/MapText/SharedMapTextComponent.cs
new file mode 100644
index 0000000000..7b8a02da6f
--- /dev/null
+++ b/Content.Shared/MapText/SharedMapTextComponent.cs
@@ -0,0 +1,51 @@
+using System.Numerics;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.MapText;
+
+///
+/// This is used for displaying text in world space
+///
+
+[NetworkedComponent, Access(typeof(SharedMapTextSystem))]
+public abstract partial class SharedMapTextComponent : Component
+{
+ public const string DefaultFont = "Default";
+
+ ///
+ /// The text to display. This will override .
+ ///
+ [DataField]
+ public string? Text;
+
+ ///
+ /// The localized-id of the text that should be displayed.
+ ///
+ [DataField]
+ public LocId LocText = "map-text-default";
+ // TODO VV: LocId editing
+
+ [DataField]
+ public Color Color = Color.White;
+
+ [DataField]
+ public string FontId = DefaultFont;
+
+ [DataField]
+ public int FontSize = 12;
+
+ [DataField]
+ public Vector2 Offset = Vector2.Zero;
+}
+
+[Serializable, NetSerializable]
+public sealed class MapTextComponentState : ComponentState
+{
+ public string? Text { get; init;}
+ public LocId LocText { get; init;}
+ public Color Color { get; init;}
+ public string FontId { get; init; } = default!;
+ public int FontSize { get; init;}
+ public Vector2 Offset { get; init;}
+}
diff --git a/Content.Shared/MapText/SharedMapTextSystem.cs b/Content.Shared/MapText/SharedMapTextSystem.cs
new file mode 100644
index 0000000000..da2adf16cf
--- /dev/null
+++ b/Content.Shared/MapText/SharedMapTextSystem.cs
@@ -0,0 +1,6 @@
+namespace Content.Shared.MapText;
+
+///
+/// This handles registering the map text overlay, caching the text font and handling component state
+///
+public abstract class SharedMapTextSystem : EntitySystem;
diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml
index b7e3286806..c5ed9267b1 100644
--- a/Resources/Changelog/Changelog.yml
+++ b/Resources/Changelog/Changelog.yml
@@ -1,19 +1,4 @@
Entries:
-- author: lzk228
- changes:
- - message: Snoring is now a trait in character preference menu.
- type: Tweak
- id: 6205
- time: '2024-03-22T07:00:57.0000000+00:00'
- url: https://github.com/space-wizards/space-station-14/pull/26322
-- author: liltenhead
- changes:
- - message: reduced the odds of budget insulated gloves being insulated and increased
- the damage multipliers on bad gloves.
- type: Tweak
- id: 6206
- time: '2024-03-22T08:20:46.0000000+00:00'
- url: https://github.com/space-wizards/space-station-14/pull/26318
- author: BlitzTheSquishy
changes:
- message: Irish Coffee recipe adjusted to be more logical
@@ -3851,3 +3836,20 @@
id: 6704
time: '2024-06-08T23:29:51.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28724
+- author: AJCM-git
+ changes:
+ - message: When people with less than an hour playing join the game, the guidebook
+ is automatically opened for them.
+ type: Tweak
+ id: 6705
+ time: '2024-06-09T16:26:48.0000000+00:00'
+ url: https://github.com/space-wizards/space-station-14/pull/28774
+- author: Aeshus
+ changes:
+ - message: Salvage has a comprehensive guide.
+ type: Add
+ - message: Cargo's guide is up-to-date.
+ type: Tweak
+ id: 6706
+ time: '2024-06-09T18:28:46.0000000+00:00'
+ url: https://github.com/space-wizards/space-station-14/pull/28775
diff --git a/Resources/Locale/en-US/administration/commands/babyjail.ftl b/Resources/Locale/en-US/administration/commands/babyjail.ftl
new file mode 100644
index 0000000000..5a9d949051
--- /dev/null
+++ b/Resources/Locale/en-US/administration/commands/babyjail.ftl
@@ -0,0 +1,19 @@
+cmd-babyjail-desc = Toggles the baby jail, which enables stricter restrictions on who's allowed to join the server.
+cmd-babyjail-help = Usage: babyjail
+babyjail-command-enabled = Baby jail has been enabled.
+babyjail-command-disabled = Baby jail has been disabled.
+
+cmd-babyjail_show_reason-desc = Toggles whether or not to show connecting clients the reason why the baby jail blocked them from joining.
+cmd-babyjail_show_reason-help = Usage: babyjail_show_reason
+babyjail-command-show-reason-enabled = The baby jail will now show a reason to users it blocks from connecting.
+babyjail-command-show-reason-disabled = The baby jail will no longer show a reason to users it blocks from connecting.
+
+cmd-babyjail_max_account_age-desc = Gets or sets the maximum account age in minutes that an account can have to be allowed to connect with the baby jail enabled.
+cmd-babyjail_max_account_age-help = Usage: babyjail_max_account_age
+babyjail-command-max-account-age-is = The maximum account age for the baby jail is {$minutes} minutes.
+babyjail-command-max-account-age-set = Set the maximum account age for the baby jail to {$minutes} minutes.
+
+cmd-babyjail_max_overall_minutes-desc = Gets or sets the maximum overall playtime in minutes that an account can have to be allowed to connect with the baby jail enabled.
+cmd-babyjail_max_overall_minutes-help = Usage: babyjail_max_overall_minutes
+babyjail-command-max-overall-minutes-is = The maximum overall playtime for the baby jail is {$minutes} minutes.
+babyjail-command-max-overall-minutes-set = Set the maximum overall playtime for the baby jail to {$minutes} minutes.
diff --git a/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl b/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl
index c759e4c2cb..03b2046a9e 100644
--- a/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl
+++ b/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl
@@ -7,5 +7,6 @@ admin-menu-atmos-tab = Atmos
admin-menu-round-tab = Round
admin-menu-server-tab = Server
admin-menu-panic-bunker-tab = Panic Bunker
+admin-menu-baby-jail-tab = Baby Jail
admin-menu-players-tab = Players
admin-menu-objects-tab = Objects
diff --git a/Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl b/Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl
new file mode 100644
index 0000000000..ad58e436b3
--- /dev/null
+++ b/Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl
@@ -0,0 +1,16 @@
+admin-ui-baby-jail-window-title = Baby Jail
+
+admin-ui-baby-jail-enabled = Baby Jail Enabled
+admin-ui-baby-jail-disabled = Baby Jail Disabled
+admin-ui-baby-jail-tooltip = The baby jail restricts players from joining if their account is too old or they do have too much overall playtime on this server.
+
+admin-ui-baby-jail-show-reason = Show Reason
+admin-ui-baby-jail-show-reason-tooltip = Show the user why they were blocked from connecting by the baby jail.
+
+admin-ui-baby-jail-max-account-age = Max. Account Age
+admin-ui-baby-jail-max-overall-minutes = Max. Overall Playtime
+
+admin-ui-baby-jail-is-enabled = The baby jail is currently enabled.
+
+admin-ui-baby-jail-enabled-admin-alert = The baby jail has been enabled.
+admin-ui-baby-jail-disabled-admin-alert = The baby jail has been disabled.
diff --git a/Resources/Locale/en-US/connection-messages.ftl b/Resources/Locale/en-US/connection-messages.ftl
index 7f4afce8b9..88b6442e39 100644
--- a/Resources/Locale/en-US/connection-messages.ftl
+++ b/Resources/Locale/en-US/connection-messages.ftl
@@ -39,3 +39,9 @@ panic-bunker-account-denied = This server is in panic bunker mode, often enabled
panic-bunker-account-denied-reason = This server is in panic bunker mode, often enabled as a precaution against raids. New connections by accounts not meeting certain requirements are temporarily not accepted. Try again later. Reason: "{$reason}"
panic-bunker-account-reason-account = Your Space Station 14 account is too new. It must be older than {$minutes} minutes
panic-bunker-account-reason-overall = Your overall playtime on the server must be greater than {$hours} hours
+
+baby-jail-account-denied = This server is a newbie server, intended for new players and those who want to help them. New connections by accounts that are too old or are not on a whitelist are not accepted. Check out some other servers and see everything Space Station 14 has to offer. Have fun!
+baby-jail-account-denied-reason = This server is a newbie server, intended for new players and those who want to help them. New connections by accounts that are too old or are not on a whitelist are not accepted. Check out some other servers and see everything Space Station 14 has to offer. Have fun! Reason: "{$reason}"
+baby-jail-account-reason-account = Your Space Station 14 account is too old. It must be younger than {$minutes} minutes
+baby-jail-account-reason-overall = Your overall playtime on the server must be younger than {$hours} hours
+
diff --git a/Resources/Locale/en-US/generic.ftl b/Resources/Locale/en-US/generic.ftl
index d9c1305e41..3504097885 100644
--- a/Resources/Locale/en-US/generic.ftl
+++ b/Resources/Locale/en-US/generic.ftl
@@ -10,6 +10,7 @@ generic-error = error
generic-invalid = invalid
generic-hours = hours
+generic-minutes = minutes
generic-playtime-title = Playtime
diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl
index 838d2ce4a6..dcd5ee04b6 100644
--- a/Resources/Locale/en-US/guidebook/guides.ftl
+++ b/Resources/Locale/en-US/guidebook/guides.ftl
@@ -50,13 +50,13 @@ guide-entry-command = Command
guide-entry-service = Service
guide-entry-newplayer = New? Start here!
-guide-entry-charactercreation = Character Creation
+guide-entry-charactercreation = Creating Characters
guide-entry-species = Species
guide-entry-yourfirstcharacter = Your First Character
guide-entry-controls = Controls
guide-entry-radio = Radio and Speech
-guide-entry-references = Tables and References
+guide-entry-references = Tables & References
guide-entry-drinks = Drinks
guide-entry-foodrecipes = Food Recipes
guide-entry-chemicals = Chemicals
diff --git a/Resources/Locale/en-US/mapping/map-text-component.ftl b/Resources/Locale/en-US/mapping/map-text-component.ftl
new file mode 100644
index 0000000000..0a4d54b485
--- /dev/null
+++ b/Resources/Locale/en-US/mapping/map-text-component.ftl
@@ -0,0 +1,2 @@
+map-text-default = Use VV to change the displayed text
+map-text-font-error = "Error - invalid font"
diff --git a/Resources/Prototypes/Entities/Markers/map_text.yml b/Resources/Prototypes/Entities/Markers/map_text.yml
new file mode 100644
index 0000000000..e8496138ac
--- /dev/null
+++ b/Resources/Prototypes/Entities/Markers/map_text.yml
@@ -0,0 +1,11 @@
+- type: entity
+ id: MapText
+ parent: MarkerBase
+ name: map text
+ placement:
+ mode: PlaceFree
+ components:
+ - type: MapText
+ - type: Sprite
+ state: pink
+
diff --git a/Resources/Prototypes/Guidebook/station.yml b/Resources/Prototypes/Guidebook/station.yml
index 9570b617c0..d868a6b58f 100644
--- a/Resources/Prototypes/Guidebook/station.yml
+++ b/Resources/Prototypes/Guidebook/station.yml
@@ -3,7 +3,6 @@
name: guide-entry-ss14
text: "/ServerInfo/Guidebook/SpaceStation14.xml"
children:
- - Controls
- Jobs
- Survival
- Antagonists
diff --git a/Resources/ServerInfo/Guidebook/Cargo/TradeStation.xml b/Resources/ServerInfo/Guidebook/Cargo/TradeStation.xml
deleted file mode 100644
index bde0506b17..0000000000
--- a/Resources/ServerInfo/Guidebook/Cargo/TradeStation.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
- # Trade Station
- The Trade Station is seperate from the station, usually floating around in space visible on the scanner and shuttle computer. It is self maintaining, and self powered with solar pannels, so cargo doesn't need to do anything for it to work.
-
- ## Selling
- Using the [color=#a4885c]cargo shuttle[/color], you are able to transport items and bounties to sell at the trade station by flying the shuttle (explained in [textlink="cargo" link="Cargo"]) and docking it to the trade station. Then you bring the objects to sell over the cargo pallets, go to the computer inbetween the 2 sets of pallets and sell what can be sold. Dont worry, you can't sell yourself! Then [color=#118C4F]Spessos[/color] should come out of the computer, and can be inserted into the Quartermasters Digiboard or the Cargo Computer to be added to the Bank.
-
- ## Collecting Orders
- All cargo orders are sent to the [color=#a4885c]Trade Station[/color] to be [color=#a4885c]collected[/color], this includes items sent by centcom. To collect them you have to travel to the Trade station and they should be there on the cargo pallets in a crate, ready to be taken back to the station.
-
diff --git a/Resources/ServerInfo/Guidebook/NewPlayer/NewPlayer.xml b/Resources/ServerInfo/Guidebook/NewPlayer/NewPlayer.xml
index 303230593b..50a102beff 100644
--- a/Resources/ServerInfo/Guidebook/NewPlayer/NewPlayer.xml
+++ b/Resources/ServerInfo/Guidebook/NewPlayer/NewPlayer.xml
@@ -1,28 +1,42 @@
-# Welcome!
+# Welcome to Space Station 14!
-Welcome to the alpha build of Space Station 14!
-You have just started your shift aboard a Nanotrasen space station. You and your crewmates should cooperate, perform your jobs the best you can and have fun.
+You've just begun your shift aboard a Nanotrasen space station.
+You and your fellow crewmates are tasked with working together, surviving, and having fun!
-We hope you enjoy the game, and this entry will serve to guide you on how to learn to play.
+[bold]Make sure to follow all server rules.[/bold] You can ask questions or report rule breaks to the [color=#EB2D3A][bold]admin team[/bold][/color] by using the [bold]admin help menu[/bold]. ([italic]accessible by pressing [/italic][color=yellow][bold][keybind="OpenAHelp"/][/bold][/color])
-There's quite a bit to read, but SS14 is in itself a very large and in-depth game, and hopefully these guides help you enjoy it to its fullest.
+More detailed guides about [italic]specific jobs[/italic] and [italic]mechanics[/italic] can be found [textlink="here." link="SS14"/]
-This entry and its subentries contain simple and broad information new players need to play the game on most servers.
+This guide can be reopened at any time using [color=yellow][bold][keybind="OpenGuidebook"][/bold][/color].
-Some information given here may be repeated or further touched upon in the Station and Shifts entry, [textlink="which can be found here." link="SS14"]
+## Basic Controls
+The following are the controls you'll need for regular gameplay:
-## How to use this guidebook
-This guidebook contains [color=#a4885c]step-by-step instructions[/color] on how to perform tasks, [color=#a4885c]tables of components[/color] one might require,
-or simply [color=#a4885c]tips, tricks and guidelines[/color] on how to perform your job.
+- Run using [color=yellow][bold][keybind="MoveUp"][keybind="MoveLeft"][keybind="MoveDown"][keybind="MoveRight"][/bold][/color]. Hold down [color=yellow][bold][keybind="Walk"][/bold][/color] to walk
-You can refresh yourself on your job before starting your shift or reference certain pages as needed, [textlink="such as recipes for cocktails" link="Drinks"].
+- Examine the world around you with [color=yellow][bold][keybind="ExamineEntity"][/bold][/color]
-## Wait, what's Space Station 14?
-Space Station 14 is a free (forever) open source remake of the infamous Space Station 13, hoping to provide an improved experience for both newcomers and old players alike. Space Station 14 is designed as a fully moddable experience that you can modify to your liking with custom servers, adding entire swaths of new content for people to explore.
+- Interact with the world using [color=yellow][bold][keybind="Use"][/bold][/color]
-Please make sure you understand the server rules (F1) and if you have any questions, consult this guide, your colleagues on shift in person or over radio or via discord. You can also ask admins for help (ahelp) by hitting the escape key > admin help and typing a message to any online admins. Especially do this if you witness someone breaking rules.
+- Hovering over an entity and pressing [color=yellow][bold][keybind="UseSecondary"][/bold][/color] shows the [italic]context menu[/italic], which contains additional interactions
-[color=#a4885c]To reiterate, you are all a crew of people who work together[/color] and you should not be harming each other unless you have an extremely good reason (such as self-defence) and even then, you should call security over radio.
+- Swap hands using [color=yellow][bold][keybind="SwapHands"][/bold][/color] and activate the currently held item with [color=yellow][bold][keybind="ActivateItemInHand"][/bold][/color]
+
+- Drop held items with [color=yellow][bold][keybind="Drop"][/bold][/color] or throw them using [color=yellow][bold][keybind="ThrowItemInHand"][/bold][/color]
+
+- Pull and release things with [color=yellow][bold][keybind="TryPullObject"][/bold][/color] and [color=yellow][bold][keybind="ReleasePulledObject"][/bold][/color]
+
+A more comprehensive list can be found on the [textlink="controls" link="Controls"] page.
+
+## What is SS14?
+Space Station 14 is a multiplayer game about paranoia and chaos on a space station and a remake of the cult-classic Space Station 13.
+
+Join dozens of other players on an intricately designed and simulated space station, immersing yourself in roleplay and dealing with threats along the way.
+
+SS14 is an [italic]open-source game[/italic] developed by people [bold]just like you![/bold]
+It receives [color=lime]daily updates[/color] and may at times be [color=#EB2D3A][bold]unstable.[/bold][/color]
+
+[italic]If you want to report bugs or help with development, join the discord and learn how to get started.[/italic]