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:
Patman64
2015-03-08 18:06:10 -04:00
parent b01d441dbd
commit 3490d21bcd
5 changed files with 673 additions and 77 deletions

View File

@@ -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;
}
/*