diff --git a/src/c_console.cpp b/src/c_console.cpp index a9cd97557..649559966 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -69,6 +69,8 @@ #include "c_consolebuffer.h" #include "g_levellocals.h" +FString FStringFormat(VM_ARGS); // extern from thingdef_data.cpp + #include "gi.h" #define LEFTMARGIN 8 @@ -1332,6 +1334,14 @@ DEFINE_ACTION_FUNCTION(_Console, HideConsole) return 0; } +DEFINE_ACTION_FUNCTION(_Console, Printf) +{ + PARAM_PROLOGUE; + FString s = FStringFormat(param, defaultparam, numparam, ret, numret); + Printf("%s", s); + return 0; +} + static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer) { int data1 = ev->data1; diff --git a/src/events.cpp b/src/events.cpp index 60e2e3a49..b80aac2ef 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -61,6 +61,53 @@ bool E_IsStaticType(PClass* type) void E_SerializeEvents(FSerializer& arc) { // todo : stuff + if (arc.BeginArray("eventhandlers")) + { + int numlocalhandlers = 0; + TArray handlers; + if (arc.isReading()) + { + numlocalhandlers = arc.ArraySize(); + // delete all current local handlers, if any + for (DStaticEventHandler* lhandler = E_FirstEventHandler; lhandler; lhandler = lhandler->next) + if (!lhandler->IsStatic()) lhandler->Destroy(); + } + else + { + for (DStaticEventHandler* lhandler = E_FirstEventHandler; lhandler; lhandler = lhandler->next) + { + if (lhandler->IsStatic()) continue; + numlocalhandlers++; + handlers.Push(lhandler); + } + } + + for (int i = 0; i < numlocalhandlers; i++) + { + // serialize the object properly. + if (arc.isReading()) + { + // get object and put it into the array + DStaticEventHandler* lhandler; + arc(nullptr, lhandler); + if (lhandler != nullptr) + handlers.Push(lhandler); + } + else + { + ::Serialize(arc, nullptr, handlers[i], nullptr); + } + } + + if (arc.isReading()) + { + // add all newly deserialized handlers into the list + for (int i = 0; i < numlocalhandlers; i++) + E_RegisterHandler(handlers[i]); + } + + arc.EndArray(); + } } static void E_InitStaticHandler(PClass* type, FString typestring, bool map) @@ -124,23 +171,17 @@ void E_InitStaticHandlers(bool map) } } -void E_MapLoaded() -{ - for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) - handler->MapLoaded(); +#define DEFINE_EVENT_LOOPER(name) void E_##name() \ +{ \ + for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) \ + handler->name(); \ } -void E_MapUnloading() -{ - for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) - handler->MapUnloading(); -} - -void E_RenderFrame() -{ - for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) - handler->RenderFrame(); -} +DEFINE_EVENT_LOOPER(WorldLoaded) +DEFINE_EVENT_LOOPER(WorldUnloading) +DEFINE_EVENT_LOOPER(WorldLoadedUnsafe) +DEFINE_EVENT_LOOPER(WorldUnloadingUnsafe) +DEFINE_EVENT_LOOPER(RenderFrame) // declarations IMPLEMENT_CLASS(DStaticEventHandler, false, false); @@ -230,15 +271,17 @@ void cls::funcname(args) \ } \ } -DEFINE_EVENT_HANDLER(DStaticEventHandler, MapLoaded,) -DEFINE_EVENT_HANDLER(DStaticEventHandler, MapUnloading,) -DEFINE_EVENT_HANDLER(DStaticEventHandler, RenderFrame,) +DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldLoaded,) +DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldUnloading,) +DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldLoadedUnsafe, ) +DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldUnloadingUnsafe, ) +DEFINE_EVENT_HANDLER(DStaticEventHandler, RenderFrame, ) // void DStaticEventHandler::OnDestroy() { E_UnregisterHandler(this); - DObject::OnDestroy(); + Super::OnDestroy(); } void DStaticRenderEventHandler::Setup() @@ -254,6 +297,5 @@ void DStaticRenderEventHandler::Setup() void DStaticRenderEventHandler::RenderFrame() { Setup(); - DStaticEventHandler::RenderFrame(); + Super::RenderFrame(); } - diff --git a/src/events.h b/src/events.h index 4ea37c513..d7ceb4056 100755 --- a/src/events.h +++ b/src/events.h @@ -18,9 +18,13 @@ bool E_IsStaticType(PClass* type); void E_InitStaticHandlers(bool map); // called right after the map has loaded (approximately same time as OPEN ACS scripts) -void E_MapLoaded(); +void E_WorldLoaded(); // called when the map is about to unload (approximately same time as UNLOADING ACS scripts) -void E_MapUnloading(); +void E_WorldUnloading(); +// called right after the map has loaded (every time, UNSAFE VERSION) +void E_WorldLoadedUnsafe(); +// called right before the map is unloaded (every time, UNSAFE VERSION) +void E_WorldUnloadingUnsafe(); // called on each render frame once. void E_RenderFrame(); @@ -38,17 +42,28 @@ public: bool isMapScope; // this is only used with IsStatic=true virtual bool IsStatic() { return true; } + // serialization handler. let's keep it here so that I don't get lost in serialized/not serialized fields + void Serialize(FSerializer& arc) override + { + Super::Serialize(arc); + if (arc.isReading()) + { + Printf("DStaticEventHandler::Serialize: reading object %s\n", GetClass()->TypeName.GetChars()); + } + else + { + Printf("DStaticEventHandler::Serialize: store object %s\n", GetClass()->TypeName.GetChars()); + } + /* do nothing */ + } + // destroy handler. this unlinks EventHandler from the list automatically. void OnDestroy() override; - // this checks if we are /actually/ static, using DObject dynamic typing system. - static bool IsActuallyStatic(PClass* type); - - // called right after the map has loaded (approximately same time as OPEN ACS scripts) - virtual void MapLoaded(); - // called when the map is about to unload (approximately same time as UNLOADING ACS scripts) - virtual void MapUnloading(); - // called on each render frame once. + virtual void WorldLoaded(); + virtual void WorldUnloading(); + virtual void WorldLoadedUnsafe(); + virtual void WorldUnloadingUnsafe(); virtual void RenderFrame(); }; class DEventHandler : public DStaticEventHandler @@ -71,6 +86,18 @@ public: double FracTic; // 0..1 value that describes where we are inside the current gametic, render-wise. AActor* Camera; + // serialization handler for our local stuff + void Serialize(FSerializer& arc) override + { + Super::Serialize(arc); + arc("ViewPos", ViewPos); + arc("ViewAngle", ViewAngle); + arc("ViewPitch", ViewPitch); + arc("ViewRoll", ViewRoll); + arc("FracTic", FracTic); + arc("Camera", Camera); + } + void RenderFrame() override; private: diff --git a/src/g_level.cpp b/src/g_level.cpp index ec4e3049e..06fc40ff9 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -407,6 +407,10 @@ void G_InitNew (const char *mapname, bool bTitleLevel) bool wantFast; int i; + // did we have any level before? + if (level.info != nullptr) + E_WorldUnloadingUnsafe(); + if (!savegamerestore) { G_ClearHubInfo(); @@ -656,7 +660,10 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill // [RH] Give scripts a chance to do something unloading = true; FBehavior::StaticStartTypedScripts (SCRIPT_Unloading, NULL, false, 0, true); - E_MapUnloading(); + // [ZZ] safe world unload + E_WorldUnloading(); + // [ZZ] unsafe world unload (changemap != map) + E_WorldUnloadingUnsafe(); unloading = false; STAT_ChangeLevel(nextlevel); @@ -1068,8 +1075,8 @@ void G_DoLoadLevel (int position, bool autosave) StatusBar->AttachToPlayer (&players[consoleplayer]); // [ZZ] init per-map static handlers E_InitStaticHandlers(true); - // call map load hook - E_MapLoaded(); + // unsafe world load + E_WorldLoadedUnsafe(); P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map. if (demoplayback || oldgs == GS_STARTUP || oldgs == GS_TITLELEVEL) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 0eb619723..b52166347 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -1443,6 +1443,8 @@ void P_SpawnSpecials (void) break; } } + // [ZZ] safe world load + E_WorldLoaded(); // [RH] Start running any open scripts on this map FBehavior::StaticStartTypedScripts (SCRIPT_Open, NULL, false); } diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 9fa33314e..d349691c6 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -945,7 +945,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Replace) return 0; } -static FString FStringFormat(VM_ARGS) +FString FStringFormat(VM_ARGS) { assert(param[0].Type == REGT_STRING); FString fmtstring = param[0].s().GetChars(); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index a1177289e..55da329c9 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -41,6 +41,7 @@ struct Console native { native static void HideConsole(); native static void MidPrint(string fontname, string textlabel, bool bold = false); // always uses the stringtable. + native static vararg void Printf(string fmt, ...); } struct DamageTypeDefinition native diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 6d0be8cf5..80c022efb 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -1,12 +1,11 @@ class StaticEventHandler : Object native { - virtual native void MapLoaded(); - virtual native void MapUnloading(); + virtual native void WorldLoaded(); + virtual native void WorldLoadedUnsafe(); + virtual native void WorldUnloading(); + virtual native void WorldUnloadingUnsafe(); virtual native void RenderFrame(); - virtual native void RenderCamera(); - virtual native void RenderBeforeThing(); - virtual native void RenderAfterThing(); } class StaticRenderEventHandler : StaticEventHandler native @@ -17,10 +16,7 @@ class StaticRenderEventHandler : StaticEventHandler native native readonly double ViewPitch; native readonly double ViewRoll; native readonly double FracTic; - // for camera native readonly Actor Camera; - // for thing - native readonly Actor CurrentThing; } class EventHandler : StaticEventHandler native