diff --git a/example/Dll.c b/example/Dll.c index 1ccccc6..845e8e3 100644 --- a/example/Dll.c +++ b/example/Dll.c @@ -206,6 +206,7 @@ void onUnitMorph(AIModule* module, Unit* unit) {} void onUnitRenegade(AIModule* module, Unit* unit) {} void onSaveGame(AIModule* module, const char* gameName) {} void onUnitComplete(AIModule* module, Unit* unit) {} +void drop(AIModule* module) {} static AIModule_vtable module_vtable = { onStart, @@ -224,7 +225,8 @@ static AIModule_vtable module_vtable = { onUnitMorph, onUnitRenegade, onSaveGame, - onUnitComplete + onUnitComplete, + drop }; DLLEXPORT void gameInit(BWAPI_Game* game) { diff --git a/include/AIModule.h b/include/AIModule.h index 459e7bb..1450cb7 100644 --- a/include/AIModule.h +++ b/include/AIModule.h @@ -37,7 +37,7 @@ extern "C" { /// /// Example: /// typedef struct MyModule { -/// truct AIModule; // base +/// struct AIModule; // base /// int my_field; /// ... /// } MyModule; @@ -49,6 +49,7 @@ typedef struct AIModule { struct AIModule_vtable { + // Game lifecycle hooks void (*onStart)(AIModule* module); void (*onEnd)(AIModule* module, bool isWinner); void (*onFrame)(AIModule* module); @@ -66,6 +67,9 @@ struct AIModule_vtable void (*onUnitRenegade)(AIModule* module, Unit* unit); void (*onSaveGame)(AIModule* module, const char* gameName); void (*onUnitComplete)(AIModule* module, Unit* unit); + // Destructor + // It is guaranteed drop() will be called exactly once, and after all other calls + void (*drop)(AIModule* module); }; /* BWAPI::AIModule* */ void* createAIModuleWrapper(AIModule* module); diff --git a/src/AIModule.cpp b/src/AIModule.cpp index 1976f57..970c253 100644 --- a/src/AIModule.cpp +++ b/src/AIModule.cpp @@ -1,6 +1,14 @@ #include #include "Cast.hpp" +template +void checkPointer(T* pointer) { + if (pointer == nullptr) { + printf("[BWAPI-C] FATAL ERROR: incorrect pointer"); + abort(); + } +} + class AIModuleWrapper : public BWAPI::AIModule { protected: @@ -58,10 +66,34 @@ class AIModuleWrapper : public BWAPI::AIModule virtual void onUnitComplete(BWAPI::Unit unit) override { module->vtable->onUnitComplete(module, reinterpret_cast(unit)); } + virtual ~AIModuleWrapper() override { + module->vtable->drop(module); + } AIModuleWrapper(::AIModule* module) : module(module) { + checkPointer(module); + checkPointer(module->vtable); + checkPointer(module->vtable->onStart); + checkPointer(module->vtable->onEnd); + checkPointer(module->vtable->onFrame); + checkPointer(module->vtable->onSendText); + checkPointer(module->vtable->onReceiveText); + checkPointer(module->vtable->onPlayerLeft); + checkPointer(module->vtable->onNukeDetect); + checkPointer(module->vtable->onUnitDiscover); + checkPointer(module->vtable->onUnitEvade); + checkPointer(module->vtable->onUnitShow); + checkPointer(module->vtable->onUnitHide); + checkPointer(module->vtable->onUnitCreate); + checkPointer(module->vtable->onUnitDestroy); + checkPointer(module->vtable->onUnitMorph); + checkPointer(module->vtable->onUnitRenegade); + checkPointer(module->vtable->onSaveGame); + checkPointer(module->vtable->onUnitComplete); + checkPointer(module->vtable->drop); + } };