From ed679ae141e9783a85847e0ee37f8467ae6f1c48 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 22 Jul 2024 02:22:54 -0400 Subject: [PATCH] fix: queue actor actions --- client/Util/ActorManager.cs | 189 +++++++++++++++++++++++------------- 1 file changed, 124 insertions(+), 65 deletions(-) diff --git a/client/Util/ActorManager.cs b/client/Util/ActorManager.cs index 5d3da10..e966b47 100644 --- a/client/Util/ActorManager.cs +++ b/client/Util/ActorManager.cs @@ -7,13 +7,7 @@ namespace OrangeGuidanceTomestone.Util; internal class ActorManager : IDisposable { private Plugin Plugin { get; } private uint? _idx; - private readonly Queue _tasks = []; - - private enum Mode { - Enable, - Disable, - Delete, - } + private readonly Queue _tasks = []; internal ActorManager(Plugin plugin) { this.Plugin = plugin; @@ -31,49 +25,28 @@ internal class ActorManager : IDisposable { return; } - if (!this._tasks.TryPeek(out var mode)) { + if (!this._tasks.TryPeek(out var actorAction)) { return; } - var success = false; - var objMan = ClientObjectManager.Instance(); - var obj = objMan->GetObjectByIndex((ushort) idx); - if (obj == null) { - Plugin.Log.Warning("actor by index was null"); - return; + var success = false; + + if (actorAction.Tries < 10) { + try { + actorAction.Tries += 1; + success = actorAction.Run(this, objMan); + } catch (Exception ex) { + Plugin.Log.Error(ex, "Error in actor action queue"); + } + } else { + Plugin.Log.Warning("too many retries, skipping"); + success = true; } - switch (mode) { - case Mode.Disable: { - Plugin.Log.Debug("disabling actor"); - obj->DisableDraw(); - success = true; - break; - } - case Mode.Enable: { - if (!obj->IsReadyToDraw()) { - Plugin.Log.Debug("not ready to draw"); - break; - } - - Plugin.Log.Debug("drawing actor"); - obj->EnableDraw(); - success = true; - break; - } - case Mode.Delete: { - Plugin.Log.Debug("deleting actor"); - objMan->DeleteObjectByIndex((ushort) idx, 0); - this._idx = null; - success = true; - break; - } - } if (success) { - var res = this._tasks.Dequeue(); - Plugin.Log.Debug($"deq {Enum.GetName(res)}"); + this._tasks.Dequeue(); } } @@ -95,28 +68,7 @@ internal class ActorManager : IDisposable { Plugin.Log.Debug("spawning actor"); - var objMan = ClientObjectManager.Instance(); - var idx = objMan->CreateBattleCharacter(); - if (idx == 0xFFFFFFFF) { - Plugin.Log.Debug("actor could not be spawned"); - return; - } - this._idx = idx; - - var chara = (BattleChara*) objMan->GetObjectByIndex((ushort) idx); - - chara->ObjectKind = ObjectKind.BattleNpc; - chara->Position = message.Position; - chara->Rotation = message.Yaw; - var drawData = &chara->DrawData; - drawData->CustomizeData = new CustomizeData(); - - chara->Alpha = 0.25f; - chara->SetMode(CharacterModes.AnimLock, 0); - chara->Timeline.BaseOverride = 4818; - - this._tasks.Enqueue(Mode.Enable); } internal void Despawn() { @@ -124,7 +76,114 @@ internal class ActorManager : IDisposable { return; } - this._tasks.Enqueue(Mode.Disable); - this._tasks.Enqueue(Mode.Delete); + this._tasks.Enqueue(new DisableAction()); + this._tasks.Enqueue(new DeleteAction()); + } + + private unsafe abstract class BaseActorAction { + /// + /// Run this action. + /// + /// true if the action is finished, false if it should be run again + public abstract bool Run(ActorManager manager, ClientObjectManager* objMan); + + public int Tries { get; set; } + + protected bool TryGetBattleChara( + ActorManager manager, + ClientObjectManager* objMan, + out BattleChara* chara + ) { + chara = null; + + if (manager._idx is not { } idx) { + Plugin.Log.Warning("tried to get battlechara but idx was null"); + return false; + } + + var obj = objMan->GetObjectByIndex((ushort) idx); + if (obj == null) { + Plugin.Log.Warning("tried to get battlechara but it was null"); + return false; + } + + chara = (BattleChara*) obj; + return true; + } + } + + private unsafe class SpawnAction(Message message) : BaseActorAction { + public override bool Run(ActorManager manager, ClientObjectManager* objMan) { + if (manager._idx != null) { + Plugin.Log.Warning("refusing to spawn a second actor"); + return true; + } + + var idx = objMan->CreateBattleCharacter(); + if (idx == 0xFFFFFFFF) { + Plugin.Log.Debug("actor could not be spawned"); + return true; + } + + manager._idx = idx; + + var chara = (BattleChara*) objMan->GetObjectByIndex((ushort) idx); + + chara->ObjectKind = ObjectKind.BattleNpc; + chara->Position = message.Position; + chara->Rotation = message.Yaw; + var drawData = &chara->DrawData; + drawData->CustomizeData = new CustomizeData(); + + chara->Alpha = 0.25f; + chara->SetMode(CharacterModes.AnimLock, 0); + chara->Timeline.BaseOverride = 4818; + + manager._tasks.Enqueue(new EnableAction()); + return true; + } + } + + private unsafe class EnableAction : BaseActorAction { + public override bool Run(ActorManager manager, ClientObjectManager* objMan) { + if (!this.TryGetBattleChara(manager, objMan, out var chara)) { + return true; + } + + if (!chara->IsReadyToDraw()) { + return false; + } + + chara->EnableDraw(); + return true; + } + } + + private unsafe class DisableAction : BaseActorAction { + public override bool Run(ActorManager manager, ClientObjectManager* objMan) { + if (!this.TryGetBattleChara(manager, objMan, out var chara)) { + return true; + } + + chara->DisableDraw(); + return true; + } + } + + private unsafe class DeleteAction : BaseActorAction { + public override bool Run(ActorManager manager, ClientObjectManager* objMan) { + if (manager._idx is not { } idx) { + Plugin.Log.Warning("delete action but idx was null"); + return true; + } + + if (objMan->GetObjectByIndex((ushort) idx) == null) { + Plugin.Log.Warning("delete action but object at idx was null"); + return true; + } + + objMan->DeleteObjectByIndex((ushort) idx, 0); + return true; + } } }