diff --git a/src/events.cpp b/src/events.cpp index c9aba1660..23a8a8800 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -7,6 +7,7 @@ #include "actor.h" DStaticEventHandler* E_FirstEventHandler = nullptr; +DStaticEventHandler* E_LastEventHandler = nullptr; bool E_RegisterHandler(DStaticEventHandler* handler) { @@ -14,17 +15,53 @@ bool E_RegisterHandler(DStaticEventHandler* handler) return false; if (E_CheckHandler(handler)) return false; + // link into normal list - handler->prev = nullptr; - handler->next = E_FirstEventHandler; - if (handler->next) - handler->next->prev = handler; - E_FirstEventHandler = handler; + // update: link at specific position based on order. + DStaticEventHandler* before = nullptr; + for (DStaticEventHandler* existinghandler = E_FirstEventHandler; existinghandler; existinghandler = existinghandler->next) + { + if (existinghandler->GetOrder() > handler->GetOrder()) + { + before = existinghandler; + break; + } + } + + // 1. MyHandler2->1: + // E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler2 + // 2. MyHandler3->2: + // E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler3 + + if (before != nullptr) + { + // if before is not null, link it before the existing handler. + // note that before can be first handler, check for this. + handler->next = before; + handler->prev = before->prev; + before->prev = handler; + if (before == E_FirstEventHandler) + E_FirstEventHandler = handler; + } + else + { + // so if before is null, it means add last. + // it can also mean that we have no handlers at all yet. + handler->prev = E_LastEventHandler; + handler->next = nullptr; + if (E_FirstEventHandler == nullptr) + E_FirstEventHandler = handler; + E_LastEventHandler = handler; + if (handler->prev != nullptr) + handler->prev->next = handler; + } + if (handler->IsStatic()) { handler->ObjectFlags |= OF_Fixed; handler->ObjectFlags |= OF_Transient; } + return true; } @@ -41,6 +78,8 @@ bool E_UnregisterHandler(DStaticEventHandler* handler) handler->next->prev = handler->prev; if (handler == E_FirstEventHandler) E_FirstEventHandler = handler->next; + if (handler == E_LastEventHandler) + E_LastEventHandler = handler->prev; if (handler->IsStatic()) { handler->ObjectFlags &= ~(OF_Fixed|OF_Transient); @@ -224,7 +263,7 @@ void E_WorldLoaded() void E_WorldUnloaded() { - for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + for (DStaticEventHandler* handler = E_LastEventHandler; handler; handler = handler->prev) { if (handler->IsStatic()) continue; handler->WorldUnloaded(); @@ -242,7 +281,7 @@ void E_WorldLoadedUnsafe() void E_WorldUnloadedUnsafe() { - for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + for (DStaticEventHandler* handler = E_LastEventHandler; handler; handler = handler->prev) { if (!handler->IsStatic()) continue; handler->WorldUnloaded(); @@ -294,7 +333,7 @@ void E_WorldThingDestroyed(AActor* actor) // this is because Destroyed should be reverse of Spawned. we don't want to catch random inventory give failures. if (!(actor->ObjectFlags & OF_Spawned)) return; - for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + for (DStaticEventHandler* handler = E_LastEventHandler; handler; handler = handler->prev) handler->WorldThingDestroyed(actor); } @@ -449,6 +488,12 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLightning) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldTick) DEFINE_EMPTY_HANDLER(DStaticEventHandler, RenderFrame) +DEFINE_ACTION_FUNCTION(DStaticEventHandler, GetOrder) +{ + PARAM_SELF_PROLOGUE(DStaticEventHandler); + ACTION_RETURN_INT(0); +} + // =========================================== // // Event handlers @@ -630,3 +675,22 @@ void DStaticEventHandler::OnDestroy() E_UnregisterHandler(this); Super::OnDestroy(); } + +// +int DStaticEventHandler::GetOrder() +{ + // if we have cached order, return it. + // otherwise call VM. + if (haveorder) + return order; + + IFVIRTUAL(DStaticEventHandler, GetOrder) + { + VMReturn results[1] = { &order }; + VMValue params[1] = { (DStaticEventHandler*)this }; + GlobalVMStack.Call(func, params, 1, results, 1, nullptr); + haveorder = true; + } + + return order; +} \ No newline at end of file diff --git a/src/events.h b/src/events.h index 4bbeb6174..50a227c77 100755 --- a/src/events.h +++ b/src/events.h @@ -59,12 +59,18 @@ public: { prev = 0; next = 0; + order = 0; + haveorder = false; } DStaticEventHandler* prev; DStaticEventHandler* next; virtual bool IsStatic() { return true; } + // order is cached to avoid calling the VM for sorting too much + int order; + bool haveorder; + // serialization handler. let's keep it here so that I don't get lost in serialized/not serialized fields void Serialize(FSerializer& arc) override { @@ -93,6 +99,9 @@ public: virtual void WorldLightning(); virtual void WorldTick(); virtual void RenderFrame(); + + // gets the order of this item. + int GetOrder(); }; class DEventHandler : public DStaticEventHandler { diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index a1029ff93..3f155c145 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -49,6 +49,11 @@ class StaticEventHandler : Object native virtual native void WorldTick(WorldEvent e); virtual native void RenderFrame(RenderEvent e); + + // this function should return a value that will be queried on Register() to decide the relative order of this handler to every other. + // this is most useful in UI systems. + // default is 0. + virtual native int GetOrder(); } class EventHandler : StaticEventHandler native