From 16af5cff9cfdcb02bfbb2424d26335e3fdc7e9f3 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 5 Jan 2024 04:36:41 +0100 Subject: [PATCH 01/16] Delete FodyWeavers.xsd We used to use Fody. We don't anymore. Yeet. --- Robust.Client/FodyWeavers.xsd | 82 ----------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 Robust.Client/FodyWeavers.xsd diff --git a/Robust.Client/FodyWeavers.xsd b/Robust.Client/FodyWeavers.xsd deleted file mode 100644 index 15adf7906a8..00000000000 --- a/Robust.Client/FodyWeavers.xsd +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - Defines if sequence points should be generated for each emitted IL instruction. Default value: Debug - - - - - - Insert sequence points in Debug builds only (this is the default). - - - - - Insert sequence points in Release builds only. - - - - - Always insert sequence points. - - - - - Never insert sequence points. - - - - - - - - Defines how warnings should be handled. Default value: Warnings - - - - - - Emit build warnings (this is the default). - - - - - Do not emit warnings. - - - - - Treat warnings as errors. - - - - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file From ac147dc2a1b6d5c062927a5d8056dee069d449b2 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:38:41 +1100 Subject: [PATCH 02/16] Remove physics sleep cancelling (#4813) * Remove physics sleep cancelling Was a bad idea, in the future I'm going to add even more asserts around this but for now I can fix content. * Fix this --- Robust.Shared/Physics/PhysicsWakeEvent.cs | 9 +------- .../Systems/SharedPhysicsSystem.Components.cs | 21 +++---------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/Robust.Shared/Physics/PhysicsWakeEvent.cs b/Robust.Shared/Physics/PhysicsWakeEvent.cs index 549d7d7c66a..145e82776ae 100644 --- a/Robust.Shared/Physics/PhysicsWakeEvent.cs +++ b/Robust.Shared/Physics/PhysicsWakeEvent.cs @@ -7,11 +7,4 @@ namespace Robust.Shared.Physics; public readonly record struct PhysicsWakeEvent(EntityUid Entity, PhysicsComponent Body); [ByRefEvent] -public record struct PhysicsSleepEvent(EntityUid Entity, PhysicsComponent Body) -{ - /// - /// Marks the entity as still being awake and cancels sleeping. - /// This only works if is still enabled and the object is not a static object.. - /// - public bool Cancelled; -}; +public record struct PhysicsSleepEvent(EntityUid Entity, PhysicsComponent Body); diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs index 4f4a8cc6ee1..303d9796b2d 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs @@ -401,29 +401,14 @@ public void SetAwake(Entity ent, bool value, bool updateSleepT } else { - // TODO C# event? var ev = new PhysicsSleepEvent(uid, body); RaiseLocalEvent(uid, ref ev, true); - - // Reset the sleep timer. - if (ev.Cancelled && canWake) - { - body.Awake = true; - // TODO C# event? - var wakeEv = new PhysicsWakeEvent(uid, body); - RaiseLocalEvent(uid, ref wakeEv, true); - - if (updateSleepTime) - SetSleepTime(body, 0); - - return; - } - ResetDynamics(body, dirty: false); } - // Update wake system after we are sure that the wake/sleep event wasn't cancelled. - _wakeSystem.UpdateCanCollide(ent, checkTerminating: false, dirty: false); + // Update wake system last, if sleeping but still colliding. + if (!value && body.CanCollide) + _wakeSystem.UpdateCanCollide(ent, checkTerminating: false, dirty: false); if (updateSleepTime) SetSleepTime(body, 0); From d142393221bb6ac680ec43999a3b87bf8fd87898 Mon Sep 17 00:00:00 2001 From: Vasilis Date: Sat, 6 Jan 2024 03:48:04 +0100 Subject: [PATCH 03/16] Clamp volume gain calculations (#4815) --- Robust.Shared/Audio/Systems/SharedAudioSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Robust.Shared/Audio/Systems/SharedAudioSystem.cs b/Robust.Shared/Audio/Systems/SharedAudioSystem.cs index 9be9d0bc823..4db97c30c09 100644 --- a/Robust.Shared/Audio/Systems/SharedAudioSystem.cs +++ b/Robust.Shared/Audio/Systems/SharedAudioSystem.cs @@ -154,7 +154,7 @@ public static float GainToVolume(float value) { if (value < 0f) { - throw new InvalidOperationException($"Tried to get volume calculation for gain of {value}."); + value = 0f; } return 10f * MathF.Log10(value); From 2f0283edb716c8528441975e141fe568515b49c3 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:50:19 -0500 Subject: [PATCH 04/16] Freeze event bus dictionaries (#4727) * Make component factory use frozen collections * Fix integration tests * Also freeze _entTraitDict * A * I love integration test setup logic * Re-add public method * Freeze event bus * Remove per-component dictionary lookup on EntAddComponent * release notes + fix test jank * Fix merge * fix tests * Shutdown --- RELEASE-NOTES.md | 8 +- Robust.Shared/GameObjects/ComponentFactory.cs | 18 ++- .../GameObjects/EntityEventBus.Broadcast.cs | 7 +- .../GameObjects/EntityEventBus.Common.cs | 19 ++- .../GameObjects/EntityEventBus.Directed.cs | 96 ++++++++---- Robust.Shared/GameObjects/EntityManager.cs | 4 +- .../GameObjects/IComponentFactory.cs | 5 + Robust.UnitTesting/RobustUnitTest.cs | 145 ++++-------------- .../Server/Maps/MapLoaderTest.cs | 15 +- .../GameObjects/ComponentFactory_Tests.cs | 8 +- .../EntityEventBusTests.ComponentEvent.cs | 4 + .../EntityEventBusTests.SystemEvent.cs | 7 + .../Systems/AnchoredSystemTests.cs | 101 +++++++----- .../Systems/TransformSystemTests.cs | 17 +- .../Shared/Localization/LocalizationTests.cs | 5 +- .../Shared/Prototypes/HotReloadTest.cs | 8 +- .../Prototypes/PrototypeManager_Test.cs | 7 +- .../InheritanceSerializationTest.cs | 10 +- .../ComponentRegistrySerializerTest.cs | 8 +- 19 files changed, 235 insertions(+), 257 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 5e57688372e..01197a6614e 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -35,11 +35,15 @@ END TEMPLATE--> ### Breaking changes -*None yet* +* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered. +* Event bus subscriptions are now locked after `IEntityManager` has started, instead of after the first component gets added. Any event subscriptions now need to happen before startup (but after init). +* Event bus subscriptions must now be locked before raising any events. ### New features -*None yet* +* `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components. +* `IComponentFactory.RegisterIgnore()` now accepts more than one string. +* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components. ### Bugfixes diff --git a/Robust.Shared/GameObjects/ComponentFactory.cs b/Robust.Shared/GameObjects/ComponentFactory.cs index c81d7365230..d0437ada609 100644 --- a/Robust.Shared/GameObjects/ComponentFactory.cs +++ b/Robust.Shared/GameObjects/ComponentFactory.cs @@ -388,10 +388,10 @@ public bool TryGetRegistration(IComponent component, [NotNullWhen(true)] out Com public void DoAutoRegistrations() { var types = _reflectionManager.FindTypesWithAttribute().ToArray(); - RegisterClasses(types, false); + RegisterTypesInternal(types, false); } - private void RegisterClasses(Type[] types, bool overwrite) + private void RegisterTypesInternal(Type[] types, bool overwrite) { var names = _names.ToDictionary(); var lowerCaseNames = _lowerCaseNames.ToDictionary(); @@ -419,7 +419,19 @@ private void RegisterClasses(Type[] types, bool overwrite) public void RegisterClass(bool overwrite = false) where T : IComponent, new() { - RegisterClasses(new []{typeof(T)}, overwrite); + RegisterTypesInternal(new []{typeof(T)}, overwrite); + } + + /// + public void RegisterTypes(params Type[] types) + { + foreach (var type in types) + { + if (!type.IsAssignableTo(typeof(IComponent)) || !type.HasParameterlessConstructor()) + throw new InvalidOperationException($"Invalid type: {type}"); + } + + RegisterTypesInternal(types, false); } public IEnumerable GetAllRefTypes() diff --git a/Robust.Shared/GameObjects/EntityEventBus.Broadcast.cs b/Robust.Shared/GameObjects/EntityEventBus.Broadcast.cs index 837df689745..736209cd9e2 100644 --- a/Robust.Shared/GameObjects/EntityEventBus.Broadcast.cs +++ b/Robust.Shared/GameObjects/EntityEventBus.Broadcast.cs @@ -297,7 +297,10 @@ public void QueueEvent(EventSource source, EntityEventArgs toRaise) private void UnsubscribeEvent(Type eventType, BroadcastRegistration tuple, IEntityEventSubscriber subscriber) { - if (_eventData.TryGetValue(eventType, out var subscriptions) + if (_subscriptionLock) + throw new InvalidOperationException("Subscription locked."); + + if (_eventDataUnfrozen.TryGetValue(eventType, out var subscriptions) && subscriptions.BroadcastRegistrations.Contains(tuple)) subscriptions.BroadcastRegistrations.Remove(tuple); @@ -307,7 +310,7 @@ private void UnsubscribeEvent(Type eventType, BroadcastRegistration tuple, IEnti private void ProcessSingleEvent(EventSource source, ref Unit unitRef, Type eventType) { - if (!_eventData.TryGetValue(eventType, out var subs)) + if (!_eventData!.TryGetValue(eventType, out var subs)) return; if (subs.IsOrdered && !subs.OrderingUpToDate) diff --git a/Robust.Shared/GameObjects/EntityEventBus.Common.cs b/Robust.Shared/GameObjects/EntityEventBus.Common.cs index 0e43936e4f6..c251e42fa59 100644 --- a/Robust.Shared/GameObjects/EntityEventBus.Common.cs +++ b/Robust.Shared/GameObjects/EntityEventBus.Common.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -14,7 +15,8 @@ internal sealed partial class EntityEventBus : IEventBus private IComponentFactory _comFac; // Data on individual events. Used to check ordering info and fire broadcast events. - private readonly Dictionary _eventData = new(); + private FrozenDictionary _eventData = FrozenDictionary.Empty; + private readonly Dictionary _eventDataUnfrozen = new(); // Inverse subscriptions to be able to unsubscribe an IEntityEventSubscriber. private readonly Dictionary> _inverseEventSubscriptions @@ -28,10 +30,18 @@ private readonly Dictionary _entEventTables = new(); // CompType -> EventType -> Handler - internal Dictionary?[] _entSubscriptions = + internal FrozenDictionary?[] _entSubscriptions = default!; + + // Variant of _entSubscriptions that omits any events with the ComponentEventAttribute + internal FrozenDictionary?[] _entSubscriptionsNoCompEv = default!; + + // pre-freeze _entSubscriptions data + internal Dictionary?[] _entSubscriptionsUnfrozen = Array.Empty?>(); // EventType -> { CompType1, ... CompType N } + // Only required to sort ordered subscriptions, which only happens during initialization + // so doesn't need to be a frozen dictionary. private Dictionary> _entSubscriptionsInv = new(); // prevents shitcode, get your subscriptions figured out before you start spawning entities @@ -52,7 +62,10 @@ private static ref Unit ExtractUnitRef(ref object obj, Type objType) private void RegisterCommon(Type eventType, OrderingData? data, out EventData subs) { - subs = _eventData.GetOrNew(eventType); + if (_subscriptionLock) + throw new InvalidOperationException("Subscription locked."); + + subs = _eventDataUnfrozen.GetOrNew(eventType); if (data == null) return; diff --git a/Robust.Shared/GameObjects/EntityEventBus.Directed.cs b/Robust.Shared/GameObjects/EntityEventBus.Directed.cs index fe38bca7d39..d8ccd62431b 100644 --- a/Robust.Shared/GameObjects/EntityEventBus.Directed.cs +++ b/Robust.Shared/GameObjects/EntityEventBus.Directed.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Robust.Shared.Collections; @@ -122,16 +124,7 @@ public EntityEventBus(IEntityManager entMan) // Dynamic handling of components is only for RobustUnitTest compatibility spaghetti. _comFac.ComponentsAdded += ComFacOnComponentsAdded; - - InitEntSubscriptionsArray(); - } - - private void InitEntSubscriptionsArray() - { - foreach (var reg in _comFac.GetAllRegistrations()) - { - CompIdx.AssignArray(ref _entSubscriptions, reg.Idx, new Dictionary()); - } + ComFacOnComponentsAdded(_comFac.GetAllRegistrations().ToArray()); } /// @@ -329,9 +322,12 @@ public void UnsubscribeLocalEvent() private void ComFacOnComponentsAdded(ComponentRegistration[] regs) { + if (_subscriptionLock) + throw new InvalidOperationException("Subscription locked."); + foreach (var reg in regs) { - CompIdx.RefArray(ref _entSubscriptions, reg.Idx) ??= new Dictionary(); + CompIdx.RefArray(ref _entSubscriptionsUnfrozen, reg.Idx) ??= new(); } } @@ -346,10 +342,43 @@ public void OnEntityDeleted(EntityUid e) } public void OnComponentAdded(in AddedComponentEventArgs e) + { + EntAddComponent(e.BaseArgs.Owner, e.ComponentType.Idx); + } + + internal void LockSubscriptions() { _subscriptionLock = true; + _eventData = _eventDataUnfrozen.ToFrozenDictionary(); - EntAddComponent(e.BaseArgs.Owner, e.ComponentType.Idx); + _entSubscriptions = _entSubscriptionsUnfrozen + .Select(x => x?.ToFrozenDictionary()) + .ToArray(); + + _entSubscriptionsNoCompEv = _entSubscriptionsUnfrozen.Select(FreezeWithoutComponentEvent).ToArray(); + + CalcOrdering(); + } + + /// + /// Freezes a dictionary while committing events with the . + /// This avoids unnecessarily adding one-off events to the list of subscriptions. + /// + private FrozenDictionary? FreezeWithoutComponentEvent( + Dictionary? input) + { + if (input == null) + return null; + + return input.Where(x => !IsComponentEvent(x.Key)) + .ToFrozenDictionary(); + } + + private bool IsComponentEvent(Type t) + { + var isCompEv = _eventData[t].ComponentEvent; + DebugTools.Assert(isCompEv == t.HasCustomAttribute()); + return isCompEv; } public void OnComponentRemoved(in RemovedComponentEventArgs e) @@ -366,7 +395,8 @@ private void EntAddSubscription( if (_subscriptionLock) throw new InvalidOperationException("Subscription locked."); - if (compType.Value >= _entSubscriptions.Length || _entSubscriptions[compType.Value] is not { } compSubs) + if (compType.Value >= _entSubscriptionsUnfrozen.Length + || _entSubscriptionsUnfrozen[compType.Value] is not { } compSubs) { if (IgnoreUnregisteredComponents) return; @@ -375,13 +405,13 @@ private void EntAddSubscription( } if (compSubs.ContainsKey(eventType)) + { throw new InvalidOperationException( $"Duplicate Subscriptions for comp={compTypeObj}, event={eventType.Name}"); + } compSubs.Add(eventType, registration); - - var invSubs = _entSubscriptionsInv.GetOrNew(eventType); - invSubs.Add(compType); + _entSubscriptionsInv.GetOrNew(eventType).Add(compType); RegisterCommon(eventType, registration.Ordering, out var data); data.ComponentEvent = eventType.HasCustomAttribute(); @@ -408,7 +438,8 @@ private void EntUnsubscribe(CompIdx compType, Type eventType) if (_subscriptionLock) throw new InvalidOperationException("Subscription locked."); - if (compType.Value >= _entSubscriptions.Length || _entSubscriptions[compType.Value] is not { } compSubs) + if (compType.Value >= _entSubscriptionsUnfrozen.Length + || _entSubscriptionsUnfrozen[compType.Value] is not { } compSubs) { if (IgnoreUnregisteredComponents) return; @@ -435,14 +466,14 @@ private void EntRemoveEntity(EntityUid euid) private void EntAddComponent(EntityUid euid, CompIdx compType) { + DebugTools.Assert(_subscriptionLock); + var eventTable = _entEventTables[euid]; - var compSubs = _entSubscriptions[compType.Value]!; + var compSubs = _entSubscriptionsNoCompEv[compType.Value]!; foreach (var evType in compSubs.Keys) { - // Skip adding this to significantly reduce memory use and GC noise on entity create. - if (_eventData[evType].ComponentEvent) - continue; + DebugTools.Assert(!_eventData[evType].ComponentEvent); if (eventTable.Free < 0) GrowEventTable(eventTable); @@ -607,17 +638,16 @@ private bool EntTryGetSubscriptions(Type eventType, EntityUid euid, out Subscrip return true; } - private void EntClear() + public void ClearSubscriptions() { - _entEventTables = new(); _subscriptionLock = false; - } - - public void ClearEventTables() - { - EntClear(); - - foreach (var sub in _entSubscriptions) + _eventDataUnfrozen.Clear(); + _entEventTables.Clear(); + _inverseEventSubscriptions.Clear(); + _entSubscriptions = default!; + _entSubscriptionsNoCompEv = default!; + _eventData = FrozenDictionary.Empty; + foreach (var sub in _entSubscriptionsUnfrozen) { sub?.Clear(); } @@ -632,6 +662,8 @@ public void Dispose() _comFac = null!; _entEventTables = null!; _entSubscriptions = null!; + _entSubscriptionsNoCompEv = null!; + _entSubscriptionsUnfrozen = null!; _entSubscriptionsInv = null!; } @@ -639,7 +671,7 @@ private struct SubscriptionsEnumerator { private readonly Type _eventType; private readonly EntityUid _uid; - private readonly Dictionary?[] _subscriptions; + private readonly FrozenDictionary?[] _subscriptions; private readonly IEntityManager _entityManager; private readonly EventTableListEntry[] _list; private int _idx; @@ -648,7 +680,7 @@ public SubscriptionsEnumerator( Type eventType, int startEntry, EventTableListEntry[] list, - Dictionary?[] subscriptions, + FrozenDictionary?[] subscriptions, EntityUid uid, IEntityManager entityManager) { diff --git a/Robust.Shared/GameObjects/EntityManager.cs b/Robust.Shared/GameObjects/EntityManager.cs index 02c4ec88d3e..90d80a5461a 100644 --- a/Robust.Shared/GameObjects/EntityManager.cs +++ b/Robust.Shared/GameObjects/EntityManager.cs @@ -203,7 +203,7 @@ public virtual void Startup() // TODO: Probably better to call this on its own given it's so infrequent. _entitySystemManager.Initialize(); Started = true; - _eventBus.CalcOrdering(); + _eventBus.LockSubscriptions(); _mapSystem = System(); _xforms = System(); _containers = System(); @@ -216,7 +216,7 @@ public virtual void Shutdown() { ShuttingDown = true; FlushEntities(); - _eventBus.ClearEventTables(); + _eventBus.ClearSubscriptions(); _entitySystemManager.Shutdown(); ClearComponents(); ShuttingDown = false; diff --git a/Robust.Shared/GameObjects/IComponentFactory.cs b/Robust.Shared/GameObjects/IComponentFactory.cs index f98d0f2898c..b7ade646198 100644 --- a/Robust.Shared/GameObjects/IComponentFactory.cs +++ b/Robust.Shared/GameObjects/IComponentFactory.cs @@ -83,6 +83,11 @@ public interface IComponentFactory /// If the component already exists, will this replace it? void RegisterClass(bool overwrite = false) where T : IComponent, new(); + /// + /// Registers component types with the factory. + /// + void RegisterTypes(params Type[] type); + /// /// Registers a component name as being ignored. /// diff --git a/Robust.UnitTesting/RobustUnitTest.cs b/Robust.UnitTesting/RobustUnitTest.cs index eda739c47d5..0cacf50afee 100644 --- a/Robust.UnitTesting/RobustUnitTest.cs +++ b/Robust.UnitTesting/RobustUnitTest.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using NUnit.Framework; using Robust.Client.ComponentTrees; @@ -39,6 +38,33 @@ public enum UnitTestProject : byte [Parallelizable] public abstract partial class RobustUnitTest { + protected virtual Type[]? ExtraComponents => null; + private static Type[] _components = new [] + { + typeof(EyeComponent), + typeof(MapComponent), + typeof(MapGridComponent), + typeof(ContainerManagerComponent), + typeof(MetaDataComponent), + typeof(TransformComponent), + typeof(PhysicsComponent), + typeof(PhysicsMapComponent), + typeof(BroadphaseComponent), + typeof(FixturesComponent), + typeof(JointComponent), + typeof(GridTreeComponent), + typeof(MovedGridsComponent), + typeof(JointRelayTargetComponent), + typeof(OccluderComponent), + typeof(OccluderTreeComponent), + typeof(SpriteTreeComponent), + typeof(LightTreeComponent), + typeof(CollisionWakeComponent), + typeof(CollideOnAnchorComponent), + typeof(Gravity2DComponent), + typeof(ActorComponent) + }; + public virtual UnitTestProject Project => UnitTestProject.Server; [OneTimeSetUp] @@ -122,6 +148,7 @@ public void BaseSetup() systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); + systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); @@ -133,117 +160,11 @@ public void BaseSetup() // Required components for the engine to work // Why are we still here? Just to suffer? Why can't we just use [RegisterComponent] magic? // TODO End Suffering. + // suffering has been alleviated, but still present var compFactory = deps.Resolve(); - - if (!compFactory.AllRegisteredTypes.Contains(typeof(EyeComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(MapComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(MapGridComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(ContainerManagerComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(MetaDataComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(TransformComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(PhysicsComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(PhysicsMapComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(BroadphaseComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(FixturesComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(JointComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(GridTreeComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(MovedGridsComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(JointRelayTargetComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(OccluderComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(OccluderTreeComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(SpriteTreeComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(LightTreeComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(Gravity2DComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(CollisionWakeComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(CollideOnAnchorComponent))) - { - compFactory.RegisterClass(); - } - - if (!compFactory.AllRegisteredTypes.Contains(typeof(ActorComponent))) - { - compFactory.RegisterClass(); - } + compFactory.RegisterTypes(_components); + if (ExtraComponents != null) + compFactory.RegisterTypes(ExtraComponents); deps.Resolve().Initialize(); @@ -252,7 +173,7 @@ public void BaseSetup() entMan.Initialize(); // RobustUnitTest is complete hot garbage. // This makes EventTables ignore *all* the screwed up component abuse it causes. - entMan.EventBus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); + entMan.EventBus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); // The nightmare never ends mapMan.Initialize(); systems.Initialize(); diff --git a/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs b/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs index 6a22e5271c8..515e4709235 100644 --- a/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs +++ b/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using NUnit.Framework; using Robust.Server.GameObjects; @@ -52,24 +53,14 @@ public sealed partial class MapLoaderTest : RobustUnitTest - type: MapDeserializeTest foo: 1 bar: 2 - "; + protected override Type[]? ExtraComponents => new[] { typeof(MapDeserializeTestComponent), typeof(VisibilityComponent), typeof(IgnoreUIRangeComponent)}; + [OneTimeSetUp] public void Setup() { - var compFactory = IoCManager.Resolve(); - compFactory.RegisterClass(); - compFactory.RegisterClass(); - compFactory.RegisterClass(); - compFactory.GenerateNetIds(); IoCManager.Resolve().Initialize(); - - // For some reason RobustUnitTest doesn't discover PVSSystem but this does here so ? - var syssy = IoCManager.Resolve(); - syssy.Shutdown(); - syssy.Initialize(); - var resourceManager = IoCManager.Resolve(); resourceManager.Initialize(null); resourceManager.MountString("/TestMap.yml", MapData); diff --git a/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs b/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs index 7636cab7bdd..6097e79c05f 100644 --- a/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs @@ -1,3 +1,4 @@ +using System; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -13,12 +14,7 @@ public sealed partial class ComponentFactory_Tests : RobustUnitTest private const string TestComponentName = "A"; private const string LowercaseTestComponentName = "a"; private const string NonexistentComponentName = "B"; - - [OneTimeSetUp] - public void OneTimeSetUp() - { - IoCManager.Resolve().RegisterClass(); - } + protected override Type[]? ExtraComponents => new[] {typeof(TestComponent)}; [Test] public void GetComponentAvailabilityTest() diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs index 406022670d4..ff42c0e1022 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs @@ -41,6 +41,7 @@ public void SubscribeCompEvent() // Subscribe int calledCount = 0; bus.SubscribeLocalEvent(HandleTestEvent); + bus.LockSubscriptions(); // add a component to the system bus.OnEntityAdded(entUid); @@ -98,6 +99,7 @@ public void UnsubscribeCompEvent() int calledCount = 0; bus.SubscribeLocalEvent(HandleTestEvent); bus.UnsubscribeLocalEvent(); + bus.LockSubscriptions(); // add a component to the system bus.OnEntityAdded(entUid); @@ -153,6 +155,7 @@ public void SubscribeCompLifeEvent() // Subscribe int calledCount = 0; bus.SubscribeLocalEvent(HandleTestEvent); + bus.LockSubscriptions(); // add a component to the system entManMock.Raise(m => m.EntityAdded += null, entUid); @@ -232,6 +235,7 @@ void HandlerB(EntityUid uid, Component comp, TestEvent ev) bus.SubscribeLocalEvent(HandlerA, typeof(OrderAComponent), before: new []{typeof(OrderBComponent), typeof(OrderCComponent)}); bus.SubscribeLocalEvent(HandlerB, typeof(OrderBComponent), after: new []{typeof(OrderCComponent)}); bus.SubscribeLocalEvent(HandlerC, typeof(OrderCComponent)); + bus.LockSubscriptions(); // add a component to the system bus.OnEntityAdded(entUid); diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs index da734c1f55f..44bb208ecc7 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs @@ -137,6 +137,7 @@ public void SubscribeEvent_MultipleSubscriptions_IndividuallyCalled() bus.SubscribeEvent(EventSource.Local, subscriber, ev => delFooCount++); bus.SubscribeEvent(EventSource.Local, subscriber, ev => delBarCount++); + bus.LockSubscriptions(); // Act & Assert bus.RaiseEvent(EventSource.Local, new TestEventArgs()); @@ -184,6 +185,7 @@ void Handler(TestEventArgs ev) { } bus.SubscribeEvent(EventSource.Local, subscriber, Handler); bus.UnsubscribeEvent(EventSource.Local, subscriber); + bus.LockSubscriptions(); // Act bus.UnsubscribeEvent(EventSource.Local, subscriber); @@ -253,6 +255,7 @@ public void RaiseEvent_NoSubscriptions_Nop() int delCalledCount = 0; bus.SubscribeEvent(EventSource.Local, subscriber, ev => delCalledCount++); + bus.LockSubscriptions(); // Act bus.RaiseEvent(EventSource.Local, new TestEventArgs()); @@ -276,6 +279,7 @@ public void RaiseEvent_Unsubscribed_Nop() bus.SubscribeEvent(EventSource.Local, subscriber, Handler); bus.UnsubscribeEvent(EventSource.Local, subscriber); + bus.LockSubscriptions(); // Act bus.RaiseEvent(EventSource.Local, new TestEventArgs()); @@ -348,6 +352,7 @@ public void UnsubscribeEvents_UnsubscribedHandler_Nop() bus.SubscribeEvent(EventSource.Local, subscriber, Handler); bus.UnsubscribeEvents(subscriber); + bus.LockSubscriptions(); // Act bus.RaiseEvent(EventSource.Local, new TestEventArgs()); @@ -425,6 +430,7 @@ public void ProcessQueue_EventQueued_HandlerRaised() void Handler(TestEventArgs ev) => delCallCount++; bus.SubscribeEvent(EventSource.Local, subscriber, Handler); + bus.LockSubscriptions(); bus.QueueEvent(EventSource.Local, new TestEventArgs()); // Act @@ -464,6 +470,7 @@ void HandlerB(TestEventArgs ev) bus.SubscribeEvent(EventSource.Local, new SubA(), HandlerA, typeof(SubA), before: new []{typeof(SubB), typeof(SubC)}); bus.SubscribeEvent(EventSource.Local, new SubB(), HandlerB, typeof(SubB), after: new []{typeof(SubC)}); bus.SubscribeEvent(EventSource.Local, new SubC(), HandlerC, typeof(SubC)); + bus.LockSubscriptions(); // Act bus.RaiseEvent(EventSource.Local, new TestEventArgs()); diff --git a/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs b/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs index 3f24e68937e..fbe135b63a3 100644 --- a/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using Robust.Shared.Containers; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Physics; @@ -33,6 +34,7 @@ private static (ISimulation, EntityUid gridId) SimulationFactory() { var sim = RobustServerSimulation .NewSimulation() + .RegisterEntitySystems(f => f.LoadExtraSystemType()) .RegisterPrototypes(f=> { f.LoadString(Prototypes); @@ -69,7 +71,6 @@ public void OnAnchored_WorldPosition_TileCenter() var (sim, gridId) = SimulationFactory(); var entMan = sim.Resolve(); var mapMan = sim.Resolve(); - var xform = entMan.System(); var coordinates = new MapCoordinates(new Vector2(7, 7), TestMapId); @@ -77,20 +78,13 @@ public void OnAnchored_WorldPosition_TileCenter() var grid = mapMan.GetGrid(gridId); grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); - int calledCount = 0; var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after - xform.OnGlobalMoveEvent += MoveEventHandler; // Act + entMan.System().ResetCounters(); entMan.GetComponent(ent1).Anchored = true; - Assert.That(entMan.GetComponent(ent1).WorldPosition, Is.EqualTo(new Vector2(7.5f, 7.5f))); // centered on tile - Assert.That(calledCount, Is.EqualTo(1)); // because the ent was moved from snapping, a MoveEvent was raised. - xform.OnGlobalMoveEvent -= MoveEventHandler; - void MoveEventHandler(ref MoveEvent ev) - { - calledCount++; - } + entMan.System().AssertMoved(false); } [ComponentProtoName("AnchorOnInit")] @@ -105,6 +99,55 @@ public override void Initialize() } } + internal sealed class MoveEventTestSystem : EntitySystem + { + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void Initialize() + { + base.Initialize(); + _transform.OnGlobalMoveEvent += OnMove; + SubscribeLocalEvent(OnReparent); + } + + + public override void Shutdown() + { + base.Shutdown(); + _transform.OnGlobalMoveEvent -= OnMove; + } + + public bool FailOnMove = false; + public int MoveCounter = 0; + public int ParentCounter = 0; + + private void OnMove(ref MoveEvent ev) + { + MoveCounter++; + if (FailOnMove) + Assert.Fail($"Move event was raised"); + } + private void OnReparent(ref EntParentChangedMessage ev) + { + ParentCounter++; + if (FailOnMove) + Assert.Fail($"Move event was raised"); + } + + public void ResetCounters() + { + ParentCounter = 0; + MoveCounter = 0; + } + + public void AssertMoved(bool parentChanged = true) + { + if (parentChanged) + Assert.That(ParentCounter, Is.EqualTo(1)); + Assert.That(MoveCounter, Is.EqualTo(1)); + } + } + /// /// Ensures that if an entity gets added to lookups when anchored during init by some system. /// @@ -159,23 +202,13 @@ public void OnAnchored_Parent_SetToGrid() var traversal = entMan.System(); traversal.Enabled = false; - - var subscriber = new Subscriber(); - int calledCount = 0; var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after - entMan.EventBus.SubscribeEvent(EventSource.Local, subscriber, ParentChangedHandler); // Act + entMan.System().ResetCounters(); entMan.GetComponent(ent1).Anchored = true; - Assert.That(entMan.GetComponent(ent1).ParentUid, Is.EqualTo(grid.Owner)); - Assert.That(calledCount, Is.EqualTo(1)); - void ParentChangedHandler(ref EntParentChangedMessage ev) - { - Assert.That(ev.Entity, Is.EqualTo(ent1)); - calledCount++; - - } + entMan.System().AssertMoved(); traversal.Enabled = true; } @@ -247,23 +280,16 @@ public void Anchored_SetPosition_Nop() var grid = mapMan.GetGrid(gridId); grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); - int calledCount = 0; var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after entMan.GetComponent(ent1).Anchored = true; // Anchoring will change parent if needed, raising MoveEvent, subscribe after - xform.OnGlobalMoveEvent += MoveEventHandler; + entMan.System().FailOnMove = true; // Act entMan.GetComponent(ent1).WorldPosition = new Vector2(99, 99); entMan.GetComponent(ent1).LocalPosition = new Vector2(99, 99); Assert.That(entMan.GetComponent(ent1).MapPosition, Is.EqualTo(coordinates)); - Assert.That(calledCount, Is.EqualTo(0)); - xform.OnGlobalMoveEvent -= MoveEventHandler; - void MoveEventHandler(ref MoveEvent ev) - { - Assert.Fail("MoveEvent raised when entity is anchored."); - calledCount++; - } + entMan.System().FailOnMove = false; } /// @@ -517,22 +543,13 @@ public void Unanchored_Unanchor_Nop() var traversal = entMan.System(); traversal.Enabled = false; - - var subscriber = new Subscriber(); - int calledCount = 0; var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after - entMan.EventBus.SubscribeEvent(EventSource.Local, subscriber, ParentChangedHandler); // Act + entMan.System().FailOnMove = true; entMan.GetComponent(ent1).Anchored = false; - Assert.That(entMan.GetComponent(ent1).ParentUid, Is.EqualTo(mapMan.GetMapEntityId(TestMapId))); - Assert.That(calledCount, Is.EqualTo(0)); - void ParentChangedHandler(ref EntParentChangedMessage ev) - { - Assert.That(ev.Entity, Is.EqualTo(ent1)); - calledCount++; - } + entMan.System().FailOnMove = false; traversal.Enabled = true; } diff --git a/Robust.UnitTesting/Shared/GameObjects/Systems/TransformSystemTests.cs b/Robust.UnitTesting/Shared/GameObjects/Systems/TransformSystemTests.cs index e4cc58012b3..8af756effe8 100644 --- a/Robust.UnitTesting/Shared/GameObjects/Systems/TransformSystemTests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/Systems/TransformSystemTests.cs @@ -3,7 +3,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.UnitTesting.Server; namespace Robust.UnitTesting.Shared.GameObjects.Systems @@ -15,6 +14,7 @@ private static ISimulation SimulationFactory() { var sim = RobustServerSimulation .NewSimulation() + .RegisterEntitySystems(f => f.LoadExtraSystemType()) .InitializeInstance(); // Adds the map with id 1, and spawns entity 1 as the map entity. @@ -31,22 +31,11 @@ public void OnMove_LocalPosChanged_RaiseMoveEvent() { var sim = SimulationFactory(); var entMan = sim.Resolve(); - var xform = entMan.System(); - - int calledCount = 0; - xform.OnGlobalMoveEvent += MoveEventHandler; var ent1 = entMan.SpawnEntity(null, new MapCoordinates(Vector2.Zero, new MapId(1))); + entMan.System().ResetCounters(); IoCManager.Resolve().GetComponent(ent1).LocalPosition = Vector2.One; - - Assert.That(calledCount, Is.EqualTo(1)); - xform.OnGlobalMoveEvent -= MoveEventHandler; - void MoveEventHandler(ref MoveEvent ev) - { - calledCount++; - Assert.That(ev.OldPosition, Is.EqualTo(new EntityCoordinates(EntityUid.FirstUid, Vector2.Zero))); - Assert.That(ev.NewPosition, Is.EqualTo(new EntityCoordinates(EntityUid.FirstUid, Vector2.One))); - } + entMan.System().AssertMoved(false); } /// diff --git a/Robust.UnitTesting/Shared/Localization/LocalizationTests.cs b/Robust.UnitTesting/Shared/Localization/LocalizationTests.cs index 8df563bcffc..e8bd66296dd 100644 --- a/Robust.UnitTesting/Shared/Localization/LocalizationTests.cs +++ b/Robust.UnitTesting/Shared/Localization/LocalizationTests.cs @@ -16,15 +16,12 @@ namespace Robust.UnitTesting.Shared.Localization [TestFixture] internal sealed class LocalizationTests : RobustUnitTest { + protected override Type[] ExtraComponents => new[] {typeof(GrammarComponent)}; [OneTimeSetUp] public void Setup() { IoCManager.Resolve().Initialize(); - var componentFactory = IoCManager.Resolve(); - componentFactory.RegisterClass(); - componentFactory.GenerateNetIds(); - var res = IoCManager.Resolve(); res.MountString("/Locale/en-US/a.ftl", FluentCode); res.MountString("/EnginePrototypes/a.yml", YAMLCode); diff --git a/Robust.UnitTesting/Shared/Prototypes/HotReloadTest.cs b/Robust.UnitTesting/Shared/Prototypes/HotReloadTest.cs index 0316c415b21..481879b02f5 100644 --- a/Robust.UnitTesting/Shared/Prototypes/HotReloadTest.cs +++ b/Robust.UnitTesting/Shared/Prototypes/HotReloadTest.cs @@ -33,19 +33,15 @@ public sealed class HotReloadTest : RobustUnitTest value: 10 - type: {HotReloadTestComponentTwoId}"; - private IComponentFactory _components = default!; private PrototypeManager _prototypes = default!; private IMapManager _maps = default!; private IEntityManager _entities = default!; + protected override Type[]? ExtraComponents => new[] {typeof(HotReloadTestOneComponent), typeof(HotReloadTestTwoComponent)}; + [OneTimeSetUp] public void Setup() { - _components = IoCManager.Resolve(); - _components.RegisterClass(); - _components.RegisterClass(); - _components.GenerateNetIds(); - IoCManager.Resolve().Initialize(); _prototypes = (PrototypeManager) IoCManager.Resolve(); _prototypes.RegisterKind(typeof(EntityPrototype)); diff --git a/Robust.UnitTesting/Shared/Prototypes/PrototypeManager_Test.cs b/Robust.UnitTesting/Shared/Prototypes/PrototypeManager_Test.cs index bb1a8b1b46d..bcf803e0b85 100644 --- a/Robust.UnitTesting/Shared/Prototypes/PrototypeManager_Test.cs +++ b/Robust.UnitTesting/Shared/Prototypes/PrototypeManager_Test.cs @@ -1,3 +1,4 @@ +using System; using System.Numerics; using JetBrains.Annotations; using NUnit.Framework; @@ -21,13 +22,11 @@ public sealed class PrototypeManager_Test : RobustUnitTest private const string LoadStringTestDummyId = "LoadStringTestDummy"; private IPrototypeManager manager = default!; + protected override Type[] ExtraComponents => new[] {typeof(TestBasicPrototypeComponent), typeof(PointLightComponent)}; + [OneTimeSetUp] public void Setup() { - var factory = IoCManager.Resolve(); - factory.RegisterClass(); - factory.RegisterClass(); - IoCManager.Resolve().Initialize(); manager = IoCManager.Resolve(); manager.RegisterKind(typeof(EntityPrototype)); diff --git a/Robust.UnitTesting/Shared/Serialization/InheritanceSerializationTest.cs b/Robust.UnitTesting/Shared/Serialization/InheritanceSerializationTest.cs index a2691cbdbab..3fc8251ac33 100644 --- a/Robust.UnitTesting/Shared/Serialization/InheritanceSerializationTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/InheritanceSerializationTest.cs @@ -1,3 +1,4 @@ +using System; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -43,16 +44,11 @@ public sealed partial class InheritanceSerializationTest : RobustUnitTest inheritorField: {InheritorComponentFieldValue} finalField: {FinalComponentFieldValue}"; + protected override Type[]? ExtraComponents => new[] {typeof(TestBaseComponent), typeof(TestInheritorComponent), typeof(TestFinalComponent)}; + [Test] public void Test() { - var componentFactory = IoCManager.Resolve(); - - componentFactory.RegisterClass(); - componentFactory.RegisterClass(); - componentFactory.RegisterClass(); - componentFactory.GenerateNetIds(); - var serializationManager = IoCManager.Resolve(); serializationManager.Initialize(); diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs index 37e75089b8b..cdeab9532b0 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using NUnit.Framework; using Robust.Shared.GameObjects; @@ -19,12 +20,7 @@ namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers [TestOf(typeof(ComponentRegistrySerializer))] public sealed class ComponentRegistrySerializerTest : SerializationTest { - [OneTimeSetUp] - public new void OneTimeSetup() - { - var componentFactory = IoCManager.Resolve(); - componentFactory.RegisterClass(); - } + protected override Type[]? ExtraComponents => new[] {typeof(TestComponent)}; [Test] public void SerializationTest() From 45af00096fb0647760285680e85b731d9d8ae197 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sat, 6 Jan 2024 18:43:23 +1100 Subject: [PATCH 05/16] Version: 203.0.0 --- MSBuild/Robust.Engine.Version.props | 2 +- RELEASE-NOTES.md | 29 +++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/MSBuild/Robust.Engine.Version.props b/MSBuild/Robust.Engine.Version.props index eb4a9d76311..eca9f77c4ed 100644 --- a/MSBuild/Robust.Engine.Version.props +++ b/MSBuild/Robust.Engine.Version.props @@ -1,4 +1,4 @@ - 202.1.1 + 203.0.0 diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 01197a6614e..adff72fdf2b 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -35,15 +35,11 @@ END TEMPLATE--> ### Breaking changes -* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered. -* Event bus subscriptions are now locked after `IEntityManager` has started, instead of after the first component gets added. Any event subscriptions now need to happen before startup (but after init). -* Event bus subscriptions must now be locked before raising any events. +*None yet* ### New features -* `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components. -* `IComponentFactory.RegisterIgnore()` now accepts more than one string. -* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components. +*None yet* ### Bugfixes @@ -58,6 +54,27 @@ END TEMPLATE--> *None yet* +## 203.0.0 + +### Breaking changes + +* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered. +* Event bus subscriptions are now locked after `IEntityManager` has started, instead of after the first component gets added. Any event subscriptions now need to happen before startup (but after init). +* Event bus subscriptions must now be locked before raising any events. +* Delete FodyWeavers.xsd as it hasn't been used for a long time. +* Remove physics sleep cancelling as it was, in hindsight, a bad idea. + +### New features + +* `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components. +* `IComponentFactory.RegisterIgnore()` now accepts more than one string. +* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components. + +### Bugfixes + +* Clamp volume calculations for audio rather than throwing. + + ## 202.1.1 ### Bugfixes From 9de5840017d2efc3a8b46f102c6deaa8abc99ea1 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 6 Jan 2024 05:05:02 -0800 Subject: [PATCH 06/16] Make IEntityManager.EntityNetManager not nullable, make EntityManager abstract (#4445) Co-authored-by: metalgearsloth --- Robust.Shared/GameObjects/EntityManager.cs | 4 ++-- Robust.Shared/GameObjects/IEntityManager.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Robust.Shared/GameObjects/EntityManager.cs b/Robust.Shared/GameObjects/EntityManager.cs index 90d80a5461a..f903aa7c8f8 100644 --- a/Robust.Shared/GameObjects/EntityManager.cs +++ b/Robust.Shared/GameObjects/EntityManager.cs @@ -26,7 +26,7 @@ namespace Robust.Shared.GameObjects /// [Virtual] - public partial class EntityManager : IEntityManager + public abstract partial class EntityManager : IEntityManager { #region Dependencies @@ -61,7 +61,7 @@ public partial class EntityManager : IEntityManager public IEntitySystemManager EntitySysManager => _entitySystemManager; /// - public virtual IEntityNetworkManager? EntityNetManager => null; + public abstract IEntityNetworkManager EntityNetManager { get; } protected readonly Queue QueuedDeletions = new(); protected readonly HashSet QueuedDeletionsSet = new(); diff --git a/Robust.Shared/GameObjects/IEntityManager.cs b/Robust.Shared/GameObjects/IEntityManager.cs index 7884a4fac53..fc052dc8bfc 100644 --- a/Robust.Shared/GameObjects/IEntityManager.cs +++ b/Robust.Shared/GameObjects/IEntityManager.cs @@ -46,7 +46,7 @@ public partial interface IEntityManager IComponentFactory ComponentFactory { get; } IEntitySystemManager EntitySysManager { get; } - IEntityNetworkManager? EntityNetManager { get; } + IEntityNetworkManager EntityNetManager { get; } IEventBus EventBus { get; } #region Entity Management From 359811f71ee920220a86a570121173f731058748 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 6 Jan 2024 15:47:39 +0100 Subject: [PATCH 07/16] Fix incorrect IPostInjectInit in DebugConsole IPostInjectInit does not run through IoCManager.InjectDependencies(). This meant the logger was not initialized, and loading failures in the console history would crash the client startup due to an NRE while trying to log a warning. See https://github.com/space-wizards/space-station-14/issues/23624 --- .../UserInterface/CustomControls/DebugConsole.xaml.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Robust.Client/UserInterface/CustomControls/DebugConsole.xaml.cs b/Robust.Client/UserInterface/CustomControls/DebugConsole.xaml.cs index 86fb7afb45c..59104cc0aca 100644 --- a/Robust.Client/UserInterface/CustomControls/DebugConsole.xaml.cs +++ b/Robust.Client/UserInterface/CustomControls/DebugConsole.xaml.cs @@ -39,7 +39,7 @@ public interface IDebugConsoleView // And also if Update() stops firing due to an exception loop the console will still work. // (At least from the main thread, which is what's throwing the exceptions..) [GenerateTypedNameReferences] - public sealed partial class DebugConsole : Control, IDebugConsoleView, IPostInjectInit + public sealed partial class DebugConsole : Control, IDebugConsoleView { [Dependency] private readonly IClientConsoleHost _consoleHost = default!; [Dependency] private readonly IResourceManager _resourceManager = default!; @@ -49,7 +49,7 @@ public sealed partial class DebugConsole : Control, IDebugConsoleView, IPostInje private static readonly ResPath HistoryPath = new("/debug_console_history.json"); private readonly ConcurrentQueue _messageQueue = new(); - private ISawmill _logger = default!; + private readonly ISawmill _logger; public DebugConsole() { @@ -57,6 +57,8 @@ public DebugConsole() IoCManager.InjectDependencies(this); + _logger = _logMan.GetSawmill("dbgconsole"); + InitCompletions(); CommandBar.OnTextChanged += OnCommandChanged; @@ -282,10 +284,5 @@ await Task.Run(async () => } }); } - - void IPostInjectInit.PostInject() - { - _logger = _logMan.GetSawmill("dbgconsole"); - } } } From 9502c86a65e081316432cf3c252069a767b1a63a Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 6 Jan 2024 18:51:54 +0100 Subject: [PATCH 08/16] Fix GLFW clipboard sometimes returning null. Fixes #4817 --- RELEASE-NOTES.md | 10 +++++----- Robust.Client/Graphics/Clyde/Windowing/Glfw.Windows.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index adff72fdf2b..11b08415003 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -43,7 +43,7 @@ END TEMPLATE--> ### Bugfixes -*None yet* +* Fix `IClipboardManager.GetText()` returning null in some cases. ### Other @@ -67,8 +67,8 @@ END TEMPLATE--> ### New features * `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components. -* `IComponentFactory.RegisterIgnore()` now accepts more than one string. -* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components. +* `IComponentFactory.RegisterIgnore()` now accepts more than one string. +* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components. ### Bugfixes @@ -82,7 +82,7 @@ END TEMPLATE--> * Reverted some map/grid initialisation changes that might've been causing broadphase/physics errors. * Fixed PVS sometimes sending entities without first sending their children. * Fixed a container state handling bug caused by containers not removing expected entities when shutting down. -* Fixed a `EnsureEntity` state handling bug caused by improper handling of entity deletions. +* Fixed a `EnsureEntity` state handling bug caused by improper handling of entity deletions. * Fixed a bad NetSyncEnabled debug assert. @@ -98,7 +98,7 @@ END TEMPLATE--> ### Breaking changes * Various entity manager methods now have a new `where T : IComponent` constraint. -* The `IComponentFactory.ComponentAdded` event has been renamed to `ComponentsAdded` and now provides an array of component registrations. +* The `IComponentFactory.ComponentAdded` event has been renamed to `ComponentsAdded` and now provides an array of component registrations. * `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered. ### New features diff --git a/Robust.Client/Graphics/Clyde/Windowing/Glfw.Windows.cs b/Robust.Client/Graphics/Clyde/Windowing/Glfw.Windows.cs index 2ae9430a1ef..5aa9d69497e 100644 --- a/Robust.Client/Graphics/Clyde/Windowing/Glfw.Windows.cs +++ b/Robust.Client/Graphics/Clyde/Windowing/Glfw.Windows.cs @@ -654,7 +654,7 @@ public Task ClipboardGetText(WindowReg mainWindow) private static void WinThreadGetClipboard(CmdGetClipboard cmd) { - var clipboard = GLFW.GetClipboardString((Window*) cmd.Window); + var clipboard = GLFW.GetClipboardString((Window*) cmd.Window) ?? ""; // Don't have to care about synchronization I don't think so just fire this immediately. cmd.Tcs.TrySetResult(clipboard); } From bab6c29fbef546cf087c9b090d3b7243f100bc70 Mon Sep 17 00:00:00 2001 From: Kot <1192090+koteq@users.noreply.github.com> Date: Sat, 6 Jan 2024 21:59:19 +0400 Subject: [PATCH 09/16] Add TextEdit.OnTextChanged event (#4818) --- .../UserInterface/Controls/TextEdit.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Robust.Client/UserInterface/Controls/TextEdit.cs b/Robust.Client/UserInterface/Controls/TextEdit.cs index 30d60fce687..d22575698fa 100644 --- a/Robust.Client/UserInterface/Controls/TextEdit.cs +++ b/Robust.Client/UserInterface/Controls/TextEdit.cs @@ -90,6 +90,8 @@ public sealed class TextEdit : Control internal bool DebugOverlay; private Vector2? _lastDebugMousePos; + public event Action? OnTextChanged; + public TextEdit() { IoCManager.InjectDependencies(this); @@ -315,7 +317,7 @@ protected internal override void KeyBindDown(GUIBoundKeyEventArgs args) if (changed) { _selectionStart = _cursorPosition; - // OnTextChanged?.Invoke(new LineEditEventArgs(this, _text)); + OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope)); // _updatePseudoClass(); // OnBackspace?.Invoke(new LineEditBackspaceEventArgs(oldText, _text, cursor, selectStart)); } @@ -349,7 +351,7 @@ protected internal override void KeyBindDown(GUIBoundKeyEventArgs args) if (changed) { _selectionStart = _cursorPosition; - // OnTextChanged?.Invoke(new LineEditEventArgs(this, _text)); + OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope)); // _updatePseudoClass(); } @@ -382,7 +384,10 @@ protected internal override void KeyBindDown(GUIBoundKeyEventArgs args) } if (changed) + { _selectionStart = _cursorPosition; + OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope)); + } InvalidateHorizontalCursorPos(); args.Handle(); @@ -411,7 +416,10 @@ protected internal override void KeyBindDown(GUIBoundKeyEventArgs args) } if (changed) + { _selectionStart = _cursorPosition; + OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope)); + } InvalidateHorizontalCursorPos(); args.Handle(); @@ -748,6 +756,7 @@ protected internal override void TextEditing(GUITextEditingEventArgs args) var startPos = _cursorPosition; TextRope = Rope.Insert(TextRope, startPos.Index, ev.Text); + OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope)); _selectionStart = _cursorPosition = new CursorPos(startPos.Index + startChars, LineBreakBias.Top); _imeData = (startPos, ev.Text.Length); @@ -844,6 +853,7 @@ public void InsertAtCursor(string text) var upper = SelectionUpper.Index; TextRope = Rope.ReplaceSubstring(TextRope, lower, upper - lower, text); + OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope)); _selectionStart = _cursorPosition = new CursorPos(lower + text.Length, LineBreakBias.Top); // OnTextChanged?.Invoke(new LineEditEventArgs(this, _text)); @@ -1441,6 +1451,12 @@ protected internal override void KeyboardFocusExited() AbortIme(delete: false); } + public sealed class TextEditEventArgs(TextEdit control, Rope.Node textRope) : EventArgs + { + public TextEdit Control { get; } = control; + public Rope.Node TextRope { get; } = textRope; + } + /// /// Specifies which line the cursor is positioned at when on a word-wrapping break. /// From b51cb06d53307cef8be315329ecf062b8fd0cdf1 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 6 Jan 2024 19:23:58 +0100 Subject: [PATCH 10/16] Changelog for #4818 --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 11b08415003..13bb51b7857 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -39,7 +39,7 @@ END TEMPLATE--> ### New features -*None yet* +* `TextEdit.OnTextChanged` ### Bugfixes From 2bf284bce84bf8186799e6306a5ab35ba8b3a86d Mon Sep 17 00:00:00 2001 From: Kara Date: Sat, 6 Jan 2024 19:27:58 -0700 Subject: [PATCH 11/16] Infer `VVAccess.ReadWrite` for all datafields (#4442) --- Robust.Shared/ViewVariables/ViewVariablesUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Robust.Shared/ViewVariables/ViewVariablesUtility.cs b/Robust.Shared/ViewVariables/ViewVariablesUtility.cs index 65071f44a29..827bb990d0c 100644 --- a/Robust.Shared/ViewVariables/ViewVariablesUtility.cs +++ b/Robust.Shared/ViewVariables/ViewVariablesUtility.cs @@ -40,7 +40,7 @@ public static bool TryGetViewVariablesAccess(MemberInfo info, [NotNullWhen(true) if (info.HasCustomAttribute() || info.HasCustomAttribute()) { - access = VVAccess.ReadOnly; + access = VVAccess.ReadWrite; return true; } From 538418ea932e92e291269aa145b65e0656010f6d Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 7 Jan 2024 04:22:38 +0100 Subject: [PATCH 12/16] Fix exception when VVing non-networked components The VV code forcibly dirties all components, which trips an assert for non-networked components. --- Robust.Server/ViewVariables/ViewVariablesSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Robust.Server/ViewVariables/ViewVariablesSession.cs b/Robust.Server/ViewVariables/ViewVariablesSession.cs index eeaa1c039bb..bbd10681e7b 100644 --- a/Robust.Server/ViewVariables/ViewVariablesSession.cs +++ b/Robust.Server/ViewVariables/ViewVariablesSession.cs @@ -97,7 +97,7 @@ public void Modify(object[] propertyIndex, object value) // Auto-dirty component. Only works when modifying a field that is directly on a component, // Does not work for nested objects. - if (Object is Component comp) + if (Object is Component { NetSyncEnabled: true } comp) EntityManager.Dirty(comp.Owner, comp); } From da2a2ce4ff50b56e935265c07eb5bc010f04bb0f Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 7 Jan 2024 04:43:59 +0100 Subject: [PATCH 13/16] Remove "Do not use from content" public members. --- Robust.Shared/GameObjects/Component.cs | 58 +++++++++++++++---- .../Components/MetaDataComponent.cs | 3 +- Robust.Shared/GameObjects/IComponent.cs | 16 +++-- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/Robust.Shared/GameObjects/Component.cs b/Robust.Shared/GameObjects/Component.cs index c21445cdd38..cddbb8cea36 100644 --- a/Robust.Shared/GameObjects/Component.cs +++ b/Robust.Shared/GameObjects/Component.cs @@ -16,8 +16,13 @@ public abstract partial class Component : IComponent [ViewVariables(VVAccess.ReadWrite)] private bool _netSync { get; set; } = true; - [Obsolete("Do not use from content")] - public bool Networked { get; set; } = true; + internal bool Networked { get; set; } = true; + + bool IComponent.Networked + { + get => Networked; + set => Networked = value; + } /// public bool NetSyncEnabled @@ -31,9 +36,14 @@ public bool NetSyncEnabled [Obsolete("Update your API to allow accessing Owner through other means")] public EntityUid Owner { get; set; } = EntityUid.Invalid; - /// [ViewVariables] - public ComponentLifeStage LifeStage { get; [Obsolete("Do not use from content")] set; } = ComponentLifeStage.PreAdd; + public ComponentLifeStage LifeStage { get; internal set; } = ComponentLifeStage.PreAdd; + + ComponentLifeStage IComponent.LifeStage + { + get => LifeStage; + set => LifeStage = value; + } public virtual bool SendOnlyToOwner => false; @@ -51,13 +61,29 @@ public bool NetSyncEnabled [ViewVariables] public bool Deleted => LifeStage >= ComponentLifeStage.Removing; - /// + /// + /// This is the tick the component was created. + /// [ViewVariables] - public GameTick CreationTick { get; [Obsolete("Do not use from content")] set; } + public GameTick CreationTick { get; internal set; } - /// + GameTick IComponent.CreationTick + { + get => CreationTick; + set => CreationTick = value; + } + + /// + /// Marks the component as dirty so that the network will re-sync it with clients. + /// [ViewVariables] - public GameTick LastModifiedTick { get; [Obsolete("Do not use from content")] set; } + public GameTick LastModifiedTick { get; internal set; } + + GameTick IComponent.LastModifiedTick + { + get => LastModifiedTick; + set => LastModifiedTick = value; + } /// [Obsolete] @@ -69,15 +95,23 @@ public void Dirty(IEntityManager? entManager = null) // these two methods clear the LastModifiedTick/CreationTick to mark it as "not different from prototype load". // This is used as optimization in the game state system to avoid sending redundant component data. - [Obsolete("Do not use from content")] - public virtual void ClearTicks() + void IComponent.ClearTicks() + { + ClearTicks(); + } + + private protected virtual void ClearTicks() { LastModifiedTick = GameTick.Zero; ClearCreationTick(); } - [Obsolete("Do not use from content")] - public void ClearCreationTick() + void IComponent.ClearCreationTick() + { + ClearCreationTick(); + } + + private protected void ClearCreationTick() { CreationTick = GameTick.Zero; } diff --git a/Robust.Shared/GameObjects/Components/MetaDataComponent.cs b/Robust.Shared/GameObjects/Components/MetaDataComponent.cs index a19cf7221d1..8ebf986bca7 100644 --- a/Robust.Shared/GameObjects/Components/MetaDataComponent.cs +++ b/Robust.Shared/GameObjects/Components/MetaDataComponent.cs @@ -192,8 +192,7 @@ internal set [ViewVariables] internal PvsChunkLocation? LastPvsLocation; - [Obsolete("Do not use from content")] - public override void ClearTicks() + private protected override void ClearTicks() { // Do not clear modified ticks. // MetaDataComponent is used in the game state system to carry initial data like prototype ID. diff --git a/Robust.Shared/GameObjects/IComponent.cs b/Robust.Shared/GameObjects/IComponent.cs index aab4e33894e..c3999910f6c 100644 --- a/Robust.Shared/GameObjects/IComponent.cs +++ b/Robust.Shared/GameObjects/IComponent.cs @@ -16,10 +16,9 @@ public partial interface IComponent /// The current lifetime stage of this component. You can use this to check /// if the component is initialized or being deleted. /// - ComponentLifeStage LifeStage { get; [Obsolete("Do not use from content")] set; } + ComponentLifeStage LifeStage { get; internal set; } - [Obsolete("Do not use from content")] - bool Networked { get; set; } + internal bool Networked { get; set; } /// /// Whether this component should be synchronized with clients when modified. @@ -68,22 +67,21 @@ public partial interface IComponent /// /// Marks the component as dirty so that the network will re-sync it with clients. /// + [Obsolete] void Dirty(IEntityManager? entManager = null); /// /// This is the tick the component was created. /// - GameTick CreationTick { get; set; } + GameTick CreationTick { get; internal set; } /// /// This is the last game tick Dirty() was called. /// - GameTick LastModifiedTick { get; set; } + GameTick LastModifiedTick { get; internal set; } - [Obsolete("Do not use from content")] - void ClearTicks(); + internal void ClearTicks(); - [Obsolete("Do not use from content")] - void ClearCreationTick(); + internal void ClearCreationTick(); } } From c68b3dccb7886acfbd1eeb189ddb83b04c98f521 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 8 Jan 2024 11:17:38 +0100 Subject: [PATCH 14/16] Fix server NRE in console command completions. Happens if you just type "tp:to" into the console. Toolshed can fail to provide completion results, in which case a null propagates and possibly crashes the server. --- RELEASE-NOTES.md | 1 + Robust.Server/Console/ServerConsoleHost.cs | 3 +++ Robust.Shared/Network/Messages/MsgConCompletionResp.cs | 4 +--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 13bb51b7857..885ce68744b 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -44,6 +44,7 @@ END TEMPLATE--> ### Bugfixes * Fix `IClipboardManager.GetText()` returning null in some cases. +* Fix possible NRE in server-side console command completion code. ### Other diff --git a/Robust.Server/Console/ServerConsoleHost.cs b/Robust.Server/Console/ServerConsoleHost.cs index aa1670d547b..ec072835589 100644 --- a/Robust.Server/Console/ServerConsoleHost.cs +++ b/Robust.Server/Console/ServerConsoleHost.cs @@ -259,6 +259,9 @@ private async void HandleConCompletions(MsgConCompletion message) } done: + + result ??= CompletionResult.Empty; + var msg = new MsgConCompletionResp { Result = result, diff --git a/Robust.Shared/Network/Messages/MsgConCompletionResp.cs b/Robust.Shared/Network/Messages/MsgConCompletionResp.cs index 3a64364c7c7..68bea41274e 100644 --- a/Robust.Shared/Network/Messages/MsgConCompletionResp.cs +++ b/Robust.Shared/Network/Messages/MsgConCompletionResp.cs @@ -4,14 +4,12 @@ namespace Robust.Shared.Network.Messages; -#nullable disable - public sealed class MsgConCompletionResp : NetMessage { public override MsgGroups MsgGroup => MsgGroups.Command; public int Seq { get; set; } - public CompletionResult Result { get; set; } + public CompletionResult Result { get; set; } = default!; public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { From 7592997f4e83273032484dba93818c4568a6b5a7 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 9 Jan 2024 00:37:19 +1100 Subject: [PATCH 15/16] Add random pick / take methods for sets (#4821) * Add random pick / take methods for sets I want it don't at me. * cleanup (mraow) * Revert "cleanup (mraow)" This reverts commit e279957f21efd4271c54a68164195dcbbfdd37d2. * flatpak * notes --- RELEASE-NOTES.md | 1 + Robust.Shared/Random/IRobustRandom.cs | 2 ++ Robust.Shared/Random/RandomExtensions.cs | 33 +++++++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 885ce68744b..bbe3589c4c0 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -40,6 +40,7 @@ END TEMPLATE--> ### New features * `TextEdit.OnTextChanged` +* Add Pick and PickAndTake versions for System.Random for ICollections. ### Bugfixes diff --git a/Robust.Shared/Random/IRobustRandom.cs b/Robust.Shared/Random/IRobustRandom.cs index 4c50b929233..b7a0e2bdbdd 100644 --- a/Robust.Shared/Random/IRobustRandom.cs +++ b/Robust.Shared/Random/IRobustRandom.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using Robust.Shared.Collections; using Robust.Shared.Maths; +using Robust.Shared.Toolshed.Commands.Generic; namespace Robust.Shared.Random; diff --git a/Robust.Shared/Random/RandomExtensions.cs b/Robust.Shared/Random/RandomExtensions.cs index 1ca88e1bea0..891105128ae 100644 --- a/Robust.Shared/Random/RandomExtensions.cs +++ b/Robust.Shared/Random/RandomExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using Robust.Shared.Collections; using Robust.Shared.Maths; using Robust.Shared.Utility; @@ -47,7 +48,7 @@ public static T Pick(this IRobustRandom random, IReadOnlyCollection collec } } - throw new InvalidOperationException("This should be unreachable!"); + throw new UnreachableException("This should be unreachable!"); } public static T PickAndTake(this IRobustRandom random, IList list) @@ -58,6 +59,36 @@ public static T PickAndTake(this IRobustRandom random, IList list) return element; } + /// + /// Picks a random element from a set and returns it. + /// This is O(n) as it has to iterate the collection until the target index. + /// + public static T Pick(this System.Random random, ICollection collection) + { + var index = random.Next(collection.Count); + var i = 0; + foreach (var t in collection) + { + if (i++ == index) + { + return t; + } + } + + throw new UnreachableException("This should be unreachable!"); + } + + /// + /// Picks a random from a collection then removes it and returns it. + /// This is O(n) as it has to iterate the collection until the target index. + /// + public static T PickAndTake(this System.Random random, ICollection set) + { + var tile = Pick(random, set); + set.Remove(tile); + return tile; + } + /// /// Generate a random number from a normal (gaussian) distribution. /// From ac60567583aec7edddd75425bf4f36f69a7b6439 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Tue, 9 Jan 2024 21:52:36 +1100 Subject: [PATCH 16/16] Version: 204.0.0 --- MSBuild/Robust.Engine.Version.props | 2 +- RELEASE-NOTES.md | 30 +++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/MSBuild/Robust.Engine.Version.props b/MSBuild/Robust.Engine.Version.props index eca9f77c4ed..3f86b934a0f 100644 --- a/MSBuild/Robust.Engine.Version.props +++ b/MSBuild/Robust.Engine.Version.props @@ -1,4 +1,4 @@ - 203.0.0 + 204.0.0 diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index bbe3589c4c0..64e8362b8f7 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -39,13 +39,11 @@ END TEMPLATE--> ### New features -* `TextEdit.OnTextChanged` -* Add Pick and PickAndTake versions for System.Random for ICollections. +*None yet* ### Bugfixes -* Fix `IClipboardManager.GetText()` returning null in some cases. -* Fix possible NRE in server-side console command completion code. +*None yet* ### Other @@ -56,6 +54,30 @@ END TEMPLATE--> *None yet* +## 204.0.0 + +### Breaking changes + +* Make EntityManager abstract and make IEntityManager.EntityNetManager not nullable. +* Make VVAccess.ReadWrite default for all Datafields instead of VVAccess.ReadOnly + +### New features + +* `TextEdit.OnTextChanged` +* Add Pick and PickAndTake versions for System.Random for ICollections. + +### Bugfixes + +* Fix `IClipboardManager.GetText()` returning null in some cases. +* Fix possible NRE in server-side console command completion code. +* Fix possible NRE on DebugConsole logs. +* Fix exception when VVing non-networked components. + +### Other + +* Remove "Do not use from content" from IComponent. + + ## 203.0.0 ### Breaking changes