diff --git a/.editorconfig b/.editorconfig
index 58d0d332bbe6..a5dfab07a503 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,4 +1,5 @@
root = true
+
[*]
charset = utf-8
@@ -9,9 +10,10 @@ indent_style = space
tab_width = 4
# New line preferences
-end_of_line = crlf:suggestion
+#end_of_line = crlf
insert_final_newline = true
trim_trailing_whitespace = true
+max_line_length = 120
#### .NET Coding Conventions ####
@@ -104,7 +106,6 @@ csharp_preferred_modifier_order = public, private, protected, internal, new, abs
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
-csharp_style_namespace_declarations = file_scoped:suggestion
#### C# Formatting Rules ####
@@ -278,7 +279,7 @@ dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case
dotnet_naming_style.t_upper_camel_case_style.required_prefix = T
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
-dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
+dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
dotnet_naming_symbols.constants_symbols.required_modifiers = const
@@ -317,27 +318,32 @@ dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
-dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly
+dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static, readonly
dotnet_naming_symbols.property_symbols.applicable_accessibilities = *
dotnet_naming_symbols.property_symbols.applicable_kinds = property
-dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
+dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
-dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
+dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
-dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
+dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static, readonly
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = *
-dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate
+dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace, class, struct, enum, delegate
dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = *
dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter
# ReSharper properties
resharper_braces_for_ifelse = required_for_multiline
+resharper_csharp_wrap_arguments_style = chop_if_long
+resharper_csharp_wrap_parameters_style = chop_if_long
resharper_keep_existing_attribute_arrangement = true
+resharper_wrap_chained_binary_patterns = chop_if_long
+resharper_wrap_chained_method_calls = chop_if_long
+resharper_csharp_trailing_comma_in_multiline_lists = true
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
indent_size = 2
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 69c5f0138ff5..da9d4d693a81 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,11 +24,12 @@
/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
-/Content.*/Anomaly/ @EmoGarbage404
+/Content.*/Anomaly/ @EmoGarbage404 @TheShuEd
/Content.*/Lathe/ @EmoGarbage404
/Content.*/Materials/ @EmoGarbage404
/Content.*/Mech/ @EmoGarbage404
@@ -35,7 +37,7 @@
/Content.*/Stack/ @EmoGarbage404
/Content.*/Xenoarchaeology/ @EmoGarbage404
/Content.*/Zombies/ @EmoGarbage404
-/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404
+/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404 @TheShuEd
/Resources/Prototypes/Research/ @EmoGarbage404
/Content.*/Forensics/ @ficcialfaint
diff --git a/.github/labeler.yml b/.github/labeler.yml
index b088f229434c..886ce89708c8 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,12 +1,18 @@
"Changes: Sprites":
-- '**/*.rsi/*.png'
+- changed-files:
+ - any-glob-to-any-file: '**/*.rsi/*.png'
"Changes: Map":
-- 'Resources/Maps/*.yml'
-- 'Resources/Prototypes/Maps/*.yml'
+- changed-files:
+ - any-glob-to-any-file:
+ - 'Resources/Maps/*.yml'
+ - 'Resources/Prototypes/Maps/*.yml'
"Changes: UI":
-- '**/*.xaml*'
+- changed-files:
+ - any-glob-to-any-file: '**/*.xaml*'
"No C#":
-- all: ["!**/*.cs"]
+- changed-files:
+ # Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
+ - all-globs-to-all-files: "!**/*.cs"
diff --git a/.github/workflows/conflict-labeler.yml b/.github/workflows/conflict-labeler.yml
index a78716bde685..1e2125c30a23 100644
--- a/.github/workflows/conflict-labeler.yml
+++ b/.github/workflows/conflict-labeler.yml
@@ -1,18 +1,20 @@
name: Check Merge Conflicts
on:
- push:
- branches:
- - master
pull_request_target:
+ types:
+ - opened
+ - synchronize
+ - reopened
+ - ready_for_review
jobs:
Label:
- if: github.actor != 'PJBot'
+ if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
runs-on: ubuntu-latest
steps:
- name: Check for Merge Conflicts
- uses: ike709/actions-label-merge-conflict@9eefdd17e10566023c46d2dc6dc04fcb8ec76142
+ uses: eps1lon/actions-label-merge-conflict@v3.0.0
with:
dirtyLabel: "Merge Conflict"
repoToken: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/labeler-pr.yml b/.github/workflows/labeler-pr.yml
index 711eb0ccac01..42ed10098120 100644
--- a/.github/workflows/labeler-pr.yml
+++ b/.github/workflows/labeler-pr.yml
@@ -6,8 +6,9 @@ on:
jobs:
labeler:
if: github.actor != 'PJBot'
+ permissions:
+ contents: read
+ pull-requests: write
runs-on: ubuntu-latest
steps:
- - uses: actions/labeler@v3
- with:
- repo-token: "${{ secrets.GITHUB_TOKEN }}"
+ - uses: actions/labeler@v5
diff --git a/.github/workflows/rsi-diff.yml b/.github/workflows/rsi-diff.yml
index 1f122526d738..98cc97e9221d 100644
--- a/.github/workflows/rsi-diff.yml
+++ b/.github/workflows/rsi-diff.yml
@@ -15,9 +15,12 @@ jobs:
- name: Get changed files
id: files
- uses: Ana06/get-changed-files@v1.2
+ uses: Ana06/get-changed-files@v2.3.0
with:
format: 'space-delimited'
+ filter: |
+ **.rsi
+ **.png
- name: Diff changed RSIs
id: diff
diff --git a/Content.Benchmarks/ComponentQueryBenchmark.cs b/Content.Benchmarks/ComponentQueryBenchmark.cs
new file mode 100644
index 000000000000..11c7ab9d5f59
--- /dev/null
+++ b/Content.Benchmarks/ComponentQueryBenchmark.cs
@@ -0,0 +1,273 @@
+#nullable enable
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Configs;
+using Content.IntegrationTests;
+using Content.IntegrationTests.Pair;
+using Content.Shared.Clothing.Components;
+using Content.Shared.Doors.Components;
+using Content.Shared.Item;
+using Robust.Server.GameObjects;
+using Robust.Shared;
+using Robust.Shared.Analyzers;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Random;
+
+namespace Content.Benchmarks;
+
+///
+/// Benchmarks for comparing the speed of various component fetching/lookup related methods, including directed event
+/// subscriptions
+///
+[Virtual]
+[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
+[CategoriesColumn]
+public class ComponentQueryBenchmark
+{
+ public const string Map = "Maps/atlas.yml";
+
+ private TestPair _pair = default!;
+ private IEntityManager _entMan = default!;
+ private MapId _mapId = new(10);
+ private EntityQuery _itemQuery;
+ private EntityQuery _clothingQuery;
+ private EntityQuery _mapQuery;
+ private EntityUid[] _items = default!;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ ProgramShared.PathOffset = "../../../../";
+ PoolManager.Startup(typeof(QueryBenchSystem).Assembly);
+
+ _pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
+ _entMan = _pair.Server.ResolveDependency();
+
+ _itemQuery = _entMan.GetEntityQuery();
+ _clothingQuery = _entMan.GetEntityQuery();
+ _mapQuery = _entMan.GetEntityQuery();
+
+ _pair.Server.ResolveDependency().SetSeed(42);
+ _pair.Server.WaitPost(() =>
+ {
+ var success = _entMan.System().TryLoad(_mapId, Map, out _);
+ if (!success)
+ throw new Exception("Map load failed");
+ _pair.Server.MapMan.DoMapInitialize(_mapId);
+ }).GetAwaiter().GetResult();
+
+ _items = new EntityUid[_entMan.Count()];
+ var i = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out var uid, out _))
+ {
+ _items[i++] = uid;
+ }
+ }
+
+ [GlobalCleanup]
+ public async Task Cleanup()
+ {
+ await _pair.DisposeAsync();
+ PoolManager.Shutdown();
+ }
+
+ #region TryComp
+
+ ///
+ /// Baseline TryComp benchmark. When the benchmark was created, around 40% of the items were clothing.
+ ///
+ [Benchmark(Baseline = true)]
+ [BenchmarkCategory("TryComp")]
+ public int TryComp()
+ {
+ var hashCode = 0;
+ foreach (var uid in _items)
+ {
+ if (_clothingQuery.TryGetComponent(uid, out var clothing))
+ hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
+ }
+ return hashCode;
+ }
+
+ ///
+ /// Variant of that is meant to always fail to get a component.
+ ///
+ [Benchmark]
+ [BenchmarkCategory("TryComp")]
+ public int TryCompFail()
+ {
+ var hashCode = 0;
+ foreach (var uid in _items)
+ {
+ if (_mapQuery.TryGetComponent(uid, out var map))
+ hashCode = HashCode.Combine(hashCode, map.GetHashCode());
+ }
+ return hashCode;
+ }
+
+ ///
+ /// Variant of that is meant to always succeed getting a component.
+ ///
+ [Benchmark]
+ [BenchmarkCategory("TryComp")]
+ public int TryCompSucceed()
+ {
+ var hashCode = 0;
+ foreach (var uid in _items)
+ {
+ if (_itemQuery.TryGetComponent(uid, out var item))
+ hashCode = HashCode.Combine(hashCode, item.GetHashCode());
+ }
+ return hashCode;
+ }
+
+ ///
+ /// Variant of that uses `Resolve()` to try get the component.
+ ///
+ [Benchmark]
+ [BenchmarkCategory("TryComp")]
+ public int Resolve()
+ {
+ var hashCode = 0;
+ foreach (var uid in _items)
+ {
+ DoResolve(uid, ref hashCode);
+ }
+ return hashCode;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void DoResolve(EntityUid uid, ref int hash, ClothingComponent? clothing = null)
+ {
+ if (_clothingQuery.Resolve(uid, ref clothing, false))
+ hash = HashCode.Combine(hash, clothing.GetHashCode());
+ }
+
+ #endregion
+
+ #region Enumeration
+
+ [Benchmark]
+ [BenchmarkCategory("Item Enumerator")]
+ public int SingleItemEnumerator()
+ {
+ var hashCode = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out var item))
+ {
+ hashCode = HashCode.Combine(hashCode, item.GetHashCode());
+ }
+
+ return hashCode;
+ }
+
+ [Benchmark]
+ [BenchmarkCategory("Item Enumerator")]
+ public int DoubleItemEnumerator()
+ {
+ var hashCode = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out _, out var item))
+ {
+ hashCode = HashCode.Combine(hashCode, item.GetHashCode());
+ }
+
+ return hashCode;
+ }
+
+ [Benchmark]
+ [BenchmarkCategory("Item Enumerator")]
+ public int TripleItemEnumerator()
+ {
+ var hashCode = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out _, out _, out var xform))
+ {
+ hashCode = HashCode.Combine(hashCode, xform.GetHashCode());
+ }
+
+ return hashCode;
+ }
+
+ [Benchmark]
+ [BenchmarkCategory("Airlock Enumerator")]
+ public int SingleAirlockEnumerator()
+ {
+ var hashCode = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out var airlock))
+ {
+ hashCode = HashCode.Combine(hashCode, airlock.GetHashCode());
+ }
+
+ return hashCode;
+ }
+
+ [Benchmark]
+ [BenchmarkCategory("Airlock Enumerator")]
+ public int DoubleAirlockEnumerator()
+ {
+ var hashCode = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out _, out var door))
+ {
+ hashCode = HashCode.Combine(hashCode, door.GetHashCode());
+ }
+
+ return hashCode;
+ }
+
+ [Benchmark]
+ [BenchmarkCategory("Airlock Enumerator")]
+ public int TripleAirlockEnumerator()
+ {
+ var hashCode = 0;
+ var enumerator = _entMan.AllEntityQueryEnumerator();
+ while (enumerator.MoveNext(out _, out _, out var xform))
+ {
+ hashCode = HashCode.Combine(hashCode, xform.GetHashCode());
+ }
+
+ return hashCode;
+ }
+
+ #endregion
+
+ [Benchmark(Baseline = true)]
+ [BenchmarkCategory("Events")]
+ public int StructEvents()
+ {
+ var ev = new QueryBenchEvent();
+ foreach (var uid in _items)
+ {
+ _entMan.EventBus.RaiseLocalEvent(uid, ref ev);
+ }
+
+ return ev.HashCode;
+ }
+}
+
+[ByRefEvent]
+public struct QueryBenchEvent
+{
+ public int HashCode;
+}
+
+public sealed class QueryBenchSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnEvent);
+ }
+
+ private void OnEvent(EntityUid uid, ClothingComponent component, ref QueryBenchEvent args)
+ {
+ args.HashCode = HashCode.Combine(args.HashCode, component.GetHashCode());
+ }
+}
diff --git a/Content.Benchmarks/EntityManagerGetAllComponents.cs b/Content.Benchmarks/EntityManagerGetAllComponents.cs
index 0b9683a4abb6..8e02b8d71de6 100644
--- a/Content.Benchmarks/EntityManagerGetAllComponents.cs
+++ b/Content.Benchmarks/EntityManagerGetAllComponents.cs
@@ -47,6 +47,7 @@ public void Setup()
var componentFactory = new Mock();
componentFactory.Setup(p => p.GetComponent()).Returns(new DummyComponent());
+ componentFactory.Setup(m => m.GetIndex(typeof(DummyComponent))).Returns(CompIdx.Index());
componentFactory.Setup(p => p.GetRegistration(It.IsAny())).Returns(dummyReg);
componentFactory.Setup(p => p.GetAllRegistrations()).Returns(new[] { dummyReg });
componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] { CompIdx.Index() });
diff --git a/Content.Benchmarks/EntityQueryBenchmark.cs b/Content.Benchmarks/EntityQueryBenchmark.cs
deleted file mode 100644
index cef6a5e35c58..000000000000
--- a/Content.Benchmarks/EntityQueryBenchmark.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-#nullable enable
-using System;
-using System.Threading.Tasks;
-using BenchmarkDotNet.Attributes;
-using Content.IntegrationTests;
-using Content.IntegrationTests.Pair;
-using Content.Shared.Clothing.Components;
-using Content.Shared.Item;
-using Robust.Server.GameObjects;
-using Robust.Shared;
-using Robust.Shared.Analyzers;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
-using Robust.Shared.Random;
-
-namespace Content.Benchmarks;
-
-[Virtual]
-public class EntityQueryBenchmark
-{
- public const string Map = "Maps/atlas.yml";
-
- private TestPair _pair = default!;
- private IEntityManager _entMan = default!;
- private MapId _mapId = new MapId(10);
- private EntityQuery _clothingQuery;
-
- [GlobalSetup]
- public void Setup()
- {
- ProgramShared.PathOffset = "../../../../";
- PoolManager.Startup(null);
-
- _pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
- _entMan = _pair.Server.ResolveDependency();
-
- _pair.Server.ResolveDependency().SetSeed(42);
- _pair.Server.WaitPost(() =>
- {
- var success = _entMan.System().TryLoad(_mapId, Map, out _);
- if (!success)
- throw new Exception("Map load failed");
- _pair.Server.MapMan.DoMapInitialize(_mapId);
- }).GetAwaiter().GetResult();
-
- _clothingQuery = _entMan.GetEntityQuery();
-
- // Apparently ~40% of entities are items, and 1 in 6 of those are clothing.
- /*
- var entCount = _entMan.EntityCount;
- var itemCount = _entMan.Count();
- var clothingCount = _entMan.Count();
- var itemRatio = (float) itemCount / entCount;
- var clothingRatio = (float) clothingCount / entCount;
- Console.WriteLine($"Entities: {entCount}. Items: {itemRatio:P2}. Clothing: {clothingRatio:P2}.");
- */
- }
-
- [GlobalCleanup]
- public async Task Cleanup()
- {
- await _pair.DisposeAsync();
- PoolManager.Shutdown();
- }
-
- [Benchmark]
- public int HasComponent()
- {
- var hashCode = 0;
- var enumerator = _entMan.AllEntityQueryEnumerator();
- while (enumerator.MoveNext(out var uid, out var _))
- {
- if (_entMan.HasComponent(uid))
- hashCode = HashCode.Combine(hashCode, uid.Id);
- }
-
- return hashCode;
- }
-
- [Benchmark]
- public int HasComponentQuery()
- {
- var hashCode = 0;
- var enumerator = _entMan.AllEntityQueryEnumerator();
- while (enumerator.MoveNext(out var uid, out var _))
- {
- if (_clothingQuery.HasComponent(uid))
- hashCode = HashCode.Combine(hashCode, uid.Id);
- }
-
- return hashCode;
- }
-
- [Benchmark]
- public int TryGetComponent()
- {
- var hashCode = 0;
- var enumerator = _entMan.AllEntityQueryEnumerator();
- while (enumerator.MoveNext(out var uid, out var _))
- {
- if (_entMan.TryGetComponent(uid, out ClothingComponent? clothing))
- hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
- }
-
- return hashCode;
- }
-
- [Benchmark]
- public int TryGetComponentQuery()
- {
- var hashCode = 0;
- var enumerator = _entMan.AllEntityQueryEnumerator();
- while (enumerator.MoveNext(out var uid, out var _))
- {
- if (_clothingQuery.TryGetComponent(uid, out var clothing))
- hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
- }
-
- return hashCode;
- }
-
- ///
- /// Enumerate all entities with both an item and clothing component.
- ///
- [Benchmark]
- public int Enumerator()
- {
- var hashCode = 0;
- var enumerator = _entMan.AllEntityQueryEnumerator();
- while (enumerator.MoveNext(out var _, out var clothing))
- {
- hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
- }
-
- return hashCode;
- }
-}
diff --git a/Content.Benchmarks/MapLoadBenchmark.cs b/Content.Benchmarks/MapLoadBenchmark.cs
index ce95e9a158db..a3319e3067ff 100644
--- a/Content.Benchmarks/MapLoadBenchmark.cs
+++ b/Content.Benchmarks/MapLoadBenchmark.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -26,7 +26,7 @@ public class MapLoadBenchmark
public void Setup()
{
ProgramShared.PathOffset = "../../../../";
- PoolManager.Startup(null);
+ PoolManager.Startup();
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
var server = _pair.Server;
@@ -46,7 +46,7 @@ public async Task Cleanup()
PoolManager.Shutdown();
}
- public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry" };
+ public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry", "Oasis" };
[ParamsSource(nameof(MapsSource))]
public string Map;
diff --git a/Content.Benchmarks/PvsBenchmark.cs b/Content.Benchmarks/PvsBenchmark.cs
index c7f22bdb0cdb..fa7f9d454226 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;
@@ -49,7 +47,7 @@ public void Setup()
#if !DEBUG
ProgramShared.PathOffset = "../../../../";
#endif
- PoolManager.Startup(null);
+ PoolManager.Startup();
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
_entMan = _pair.Server.ResolveDependency();
@@ -58,15 +56,20 @@ public void Setup()
_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 void Setup()
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 void CycleTick()
}).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.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs
index 8512107b69de..0638d945aa5c 100644
--- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs
+++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs
@@ -32,7 +32,7 @@ public class SpawnEquipDeleteBenchmark
public async Task SetupAsync()
{
ProgramShared.PathOffset = "../../../../";
- PoolManager.Startup(null);
+ PoolManager.Startup();
_pair = await PoolManager.GetServerClient();
var server = _pair.Server;
diff --git a/Content.Client/Access/IdCardSystem.cs b/Content.Client/Access/IdCardSystem.cs
index fcf2bf57de38..e0c02976f7b3 100644
--- a/Content.Client/Access/IdCardSystem.cs
+++ b/Content.Client/Access/IdCardSystem.cs
@@ -2,6 +2,4 @@
namespace Content.Client.Access;
-public sealed class IdCardSystem : SharedIdCardSystem
-{
-}
+public sealed class IdCardSystem : SharedIdCardSystem;
diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs
index 73f18aec8d6b..761f52988a9d 100644
--- a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs
+++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs
@@ -1,5 +1,7 @@
using Content.Shared.Access.Systems;
+using Content.Shared.StatusIcon;
using Robust.Client.GameObjects;
+using Robust.Shared.Prototypes;
namespace Content.Client.Access.UI
{
@@ -40,9 +42,9 @@ private void OnJobChanged(string newJob)
SendMessage(new AgentIDCardJobChangedMessage(newJob));
}
- public void OnJobIconChanged(string newJobIcon)
+ public void OnJobIconChanged(ProtoId newJobIconId)
{
- SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon));
+ SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
}
///
@@ -57,7 +59,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.SetCurrentName(cast.CurrentName);
_window.SetCurrentJob(cast.CurrentJob);
- _window.SetAllowedIcons(cast.Icons);
+ _window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
}
protected override void Dispose(bool disposing)
diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs
index beca0c41ba93..6d0b2a184f49 100644
--- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs
+++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs
@@ -38,7 +38,7 @@ public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
}
- public void SetAllowedIcons(HashSet icons)
+ public void SetAllowedIcons(HashSet> icons, string currentJobIconId)
{
IconGrid.DisposeAllChildren();
@@ -46,10 +46,8 @@ public void SetAllowedIcons(HashSet icons)
var i = 0;
foreach (var jobIconId in icons)
{
- if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
- {
+ if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
continue;
- }
String styleBase = StyleBase.ButtonOpenBoth;
var modulo = i % JobIconColumnCount;
@@ -77,8 +75,12 @@ public void SetAllowedIcons(HashSet icons)
};
jobIconButton.AddChild(jobIconTexture);
- jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
+ jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
IconGrid.AddChild(jobIconButton);
+
+ if (jobIconId.Equals(currentJobIconId))
+ jobIconButton.Pressed = true;
+
i++;
}
}
diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
index 5b7011c195ab..a321b4121e58 100644
--- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
+++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
@@ -1,6 +1,5 @@
using Content.Shared.Access;
using Content.Shared.Access.Components;
-using Content.Shared.Access;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest;
diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
index 298912e7d536..82f6ebd8b59c 100644
--- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
+++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
@@ -27,6 +27,9 @@ public sealed partial class IdCardConsoleWindow : DefaultWindow
private string? _lastJobTitle;
private string? _lastJobProto;
+ // The job that will be picked if the ID doesn't have a job on the station.
+ private static ProtoId _defaultJob = "Passenger";
+
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager,
List> accessLevels)
{
@@ -65,7 +68,6 @@ public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeMana
}
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
-
_accessButtons.Populate(accessLevels, prototypeManager);
AccessLevelControlContainer.AddChild(_accessButtons);
@@ -172,11 +174,15 @@ public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
new List>());
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
- if (jobIndex >= 0)
+ // If the job index is < 0 that means they don't have a job registered in the station records.
+ // For example, a new ID from a box would have no job index.
+ if (jobIndex < 0)
{
- JobPresetOptionButton.SelectId(jobIndex);
+ jobIndex = _jobPrototypeIds.IndexOf(_defaultJob);
}
+ JobPresetOptionButton.SelectId(jobIndex);
+
_lastFullName = state.TargetIdFullName;
_lastJobTitle = state.TargetIdJobTitle;
_lastJobProto = state.TargetIdJobPrototype;
diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs
index 5ff003452ab7..aff6c1ff7be6 100644
--- a/Content.Client/Actions/ActionsSystem.cs
+++ b/Content.Client/Actions/ActionsSystem.cs
@@ -247,7 +247,10 @@ public void TriggerAction(EntityUid actionId, BaseActionComponent action)
if (action.ClientExclusive)
{
if (instantAction.Event != null)
+ {
instantAction.Event.Performer = user;
+ instantAction.Event.Action = actionId;
+ }
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
}
diff --git a/Content.Client/Administration/Components/HeadstandComponent.cs b/Content.Client/Administration/Components/HeadstandComponent.cs
index d95e74576bfd..a4e3bfc5aaf4 100644
--- a/Content.Client/Administration/Components/HeadstandComponent.cs
+++ b/Content.Client/Administration/Components/HeadstandComponent.cs
@@ -3,7 +3,7 @@
namespace Content.Client.Administration.Components;
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
diff --git a/Content.Client/Administration/Components/KillSignComponent.cs b/Content.Client/Administration/Components/KillSignComponent.cs
index 1cf47b93ff50..91c44ef3f275 100644
--- a/Content.Client/Administration/Components/KillSignComponent.cs
+++ b/Content.Client/Administration/Components/KillSignComponent.cs
@@ -3,6 +3,5 @@
namespace Content.Client.Administration.Components;
-[NetworkedComponent, RegisterComponent]
-public sealed partial class KillSignComponent : SharedKillSignComponent
-{ }
+[RegisterComponent]
+public sealed partial class KillSignComponent : SharedKillSignComponent;
diff --git a/Content.Client/Administration/Managers/ClientAdminManager.cs b/Content.Client/Administration/Managers/ClientAdminManager.cs
index fdd62fb6a2dd..0f740c810459 100644
--- a/Content.Client/Administration/Managers/ClientAdminManager.cs
+++ b/Content.Client/Administration/Managers/ClientAdminManager.cs
@@ -126,12 +126,15 @@ void IPostInjectInit.PostInject()
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
{
- return uid == _player.LocalEntity ? _adminData : null;
+ if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin))
+ return _adminData;
+
+ return null;
}
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
{
- if (_player.LocalUser == session.UserId)
+ if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin))
return _adminData;
return null;
diff --git a/Content.Client/Administration/Systems/AdminFrozenSystem.cs b/Content.Client/Administration/Systems/AdminFrozenSystem.cs
new file mode 100644
index 000000000000..885585f985ce
--- /dev/null
+++ b/Content.Client/Administration/Systems/AdminFrozenSystem.cs
@@ -0,0 +1,7 @@
+using Content.Shared.Administration;
+
+namespace Content.Client.Administration.Systems;
+
+public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
+{
+}
diff --git a/Content.Client/Administration/Systems/AdminVerbSystem.cs b/Content.Client/Administration/Systems/AdminVerbSystem.cs
index e0f84bc4f039..dced59bbf2e0 100644
--- a/Content.Client/Administration/Systems/AdminVerbSystem.cs
+++ b/Content.Client/Administration/Systems/AdminVerbSystem.cs
@@ -1,3 +1,6 @@
+using Content.Shared.Administration;
+using Content.Shared.Administration.Managers;
+using Content.Shared.Mind.Components;
using Content.Shared.Verbs;
using Robust.Client.Console;
using Robust.Shared.Utility;
@@ -11,10 +14,12 @@ sealed class AdminVerbSystem : EntitySystem
{
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
+ [Dependency] private readonly ISharedAdminManager _admin = default!;
public override void Initialize()
{
SubscribeLocalEvent>(AddAdminVerbs);
+
}
private void AddAdminVerbs(GetVerbsEvent args)
@@ -33,6 +38,24 @@ private void AddAdminVerbs(GetVerbsEvent args)
};
args.Verbs.Add(verb);
}
+
+ if (!_admin.IsAdmin(args.User))
+ return;
+
+ if (_admin.HasAdminFlag(args.User, AdminFlags.Admin))
+ args.ExtraCategories.Add(VerbCategory.Admin);
+
+ if (_admin.HasAdminFlag(args.User, AdminFlags.Fun) && HasComp(args.Target))
+ args.ExtraCategories.Add(VerbCategory.Antag);
+
+ if (_admin.HasAdminFlag(args.User, AdminFlags.Debug))
+ args.ExtraCategories.Add(VerbCategory.Debug);
+
+ if (_admin.HasAdminFlag(args.User, AdminFlags.Fun))
+ args.ExtraCategories.Add(VerbCategory.Smite);
+
+ if (_admin.HasAdminFlag(args.User, AdminFlags.Admin))
+ args.ExtraCategories.Add(VerbCategory.Tricks);
}
}
}
diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml b/Content.Client/Administration/UI/AdminMenuWindow.xaml
index 311d67b826c7..d3d3df02d936 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 c3ea67a3edb9..f3aa2572f2f9 100644
--- a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
+++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs
@@ -21,8 +21,12 @@ public AdminMenuWindow()
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/BanPanel/BanPanel.xaml.cs b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs
index 1f32640f7ddf..588d62e56036 100644
--- a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs
+++ b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs
@@ -3,6 +3,7 @@
using System.Net.Sockets;
using Content.Client.Administration.UI.CustomControls;
using Content.Shared.Administration;
+using Content.Shared.CCVar;
using Content.Shared.Database;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
@@ -11,6 +12,7 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -32,8 +34,11 @@ public sealed partial class BanPanel : DefaultWindow
// This is less efficient than just holding a reference to the root control and enumerating children, but you
// have to know how the controls are nested, which makes the code more complicated.
private readonly List _roleCheckboxes = new();
+ private readonly ISawmill _banpanelSawmill;
[Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly ILogManager _logManager = default!;
private enum TabNumbers
{
@@ -65,6 +70,7 @@ public BanPanel()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+ _banpanelSawmill = _logManager.GetSawmill("admin.banpanel");
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
PlayerCheckbox.OnPressed += _ =>
@@ -104,6 +110,11 @@ public BanPanel()
};
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
+ IpCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanIpBanDefault);
+ HwidCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanHwidBanDefault);
+ LastConnCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanUseLastDetails);
+ EraseCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanErasePlayer);
+
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
@@ -136,7 +147,7 @@ public BanPanel()
var prototypeManager = IoCManager.Resolve();
foreach (var proto in prototypeManager.EnumeratePrototypes())
{
- CreateRoleGroup(proto.ID, proto.Roles, proto.Color);
+ CreateRoleGroup(proto.ID, proto.Roles.Select(p => p.Id), proto.Color);
}
CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes().Select(p => p.ID), Color.Red);
@@ -175,6 +186,39 @@ private void CreateRoleGroup(string roleName, IEnumerable roleList, Colo
c.Pressed = args.Pressed;
}
}
+
+ if (args.Pressed)
+ {
+ if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), true, out NoteSeverity newSeverity))
+ {
+ _banpanelSawmill
+ .Warning("Departmental role ban severity could not be parsed from config!");
+ return;
+ }
+ SeverityOption.SelectId((int) newSeverity);
+ }
+ else
+ {
+ foreach (var childContainer in RolesContainer.Children)
+ {
+ if (childContainer is Container)
+ {
+ foreach (var child in childContainer.Children)
+ {
+ if (child is CheckBox { Pressed: true })
+ return;
+ }
+ }
+ }
+
+ if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity newSeverity))
+ {
+ _banpanelSawmill
+ .Warning("Role ban severity could not be parsed from config!");
+ return;
+ }
+ SeverityOption.SelectId((int) newSeverity);
+ }
};
outerContainer.AddChild(innerContainer);
foreach (var role in roleList)
@@ -353,6 +397,35 @@ private void OnTypeChanged()
{
TypeOption.ModulateSelfOverride = null;
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
+ NoteSeverity? newSeverity = null;
+ switch (TypeOption.SelectedId)
+ {
+ case (int)Types.Server:
+ if (Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), true, out NoteSeverity serverSeverity))
+ newSeverity = serverSeverity;
+ else
+ {
+ _banpanelSawmill
+ .Warning("Server ban severity could not be parsed from config!");
+ }
+
+ break;
+ case (int) Types.Role:
+
+ if (Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity roleSeverity))
+ {
+ newSeverity = roleSeverity;
+ }
+ else
+ {
+ _banpanelSawmill
+ .Warning("Role ban severity could not be parsed from config!");
+ }
+ break;
+ }
+
+ if (newSeverity != null)
+ SeverityOption.SelectId((int) newSeverity.Value);
}
private void UpdateSubmitEnabled()
diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
index af977f763c6c..ddd66623bd4e 100644
--- a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
+++ b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
@@ -75,7 +75,7 @@ public BwoinkControl()
if (info.Antag && info.ActiveThisRound)
sb.Append(new Rune(0x1F5E1)); // 🗡
- if (info.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
+ if (info.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
sb.Append(new Rune(0x23F2)); // ⏲
sb.AppendFormat("\"{0}\"", text);
@@ -226,7 +226,7 @@ private string FormatTabTitle(ItemList.Item li, PlayerInfo? pl = default)
if (pl.Antag)
sb.Append(new Rune(0x1F5E1)); // 🗡
- if (pl.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
+ if (pl.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
sb.Append(new Rune(0x23F2)); // ⏲
sb.AppendFormat("\"{0}\"", pl.CharacterName);
@@ -243,9 +243,9 @@ private void SwitchToChannel(NetUserId? ch)
{
UpdateButtons();
+ AHelpHelper.HideAllPanels();
if (ch != null)
{
- AHelpHelper.HideAllPanels();
var panel = AHelpHelper.EnsurePanel(ch.Value);
panel.Visible = true;
}
diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
index f8d06f758f40..30f9d24df1df 100644
--- a/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
+++ b/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
@@ -16,14 +16,17 @@ public BwoinkWindow()
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
{
- if (sel is not null)
+ if (sel is null)
{
- Title = $"{sel.CharacterName} / {sel.Username}";
+ Title = Loc.GetString("bwoink-title-none-selected");
+ return;
+ }
+
+ Title = $"{sel.CharacterName} / {sel.Username}";
- if (sel.OverallPlaytime != null)
- {
- Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
- }
+ if (sel.OverallPlaytime != null)
+ {
+ Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
}
};
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
index fdf935d7c048..12522d552d71 100644
--- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
@@ -20,7 +20,7 @@ public sealed partial class PlayerListControl : BoxContainer
private List _playerList = new();
private readonly List _sortedPlayerList = new();
- public event Action? OnSelectionChanged;
+ public event Action? OnSelectionChanged;
public IReadOnlyList PlayerInfo => _playerList;
public Func? OverrideText;
@@ -41,12 +41,19 @@ public PlayerListControl()
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
+ PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
}
+ private void PlayerListNoItemSelected()
+ {
+ _selectedPlayer = null;
+ OnSelectionChanged?.Invoke(null);
+ }
+
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
diff --git a/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs b/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs
index eede3a6217f0..d60094ad8978 100644
--- a/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs
+++ b/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs
@@ -25,7 +25,7 @@ public sealed class ExplosionDebugOverlay : Overlay
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
- public Matrix3 SpaceMatrix;
+ public Matrix3x2 SpaceMatrix;
public MapId Map;
private readonly Font _font;
@@ -78,7 +78,8 @@ private void DrawScreen(OverlayDrawArgs args)
if (SpaceTiles == null)
return;
- gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds);
+ Matrix3x2.Invert(SpaceMatrix, out var invSpace);
+ gridBounds = invSpace.TransformBox(args.WorldBounds);
DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize);
}
@@ -86,7 +87,7 @@ private void DrawScreen(OverlayDrawArgs args)
private void DrawText(
DrawingHandleScreen handle,
Box2 gridBounds,
- Matrix3 transform,
+ Matrix3x2 transform,
Dictionary> tileSets,
ushort tileSize)
{
@@ -103,7 +104,7 @@ private void DrawText(
if (!gridBounds.Contains(centre))
continue;
- var worldCenter = transform.Transform(centre);
+ var worldCenter = Vector2.Transform(centre, transform);
var screenCenter = _eyeManager.WorldToScreen(worldCenter);
@@ -119,7 +120,7 @@ private void DrawText(
if (tileSets.TryGetValue(0, out var set))
{
var epicenter = set.First();
- var worldCenter = transform.Transform((epicenter + Vector2Helpers.Half) * tileSize);
+ var worldCenter = Vector2.Transform((epicenter + Vector2Helpers.Half) * tileSize, transform);
var screenCenter = _eyeManager.WorldToScreen(worldCenter) + new Vector2(-24, -24);
var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}";
handle.DrawString(_font, screenCenter, text);
@@ -148,11 +149,12 @@ private void DrawWorld(in OverlayDrawArgs args)
if (SpaceTiles == null)
return;
- gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds).Enlarged(2);
+ Matrix3x2.Invert(SpaceMatrix, out var invSpace);
+ gridBounds = invSpace.TransformBox(args.WorldBounds).Enlarged(2);
handle.SetTransform(SpaceMatrix);
DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize);
- handle.SetTransform(Matrix3.Identity);
+ handle.SetTransform(Matrix3x2.Identity);
}
private void DrawTiles(
diff --git a/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs b/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs
index 5f187cad794e..b0d8a946ec5f 100644
--- a/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs
+++ b/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs
@@ -3,6 +3,7 @@
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
+using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -22,7 +23,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
-
+ private readonly SharedTransformSystem _transform = default!;
private readonly SpawnExplosionEui _eui;
private List _mapData = new();
@@ -37,6 +38,7 @@ public SpawnExplosionWindow(SpawnExplosionEui eui)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+ _transform = _entMan.System();
_eui = eui;
ExplosionOption.OnItemSelected += ExplosionSelected;
@@ -104,7 +106,7 @@ private void SetLocation()
_pausePreview = true;
MapOptions.Select(_mapData.IndexOf(transform.MapID));
- (MapX.Value, MapY.Value) = transform.MapPosition.Position;
+ (MapX.Value, MapY.Value) = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: transform).Position;
_pausePreview = false;
UpdatePreview();
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 000000000000..b8034faf52ab
--- /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 000000000000..9e1d53818f24
--- /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 000000000000..dd770c2be53c
--- /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 000000000000..aa9d6ced9517
--- /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 fb68e6c79089..ea89916ba8c2 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 a5c300843658..c8606ca80d5e 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 ObjectsTab()
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 @@ private void RefreshObjectList(ObjectsTabSelection selection)
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