diff --git a/Content.Tests/DMProject/Tests/Stdlib/List/remove.dm b/Content.Tests/DMProject/Tests/Stdlib/List/remove.dm index 0b9180a093..6015d2d914 100644 --- a/Content.Tests/DMProject/Tests/Stdlib/List/remove.dm +++ b/Content.Tests/DMProject/Tests/Stdlib/List/remove.dm @@ -8,3 +8,7 @@ L = list(1,2,3,2,1) L.Remove(list(2)) ASSERT(L ~= list(1,2,3,1)) + + L = list(1,2,3,2,1) + L.Remove(L) + ASSERT(L ~= list()) \ No newline at end of file diff --git a/OpenDreamClient/ClientVerbSystem.cs b/OpenDreamClient/ClientVerbSystem.cs index f92212d5d5..154d6bf4cc 100644 --- a/OpenDreamClient/ClientVerbSystem.cs +++ b/OpenDreamClient/ClientVerbSystem.cs @@ -242,6 +242,10 @@ private void OnUpdateClientVerbsEvent(UpdateClientVerbsEvent e) { _interfaceManager.DefaultInfo?.RefreshVerbs(this); } + public void RefreshVerbs() { + _interfaceManager.DefaultInfo?.RefreshVerbs(this); + } + private void OnLocalPlayerAttached(EntityUid obj) { // Our mob changed, update our verb panels // A little hacky, but also wait half a second for verb information about our mob to arrive diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 40d1cc1803..642915f848 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -11,6 +11,9 @@ using Robust.Client.Player; using Robust.Shared.Map; using Robust.Shared.Timing; +using Robust.Shared.Asynchronous; +using System.Threading; +using System.Threading.Tasks; namespace OpenDreamClient.Rendering; @@ -56,6 +59,7 @@ public int GetAnimationFrame(IGameTiming gameTiming) { private readonly Dictionary<(int X, int Y, int Z), Flick> _turfFlicks = new(); private readonly Dictionary _movableFlicks = new(); private bool _receivedAllAppearancesMsg; + private readonly float _timeToRefreshVerbs = 3f; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IDreamResourceManager _dreamResourceManager = default!; @@ -68,6 +72,8 @@ public int GetAnimationFrame(IGameTiming gameTiming) { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly MapSystem _mapSystem = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly ClientVerbSystem _verbSystem = default!; + [Dependency] private readonly ITaskManager _taskManager = default!; public override void Initialize() { UpdatesOutsidePrediction = true; @@ -77,6 +83,8 @@ public override void Initialize() { SubscribeNetworkEvent(OnAnimation); SubscribeNetworkEvent(OnFlick); SubscribeLocalEvent(OnWorldAABB); + + _ = StartVerbRefresher(new()); } public override void Shutdown() { @@ -363,4 +371,14 @@ public string GetName(ClientObjectReference reference) { public Flick? GetMovableFlick(EntityUid entity) { return _movableFlicks.GetValueOrDefault(entity); } + + private async Task StartVerbRefresher(CancellationTokenSource cancelSource) { + while (true) { + await Task.Delay(TimeSpan.FromSeconds(_timeToRefreshVerbs)); + if (cancelSource.IsCancellationRequested) + break; + + _taskManager.RunOnMainThread(_verbSystem.RefreshVerbs); + } + } } diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 7dc0e2e830..514c3faa28 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -652,6 +652,21 @@ public override IEnumerable EnumerateValues() { yield return new(verb); } + public override bool ContainsKey(DreamValue value) { + if (!value.TryGetValueAsInteger(out var index)) { + return false; + } + + return 1 <= index && index <= Verbs.Count; + } + + public override bool ContainsValue(DreamValue value) { + if (!value.TryGetValueAsProc(out var verb)) + return false; + + return Verbs.Contains(verb); + } + public override void SetValue(DreamValue key, DreamValue value, bool allowGrowth = false) { throw new Exception("Cannot set the values of a verbs list"); } @@ -667,6 +682,18 @@ public override void AddValue(DreamValue value) { _verbSystem?.UpdateClientVerbs(_client); } + public override void RemoveValue(DreamValue value) { + if (!value.TryGetValueAsProc(out var verb)) + return; + + var valueIndex = Verbs.LastIndexOf(verb); + + if (valueIndex != -1) { + Verbs.RemoveAt(valueIndex); + _verbSystem?.UpdateClientVerbs(_client); + } + } + public override void Cut(int start = 1, int end = 0) { int verbCount = Verbs.Count + 1; if (end == 0 || end > verbCount) end = verbCount; @@ -716,6 +743,23 @@ public override IEnumerable EnumerateValues() { } } + public override bool ContainsKey(DreamValue value) { + if (!value.TryGetValueAsInteger(out var index)) { + return false; + } + + return 1 <= index && index <= GetVerbs().Length; + } + + public override bool ContainsValue(DreamValue value) { + if (!value.TryGetValueAsProc(out var verb)) + return false; + if (verb.VerbId == null) + return false; + + return GetVerbs().Contains(verb.VerbId.Value); + } + public override void SetValue(DreamValue key, DreamValue value, bool allowGrowth = false) { throw new Exception("Cannot set the values of a verbs list"); } @@ -734,6 +778,21 @@ public override void AddValue(DreamValue value) { }); } + public override void RemoveValue(DreamValue value) { + if (!value.TryGetValueAsProc(out var verb)) + return; + if (verb.VerbId == null) { + return; + } + + atomManager.UpdateAppearance(atom, appearance => { + var valueIndex = appearance.Verbs.LastIndexOf(verb.VerbId.Value); + + if (valueIndex != -1) + appearance.Verbs.RemoveAt(valueIndex); + }); + } + public override void Cut(int start = 1, int end = 0) { atomManager.UpdateAppearance(atom, appearance => { int count = appearance.Verbs.Count + 1; diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeList.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeList.cs index 7f7ee507f4..9537f843d0 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeList.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeList.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Linq; +using System.Text; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using DreamValueTypeFlag = OpenDreamRuntime.DreamValue.DreamValueTypeFlag; @@ -150,7 +151,11 @@ private static int ListRemove(IDreamList list, ReadOnlySpan args) { var itemRemoved = 0; foreach (var argument in args) { if (argument.TryGetValueAsDreamList(out var argumentList)) { - foreach (DreamValue value in argumentList.EnumerateValues()) { + // In case this is a "listx.Remove(listx)" situation, copy the contents here to avoid modification while enumerating + // TODO: check for that case first to avoid unnecessary copy? + var subtraction = argumentList.EnumerateValues().ToList(); + + foreach (DreamValue value in subtraction) { if (list.ContainsValue(value)) { list.RemoveValue(value);