diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 21c3070b80..da9d4d693a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,6 +15,7 @@ /Content.*/GameTicking/ @moonheart08 @EmoGarbage404 /Resources/ServerInfo/ @moonheart08 @Chief-Engineer /Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404 +/Resources/ServerInfo/Guidebook/ServerRules/ @Chief-Engineer /Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer /Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer @@ -23,6 +24,7 @@ /Resources/Prototypes/Body/ @DrSmugleaf # suffering /Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf /Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf +/Resources/Prototypes/Guidebook/rules.yml @Chief-Engineer /Content.*/Body/ @DrSmugleaf /Content.YAMLLinter @DrSmugleaf /Content.Shared/Damage/ @DrSmugleaf diff --git a/Content.Benchmarks/PvsBenchmark.cs b/Content.Benchmarks/PvsBenchmark.cs index 0b4dd90762..fa7f9d4542 100644 --- a/Content.Benchmarks/PvsBenchmark.cs +++ b/Content.Benchmarks/PvsBenchmark.cs @@ -1,19 +1,17 @@ #nullable enable using System; -using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; using Content.IntegrationTests.Pair; +using Content.Server.Mind; using Content.Server.Warps; using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.Analyzers; -using Robust.Shared.Enums; using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; using Robust.Shared.Map; -using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Random; @@ -58,15 +56,20 @@ public class PvsBenchmark _pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false); _sys = _entMan.System(); + SetupAsync().Wait(); + } + + private async Task SetupAsync() + { // Spawn the map _pair.Server.ResolveDependency().SetSeed(42); - _pair.Server.WaitPost(() => + await _pair.Server.WaitPost(() => { var success = _entMan.System().TryLoad(_mapId, Map, out _); if (!success) throw new Exception("Map load failed"); _pair.Server.MapMan.DoMapInitialize(_mapId); - }).Wait(); + }); // Get list of ghost warp positions _spawns = _entMan.AllComponentsList() @@ -76,17 +79,19 @@ public class PvsBenchmark Array.Resize(ref _players, PlayerCount); - // Spawn "Players". - _pair.Server.WaitPost(() => + // Spawn "Players" + _players = await _pair.Server.AddDummySessions(PlayerCount); + await _pair.Server.WaitPost(() => { + var mind = _pair.Server.System(); for (var i = 0; i < PlayerCount; i++) { var pos = _spawns[i % _spawns.Length]; var uid =_entMan.SpawnEntity("MobHuman", pos); _pair.Server.ConsoleHost.ExecuteCommand($"setoutfit {_entMan.GetNetEntity(uid)} CaptainGear"); - _players[i] = new DummySession{AttachedEntity = uid}; + mind.ControlMob(_players[i].UserId, uid); } - }).Wait(); + }); // Repeatedly move players around so that they "explore" the map and see lots of entities. // This will populate their PVS data with out-of-view entities. @@ -168,20 +173,4 @@ public class PvsBenchmark }).Wait(); _pair.Server.PvsTick(_players); } - - private sealed class DummySession : ICommonSession - { - public SessionStatus Status => SessionStatus.InGame; - public EntityUid? AttachedEntity {get; set; } - public NetUserId UserId => default; - public string Name => string.Empty; - public short Ping => default; - public INetChannel Channel { get; set; } = default!; - public LoginType AuthType => default; - public HashSet ViewSubscriptions { get; } = new(); - public DateTime ConnectedTime { get; set; } - public SessionState State => default!; - public SessionData Data => default!; - public bool ClientSide { get; set; } - } } 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..b8034faf52 --- /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..9e1d53818f --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs @@ -0,0 +1,21 @@ +using Content.Client.Message; +using Content.Client.UserInterface.Controls; +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 : FancyWindow +{ + public BabyJailStatusWindow() + { + RobustXamlLoader.Load(this); + MessageLabel.SetMarkup(Loc.GetString("admin-ui-baby-jail-is-enabled")); + } +} 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/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml index fb68e6c790..ea89916ba8 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml @@ -1,15 +1,21 @@  + xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls" + xmlns:ot="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab" + xmlns:co="clr-namespace:Content.Client.UserInterface.Controls"> + + - - - - + + + + + diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs index a5c3008436..c8606ca80d 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs @@ -1,5 +1,7 @@ using Content.Client.Station; +using Content.Client.UserInterface.Controls; using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.XAML; using Robust.Shared.Map.Components; @@ -10,20 +12,20 @@ namespace Content.Client.Administration.UI.Tabs.ObjectsTab; [GenerateTypedNameReferences] public sealed partial class ObjectsTab : Control { - [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IGameTiming _timing = default!; private readonly List _objects = new(); - private List _selections = new(); + private readonly List _selections = new(); + private bool _ascending = false; // Set to false for descending order by default + private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName; + private readonly Color _altColor = Color.FromHex("#292B38"); + private readonly Color _defaultColor = Color.FromHex("#2F2F3B"); - public event Action? OnEntryKeyBindDown; + public event Action? OnEntryKeyBindDown; - // Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController - // OR - // I can do this. - private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2); - - private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2); + private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(2); + private TimeSpan _nextUpdate; public ObjectsTab() { @@ -42,6 +44,30 @@ public sealed partial class ObjectsTab : Control ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!); } + ListHeader.OnHeaderClicked += HeaderClicked; + SearchList.SearchBar = SearchLineEdit; + SearchList.GenerateItem += GenerateButton; + SearchList.DataFilterCondition += DataFilterCondition; + + RefreshObjectList(); + // Set initial selection and refresh the list to apply the initial sort order + var defaultSelection = ObjectsTabSelection.Grids; + ObjectTypeOptions.SelectId((int)defaultSelection); // Set the default selection + RefreshObjectList(defaultSelection); // Refresh the list with the default selection + + // Initialize the next update time + _nextUpdate = TimeSpan.Zero; + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + if (_timing.CurTime < _nextUpdate) + return; + + _nextUpdate = _timing.CurTime + _updateFrequency; + RefreshObjectList(); } @@ -81,32 +107,70 @@ public sealed partial class ObjectsTab : Control throw new ArgumentOutOfRangeException(nameof(selection), selection, null); } - foreach (var control in _objects) + entities.Sort((a, b) => { - ObjectList.RemoveChild(control); + var valueA = GetComparableValue(a, _headerClicked); + var valueB = GetComparableValue(b, _headerClicked); + return _ascending ? Comparer.Default.Compare(valueA, valueB) : Comparer.Default.Compare(valueB, valueA); + }); + + var listData = new List(); + for (int index = 0; index < entities.Count; index++) + { + var info = entities[index]; + listData.Add(new ObjectsListData(info, $"{info.Name} {info.Entity}", index % 2 == 0 ? _altColor : _defaultColor)); } - _objects.Clear(); - - foreach (var (name, nent) in entities) - { - var ctrl = new ObjectsTabEntry(name, nent); - _objects.Add(ctrl); - ObjectList.AddChild(ctrl); - ctrl.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(ctrl, args); - } + SearchList.PopulateList(listData); } - protected override void FrameUpdate(FrameEventArgs args) + private void GenerateButton(ListData data, ListContainerButton button) { - base.FrameUpdate(args); - - if (_timing.CurTime < _nextUpdate) + if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor }) return; - // I do not care for precision. - _nextUpdate = _timing.CurTime + _updateFrequency; + var entry = new ObjectsTabEntry(info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor }); + button.ToolTip = $"{info.Name}, {info.Entity}"; + button.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(args, data); + button.AddChild(entry); + } + + private bool DataFilterCondition(string filter, ListData listData) + { + if (listData is not ObjectsListData { FilteringString: var filteringString }) + return false; + + // If the filter is empty, do not filter out any entries + if (string.IsNullOrEmpty(filter)) + return true; + + return filteringString.Contains(filter, StringComparison.CurrentCultureIgnoreCase); + } + + private object GetComparableValue((string Name, NetEntity Entity) entity, ObjectsTabHeader.Header header) + { + return header switch + { + ObjectsTabHeader.Header.ObjectName => entity.Name, + ObjectsTabHeader.Header.EntityID => entity.Entity.ToString(), + _ => entity.Name + }; + } + + private void HeaderClicked(ObjectsTabHeader.Header header) + { + if (_headerClicked == header) + { + _ascending = !_ascending; + } + else + { + _headerClicked = header; + _ascending = true; + } + + ListHeader.UpdateHeaderSymbols(_headerClicked, _ascending); RefreshObjectList(); } @@ -118,3 +182,4 @@ public sealed partial class ObjectsTab : Control } } +public record ObjectsListData((string Name, NetEntity Entity) Info, string FilteringString, Color BackgroundColor) : ListData; diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml index 0f6975e365..83c4cc5697 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml @@ -1,6 +1,6 @@ - - + @@ -14,4 +14,4 @@ HorizontalExpand="True" ClipText="True"/> - + diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs index c9b2cd8b57..aab06c6ccd 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs @@ -1,19 +1,21 @@ using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; namespace Content.Client.Administration.UI.Tabs.ObjectsTab; [GenerateTypedNameReferences] -public sealed partial class ObjectsTabEntry : ContainerButton +public sealed partial class ObjectsTabEntry : PanelContainer { public NetEntity AssocEntity; - public ObjectsTabEntry(string name, NetEntity nent) + public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox) { RobustXamlLoader.Load(this); AssocEntity = nent; EIDLabel.Text = nent.ToString(); NameLabel.Text = name; + BackgroundColorPanel.PanelOverride = styleBox; } } diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml new file mode 100644 index 0000000000..71a1f5c7bc --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml @@ -0,0 +1,21 @@ + + + + + diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs new file mode 100644 index 0000000000..3a91b5b948 --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs @@ -0,0 +1,86 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Input; + +namespace Content.Client.Administration.UI.Tabs.ObjectsTab +{ + [GenerateTypedNameReferences] + public sealed partial class ObjectsTabHeader : Control + { + public event Action
? OnHeaderClicked; + + private const string ArrowUp = "↑"; + private const string ArrowDown = "↓"; + + public ObjectsTabHeader() + { + RobustXamlLoader.Load(this); + + ObjectNameLabel.OnKeyBindDown += ObjectNameClicked; + EntityIDLabel.OnKeyBindDown += EntityIDClicked; + } + + public Label GetHeader(Header header) + { + return header switch + { + Header.ObjectName => ObjectNameLabel, + Header.EntityID => EntityIDLabel, + _ => throw new ArgumentOutOfRangeException(nameof(header), header, null) + }; + } + + public void ResetHeaderText() + { + ObjectNameLabel.Text = Loc.GetString("object-tab-object-name"); + EntityIDLabel.Text = Loc.GetString("object-tab-entity-id"); + } + + public void UpdateHeaderSymbols(Header headerClicked, bool ascending) + { + ResetHeaderText(); + var arrow = ascending ? ArrowUp : ArrowDown; + GetHeader(headerClicked).Text += $" {arrow}"; + } + + private void HeaderClicked(GUIBoundKeyEventArgs args, Header header) + { + if (args.Function != EngineKeyFunctions.UIClick) + { + return; + } + + OnHeaderClicked?.Invoke(header); + args.Handle(); + } + + private void ObjectNameClicked(GUIBoundKeyEventArgs args) + { + HeaderClicked(args, Header.ObjectName); + } + + private void EntityIDClicked(GUIBoundKeyEventArgs args) + { + HeaderClicked(args, Header.EntityID); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + ObjectNameLabel.OnKeyBindDown -= ObjectNameClicked; + EntityIDLabel.OnKeyBindDown -= EntityIDClicked; + } + } + + public enum Header + { + ObjectName, + EntityID + } + } +} diff --git a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml index 89827d0642..ee7ba4d34f 100644 --- a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml +++ b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml @@ -31,12 +31,12 @@ - diff --git a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs index f6212cd5ee..c3bcf3ffa0 100644 --- a/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs @@ -12,7 +12,7 @@ public sealed partial class PanicBunkerTab : Control [Dependency] private readonly IConsoleHost _console = default!; private string _minAccountAge; - private string _minOverallHours; + private string _minOverallMinutes; public PanicBunkerTab() { @@ -25,9 +25,9 @@ public sealed partial class PanicBunkerTab : Control MinAccountAge.OnFocusExit += args => SendMinAccountAge(args.Text); _minAccountAge = MinAccountAge.Text; - MinOverallHours.OnTextEntered += args => SendMinOverallHours(args.Text); - MinOverallHours.OnFocusExit += args => SendMinOverallHours(args.Text); - _minOverallHours = MinOverallHours.Text; + MinOverallMinutes.OnTextEntered += args => SendMinOverallMinutes(args.Text); + MinOverallMinutes.OnFocusExit += args => SendMinOverallMinutes(args.Text); + _minOverallMinutes = MinOverallMinutes.Text; } private void SendMinAccountAge(string text) @@ -42,16 +42,16 @@ public sealed partial class PanicBunkerTab : Control _console.ExecuteCommand($"panicbunker_min_account_age {minutes}"); } - private void SendMinOverallHours(string text) + private void SendMinOverallMinutes(string text) { if (string.IsNullOrWhiteSpace(text) || - text == _minOverallHours || - !int.TryParse(text, out var hours)) + text == _minOverallMinutes || + !int.TryParse(text, out var minutes)) { return; } - _console.ExecuteCommand($"panicbunker_min_overall_hours {hours}"); + _console.ExecuteCommand($"panicbunker_min_overall_minutes {minutes}"); } public void UpdateStatus(PanicBunkerStatus status) @@ -68,10 +68,10 @@ public sealed partial class PanicBunkerTab : Control CountDeadminnedButton.Pressed = status.CountDeadminnedAdmins; ShowReasonButton.Pressed = status.ShowReason; - MinAccountAge.Text = status.MinAccountAgeHours.ToString(); + MinAccountAge.Text = status.MinAccountAgeMinutes.ToString(); _minAccountAge = MinAccountAge.Text; - MinOverallHours.Text = status.MinOverallHours.ToString(); - _minOverallHours = MinOverallHours.Text; + MinOverallMinutes.Text = status.MinOverallMinutes.ToString(); + _minOverallMinutes = MinOverallMinutes.Text; } } diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs index a8bfaddecf..826945e7cc 100644 --- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs @@ -53,6 +53,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data); RefreshPlayerList(_adminSystem.PlayerList); + } #region Antag Overlay @@ -110,7 +111,9 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab _players = players; PlayerCount.Text = $"Players: {_playerMan.PlayerCount}"; - var sortedPlayers = new List(players); + var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList(); + + var sortedPlayers = new List(filteredPlayers); sortedPlayers.Sort(Compare); UpdateHeaderSymbols(); diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 66000a8457..453658bebf 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -27,6 +27,7 @@ namespace Content.Client.Construction [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly ExamineSystemShared _examineSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -195,9 +196,8 @@ namespace Content.Client.Construction if (GhostPresent(loc)) return false; - // This InRangeUnobstructed should probably be replaced with "is there something blocking us in that tile?" var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem)); - if (!_interactionSystem.InRangeUnobstructed(user, loc, 20f, predicate: predicate)) + if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate)) return false; if (!CheckConstructionConditions(prototype, loc, dir, user, showPopup: true)) diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml index eec5c229cb..322e4e66a9 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml +++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml @@ -8,7 +8,7 @@ - + diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs index ad19bc30f4..9f3d5695bb 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs +++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs @@ -23,7 +23,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow private readonly ItemSlotsSystem _itemSlots; private readonly FlatpackSystem _flatpack; private readonly MaterialStorageSystem _materialStorage; - private readonly SpriteSystem _spriteSystem; private readonly EntityUid _owner; @@ -31,7 +30,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow public const string NoBoardEffectId = "FlatpackerNoBoardEffect"; private EntityUid? _currentBoard = EntityUid.Invalid; - private EntityUid? _machinePreview; public event Action? PackButtonPressed; @@ -43,7 +41,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow _itemSlots = _entityManager.System(); _flatpack = _entityManager.System(); _materialStorage = _entityManager.System(); - _spriteSystem = _entityManager.System(); _owner = uid; @@ -57,17 +54,10 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow { base.FrameUpdate(args); - if (_machinePreview is not { } && _entityManager.Deleted(_machinePreview)) - { - _machinePreview = null; - MachineSprite.SetEntity(_machinePreview); - } - if (!_entityManager.TryGetComponent(_owner, out var flatpacker) || !_itemSlots.TryGetSlot(_owner, flatpacker.SlotId, out var itemSlot)) return; - MachineBoardComponent? machineBoardComp = null; if (flatpacker.Packing) { PackButton.Disabled = true; @@ -75,11 +65,10 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow else if (_currentBoard != null) { Dictionary cost; - if (_entityManager.TryGetComponent(_currentBoard, out machineBoardComp) && - machineBoardComp.Prototype is not null) + if (_entityManager.TryGetComponent(_currentBoard, out var machineBoardComp)) cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp)); else - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker)); + cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null); PackButton.Disabled = !_materialStorage.CanChangeMaterialAmount(_owner, cost); } @@ -87,9 +76,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow if (_currentBoard == itemSlot.Item) return; - if (_machinePreview != null) - _entityManager.DeleteEntity(_machinePreview); - _currentBoard = itemSlot.Item; CostHeaderLabel.Visible = _currentBoard != null; InsertLabel.Visible = _currentBoard == null; @@ -99,35 +85,32 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow string? prototype = null; Dictionary? cost = null; - if (machineBoardComp != null || _entityManager.TryGetComponent(_currentBoard, out machineBoardComp)) + if (_entityManager.TryGetComponent(_currentBoard, out var newMachineBoardComp)) { - prototype = machineBoardComp.Prototype; - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp)); + prototype = newMachineBoardComp.Prototype; + cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, newMachineBoardComp)); } else if (_entityManager.TryGetComponent(_currentBoard, out var computerBoard)) { prototype = computerBoard.Prototype; - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker)); + cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null); } if (prototype is not null && cost is not null) { var proto = _prototypeManager.Index(prototype); - _machinePreview = _entityManager.Spawn(proto.ID); - _spriteSystem.ForceUpdate(_machinePreview.Value); + MachineSprite.SetPrototype(prototype); MachineNameLabel.SetMessage(proto.Name); CostLabel.SetMarkup(GetCostString(cost)); } } else { - _machinePreview = _entityManager.Spawn(NoBoardEffectId); + MachineSprite.SetPrototype(NoBoardEffectId); CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label")); MachineNameLabel.SetMessage(" "); PackButton.Disabled = true; } - - MachineSprite.SetEntity(_machinePreview); } private string GetCostString(Dictionary costs) @@ -149,7 +132,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow ("amount", amountText), ("material", Loc.GetString(matProto.Name))); - msg.AddMarkup(text); + msg.TryAddMarkup(text, out _); if (i != orderedCosts.Length - 1) msg.PushNewline(); @@ -157,12 +140,4 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow return msg.ToMarkup(); } - - public override void Close() - { - base.Close(); - - _entityManager.DeleteEntity(_machinePreview); - _machinePreview = null; - } } diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 452b091ea6..bc3b9b6dec 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -2,11 +2,9 @@ using Content.Client.Administration.Managers; using Content.Client.Changelog; using Content.Client.Chat.Managers; using Content.Client.Eui; -using Content.Client.Flash; using Content.Client.Fullscreen; using Content.Client.GhostKick; using Content.Client.Guidebook; -using Content.Client.Info; using Content.Client.Input; using Content.Client.IoC; using Content.Client.Launcher; @@ -53,7 +51,6 @@ namespace Content.Client.Entry [Dependency] private readonly IScreenshotHook _screenshotHook = default!; [Dependency] private readonly FullscreenHook _fullscreenHook = default!; [Dependency] private readonly ChangelogManager _changelogManager = default!; - [Dependency] private readonly RulesManager _rulesManager = default!; [Dependency] private readonly ViewportManager _viewportManager = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; [Dependency] private readonly IInputManager _inputManager = default!; @@ -126,7 +123,6 @@ namespace Content.Client.Entry _screenshotHook.Initialize(); _fullscreenHook.Initialize(); _changelogManager.Initialize(); - _rulesManager.Initialize(); _viewportManager.Initialize(); _ghostKick.Initialize(); _extendedDisconnectInformation.Initialize(); diff --git a/Content.Client/GameTicking/Managers/ClientGameTicker.cs b/Content.Client/GameTicking/Managers/ClientGameTicker.cs index 309db2eb4e..fcf5ae91a4 100644 --- a/Content.Client/GameTicking/Managers/ClientGameTicker.cs +++ b/Content.Client/GameTicking/Managers/ClientGameTicker.cs @@ -4,10 +4,12 @@ using Content.Client.Lobby; using Content.Client.RoundEnd; using Content.Shared.GameTicking; using Content.Shared.GameWindow; +using Content.Shared.Roles; using JetBrains.Annotations; using Robust.Client.Graphics; using Robust.Client.State; using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; namespace Content.Client.GameTicking.Managers { @@ -17,10 +19,9 @@ namespace Content.Client.GameTicking.Managers [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IClientAdminManager _admin = default!; [Dependency] private readonly IClyde _clyde = default!; - [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; - private Dictionary> _jobsAvailable = new(); + private Dictionary, int?>> _jobsAvailable = new(); private Dictionary _stationNames = new(); [ViewVariables] public bool AreWeReady { get; private set; } @@ -32,13 +33,13 @@ namespace Content.Client.GameTicking.Managers [ViewVariables] public TimeSpan StartTime { get; private set; } [ViewVariables] public new bool Paused { get; private set; } - [ViewVariables] public IReadOnlyDictionary> JobsAvailable => _jobsAvailable; + [ViewVariables] public IReadOnlyDictionary, int?>> JobsAvailable => _jobsAvailable; [ViewVariables] public IReadOnlyDictionary StationNames => _stationNames; public event Action? InfoBlobUpdated; public event Action? LobbyStatusUpdated; public event Action? LobbyLateJoinStatusUpdated; - public event Action>>? LobbyJobsAvailableUpdated; + public event Action, int?>>>? LobbyJobsAvailableUpdated; public override void Initialize() { @@ -69,7 +70,7 @@ namespace Content.Client.GameTicking.Managers // reading the console. E.g., logs like this one could leak the nuke station/grid: // > Grid NT-Arrivals 1101 (122/n25896) changed parent. Old parent: map 10 (121/n25895). New parent: FTL (123/n26470) #if !DEBUG - _map.Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning; + EntityManager.System().Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning; #endif } diff --git a/Content.Client/Guidebook/Components/GuideHelpComponent.cs b/Content.Client/Guidebook/Components/GuideHelpComponent.cs index db19bb9dcc..bb1d30bbc1 100644 --- a/Content.Client/Guidebook/Components/GuideHelpComponent.cs +++ b/Content.Client/Guidebook/Components/GuideHelpComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using Content.Shared.Guidebook; +using Robust.Shared.Prototypes; namespace Content.Client.Guidebook.Components; @@ -13,9 +14,8 @@ public sealed partial class GuideHelpComponent : Component /// What guides to include show when opening the guidebook. The first entry will be used to select the currently /// selected guidebook. /// - [DataField("guides", customTypeSerializer: typeof(PrototypeIdListSerializer), required: true)] - [ViewVariables] - public List Guides = new(); + [DataField(required: true)] + public List> Guides = new(); /// /// Whether or not to automatically include the children of the given guides. diff --git a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml index 8dbfde3c47..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="750 700" + SetSize="900 700" MinSize="100 200" Resizable="True" Title="{Loc 'guidebook-window-title'}"> @@ -18,12 +18,16 @@ Name="SearchBar" PlaceHolder="{Loc 'guidebook-filter-placeholder-text'}" HorizontalExpand="True" - Margin="0 5 10 5"> + Margin="0 5 10 5"> + + public sealed partial class DocumentParsingManager { + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IReflectionManager _reflectionManager = default!; + [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly ISandboxHelper _sandboxHelper = default!; private readonly Dictionary> _tagControlParsers = new(); @@ -37,6 +42,21 @@ public sealed partial class DocumentParsingManager ControlParser = SkipWhitespaces.Then(_controlParser.Many()); } + public bool TryAddMarkup(Control control, ProtoId entryId, bool log = true) + { + if (!_prototype.TryIndex(entryId, out var entry)) + return false; + + using var file = _resourceManager.ContentFileReadText(entry.Text); + return TryAddMarkup(control, file.ReadToEnd(), log); + } + + public bool TryAddMarkup(Control control, GuideEntry entry, bool log = true) + { + using var file = _resourceManager.ContentFileReadText(entry.Text); + return TryAddMarkup(control, file.ReadToEnd(), log); + } + public bool TryAddMarkup(Control control, string text, bool log = true) { try diff --git a/Content.Client/Guidebook/GuidebookSystem.cs b/Content.Client/Guidebook/GuidebookSystem.cs index a0532294d6..b05a8810d3 100644 --- a/Content.Client/Guidebook/GuidebookSystem.cs +++ b/Content.Client/Guidebook/GuidebookSystem.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Client.Guidebook.Components; using Content.Client.Light; using Content.Client.Verbs; +using Content.Shared.Guidebook; using Content.Shared.Interaction; using Content.Shared.Light.Components; using Content.Shared.Speech; @@ -31,10 +32,14 @@ public sealed class GuidebookSystem : EntitySystem [Dependency] private readonly RgbLightControllerSystem _rgbLightControllerSystem = default!; [Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!; [Dependency] private readonly TagSystem _tags = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; //CrystallPunk guidebook filter - public event Action, List?, string?, bool, string?>? OnGuidebookOpen; + public event Action>, + List>?, + ProtoId?, + bool, + ProtoId?>? OnGuidebookOpen; + public const string GuideEmbedTag = "GuideEmbeded"; private EntityUid _defaultUser; @@ -99,7 +104,7 @@ public sealed class GuidebookSystem : EntitySystem }); } - public void OpenHelp(List guides) + public void OpenHelp(List> guides) { OnGuidebookOpen?.Invoke(guides, null, null, true, guides[0]); } diff --git a/Content.Client/Info/InfoSystem.cs b/Content.Client/Info/InfoSystem.cs deleted file mode 100644 index b697994981..0000000000 --- a/Content.Client/Info/InfoSystem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.Info; -using Robust.Shared.Log; - -namespace Content.Client.Info; - -public sealed class InfoSystem : EntitySystem -{ - public RulesMessage Rules = new RulesMessage("Server Rules", "The server did not send any rules."); - [Dependency] private readonly RulesManager _rules = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(OnRulesReceived); - Log.Debug("Requested server info."); - RaiseNetworkEvent(new RequestRulesMessage()); - } - - private void OnRulesReceived(RulesMessage message, EntitySessionEventArgs eventArgs) - { - Log.Debug("Received server rules."); - Rules = message; - _rules.UpdateRules(); - } -} diff --git a/Content.Client/Info/RulesAndInfoWindow.cs b/Content.Client/Info/RulesAndInfoWindow.cs index 7a763a1d6f..b9131dcb3c 100644 --- a/Content.Client/Info/RulesAndInfoWindow.cs +++ b/Content.Client/Info/RulesAndInfoWindow.cs @@ -1,10 +1,8 @@ using System.Numerics; using Content.Client.UserInterface.Systems.EscapeMenu; -using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.Configuration; using Robust.Shared.ContentPack; namespace Content.Client.Info @@ -12,7 +10,6 @@ namespace Content.Client.Info public sealed class RulesAndInfoWindow : DefaultWindow { [Dependency] private readonly IResourceManager _resourceManager = default!; - [Dependency] private readonly RulesManager _rules = default!; public RulesAndInfoWindow() { @@ -22,8 +19,14 @@ namespace Content.Client.Info var rootContainer = new TabContainer(); - var rulesList = new Info(); - var tutorialList = new Info(); + var rulesList = new RulesControl + { + Margin = new Thickness(10) + }; + var tutorialList = new Info + { + Margin = new Thickness(10) + }; rootContainer.AddChild(rulesList); rootContainer.AddChild(tutorialList); @@ -31,7 +34,6 @@ namespace Content.Client.Info TabContainer.SetTabTitle(rulesList, Loc.GetString("ui-info-tab-rules")); TabContainer.SetTabTitle(tutorialList, Loc.GetString("ui-info-tab-tutorial")); - AddSection(rulesList, _rules.RulesSection()); PopulateTutorial(tutorialList); Contents.AddChild(rootContainer); diff --git a/Content.Client/Info/RulesControl.xaml b/Content.Client/Info/RulesControl.xaml index 3b24764688..04fa719123 100644 --- a/Content.Client/Info/RulesControl.xaml +++ b/Content.Client/Info/RulesControl.xaml @@ -1,6 +1,18 @@ - + + + + + + +