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

@@ -30,19 +30,24 @@ public:
int functionReference; int functionReference;
bool isTemporary; bool isTemporary;
uint32 remainingShots; uint32 remainingShots;
int cancelCallbackRef; // Reference to a callback that will cancel this binding, or 0.
Eluna& E; Eluna& E;
Binding(Eluna& _E, int funcRef, uint32 shots) : Binding(Eluna& _E, int funcRef, uint32 shots, int cancelCallbackRef) :
functionReference(funcRef), functionReference(funcRef),
isTemporary(shots != 0), isTemporary(shots != 0 && cancelCallbackRef == 0),
remainingShots(shots), remainingShots(shots),
cancelCallbackRef(cancelCallbackRef),
E(_E) E(_E)
{ {
} }
// Remove our function from the registry when the Binding is deleted.
~Binding() ~Binding()
{ {
// Remove our function and cancel callback from the registry when the Binding is deleted.
if (cancelCallbackRef > 0)
luaL_unref(E.L, LUA_REGISTRYINDEX, cancelCallbackRef);
luaL_unref(E.L, LUA_REGISTRYINDEX, functionReference); luaL_unref(E.L, LUA_REGISTRYINDEX, functionReference);
} }
}; };
@@ -63,6 +68,8 @@ public:
// unregisters all registered functions and clears all registered events from the bindings // unregisters all registered functions and clears all registered events from the bindings
virtual void Clear() { }; virtual void Clear() { };
virtual void ClearOne(int ref, uint32 event_id, uint32 entry, uint64 guid) = 0;
}; };
template<typename T> template<typename T>
@@ -81,8 +88,27 @@ public:
for (EventToFunctionsMap::iterator itr = Bindings.begin(); itr != Bindings.end(); ++itr) for (EventToFunctionsMap::iterator itr = Bindings.begin(); itr != Bindings.end(); ++itr)
{ {
FunctionRefVector& funcrefvec = itr->second; FunctionRefVector& funcrefvec = itr->second;
std::vector<int> cancelRefVector;
for (FunctionRefVector::iterator it = funcrefvec.begin(); it != funcrefvec.end(); ++it) for (FunctionRefVector::iterator it = funcrefvec.begin(); it != funcrefvec.end(); ++it)
delete *it; {
Binding* binding = (*it);
// Can't call the callback now, since it might modify `v` and crash the server.
// Just add the ref to a list and call them all after this loop.
if (binding->cancelCallbackRef)
cancelRefVector.push_back(binding->cancelCallbackRef);
else
delete binding; // Don't bother removing from list, clear is called at end anyway.
}
// Call all of the cancel callbacks for bindings with cancel callbacks.
for (std::vector<int>::iterator i = cancelRefVector.begin(); i != cancelRefVector.end(); ++i)
{
lua_rawgeti(E.L, LUA_REGISTRYINDEX, (*i));
lua_call(E.L, 0, 0);
}
funcrefvec.clear(); funcrefvec.clear();
} }
Bindings.clear(); Bindings.clear();
@@ -91,10 +117,51 @@ public:
void Clear(uint32 event_id) void Clear(uint32 event_id)
{ {
WriteGuard guard(GetLock()); WriteGuard guard(GetLock());
FunctionRefVector& v = Bindings[event_id];
std::vector<int> cancelRefVector;
for (FunctionRefVector::iterator itr = Bindings[event_id].begin(); itr != Bindings[event_id].end(); ++itr) for (FunctionRefVector::iterator itr = v.begin(); itr != v.end(); ++itr)
delete *itr; {
Bindings[event_id].clear(); Binding* binding = (*itr);
// Can't call the callback now, since it might modify `v` and crash the server.
// Just add the ref to a list and call them all after this loop.
if (binding->cancelCallbackRef)
cancelRefVector.push_back(binding->cancelCallbackRef);
else
delete binding; // Don't bother removing from list, clear is called at end anyway.
}
// Call all of the cancel callbacks for bindings with cancel callbacks.
for (std::vector<int>::iterator i = cancelRefVector.begin(); i != cancelRefVector.end(); ++i)
{
lua_rawgeti(E.L, LUA_REGISTRYINDEX, (*i));
lua_call(E.L, 0, 0);
}
v.clear();
}
void ClearOne(int ref, uint32 event_id, uint32 entry, uint64 guid) override
{
ASSERT(entry == 0 && guid == 0);
WriteGuard guard(GetLock());
FunctionRefVector& funcrefvec = Bindings[event_id];
for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i)
{
Binding* binding = (*i);
if (binding->functionReference == ref)
{
i = funcrefvec.erase(i);
delete binding;
return;
}
}
ASSERT(false && "tried to clear function ref that doesn't exist");
} }
// Pushes the function references and updates the counters on the binds and erases them if the counter would reach 0 // Pushes the function references and updates the counters on the binds and erases them if the counter would reach 0
@@ -111,6 +178,9 @@ public:
if (binding->isTemporary) if (binding->isTemporary)
{ {
// Bad things will happen if there's a cancel callback (due to ref reuse).
ASSERT(binding->cancelCallbackRef == 0);
binding->remainingShots--; binding->remainingShots--;
if (binding->remainingShots == 0) if (binding->remainingShots == 0)
{ {
@@ -124,10 +194,10 @@ public:
Bindings.erase(event_id); Bindings.erase(event_id);
}; };
void Insert(int eventId, int funcRef, uint32 shots) // Inserts a new registered event void Insert(int eventId, int funcRef, uint32 shots, int callbackRef = 0) // Inserts a new registered event
{ {
WriteGuard guard(GetLock()); WriteGuard guard(GetLock());
Bindings[eventId].push_back(new Binding(E, funcRef, shots)); Bindings[eventId].push_back(new Binding(E, funcRef, shots, callbackRef));
} }
// Checks if there are events for ID // Checks if there are events for ID
@@ -169,8 +239,27 @@ public:
for (EventToFunctionsMap::iterator it = funcmap.begin(); it != funcmap.end(); ++it) for (EventToFunctionsMap::iterator it = funcmap.begin(); it != funcmap.end(); ++it)
{ {
FunctionRefVector& funcrefvec = it->second; FunctionRefVector& funcrefvec = it->second;
std::vector<int> cancelRefVector;
for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i) for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i)
delete *i; {
Binding* binding = (*i);
// Can't call the callback now, since it might modify `v` and crash the server.
// Just add the ref to a list and call them all after this loop.
if (binding->cancelCallbackRef)
cancelRefVector.push_back(binding->cancelCallbackRef);
else
delete binding; // Don't bother removing from list, clear is called at end anyway.
}
// Call all of the cancel callbacks for bindings with cancel callbacks.
for (std::vector<int>::iterator i = cancelRefVector.begin(); i != cancelRefVector.end(); ++i)
{
lua_rawgeti(E.L, LUA_REGISTRYINDEX, (*i));
lua_call(E.L, 0, 0);
}
funcrefvec.clear(); funcrefvec.clear();
} }
funcmap.clear(); funcmap.clear();
@@ -181,10 +270,51 @@ public:
void Clear(uint32 entry, uint32 event_id) void Clear(uint32 entry, uint32 event_id)
{ {
WriteGuard guard(GetLock()); WriteGuard guard(GetLock());
FunctionRefVector& v = Bindings[entry][event_id];
std::vector<int> cancelRefVector;
for (FunctionRefVector::iterator itr = Bindings[entry][event_id].begin(); itr != Bindings[entry][event_id].end(); ++itr) for (FunctionRefVector::iterator itr = v.begin(); itr != v.end(); ++itr)
delete *itr; {
Bindings[entry][event_id].clear(); Binding* binding = (*itr);
// Can't call the callback now, since it might modify `v` and crash the server.
// Just add the ref to a list and call them all after this loop.
if (binding->cancelCallbackRef)
cancelRefVector.push_back(binding->cancelCallbackRef);
else
delete binding; // Don't bother removing from list, clear is called at end anyway.
}
// Call all of the cancel callbacks for bindings with cancel callbacks.
for (std::vector<int>::iterator i = cancelRefVector.begin(); i != cancelRefVector.end(); ++i)
{
lua_rawgeti(E.L, LUA_REGISTRYINDEX, (*i));
lua_call(E.L, 0, 0);
}
v.clear();
}
void ClearOne(int ref, uint32 event_id, uint32 entry, uint64 guid) override
{
ASSERT(entry != 0 && guid == 0);
WriteGuard guard(GetLock());
FunctionRefVector& funcrefvec = Bindings[entry][event_id];
for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i)
{
Binding* binding = (*i);
if (binding->functionReference == ref)
{
i = funcrefvec.erase(i);
delete binding;
return;
}
}
ASSERT(false && "tried to clear function ref that doesn't exist");
} }
// Pushes the function references and updates the counters on the binds and erases them if the counter would reach 0 // Pushes the function references and updates the counters on the binds and erases them if the counter would reach 0
@@ -201,6 +331,9 @@ public:
if (binding->isTemporary) if (binding->isTemporary)
{ {
// Bad things will happen if there's a cancel callback (due to ref reuse).
ASSERT(binding->cancelCallbackRef == 0);
binding->remainingShots--; binding->remainingShots--;
if (binding->remainingShots == 0) if (binding->remainingShots == 0)
{ {
@@ -217,10 +350,10 @@ public:
Bindings.erase(entry); Bindings.erase(entry);
}; };
void Insert(uint32 entryId, int eventId, int funcRef, uint32 shots) // Inserts a new registered event void Insert(uint32 entryId, int eventId, int funcRef, uint32 shots, int callbackRef = 0) // Inserts a new registered event
{ {
WriteGuard guard(GetLock()); WriteGuard guard(GetLock());
Bindings[entryId][eventId].push_back(new Binding(E, funcRef, shots)); Bindings[entryId][eventId].push_back(new Binding(E, funcRef, shots, callbackRef));
} }
// Returns true if the entry has registered binds // Returns true if the entry has registered binds
@@ -279,8 +412,26 @@ public:
for (EventToFunctionsMap::iterator it = funcmap.begin(); it != funcmap.end(); ++it) for (EventToFunctionsMap::iterator it = funcmap.begin(); it != funcmap.end(); ++it)
{ {
FunctionRefVector& funcrefvec = it->second; FunctionRefVector& funcrefvec = it->second;
std::vector<int> cancelRefVector;
for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i) for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i)
delete *i; {
Binding* binding = (*i);
// Can't call the callback now, since it might modify `v` and crash the server.
// Just add the ref to a list and call them all after this loop.
if (binding->cancelCallbackRef)
cancelRefVector.push_back(binding->cancelCallbackRef);
else
delete binding; // Don't bother removing from list, clear is called at end anyway.
}
// Call all of the cancel callbacks for bindings with cancel callbacks.
for (std::vector<int>::iterator i = cancelRefVector.begin(); i != cancelRefVector.end(); ++i)
{
lua_rawgeti(E.L, LUA_REGISTRYINDEX, (*i));
lua_call(E.L, 0, 0);
}
funcrefvec.clear(); funcrefvec.clear();
} }
funcmap.clear(); funcmap.clear();
@@ -294,12 +445,52 @@ public:
{ {
WriteGuard guard(GetLock()); WriteGuard guard(GetLock());
FunctionRefVector& v = Bindings[guid][instanceId][event_id]; FunctionRefVector& v = Bindings[guid][instanceId][event_id];
std::vector<int> cancelRefVector;
for (FunctionRefVector::iterator itr = v.begin(); itr != v.end(); ++itr) for (FunctionRefVector::iterator itr = v.begin(); itr != v.end(); ++itr)
delete *itr; {
Binding* binding = (*itr);
// Can't call the callback now, since it might modify `v` and crash the server.
// Just add the ref to a list and call them all after this loop.
if (binding->cancelCallbackRef)
cancelRefVector.push_back(binding->cancelCallbackRef);
else
delete binding; // Don't bother removing from list, clear is called at end anyway.
}
// Call all of the cancel callbacks for bindings with cancel callbacks.
for (std::vector<int>::iterator i = cancelRefVector.begin(); i != cancelRefVector.end(); ++i)
{
lua_rawgeti(E.L, LUA_REGISTRYINDEX, (*i));
lua_call(E.L, 0, 0);
}
v.clear(); v.clear();
} }
void ClearOne(int ref, uint32 event_id, uint32 instance_id, uint64 guid) override
{
ASSERT(guid != 0);
WriteGuard guard(GetLock());
FunctionRefVector& funcrefvec = Bindings[guid][instance_id][event_id];
for (FunctionRefVector::iterator i = funcrefvec.begin(); i != funcrefvec.end(); ++i)
{
Binding* binding = (*i);
if (binding->functionReference == ref)
{
i = funcrefvec.erase(i);
delete binding;
return;
}
}
ASSERT(false && "tried to clear function ref that doesn't exist");
}
// Pushes the function references and updates the counters on the binds and erases them if the counter would reach 0 // Pushes the function references and updates the counters on the binds and erases them if the counter would reach 0
void PushFuncRefs(lua_State* L, int event_id, uint64 guid, uint32 instanceId) void PushFuncRefs(lua_State* L, int event_id, uint64 guid, uint32 instanceId)
{ {
@@ -315,6 +506,9 @@ public:
if (binding->isTemporary) if (binding->isTemporary)
{ {
// Bad things will happen if there's a cancel callback (due to ref reuse).
ASSERT(binding->cancelCallbackRef == 0);
binding->remainingShots--; binding->remainingShots--;
if (binding->remainingShots == 0) if (binding->remainingShots == 0)
{ {
@@ -334,10 +528,10 @@ public:
Bindings.erase(guid); Bindings.erase(guid);
}; };
void Insert(uint64 guid, uint32 instanceId, int eventId, int funcRef, uint32 shots) // Inserts a new registered event void Insert(uint64 guid, uint32 instanceId, int eventId, int funcRef, uint32 shots, int callbackRef = 0) // Inserts a new registered event
{ {
WriteGuard guard(GetLock()); WriteGuard guard(GetLock());
Bindings[guid][instanceId][eventId].push_back(new Binding(E, funcRef, shots)); Bindings[guid][instanceId][eventId].push_back(new Binding(E, funcRef, shots, callbackRef));
} }
// Returns true if the entry has registered binds // Returns true if the entry has registered binds

View File

@@ -130,11 +130,11 @@ namespace ElunaUtil
public: public:
#ifdef USING_BOOST #ifdef USING_BOOST
typedef boost::shared_mutex LockType; typedef boost::recursive_mutex LockType;
typedef boost::shared_lock<boost::shared_mutex> ReadGuard; typedef boost::shared_lock<boost::shared_mutex> ReadGuard;
typedef boost::unique_lock<boost::shared_mutex> WriteGuard; typedef boost::unique_lock<boost::shared_mutex> WriteGuard;
#else #else
typedef ACE_RW_Thread_Mutex LockType; typedef ACE_Recursive_Thread_Mutex LockType;
typedef ACE_Read_Guard<LockType> ReadGuard; typedef ACE_Read_Guard<LockType> ReadGuard;
typedef ACE_Write_Guard<LockType> WriteGuard; typedef ACE_Write_Guard<LockType> WriteGuard;
#endif #endif

View File

@@ -497,54 +497,88 @@ namespace LuaGlobalFunctions
return 1; return 1;
} }
static void RegisterEntryHelper(Eluna* E, lua_State* L, int regtype) static int RegisterEntryHelper(Eluna* E, lua_State* L, int regtype)
{ {
uint32 entry = Eluna::CHECKVAL<uint32>(L, 1); uint32 entry = Eluna::CHECKVAL<uint32>(L, 1);
uint32 ev = Eluna::CHECKVAL<uint32>(L, 2); uint32 ev = Eluna::CHECKVAL<uint32>(L, 2);
luaL_checktype(L, 3, LUA_TFUNCTION); luaL_checktype(L, 3, LUA_TFUNCTION);
uint32 shots = Eluna::CHECKVAL<uint32>(L, 4, 0); uint32 shots = Eluna::CHECKVAL<uint32>(L, 4, 0);
bool returnCallback = Eluna::CHECKVAL<bool>(L, 5, false);
if (shots > 0 && returnCallback)
{
luaL_argerror(L, 5, "cannot return a callback if shots is > 0");
return 0;
}
lua_pushvalue(L, 3); lua_pushvalue(L, 3);
int functionRef = luaL_ref(L, LUA_REGISTRYINDEX); int functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
if (functionRef >= 0) if (functionRef >= 0)
E->Register(regtype, entry, 0, 0, ev, functionRef, shots); return E->Register(L, regtype, entry, 0, 0, ev, functionRef, shots, returnCallback);
else else
luaL_argerror(L, 3, "unable to make a ref to function"); luaL_argerror(L, 3, "unable to make a ref to function");
return 0;
} }
static void RegisterEventHelper(Eluna* E, lua_State* L, int regtype) static int RegisterEventHelper(Eluna* E, lua_State* L, int regtype)
{ {
uint32 ev = Eluna::CHECKVAL<uint32>(L, 1); uint32 ev = Eluna::CHECKVAL<uint32>(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION); luaL_checktype(L, 2, LUA_TFUNCTION);
uint32 shots = Eluna::CHECKVAL<uint32>(L, 3, 0); uint32 shots = Eluna::CHECKVAL<uint32>(L, 3, 0);
bool returnCallback = Eluna::CHECKVAL<bool>(L, 4, false);
if (shots > 0 && returnCallback)
{
luaL_argerror(L, 5, "cannot return a callback if shots is > 0");
return 0;
}
lua_pushvalue(L, 2); lua_pushvalue(L, 2);
int functionRef = luaL_ref(L, LUA_REGISTRYINDEX); int functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
if (functionRef >= 0) if (functionRef >= 0)
E->Register(regtype, 0, 0, 0, ev, functionRef, shots); return E->Register(L, regtype, 0, 0, 0, ev, functionRef, shots, returnCallback);
else else
luaL_argerror(L, 2, "unable to make a ref to function"); luaL_argerror(L, 2, "unable to make a ref to function");
return 0;
} }
static void RegisterUniqueHelper(Eluna* E, lua_State* L, int regtype) static int RegisterUniqueHelper(Eluna* E, lua_State* L, int regtype)
{ {
uint64 guid = Eluna::CHECKVAL<uint64>(L, 1); uint64 guid = Eluna::CHECKVAL<uint64>(L, 1);
uint32 instanceId = Eluna::CHECKVAL<uint32>(L, 2); uint32 instanceId = Eluna::CHECKVAL<uint32>(L, 2);
uint32 ev = Eluna::CHECKVAL<uint32>(L, 3); uint32 ev = Eluna::CHECKVAL<uint32>(L, 3);
luaL_checktype(L, 4, LUA_TFUNCTION); luaL_checktype(L, 4, LUA_TFUNCTION);
uint32 shots = Eluna::CHECKVAL<uint32>(L, 5, 0); uint32 shots = Eluna::CHECKVAL<uint32>(L, 5, 0);
bool returnCallback = Eluna::CHECKVAL<bool>(L, 6, false);
if (shots > 0 && returnCallback)
{
luaL_argerror(L, 5, "cannot return a callback if shots is > 0");
return 0;
}
lua_pushvalue(L, 4); lua_pushvalue(L, 4);
int functionRef = luaL_ref(L, LUA_REGISTRYINDEX); int functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
if (functionRef >= 0) if (functionRef >= 0)
E->Register(regtype, 0, guid, instanceId, ev, functionRef, shots); return E->Register(L, regtype, 0, guid, instanceId, ev, functionRef, shots, returnCallback);
else else
luaL_argerror(L, 4, "unable to make a ref to function"); luaL_argerror(L, 4, "unable to make a ref to function");
return 0;
} }
/** /**
* Registers a server event handler. * Registers a server event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearServerEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearServerEvents] work as normal.
*
* enum ServerEvents * enum ServerEvents
* { * {
* // Server * // Server
@@ -600,19 +634,35 @@ namespace LuaGlobalFunctions
* SERVER_EVENT_COUNT * SERVER_EVENT_COUNT
* }; * };
* *
* @proto (event, function)
* @proto (event, function, shots)
* @proto cancel = (event, function, 0, true)
*
* @param uint32 event : server event ID, refer to ServerEvents above * @param uint32 event : server event ID, refer to ServerEvents above
* @param function function : function that will be called when the event occurs * @param function function : function that will be called when the event occurs
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterServerEvent(Eluna* E, lua_State* L) int RegisterServerEvent(Eluna* E, lua_State* L)
{ {
RegisterEventHelper(E, L, Hooks::REGTYPE_SERVER); return RegisterEventHelper(E, L, Hooks::REGTYPE_SERVER);
return 0;
} }
/** /**
* Registers a [Player] event handler. * Registers a [Player] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearPlayerEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearPlayerEvents] work as normal.
*
* <pre> * <pre>
* enum PlayerEvents * enum PlayerEvents
* { * {
@@ -665,19 +715,35 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (event, function)
* @proto (event, function, shots)
* @proto cancel = (event, function, 0, true)
*
* @param uint32 event : [Player] event Id, refer to PlayerEvents above * @param uint32 event : [Player] event Id, refer to PlayerEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterPlayerEvent(Eluna* E, lua_State* L) int RegisterPlayerEvent(Eluna* E, lua_State* L)
{ {
RegisterEventHelper(E, L, Hooks::REGTYPE_PLAYER); return RegisterEventHelper(E, L, Hooks::REGTYPE_PLAYER);
return 0;
} }
/** /**
* Registers a [Guild] event handler. * Registers a [Guild] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearGuildEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearGuildEvents] work as normal.
*
* <pre> * <pre>
* enum GuildEvents * enum GuildEvents
* { * {
@@ -698,19 +764,35 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (event, function)
* @proto (event, function, shots)
* @proto cancel = (event, function, 0, true)
*
* @param uint32 event : [Guild] event Id, refer to GuildEvents above * @param uint32 event : [Guild] event Id, refer to GuildEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterGuildEvent(Eluna* E, lua_State* L) int RegisterGuildEvent(Eluna* E, lua_State* L)
{ {
RegisterEventHelper(E, L, Hooks::REGTYPE_GUILD); return RegisterEventHelper(E, L, Hooks::REGTYPE_GUILD);
return 0;
} }
/** /**
* Registers a [Group] event handler. * Registers a [Group] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearGroupEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearGroupEvents] work as normal.
*
* <pre> * <pre>
* enum GroupEvents * enum GroupEvents
* { * {
@@ -726,9 +808,16 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (event, function)
* @proto (event, function, shots)
* @proto cancel = (event, function, 0, true)
*
* @param uint32 event : [Group] event Id, refer to GroupEvents above * @param uint32 event : [Group] event Id, refer to GroupEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterGroupEvent(Eluna* E, lua_State* L) int RegisterGroupEvent(Eluna* E, lua_State* L)
{ {
@@ -739,6 +828,16 @@ namespace LuaGlobalFunctions
/** /**
* Registers a [BattleGround] event handler. * Registers a [BattleGround] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearBattleGroundEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearBattleGroundEvents] work as normal.
*
* <pre> * <pre>
* enum BGEvents * enum BGEvents
* { * {
@@ -750,19 +849,35 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (event, function)
* @proto (event, function, shots)
* @proto cancel = (event, function, 0, true)
*
* @param uint32 event : [BattleGround] event Id, refer to BGEvents above * @param uint32 event : [BattleGround] event Id, refer to BGEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterBGEvent(Eluna* E, lua_State* L) int RegisterBGEvent(Eluna* E, lua_State* L)
{ {
RegisterEventHelper(E, L, Hooks::REGTYPE_BG); return RegisterEventHelper(E, L, Hooks::REGTYPE_BG);
return 0;
} }
/** /**
* Registers a [WorldPacket] event handler. * Registers a [WorldPacket] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearPacketEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearPacketEvents] work as normal.
*
* <pre> * <pre>
* enum PacketEvents * enum PacketEvents
* { * {
@@ -774,20 +889,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (entry, event, function)
* @proto (entry, event, function, shots)
* @proto cancel = (entry, event, function, 0, true)
*
* @param uint32 entry : opcode * @param uint32 entry : opcode
* @param uint32 event : packet event Id, refer to PacketEvents above * @param uint32 event : packet event Id, refer to PacketEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterPacketEvent(Eluna* E, lua_State* L) int RegisterPacketEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_PACKET); return RegisterEntryHelper(E, L, Hooks::REGTYPE_PACKET);
return 0;
} }
/** /**
* Registers a [Creature] gossip event handler. * Registers a [Creature] gossip event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearCreatureGossipEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearCreatureGossipEvents] work as normal.
*
* <pre> * <pre>
* enum GossipEvents * enum GossipEvents
* { * {
@@ -797,20 +928,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (menu_id, event, function)
* @proto (menu_id, event, function, shots)
* @proto cancel = (menu_id, event, function, 0, true)
*
* @param uint32 menu_id : [Creature] entry Id * @param uint32 menu_id : [Creature] entry Id
* @param uint32 event : [Creature] gossip event Id, refer to GossipEvents above * @param uint32 event : [Creature] gossip event Id, refer to GossipEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterCreatureGossipEvent(Eluna* E, lua_State* L) int RegisterCreatureGossipEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_CREATURE_GOSSIP); return RegisterEntryHelper(E, L, Hooks::REGTYPE_CREATURE_GOSSIP);
return 0;
} }
/** /**
* Registers a [GameObject] gossip event handler. * Registers a [GameObject] gossip event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearGameObjectGossipEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearGameObjectGossipEvents] work as normal.
*
* <pre> * <pre>
* enum GossipEvents * enum GossipEvents
* { * {
@@ -820,20 +967,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (menu_id, event, function)
* @proto (menu_id, event, function, shots)
* @proto cancel = (menu_id, event, function, 0, true)
*
* @param uint32 menu_id : [GameObject] entry Id * @param uint32 menu_id : [GameObject] entry Id
* @param uint32 event : [GameObject] gossip event Id, refer to GossipEvents above * @param uint32 event : [GameObject] gossip event Id, refer to GossipEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterGameObjectGossipEvent(Eluna* E, lua_State* L) int RegisterGameObjectGossipEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_GAMEOBJECT_GOSSIP); return RegisterEntryHelper(E, L, Hooks::REGTYPE_GAMEOBJECT_GOSSIP);
return 0;
} }
/** /**
* Registers an [Item] event handler. * Registers an [Item] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearItemEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearItemEvents] work as normal.
*
* <pre> * <pre>
* enum ItemEvents * enum ItemEvents
* { * {
@@ -846,20 +1009,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (entry, event, function)
* @proto (entry, event, function, shots)
* @proto cancel = (entry, event, function, 0, true)
*
* @param uint32 entry : [Item] entry Id * @param uint32 entry : [Item] entry Id
* @param uint32 event : [Item] event Id, refer to ItemEvents above * @param uint32 event : [Item] event Id, refer to ItemEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterItemEvent(Eluna* E, lua_State* L) int RegisterItemEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_ITEM); return RegisterEntryHelper(E, L, Hooks::REGTYPE_ITEM);
return 0;
} }
/** /**
* Registers an [Item] gossip event handler. * Registers an [Item] gossip event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearItemGossipEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearItemGossipEvents] work as normal.
*
* <pre> * <pre>
* enum GossipEvents * enum GossipEvents
* { * {
@@ -869,20 +1048,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (entry, event, function)
* @proto (entry, event, function, shots)
* @proto cancel = (entry, event, function, 0, true)
*
* @param uint32 entry : [Item] entry Id * @param uint32 entry : [Item] entry Id
* @param uint32 event : [Item] gossip event Id, refer to GossipEvents above * @param uint32 event : [Item] gossip event Id, refer to GossipEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterItemGossipEvent(Eluna* E, lua_State* L) int RegisterItemGossipEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_ITEM_GOSSIP); return RegisterEntryHelper(E, L, Hooks::REGTYPE_ITEM_GOSSIP);
return 0;
} }
/** /**
* Registers a [Player] gossip event handler. * Registers a [Player] gossip event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearPlayerGossipEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearPlayerGossipEvents] work as normal.
*
* <pre> * <pre>
* enum GossipEvents * enum GossipEvents
* { * {
@@ -892,20 +1087,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (menu_id, event, function)
* @proto (menu_id, event, function, shots)
* @proto cancel = (menu_id, event, function, 0, true)
*
* @param uint32 menu_id : [Player] gossip menu Id * @param uint32 menu_id : [Player] gossip menu Id
* @param uint32 event : [Player] gossip event Id, refer to GossipEvents above * @param uint32 event : [Player] gossip event Id, refer to GossipEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterPlayerGossipEvent(Eluna* E, lua_State* L) int RegisterPlayerGossipEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_PLAYER_GOSSIP); return RegisterEntryHelper(E, L, Hooks::REGTYPE_PLAYER_GOSSIP);
return 0;
} }
/** /**
* Registers a [Creature] event handler. * Registers a [Creature] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearCreatureEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearCreatureEvents] work as normal.
*
* <pre> * <pre>
* enum CreatureEvents * enum CreatureEvents
* { * {
@@ -950,20 +1161,36 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (entry, event, function)
* @proto (entry, event, function, shots)
* @proto cancel = (entry, event, function, 0, true)
*
* @param uint32 entry : the ID of one or more [Creature]s * @param uint32 entry : the ID of one or more [Creature]s
* @param uint32 event : refer to CreatureEvents above * @param uint32 event : refer to CreatureEvents above
* @param function function : function that will be called when the event occurs * @param function function : function that will be called when the event occurs
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterCreatureEvent(Eluna* E, lua_State* L) int RegisterCreatureEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_CREATURE); return RegisterEntryHelper(E, L, Hooks::REGTYPE_CREATURE);
return 0;
} }
/** /**
* Registers a [Creature] event handler for a *single* [Creature]. * Registers a [Creature] event handler for a *single* [Creature].
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearUniqueCreatureEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearUniqueCreatureEvents] work as normal.
*
* <pre> * <pre>
* enum CreatureEvents * enum CreatureEvents
* { * {
@@ -1008,21 +1235,37 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (guid, instance_id, event, function)
* @proto (guid, instance_id, event, function, shots)
* @proto cancel = (guid, instance_id, event, function, 0, true)
*
* @param uint64 guid : the GUID of a single [Creature] * @param uint64 guid : the GUID of a single [Creature]
* @param uint32 instance_id : the instance ID of a single [Creature] * @param uint32 instance_id : the instance ID of a single [Creature]
* @param uint32 event : refer to CreatureEvents above * @param uint32 event : refer to CreatureEvents above
* @param function function : function that will be called when the event occurs * @param function function : function that will be called when the event occurs
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterUniqueCreatureEvent(Eluna* E, lua_State* L) int RegisterUniqueCreatureEvent(Eluna* E, lua_State* L)
{ {
RegisterUniqueHelper(E, L, Hooks::REGTYPE_CREATURE); return RegisterUniqueHelper(E, L, Hooks::REGTYPE_CREATURE);
return 0;
} }
/** /**
* Registers a [GameObject] event handler. * Registers a [GameObject] event handler.
* *
* If `return_callback` is `true`, a function will be returned which
* cancels this event binding.
*
* (The returned function is the **only** way to cancel the bindings;
* `shots` must be `0` (e.g. the binding will never expire), and
* [Global:ClearGameObjectEvents] will skip this binding.)
*
* If `return_callback` is `false`, nothing is returned, and `shots` and
* [Global:ClearGameObjectEvents] work as normal.
*
* <pre> * <pre>
* enum GameObjectEvents * enum GameObjectEvents
* { * {
@@ -1043,15 +1286,21 @@ namespace LuaGlobalFunctions
* }; * };
* </pre> * </pre>
* *
* @proto (entry, event, function)
* @proto (entry, event, function, shots)
* @proto cancel = (entry, event, function, 0, true)
*
* @param uint32 entry : [GameObject] entry Id * @param uint32 entry : [GameObject] entry Id
* @param uint32 event : [GameObject] event Id, refer to GameObjectEvents above * @param uint32 event : [GameObject] event Id, refer to GameObjectEvents above
* @param function function : function to register * @param function function : function to register
* @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function"
* @param bool return_callback = false
*
* @return function cancel : a function that cancels the binding when called
*/ */
int RegisterGameObjectEvent(Eluna* E, lua_State* L) int RegisterGameObjectEvent(Eluna* E, lua_State* L)
{ {
RegisterEntryHelper(E, L, Hooks::REGTYPE_GAMEOBJECT); return RegisterEntryHelper(E, L, Hooks::REGTYPE_GAMEOBJECT);
return 0;
} }
/** /**
@@ -2404,7 +2653,7 @@ namespace LuaGlobalFunctions
* Otherwise, only event handlers for `event_type` are cleared. * Otherwise, only event handlers for `event_type` are cleared.
* *
* **NOTE:** this will affect all instances of the [Creature], not just one. * **NOTE:** this will affect all instances of the [Creature], not just one.
* To bind and unbind events to a single [Creature], see [Global:RegisterUniqueCreatureEvent] and [Global:ClearUniqueCreatureEvent]. * To bind and unbind events to a single [Creature], see [Global:RegisterUniqueCreatureEvent] and [Global:ClearUniqueCreatureEvents].
* *
* @proto (entry) * @proto (entry)
* @proto (entry, event_type) * @proto (entry, event_type)

View File

@@ -24,6 +24,7 @@
extern "C" extern "C"
{ {
// Base lua libraries // Base lua libraries
#include "lua.h"
#include "lualib.h" #include "lualib.h"
#include "lauxlib.h" #include "lauxlib.h"
@@ -906,56 +907,142 @@ ElunaObject* Eluna::CHECKTYPE(lua_State* luastate, int narg, const char* tname,
return *ptrHold; 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 // 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) switch (regtype)
{ {
case Hooks::REGTYPE_SERVER: case Hooks::REGTYPE_SERVER:
if (evt < Hooks::SERVER_EVENT_COUNT) 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); ServerEventBindings->Insert(evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
case Hooks::REGTYPE_PLAYER: case Hooks::REGTYPE_PLAYER:
if (evt < Hooks::PLAYER_EVENT_COUNT) 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); PlayerEventBindings->Insert(evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
case Hooks::REGTYPE_GUILD: case Hooks::REGTYPE_GUILD:
if (evt < Hooks::GUILD_EVENT_COUNT) 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); GuildEventBindings->Insert(evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
case Hooks::REGTYPE_GROUP: case Hooks::REGTYPE_GROUP:
if (evt < Hooks::GROUP_EVENT_COUNT) 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); GroupEventBindings->Insert(evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
case Hooks::REGTYPE_VEHICLE: case Hooks::REGTYPE_VEHICLE:
if (evt < Hooks::VEHICLE_EVENT_COUNT) 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); VehicleEventBindings->Insert(evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
case Hooks::REGTYPE_BG: case Hooks::REGTYPE_BG:
if (evt < Hooks::BG_EVENT_COUNT) 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); BGEventBindings->Insert(evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
@@ -966,11 +1053,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id); 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); PacketEventBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
@@ -983,7 +1077,14 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id); 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); CreatureEventBindings->Insert(id, evt, functionRef, shots);
@@ -991,9 +1092,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
else else
{ {
ASSERT(guid != 0); 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); CreatureUniqueBindings->Insert(guid, instanceId, evt, functionRef, shots);
} }
return;
return 0; // Stack: (empty)
} }
break; break;
@@ -1004,11 +1114,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id); 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); CreatureGossipBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
@@ -1019,11 +1136,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", id); 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); GameObjectEventBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
@@ -1034,11 +1158,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", id); 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); GameObjectGossipBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
@@ -1049,11 +1180,18 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a item with (ID: %d)!", id); 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); ItemEventBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
@@ -1064,24 +1202,39 @@ void Eluna::Register(uint8 regtype, uint32 id, uint64 guid, uint32 instanceId, u
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Couldn't find a item with (ID: %d)!", id); 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); ItemGossipBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
case Hooks::REGTYPE_PLAYER_GOSSIP: case Hooks::REGTYPE_PLAYER_GOSSIP:
if (evt < Hooks::GOSSIP_EVENT_COUNT) 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); playerGossipBindings->Insert(id, evt, functionRef, shots);
return; return 0; // Stack: (empty)
} }
break; break;
} }
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
luaL_error(L, "Unknown event type (regtype %d, id %d, event %d)", regtype, id, evt); luaL_error(L, "Unknown event type (regtype %d, id %d, event %d)", regtype, id, evt);
return 0;
} }
/* /*

View File

@@ -272,7 +272,7 @@ public:
bool ShouldReload() const { return reload; } bool ShouldReload() const { return reload; }
bool IsEnabled() const { return enabled && IsInitialized(); } bool IsEnabled() const { return enabled && IsInitialized(); }
bool HasLuaState() const { return L != NULL; } bool HasLuaState() const { return L != NULL; }
void Register(uint8 reg, uint32 id, uint64 guid, uint32 instanceId, uint32 evt, int func, uint32 shots); int Register(lua_State* L, uint8 reg, uint32 id, uint64 guid, uint32 instanceId, uint32 evt, int func, uint32 shots, bool returnCallback);
// Non-static pushes, to be used in hooks. // Non-static pushes, to be used in hooks.
// These just call the correct static version with the main thread's Lua state. // These just call the correct static version with the main thread's Lua state.