mirror of
https://github.com/azerothcore/mod-ale
synced 2025-11-29 15:38:17 +08:00
Make the register functions optionally return a cancel callback.
It's now possible to have complete flexibility with cancelling bindings, i.e. it's possible to re-implement all of the other ways to cancel bindings using the callback. This sytem has been designed with complete safety in mind: - It is safe to call the callback more than once (does nothing after the first call). - The callback does not expire, and can be stored for as long as necessary. - If a callback is returned, there is no other way to cancel the binding (again, due to safety).
This commit is contained in:
197
LuaEngine.cpp
197
LuaEngine.cpp
@@ -24,6 +24,7 @@
|
||||
extern "C"
|
||||
{
|
||||
// Base lua libraries
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
@@ -906,56 +907,142 @@ ElunaObject* Eluna::CHECKTYPE(lua_State* luastate, int narg, const char* tname,
|
||||
return *ptrHold;
|
||||
}
|
||||
|
||||
static int cancelBinding(lua_State *L)
|
||||
{
|
||||
int ref = lua_tointeger(L, lua_upvalueindex(1));
|
||||
uint32 event_id = lua_tounsigned(L, lua_upvalueindex(2));
|
||||
uint32 entry_id = lua_tounsigned(L, lua_upvalueindex(3));
|
||||
uint64 guid = Eluna::CHECKVAL<uint64>(L, lua_upvalueindex(4));
|
||||
|
||||
// This marker (initially `false`) is used to protect against calling this callback twice.
|
||||
// After the first call, `alreadyCalled` will be `true`, so we know not to proceed.
|
||||
bool alreadyCalled = lua_toboolean(L, lua_upvalueindex(5));
|
||||
|
||||
if (alreadyCalled)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
lua_pushboolean(L, true);
|
||||
lua_replace(L, lua_upvalueindex(5));
|
||||
}
|
||||
|
||||
ElunaBind* bindings1 = (ElunaBind*)lua_touserdata(L, lua_upvalueindex(6));
|
||||
ASSERT(bindings1 != NULL);
|
||||
bindings1->ClearOne(ref, event_id, entry_id, guid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int createCancelCallback(lua_State* L, int ref, ElunaBind* bindings1, uint32 event_id, uint32 entry_id = 0, uint64 guid = 0)
|
||||
{
|
||||
lua_pushinteger(L, ref);
|
||||
lua_pushunsigned(L, event_id);
|
||||
lua_pushunsigned(L, entry_id);
|
||||
Eluna::Push(L, guid);
|
||||
lua_pushboolean(L, false);
|
||||
lua_pushlightuserdata(L, bindings1);
|
||||
// Stack: ref, event_id, entry_id, guid, false, bindings1
|
||||
|
||||
lua_pushcclosure(L, &cancelBinding, 6);
|
||||
// Stack: cancel_callback
|
||||
|
||||
lua_pushvalue(L, -1);
|
||||
return luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
// Stack: cancel_callback
|
||||
}
|
||||
|
||||
// Saves the function reference ID given to the register type's store for given entry under the given event
|
||||
void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, uint32 evt, int functionRef, uint32 shots)
|
||||
int Eluna::Register(lua_State* L, uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, uint32 evt, int functionRef, uint32 shots, bool returnCallback)
|
||||
{
|
||||
switch (regtype)
|
||||
{
|
||||
case Hooks::REGTYPE_SERVER:
|
||||
if (evt < Hooks::SERVER_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, ServerEventBindings, evt);
|
||||
ServerEventBindings->Insert(evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
ServerEventBindings->Insert(evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
case Hooks::REGTYPE_PLAYER:
|
||||
if (evt < Hooks::PLAYER_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, PlayerEventBindings, evt);
|
||||
PlayerEventBindings->Insert(evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
PlayerEventBindings->Insert(evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
case Hooks::REGTYPE_GUILD:
|
||||
if (evt < Hooks::GUILD_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, GuildEventBindings, evt);
|
||||
GuildEventBindings->Insert(evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
GuildEventBindings->Insert(evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
case Hooks::REGTYPE_GROUP:
|
||||
if (evt < Hooks::GROUP_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, GroupEventBindings, evt);
|
||||
GroupEventBindings->Insert(evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
GroupEventBindings->Insert(evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
case Hooks::REGTYPE_VEHICLE:
|
||||
if (evt < Hooks::VEHICLE_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, VehicleEventBindings, evt);
|
||||
VehicleEventBindings->Insert(evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
VehicleEventBindings->Insert(evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
case Hooks::REGTYPE_BG:
|
||||
if (evt < Hooks::BG_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, BGEventBindings, evt);
|
||||
BGEventBindings->Insert(evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
BGEventBindings->Insert(evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -966,11 +1053,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, PacketEventBindings, evt, id);
|
||||
PacketEventBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
PacketEventBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -983,7 +1077,14 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, CreatureEventBindings, evt, id);
|
||||
CreatureEventBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
CreatureEventBindings->Insert(id, evt, functionRef, shots);
|
||||
@@ -991,9 +1092,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
else
|
||||
{
|
||||
ASSERT(guid != 0);
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, CreatureUniqueBindings, evt, instanceId, guid);
|
||||
CreatureUniqueBindings->Insert(guid, instanceId, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
CreatureUniqueBindings->Insert(guid, instanceId, evt, functionRef, shots);
|
||||
}
|
||||
return;
|
||||
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1004,11 +1114,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, CreatureGossipBindings, evt, id);
|
||||
CreatureGossipBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
CreatureGossipBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1019,11 +1136,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, GameObjectEventBindings, evt, id);
|
||||
GameObjectEventBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
GameObjectEventBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1034,11 +1158,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, GameObjectGossipBindings, evt, id);
|
||||
GameObjectGossipBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
GameObjectGossipBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1049,11 +1180,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a item with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, ItemEventBindings, evt, id);
|
||||
ItemEventBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
ItemEventBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1064,24 +1202,39 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Couldn't find a item with (ID: %d)!", id);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, ItemGossipBindings, evt, id);
|
||||
ItemGossipBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
ItemGossipBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
|
||||
case Hooks::REGTYPE_PLAYER_GOSSIP:
|
||||
if (evt < Hooks::GOSSIP_EVENT_COUNT)
|
||||
{
|
||||
if (returnCallback)
|
||||
{
|
||||
int callbackRef = createCancelCallback(L, functionRef, playerGossipBindings, evt, id);
|
||||
playerGossipBindings->Insert(id, evt, functionRef, shots, callbackRef);
|
||||
return 1; // Stack: callback
|
||||
}
|
||||
|
||||
playerGossipBindings->Insert(id, evt, functionRef, shots);
|
||||
return;
|
||||
return 0; // Stack: (empty)
|
||||
}
|
||||
break;
|
||||
}
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
|
||||
luaL_error(L, "Unknown event type (regtype %d, id %d, event %d)", regtype, id, evt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user