diff --git a/ElunaBinding.h b/ElunaBinding.h index 831e1f4..d65a94e 100644 --- a/ElunaBinding.h +++ b/ElunaBinding.h @@ -17,7 +17,7 @@ extern "C" #include "lauxlib.h" }; -class ElunaBind +class ElunaBind : public ElunaUtil::RWLockable { public: struct Binding @@ -71,6 +71,8 @@ public: // unregisters all registered functions and clears all registered events from the bind std::maps (reset) void Clear() override { + WriteGuard guard(GetLock()); + for (EventToFunctionsMap::iterator itr = Bindings.begin(); itr != Bindings.end(); ++itr) { FunctionRefVector& funcrefvec = itr->second; @@ -83,6 +85,8 @@ public: void Clear(uint32 event_id) { + WriteGuard guard(GetLock()); + for (FunctionRefVector::iterator itr = Bindings[event_id].begin(); itr != Bindings[event_id].end(); ++itr) delete *itr; Bindings[event_id].clear(); @@ -91,6 +95,8 @@ public: // 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) { + WriteGuard guard(GetLock()); + for (FunctionRefVector::iterator it = Bindings[event_id].begin(); it != Bindings[event_id].end();) { FunctionRefVector::iterator it_old = it++; @@ -115,12 +121,15 @@ public: void Insert(int eventId, int funcRef, uint32 shots) // Inserts a new registered event { + WriteGuard guard(GetLock()); Bindings[eventId].push_back(new Binding(E, funcRef, shots)); } // Checks if there are events for ID - bool HasEvents(T eventId) const + bool HasEvents(T eventId) { + ReadGuard guard(GetLock()); + if (Bindings.empty()) return false; if (Bindings.find(eventId) == Bindings.end()) @@ -144,6 +153,8 @@ public: // unregisters all registered functions and clears all registered events from the bindmap void Clear() override { + WriteGuard guard(GetLock()); + for (EntryToEventsMap::iterator itr = Bindings.begin(); itr != Bindings.end(); ++itr) { EventToFunctionsMap& funcmap = itr->second; @@ -161,6 +172,8 @@ public: void Clear(uint32 entry, uint32 event_id) { + WriteGuard guard(GetLock()); + for (FunctionRefVector::iterator itr = Bindings[entry][event_id].begin(); itr != Bindings[entry][event_id].end(); ++itr) delete *itr; Bindings[entry][event_id].clear(); @@ -169,6 +182,8 @@ public: // 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, uint32 entry) { + WriteGuard guard(GetLock()); + for (FunctionRefVector::iterator it = Bindings[entry][event_id].begin(); it != Bindings[entry][event_id].end();) { FunctionRefVector::iterator it_old = it++; @@ -196,12 +211,15 @@ public: void Insert(uint32 entryId, int eventId, int funcRef, uint32 shots) // Inserts a new registered event { + WriteGuard guard(GetLock()); Bindings[entryId][eventId].push_back(new Binding(E, funcRef, shots)); } // Returns true if the entry has registered binds - bool HasEvents(uint32 entryId, int eventId) const + bool HasEvents(T eventId, uint32 entryId) { + ReadGuard guard(GetLock()); + if (Bindings.empty()) return false; @@ -212,8 +230,10 @@ public: return itr->second.find(eventId) != itr->second.end(); } - bool HasEvents(uint32 entryId) const + bool HasEvents(uint32 entryId) { + ReadGuard guard(GetLock()); + if (Bindings.empty()) return false; diff --git a/ElunaEventMgr.cpp b/ElunaEventMgr.cpp index 48c4ca3..cc765c9 100644 --- a/ElunaEventMgr.cpp +++ b/ElunaEventMgr.cpp @@ -26,7 +26,7 @@ LuaEvent::~LuaEvent() void LuaEvent::Execute() { - ELUNA_LOCK(*events->E); + LOCK_ELUNA; // In multithread get map from object and the map's lua state lua_rawgeti((*events->E)->L, LUA_REGISTRYINDEX, funcRef); Eluna::Push((*events->E)->L, funcRef); @@ -45,7 +45,7 @@ ElunaEventProcessor::ElunaEventProcessor(Eluna** _E, WorldObject* _obj) : m_time { if (obj) { - EventMgr::WriteGuard lock((*E)->eventMgr->GetLock()); + EventMgr::WriteGuard guard((*E)->eventMgr->GetLock()); (*E)->eventMgr->processors.insert(this); } } @@ -56,7 +56,7 @@ ElunaEventProcessor::~ElunaEventProcessor() if (obj && Eluna::initialized) { - EventMgr::WriteGuard lock((*E)->eventMgr->GetLock()); + EventMgr::WriteGuard guard((*E)->eventMgr->GetLock()); (*E)->eventMgr->processors.erase(this); } } @@ -133,7 +133,7 @@ EventMgr::EventMgr(Eluna** _E) : globalProcessor(new ElunaEventProcessor(_E, NUL EventMgr::~EventMgr() { { - ReadGuard lock(GetLock()); + ReadGuard guard(GetLock()); if (!processors.empty()) for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors (*it)->RemoveEvents_internal(); @@ -145,7 +145,7 @@ EventMgr::~EventMgr() void EventMgr::RemoveEvents() { - ReadGuard lock(GetLock()); + ReadGuard guard(GetLock()); if (!processors.empty()) for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors (*it)->RemoveEvents(); @@ -154,7 +154,7 @@ void EventMgr::RemoveEvents() void EventMgr::RemoveEvent(int eventId) { - ReadGuard lock(GetLock()); + ReadGuard guard(GetLock()); if (!processors.empty()) for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors (*it)->RemoveEvent(eventId); diff --git a/ElunaUtility.h b/ElunaUtility.h index b56ade6..1ca5b29 100644 --- a/ElunaUtility.h +++ b/ElunaUtility.h @@ -119,9 +119,9 @@ namespace ElunaUtil /* * Usage: * Inherit this class, then when needing lock, use - * ReadGuard lock(_lock); + * ReadGuard guard(GetLock()); * or - * WriteGuard lock(_lock); + * WriteGuard guard(GetLock()); * * The lock is automatically released at end of scope */ diff --git a/HookMgr.cpp b/HookMgr.cpp index 92371be..b6ce2f2 100644 --- a/HookMgr.cpp +++ b/HookMgr.cpp @@ -1,8 +1,8 @@ /* -* Copyright (C) 2010 - 2015 Eluna Lua Engine -* This program is free software licensed under GPL version 3 -* Please see the included DOCS/LICENSE.md for more information -*/ + * Copyright (C) 2010 - 2015 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ #include "HookMgr.h" #include "LuaEngine.h" @@ -28,165 +28,301 @@ struct ScriptedAI; using namespace HookMgr; /* - * Call model for EventBind: + * A hook should be written in one of the following forms: * - * // Begin the call if should - * EVENT_BEGIN(bindmap, eventid, return returnvalue); - * // push arguments - * Push(L, pPlayer); - * EVENT_EXECUTE(returnedargs); - * FOR_RET(iter) - * { - * // process returned arguments - * } - * ENDCALL(); + * A. If results will be IGNORED: + * + * // Return early if there are no bindings. + * if (!WhateverBindings->HasEvents(SOME_EVENT_TYPE)) + * return; + * + * // Lock out any other threads. + * LOCK_ELUNA; + * + * // Push extra arguments, if any. + * Push(a); + * Push(b); + * Push(c); + * + * // Call all event handlers. + * CallAllFunctions(WhateverBindings, SOME_EVENT_TYPE); + * + * + * B. If results will be USED: + * + * // Return early if there are no bindings. + * if (!WhateverBindings->HasEvents(SOME_EVENT_TYPE)) + * return; + * + * // Lock out any other threads. + * LOCK_ELUNA; + * + * // Push extra arguments, if any. + * Push(a); + * Push(b); + * Push(c); + * + * // Setup the stack and get the number of functions pushed. + * // Last argument is 3 because we did 3 Pushes. + * int n = SetupStack(WhateverBindings, SOME_EVENT_TYPE, 3); + * + * // Call each event handler in order and check results. + * while (n > 0) + * { + * // Call an event handler and decrement the function counter afterward. + * // Second-last argument is 3 because we did 3 Pushes. + * // Last argument is 2 because we want 2 results. + * int r = CallOneFunction(n--, 3, 2); + * + * // Results can be popped using `r`. + * int first = CHECKVAL(L, r + 0); + * int second = CHECKVAL(L, r + 1); + * + * // Pop the results off the stack. + * lua_pop(L, 2); + * } + * + * // Clean-up the stack. Argument is 3 because we did 3 Pushes. + * CleanUpStack(3); */ -// RET is a return statement -#define EVENT_BEGIN(BINDMAP, EVENT, RET) \ - if (!BINDMAP->HasEvents(EVENT)) \ - RET; \ - ELUNA_LOCK(this); \ - const char* _LuaBindType = this->BINDMAP->groupName; \ - uint32 _LuaEvent = EVENT; \ - int _LuaStackTop = lua_gettop(L); \ - this->BINDMAP->PushFuncRefs(L, _LuaEvent); \ - int _LuaFuncTop = lua_gettop(L); \ - int _LuaFuncCount = _LuaFuncTop-_LuaStackTop; \ - Eluna::Push(L, _LuaEvent); +/* + * Sets up the stack so that event handlers can be called. + * + * Returns the number of functions that were pushed onto the stack. + * + * Use the simpler overloads for just EventBind or EntryBind instead of this overload in hooks. + */ +template +int Eluna::SetupStack(EventBind* event_bindings, EntryBind* entry_bindings, T event_id, uint32 entry, int number_of_arguments) +{ + // Ensure that if `entry_bindings` is not NULL, a valid entry is supplied. + ASSERT(!entry_bindings || (entry_bindings && entry > 0)); + ASSERT(number_of_arguments == this->push_counter); + // Stack: [arguments] -// use LUA_MULTRET for multiple return values -// return values will be at top of stack if any -#define EVENT_EXECUTE(RETVALS) \ - int _LuaReturnValues = RETVALS; \ - int _LuaParams = lua_gettop(L) - _LuaFuncTop; \ - if (_LuaParams < 1) \ - { \ - ELUNA_LOG_ERROR("[Eluna]: Executing event %u for %s, params was %i. Report to devs", _LuaEvent, _LuaBindType, _LuaParams); \ - } \ - for (int j = _LuaFuncTop-_LuaStackTop; j > 0; --j) \ - { \ - for (int i = 0; i <= _LuaParams; ++i) \ - lua_pushvalue(L, _LuaFuncTop+i); \ - Eluna::ExecuteCall(_LuaParams, _LuaReturnValues); \ - lua_remove(L, _LuaFuncTop--); \ - } \ - for (int i = _LuaParams; i > 0; --i) \ - if (!lua_isnone(L, i)) \ - lua_remove(L, i); + Push(event_id); + this->push_counter = 0; + ++number_of_arguments; + // Stack: [arguments], event_id -// RET is a return statement -#define ENTRY_BEGIN(BINDMAP, ENTRY, EVENT, RET) \ - if (!BINDMAP->HasEvents(ENTRY, EVENT)) \ - RET; \ - ELUNA_LOCK(this); \ - const char* _LuaBindType = this->BINDMAP->groupName; \ - uint32 _LuaEvent = EVENT; \ - int _LuaStackTop = lua_gettop(L); \ - this->BINDMAP->PushFuncRefs(L, _LuaEvent, ENTRY); \ - int _LuaFuncTop = lua_gettop(L); \ - int _LuaFuncCount = _LuaFuncTop-_LuaStackTop; \ - Eluna::Push(L, _LuaEvent); + int arguments_top = lua_gettop(L); + int first_argument_index = arguments_top - number_of_arguments + 1; + ASSERT(arguments_top >= number_of_arguments); -#define ENTRY_EXECUTE(RETVALS) \ - int _LuaReturnValues = RETVALS; \ - int _LuaParams = lua_gettop(L) - _LuaFuncTop; \ - if (_LuaParams < 2) \ - { \ - ELUNA_LOG_ERROR("[Eluna]: Executing event %u for %s, params was %i. Report to devs", _LuaEvent, _LuaBindType, _LuaParams); \ - } \ - for (int j = _LuaFuncTop-_LuaStackTop; j > 0; --j) \ - { \ - for (int i = 0; i <= _LuaParams; ++i) \ - lua_pushvalue(L, _LuaFuncTop+i); \ - Eluna::ExecuteCall(_LuaParams, _LuaReturnValues); \ - lua_remove(L, _LuaFuncTop--); \ - } \ - for (int i = _LuaParams; i > 0; --i) \ - if (!lua_isnone(L, i)) \ - lua_remove(L, i); + lua_insert(L, first_argument_index); + // Stack: event_id, [arguments] -#define FOR_RETS(IT) \ - for (int IT = _LuaStackTop + 1; IT <= lua_gettop(L); ++IT) + if (event_bindings) + event_bindings->PushFuncRefs(L, (int)event_id); -#define ENDCALL() \ - if (lua_gettop(L) < _LuaStackTop) \ - { \ - ELUNA_LOG_ERROR("[Eluna]: Ending event %u for %s, stack top was %i and was supposed to be >= %i. Report to devs", _LuaEvent, _LuaBindType, lua_gettop(L), _LuaStackTop); \ - } \ - if (_LuaReturnValues != LUA_MULTRET && lua_gettop(L) > _LuaStackTop + _LuaFuncCount * _LuaReturnValues) \ - { \ - ELUNA_LOG_ERROR("[Eluna]: Ending event %u for %s, stack top was %i and was supposed to be between %i and %i. Report to devs", _LuaEvent, _LuaBindType, lua_gettop(L), _LuaStackTop, _LuaStackTop + _LuaFuncCount * _LuaReturnValues); \ - } \ - lua_settop(L, _LuaStackTop); \ - if (!this->event_level) \ - this->InvalidateObjects(); // Invalidate objects on outermost hook call + if (entry_bindings) + entry_bindings->PushFuncRefs(L, (int)event_id, entry); + // Stack: event_id, [arguments], [functions] + + int number_of_functions = lua_gettop(L) - arguments_top; + return number_of_functions; +} + +/* + * Replace one of the arguments pushed before `SetupStack` with a new value. + */ +template +void Eluna::ReplaceArgument(T value, uint8 index) +{ + ASSERT(index < lua_gettop(L) && index > 0); + // Stack: event_id, [arguments], [functions], [results] + + Eluna::Push(L, value); + // Stack: event_id, [arguments], [functions], [results], value + + lua_replace(L, index + 1); + // Stack: event_id, [arguments and value], [functions], [results] +} + +/* + * Call a single event handler that was put on the stack with `Setup` and removes it from the stack. + * + * The caller is responsible for keeping track of how many times this should be called. + */ +int Eluna::CallOneFunction(int number_of_functions, int number_of_arguments, int number_of_results) +{ + ++number_of_arguments; // Caller doesn't know about `event_id`. + ASSERT(number_of_functions > 0 && number_of_arguments > 0 && number_of_results >= 0); + // Stack: event_id, [arguments], [functions] + + int functions_top = lua_gettop(L); + int first_function_index = functions_top - number_of_functions + 1; + int arguments_top = first_function_index - 1; + int first_argument_index = arguments_top - number_of_arguments + 1; + + // Copy the arguments from the bottom of the stack to the top. + for (int argument_index = first_argument_index; argument_index <= arguments_top; ++argument_index) + { + lua_pushvalue(L, argument_index); + } + // Stack: event_id, [arguments], [functions], event_id, [arguments] + + ExecuteCall(number_of_arguments, number_of_results); + --functions_top; + // Stack: event_id, [arguments], [functions - 1], [results] + + return functions_top + 1; // Return the location of the first result (if any exist). +} + +/* + * Cleans up the stack, effectively undoing all Push calls and the Setup call. + */ +void Eluna::CleanUpStack(int number_of_arguments) +{ + // Stack: event_id, [arguments] + + lua_pop(L, number_of_arguments + 1); // Add 1 because the caller doesn't know about `event_id`. + // Stack: (empty) + + if (event_level == 0) + InvalidateObjects(); +} + +/* + * Call all event handlers registered to the event ID/entry combination and ignore any results. + */ +template +void Eluna::CallAllFunctions(EventBind* event_bindings, EntryBind* entry_bindings, T event_id, uint32 entry) +{ + int number_of_arguments = this->push_counter; + // Stack: [arguments] + + int number_of_functions = SetupStack(event_bindings, entry_bindings, event_id, entry, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + CallOneFunction(number_of_functions, number_of_arguments, 0); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + + CleanUpStack(number_of_arguments); + // Stack: (empty) +} + +/* + * Call all event handlers registered to the event ID/entry combination, + * and returns `default_value` if ALL event handlers returned `default_value`, + * otherwise returns the opposite of `default_value`. + */ +template +bool Eluna::CallAllFunctionsBool(EventBind* event_bindings, EntryBind* entry_bindings, T event_id, uint32 entry, bool default_value) +{ + bool result = default_value; + // Note: number_of_arguments here does not count in eventID, which is pushed in SetupStack + int number_of_arguments = this->push_counter; + // Stack: [arguments] + + int number_of_functions = SetupStack(event_bindings, entry_bindings, event_id, entry, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + int r = CallOneFunction(number_of_functions, number_of_arguments, 1); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1], result + + if (lua_isboolean(L, r) && (lua_toboolean(L, r) == 1) != default_value) + result = !default_value; + + lua_pop(L, 1); + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + + CleanUpStack(number_of_arguments); + // Stack: (empty) + return result; +} void Eluna::OnLuaStateClose() { - EVENT_BEGIN(ServerEventBindings, ELUNA_EVENT_ON_LUA_STATE_CLOSE, return); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(ELUNA_EVENT_ON_LUA_STATE_CLOSE)) + return; + + LOCK_ELUNA; + CallAllFunctions(ServerEventBindings, ELUNA_EVENT_ON_LUA_STATE_CLOSE); } void Eluna::OnLuaStateOpen() { - EVENT_BEGIN(ServerEventBindings, ELUNA_EVENT_ON_LUA_STATE_OPEN, return); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(ELUNA_EVENT_ON_LUA_STATE_OPEN)) + return; + + LOCK_ELUNA; + CallAllFunctions(ServerEventBindings, ELUNA_EVENT_ON_LUA_STATE_OPEN); } // areatrigger bool Eluna::OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* pTrigger) { - EVENT_BEGIN(ServerEventBindings, TRIGGER_EVENT_ON_TRIGGER, return false); - Push(L, pPlayer); - Push(L, pTrigger->id); - EVENT_EXECUTE(0); - ENDCALL(); - return false; + if (!ServerEventBindings->HasEvents(TRIGGER_EVENT_ON_TRIGGER)) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pTrigger->id); + return CallAllFunctionsBool(ServerEventBindings, TRIGGER_EVENT_ON_TRIGGER); } // weather void Eluna::OnChange(Weather* weather, WeatherState state, float grade) { - EVENT_BEGIN(ServerEventBindings, WEATHER_EVENT_ON_CHANGE, return); - Push(L, weather->GetZone()); - Push(L, state); - Push(L, grade); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WEATHER_EVENT_ON_CHANGE)) + return; + + LOCK_ELUNA; + Push(weather->GetZone()); + Push(state); + Push(grade); + CallAllFunctions(ServerEventBindings, WEATHER_EVENT_ON_CHANGE); } // Auction House void Eluna::OnAdd(AuctionHouseObject* ah) { - EVENT_BEGIN(ServerEventBindings, AUCTION_EVENT_ON_ADD, return); - Push(L, (ah)); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(AUCTION_EVENT_ON_ADD)) + return; + + LOCK_ELUNA; + Push(ah); + CallAllFunctions(ServerEventBindings, AUCTION_EVENT_ON_ADD); } void Eluna::OnRemove(AuctionHouseObject* ah) { - EVENT_BEGIN(ServerEventBindings, AUCTION_EVENT_ON_REMOVE, return); - Push(L, (ah)); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(AUCTION_EVENT_ON_REMOVE)) + return; + + LOCK_ELUNA; + Push(ah); + CallAllFunctions(ServerEventBindings, AUCTION_EVENT_ON_REMOVE); } void Eluna::OnSuccessful(AuctionHouseObject* ah) { - EVENT_BEGIN(ServerEventBindings, AUCTION_EVENT_ON_SUCCESSFUL, return); - Push(L, (ah)); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(AUCTION_EVENT_ON_SUCCESSFUL)) + return; + + LOCK_ELUNA; + Push(ah); + CallAllFunctions(ServerEventBindings, AUCTION_EVENT_ON_SUCCESSFUL); } void Eluna::OnExpire(AuctionHouseObject* ah) { - EVENT_BEGIN(ServerEventBindings, AUCTION_EVENT_ON_EXPIRE, return); - Push(L, (ah)); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(AUCTION_EVENT_ON_EXPIRE)) + return; + + LOCK_ELUNA; + Push(ah); + CallAllFunctions(ServerEventBindings, AUCTION_EVENT_ON_EXPIRE); } // Packet @@ -202,45 +338,55 @@ bool Eluna::OnPacketSend(WorldSession* session, WorldPacket& packet) } void Eluna::OnPacketSendAny(Player* player, WorldPacket& packet, bool& result) { - EVENT_BEGIN(ServerEventBindings, SERVER_EVENT_ON_PACKET_SEND, return); - Push(L, new WorldPacket(packet)); - Push(L, player); - EVENT_EXECUTE(2); - FOR_RETS(i) + if (!ServerEventBindings->HasEvents(SERVER_EVENT_ON_PACKET_SEND)) + return; + + LOCK_ELUNA; + Push(new WorldPacket(packet)); + Push(player); + int n = SetupStack(ServerEventBindings, SERVER_EVENT_ON_PACKET_SEND, 2); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isuserdata(L, i)) - if (WorldPacket* data = CHECKOBJ(L, i, false)) - packet = *data; - if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isuserdata(L, r + 1)) + if (WorldPacket* data = CHECKOBJ(L, r + 1, true)) + packet = *data; + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(2); } void Eluna::OnPacketSendOne(Player* player, WorldPacket& packet, bool& result) { - ENTRY_BEGIN(PacketEventBindings, OpcodesList(packet.GetOpcode()), PACKET_EVENT_ON_PACKET_SEND, return); - Push(L, new WorldPacket(packet)); - Push(L, player); - ENTRY_EXECUTE(2); - FOR_RETS(i) + if (!PacketEventBindings->HasEvents(PACKET_EVENT_ON_PACKET_SEND, packet.GetOpcode())) + return; + + LOCK_ELUNA; + Push(new WorldPacket(packet)); + Push(player); + int n = SetupStack(PacketEventBindings, PACKET_EVENT_ON_PACKET_SEND, OpcodesList(packet.GetOpcode()), 2); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isuserdata(L, i)) - if (WorldPacket* data = CHECKOBJ(L, i, false)) - packet = *data; - if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isuserdata(L, r + 1)) + if (WorldPacket* data = CHECKOBJ(L, r + 1, true)) + packet = *data; + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(2); } bool Eluna::OnPacketReceive(WorldSession* session, WorldPacket& packet) @@ -255,105 +401,127 @@ bool Eluna::OnPacketReceive(WorldSession* session, WorldPacket& packet) } void Eluna::OnPacketReceiveAny(Player* player, WorldPacket& packet, bool& result) { - EVENT_BEGIN(ServerEventBindings, SERVER_EVENT_ON_PACKET_RECEIVE, return); - Push(L, new WorldPacket(packet)); - Push(L, player); - EVENT_EXECUTE(2); - FOR_RETS(i) + if (!ServerEventBindings->HasEvents(SERVER_EVENT_ON_PACKET_RECEIVE)) + return; + + LOCK_ELUNA; + Push(new WorldPacket(packet)); + Push(player); + int n = SetupStack(ServerEventBindings, SERVER_EVENT_ON_PACKET_RECEIVE, 2); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isuserdata(L, i)) - if (WorldPacket* data = CHECKOBJ(L, i, false)) - packet = *data; - if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isuserdata(L, r + 1)) + if (WorldPacket* data = CHECKOBJ(L, r + 1, true)) + packet = *data; + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(2); } void Eluna::OnPacketReceiveOne(Player* player, WorldPacket& packet, bool& result) { - ENTRY_BEGIN(PacketEventBindings, OpcodesList(packet.GetOpcode()), PACKET_EVENT_ON_PACKET_RECEIVE, return); - Push(L, new WorldPacket(packet)); - Push(L, player); - ENTRY_EXECUTE(2); - FOR_RETS(i) + if (!PacketEventBindings->HasEvents(PACKET_EVENT_ON_PACKET_RECEIVE, packet.GetOpcode())) + return; + + LOCK_ELUNA; + Push(new WorldPacket(packet)); + Push(player); + int n = SetupStack(PacketEventBindings, PACKET_EVENT_ON_PACKET_RECEIVE, OpcodesList(), 2); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isuserdata(L, i)) - if (WorldPacket* data = CHECKOBJ(L, i, false)) - packet = *data; - if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isuserdata(L, r + 1)) + if (WorldPacket* data = CHECKOBJ(L, r + 1, true)) + packet = *data; + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(2); } // AddOns bool Eluna::OnAddonMessage(Player* sender, uint32 type, std::string& msg, Player* receiver, Guild* guild, Group* group, Channel* channel) { - EVENT_BEGIN(ServerEventBindings, ADDON_EVENT_ON_MESSAGE, return false); - Push(L, sender); - Push(L, type); + if (!ServerEventBindings->HasEvents(ADDON_EVENT_ON_MESSAGE)) + return true; + + LOCK_ELUNA; + Push(sender); + Push(type); const char* c_msg = msg.c_str(); - Push(L, strtok((char*)c_msg, "\t")); // prefix - Push(L, strtok(NULL, "")); // msg + Push(strtok((char*)c_msg, "\t")); // prefix + Push(strtok(NULL, "")); // msg if (receiver) - Push(L, receiver); + Push(receiver); else if (guild) - Push(L, guild); + Push(guild); else if (group) - Push(L, group); + Push(group); else if (channel) - Push(L, channel->GetChannelId()); + Push(channel->GetChannelId()); else - Push(L); - EVENT_EXECUTE(0); - ENDCALL(); - return true; + Push(); + + return CallAllFunctionsBool(ServerEventBindings, ADDON_EVENT_ON_MESSAGE, true); } void Eluna::OnOpenStateChange(bool open) { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_OPEN_STATE_CHANGE, return); - Push(L, open); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_OPEN_STATE_CHANGE)) + return; + + LOCK_ELUNA; + Push(open); + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_OPEN_STATE_CHANGE); } void Eluna::OnConfigLoad(bool reload) { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_CONFIG_LOAD, return); - Push(L, reload); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_CONFIG_LOAD)) + return; + + LOCK_ELUNA; + Push(reload); + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_CONFIG_LOAD); } void Eluna::OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask) { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_SHUTDOWN_INIT, return); - Push(L, code); - Push(L, mask); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_SHUTDOWN_INIT)) + return; + + LOCK_ELUNA; + Push(code); + Push(mask); + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_SHUTDOWN_INIT); } void Eluna::OnShutdownCancel() { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_SHUTDOWN_CANCEL, return); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_SHUTDOWN_CANCEL)) + return; + + LOCK_ELUNA; + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_SHUTDOWN_CANCEL); } void Eluna::OnWorldUpdate(uint32 diff) { + LOCK_ELUNA; + if (reload) { ReloadEluna(); @@ -362,80 +530,95 @@ void Eluna::OnWorldUpdate(uint32 diff) eventMgr->globalProcessor->Update(diff); - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_UPDATE, return); - Push(L, diff); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_UPDATE)) + return; + + Push(diff); + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_UPDATE); } void Eluna::OnStartup() { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_STARTUP, return); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_STARTUP)) + return; + + LOCK_ELUNA; + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_STARTUP); } void Eluna::OnShutdown() { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_SHUTDOWN, return); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_SHUTDOWN)) + return; + + LOCK_ELUNA; + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_SHUTDOWN); } void Eluna::HandleGossipSelectOption(Player* pPlayer, Item* item, uint32 sender, uint32 action, const std::string& code) { - ENTRY_BEGIN(ItemGossipBindings, item->GetEntry(), GOSSIP_EVENT_ON_SELECT, return); + if (!ItemGossipBindings->HasEvents(GOSSIP_EVENT_ON_SELECT, item->GetEntry())) + return; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, item); - Push(L, sender); - Push(L, action); + + Push(pPlayer); + Push(item); + Push(sender); + Push(action); if (code.empty()) - Push(L); + Push(); else - Push(L, code); - ENTRY_EXECUTE(0); - ENDCALL(); + Push(code); + + CallAllFunctions(ItemGossipBindings, GOSSIP_EVENT_ON_SELECT, item->GetEntry()); } void Eluna::HandleGossipSelectOption(Player* pPlayer, uint32 menuId, uint32 sender, uint32 action, const std::string& code) { - ENTRY_BEGIN(playerGossipBindings, menuId, GOSSIP_EVENT_ON_SELECT, return); + if (!playerGossipBindings->HasEvents(GOSSIP_EVENT_ON_SELECT, menuId)) + return; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); // receiver - Push(L, pPlayer); // sender, just not to mess up the amount of args. - Push(L, sender); - Push(L, action); + + Push(pPlayer); // receiver + Push(pPlayer); // sender, just not to mess up the amount of args. + Push(sender); + Push(action); if (code.empty()) - Push(L); + Push(); else - Push(L, code); - ENTRY_EXECUTE(0); - ENDCALL(); + Push(code); + + CallAllFunctions(playerGossipBindings, GOSSIP_EVENT_ON_SELECT, menuId); } // item bool Eluna::OnDummyEffect(Unit* pCaster, uint32 spellId, SpellEffIndex effIndex, Item* pTarget) { - ENTRY_BEGIN(ItemEventBindings, pTarget->GetEntry(), ITEM_EVENT_ON_DUMMY_EFFECT, return false); - Push(L, pCaster); - Push(L, spellId); - Push(L, effIndex); - Push(L, pTarget); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!ItemEventBindings->HasEvents(ITEM_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pCaster); + Push(spellId); + Push(effIndex); + Push(pTarget); + return CallAllFunctionsBool(ItemEventBindings, ITEM_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry()); } bool Eluna::OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest) { - ENTRY_BEGIN(ItemEventBindings, pItem->GetEntry(), ITEM_EVENT_ON_QUEST_ACCEPT, return false); - Push(L, pPlayer); - Push(L, pItem); - Push(L, pQuest); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!ItemEventBindings->HasEvents(ITEM_EVENT_ON_QUEST_ACCEPT, pItem->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pItem); + Push(pQuest); + return CallAllFunctionsBool(ItemEventBindings, ITEM_EVENT_ON_QUEST_ACCEPT, pItem->GetEntry()); } bool Eluna::OnUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) @@ -470,80 +653,73 @@ bool Eluna::OnUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) bool Eluna::OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) { - bool result = true; - ENTRY_BEGIN(ItemEventBindings, pItem->GetEntry(), ITEM_EVENT_ON_USE, return result); - Push(L, pPlayer); - Push(L, pItem); + if (!ItemEventBindings->HasEvents(ITEM_EVENT_ON_USE, pItem->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pItem); #ifndef TRINITY if (GameObject* target = targets.getGOTarget()) - Push(L, target); + Push(target); else if (Item* target = targets.getItemTarget()) - Push(L, target); + Push(target); else if (Corpse* target = pPlayer->GetMap()->GetCorpse(targets.getCorpseTargetGuid())) - Push(L, target); + Push(target); else if (Unit* target = targets.getUnitTarget()) - Push(L, target); + Push(target); else - Push(L); + Push(); #else if (GameObject* target = targets.GetGOTarget()) - Push(L, target); + Push(target); else if (Item* target = targets.GetItemTarget()) - Push(L, target); + Push(target); else if (Corpse* target = targets.GetCorpseTarget()) - Push(L, target); + Push(target); else if (Unit* target = targets.GetUnitTarget()) - Push(L, target); + Push(target); else if (WorldObject* target = targets.GetObjectTarget()) - Push(L, target); + Push(target); else - Push(L); + Push(); #endif - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, result); - } - ENDCALL(); - return result; + + return CallAllFunctionsBool(ItemEventBindings, ITEM_EVENT_ON_USE, pItem->GetEntry()); } bool Eluna::OnItemGossip(Player* pPlayer, Item* pItem, SpellCastTargets const& /*targets*/) { - bool result = true; - ENTRY_BEGIN(ItemGossipBindings, pItem->GetEntry(), GOSSIP_EVENT_ON_HELLO, return result); + if (!ItemGossipBindings->HasEvents(GOSSIP_EVENT_ON_HELLO, pItem->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pItem); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, result); - } - ENDCALL(); - return result; + Push(pPlayer); + Push(pItem); + return CallAllFunctionsBool(ItemGossipBindings, GOSSIP_EVENT_ON_HELLO, pItem->GetEntry()); } bool Eluna::OnExpire(Player* pPlayer, ItemTemplate const* pProto) { - ENTRY_BEGIN(ItemEventBindings, pProto->ItemId, ITEM_EVENT_ON_EXPIRE, return false); - Push(L, pPlayer); - Push(L, pProto->ItemId); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!ItemEventBindings->HasEvents(ITEM_EVENT_ON_EXPIRE, pProto->ItemId)) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pProto->ItemId); + return CallAllFunctionsBool(ItemEventBindings, ITEM_EVENT_ON_EXPIRE, pProto->ItemId); } bool Eluna::OnRemove(Player* pPlayer, Item* item) { - ENTRY_BEGIN(ItemEventBindings, item->GetEntry(), ITEM_EVENT_ON_REMOVE, return false); - Push(L, pPlayer); - Push(L, item); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!ItemEventBindings->HasEvents(ITEM_EVENT_ON_REMOVE, item->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(item); + return CallAllFunctionsBool(ItemEventBindings, ITEM_EVENT_ON_REMOVE, item->GetEntry()); } // Player @@ -572,511 +748,612 @@ bool Eluna::OnCommand(Player* player, const char* text) } } - bool result = true; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_COMMAND, return result); - Push(L, player); - Push(L, fullcmd); - EVENT_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, result); - } - ENDCALL(); - return result; + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_COMMAND)) + return true; + + LOCK_ELUNA; + Push(player); + Push(fullcmd); + return CallAllFunctionsBool(PlayerEventBindings, PLAYER_EVENT_ON_COMMAND, true); } void Eluna::OnLootItem(Player* pPlayer, Item* pItem, uint32 count, uint64 guid) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_LOOT_ITEM, return); - Push(L, pPlayer); - Push(L, pItem); - Push(L, count); - Push(L, guid); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_LOOT_ITEM)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(pItem); + Push(count); + Push(guid); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_LOOT_ITEM); } void Eluna::OnLootMoney(Player* pPlayer, uint32 amount) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_LOOT_MONEY, return); - Push(L, pPlayer); - Push(L, amount); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_LOOT_MONEY)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(amount); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_LOOT_MONEY); } void Eluna::OnFirstLogin(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_FIRST_LOGIN, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_FIRST_LOGIN)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_FIRST_LOGIN); } void Eluna::OnRepop(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_REPOP, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_REPOP)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_REPOP); } void Eluna::OnResurrect(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_RESURRECT, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_RESURRECT)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_RESURRECT); } void Eluna::OnQuestAbandon(Player* pPlayer, uint32 questId) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_QUEST_ABANDON, return); - Push(L, pPlayer); - Push(L, questId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_QUEST_ABANDON)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(questId); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_QUEST_ABANDON); } void Eluna::OnEquip(Player* pPlayer, Item* pItem, uint8 bag, uint8 slot) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_EQUIP, return); - Push(L, pPlayer); - Push(L, pItem); - Push(L, bag); - Push(L, slot); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_EQUIP)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(pItem); + Push(bag); + Push(slot); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_EQUIP); } InventoryResult Eluna::OnCanUseItem(const Player* pPlayer, uint32 itemEntry) { + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_CAN_USE_ITEM)) + return EQUIP_ERR_OK; + + LOCK_ELUNA; InventoryResult result = EQUIP_ERR_OK; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_CAN_USE_ITEM, return result); - Push(L, pPlayer); - Push(L, itemEntry); - EVENT_EXECUTE(1); - FOR_RETS(i) + Push(pPlayer); + Push(itemEntry); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_CAN_USE_ITEM, 2); + + while (n > 0) { - if (!lua_isnumber(L, i)) - continue; - uint32 res = CHECKVAL(L, i, EQUIP_ERR_OK); - if (res != EQUIP_ERR_OK) - result = (InventoryResult)res; + int r = CallOneFunction(n--, 2, 1); + + if (lua_isnumber(L, r)) + result = (InventoryResult)CHECKVAL(L, r); + + lua_pop(L, 1); } - ENDCALL(); + + CleanUpStack(2); return result; } void Eluna::OnPlayerEnterCombat(Player* pPlayer, Unit* pEnemy) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_ENTER_COMBAT, return); - Push(L, pPlayer); - Push(L, pEnemy); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_ENTER_COMBAT)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(pEnemy); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_ENTER_COMBAT); } void Eluna::OnPlayerLeaveCombat(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_LEAVE_COMBAT, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_LEAVE_COMBAT)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_LEAVE_COMBAT); } void Eluna::OnPVPKill(Player* pKiller, Player* pKilled) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_KILL_PLAYER, return); - Push(L, pKiller); - Push(L, pKilled); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_KILL_PLAYER)) + return; + + LOCK_ELUNA; + Push(pKiller); + Push(pKilled); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_KILL_PLAYER); } void Eluna::OnCreatureKill(Player* pKiller, Creature* pKilled) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_KILL_CREATURE, return); - Push(L, pKiller); - Push(L, pKilled); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_KILL_CREATURE)) + return; + + LOCK_ELUNA; + Push(pKiller); + Push(pKilled); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_KILL_CREATURE); } void Eluna::OnPlayerKilledByCreature(Creature* pKiller, Player* pKilled) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_KILLED_BY_CREATURE, return); - Push(L, pKiller); - Push(L, pKilled); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_KILLED_BY_CREATURE)) + return; + + LOCK_ELUNA; + Push(pKiller); + Push(pKilled); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_KILLED_BY_CREATURE); } void Eluna::OnLevelChanged(Player* pPlayer, uint8 oldLevel) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_LEVEL_CHANGE, return); - Push(L, pPlayer); - Push(L, oldLevel); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_LEVEL_CHANGE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(oldLevel); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_LEVEL_CHANGE); } void Eluna::OnFreeTalentPointsChanged(Player* pPlayer, uint32 newPoints) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_TALENTS_CHANGE, return); - Push(L, pPlayer); - Push(L, newPoints); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_TALENTS_CHANGE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(newPoints); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_TALENTS_CHANGE); } void Eluna::OnTalentsReset(Player* pPlayer, bool noCost) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_TALENTS_RESET, return); - Push(L, pPlayer); - Push(L, noCost); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_TALENTS_RESET)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(noCost); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_TALENTS_RESET); } void Eluna::OnMoneyChanged(Player* pPlayer, int32& amount) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_MONEY_CHANGE, return); - Push(L, pPlayer); - Push(L, amount); - EVENT_EXECUTE(1); - FOR_RETS(i) + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_MONEY_CHANGE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(amount); + int amountIndex = lua_gettop(L); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_MONEY_CHANGE, 2); + + while (n > 0) { - if (lua_isnumber(L, i)) - amount = CHECKVAL(L, i, amount); + int r = CallOneFunction(n--, 2, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(L, r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); } - ENDCALL(); + + CleanUpStack(2); } void Eluna::OnGiveXP(Player* pPlayer, uint32& amount, Unit* pVictim) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_GIVE_XP, return); - Push(L, pPlayer); - Push(L, amount); - Push(L, pVictim); - EVENT_EXECUTE(1); - FOR_RETS(i) + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_GIVE_XP)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(amount); + Push(pVictim); + int amountIndex = lua_gettop(L) - 1; + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_GIVE_XP, 3); + + while (n > 0) { - if (lua_isnumber(L, i)) - amount = CHECKVAL(L, i, amount); + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(L, r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); } - ENDCALL(); + + CleanUpStack(3); } void Eluna::OnReputationChange(Player* pPlayer, uint32 factionID, int32& standing, bool incremental) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_REPUTATION_CHANGE, return); - Push(L, pPlayer); - Push(L, factionID); - Push(L, standing); - Push(L, incremental); - EVENT_EXECUTE(1); - FOR_RETS(i) + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_REPUTATION_CHANGE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(factionID); + Push(standing); + Push(incremental); + int standingIndex = lua_gettop(L) - 1; + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_REPUTATION_CHANGE, 4); + + while (n > 0) { - if (lua_isnumber(L, i)) - standing = CHECKVAL(L, i, standing); + int r = CallOneFunction(n--, 4, 1); + + if (lua_isnumber(L, r)) + { + standing = CHECKVAL(L, r); + // Update the stack for subsequent calls. + ReplaceArgument(standing, standingIndex); + } + + lua_pop(L, 1); } - ENDCALL(); + + CleanUpStack(4); } void Eluna::OnDuelRequest(Player* pTarget, Player* pChallenger) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_DUEL_REQUEST, return); - Push(L, pTarget); - Push(L, pChallenger); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_DUEL_REQUEST)) + return; + + LOCK_ELUNA; + Push(pTarget); + Push(pChallenger); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_DUEL_REQUEST); } void Eluna::OnDuelStart(Player* pStarter, Player* pChallenger) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_DUEL_START, return); - Push(L, pStarter); - Push(L, pChallenger); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_DUEL_START)) + return; + + LOCK_ELUNA; + Push(pStarter); + Push(pChallenger); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_DUEL_START); } void Eluna::OnDuelEnd(Player* pWinner, Player* pLoser, DuelCompleteType type) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_DUEL_END, return); - Push(L, pWinner); - Push(L, pLoser); - Push(L, type); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_DUEL_END)) + return; + + LOCK_ELUNA; + Push(pWinner); + Push(pLoser); + Push(type); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_DUEL_END); } void Eluna::OnEmote(Player* pPlayer, uint32 emote) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_EMOTE, return); - Push(L, pPlayer); - Push(L, emote); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_EMOTE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(emote); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_EMOTE); } void Eluna::OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, uint64 guid) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_TEXT_EMOTE, return); - Push(L, pPlayer); - Push(L, textEmote); - Push(L, emoteNum); - Push(L, guid); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_TEXT_EMOTE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(textEmote); + Push(emoteNum); + Push(guid); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_TEXT_EMOTE); } void Eluna::OnSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_SPELL_CAST, return); - Push(L, pPlayer); - Push(L, pSpell); - Push(L, skipCheck); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_SPELL_CAST)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(pSpell); + Push(skipCheck); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_SPELL_CAST); } void Eluna::OnLogin(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_LOGIN, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_LOGIN)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_LOGIN); } void Eluna::OnLogout(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_LOGOUT, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_LOGOUT)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_LOGOUT); } void Eluna::OnCreate(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_CHARACTER_CREATE, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_CHARACTER_CREATE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_CHARACTER_CREATE); } void Eluna::OnDelete(uint32 guidlow) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_CHARACTER_DELETE, return); - Push(L, guidlow); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_CHARACTER_DELETE)) + return; + + LOCK_ELUNA; + Push(guidlow); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_CHARACTER_DELETE); } void Eluna::OnSave(Player* pPlayer) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_SAVE, return); - Push(L, pPlayer); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_SAVE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_SAVE); } void Eluna::OnBindToInstance(Player* pPlayer, Difficulty difficulty, uint32 mapid, bool permanent) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_BIND_TO_INSTANCE, return); - Push(L, pPlayer); - Push(L, difficulty); - Push(L, mapid); - Push(L, permanent); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_BIND_TO_INSTANCE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(difficulty); + Push(mapid); + Push(permanent); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_BIND_TO_INSTANCE); } void Eluna::OnUpdateZone(Player* pPlayer, uint32 newZone, uint32 newArea) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_UPDATE_ZONE, return); - Push(L, pPlayer); - Push(L, newZone); - Push(L, newArea); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_UPDATE_ZONE)) + return; + + LOCK_ELUNA; + Push(pPlayer); + Push(newZone); + Push(newArea); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_UPDATE_ZONE); } void Eluna::OnMapChanged(Player* player) { - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_MAP_CHANGE, return); - Push(L, player); - EVENT_EXECUTE(0); - ENDCALL(); + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_MAP_CHANGE)) + return; + + LOCK_ELUNA; + Push(player); + CallAllFunctions(PlayerEventBindings, PLAYER_EVENT_ON_MAP_CHANGE); } bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg) { if (lang == LANG_ADDON) - { - OnAddonMessage(pPlayer, type, msg, NULL, NULL, NULL, NULL); + return OnAddonMessage(pPlayer, type, msg, NULL, NULL, NULL, NULL); + + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_CHAT)) return true; - } + + LOCK_ELUNA; bool result = true; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_CHAT, return result); - Push(L, pPlayer); - Push(L, msg); - Push(L, type); - Push(L, lang); - EVENT_EXECUTE(2); - FOR_RETS(i) + Push(pPlayer); + Push(msg); + Push(type); + Push(lang); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_CHAT, 4); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isstring(L, i)) - { - if (const char* c_str = CHECKVAL(L, i, NULL)) - msg = std::string(c_str); - } - else if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 4, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(4); return result; } bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Group* pGroup) { if (lang == LANG_ADDON) - { - OnAddonMessage(pPlayer, type, msg, NULL, NULL, pGroup, NULL); + return OnAddonMessage(pPlayer, type, msg, NULL, NULL, pGroup, NULL); + + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_GROUP_CHAT)) return true; - } + + LOCK_ELUNA; bool result = true; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_GROUP_CHAT, return result); - Push(L, pPlayer); - Push(L, msg); - Push(L, type); - Push(L, lang); - Push(L, pGroup); - EVENT_EXECUTE(2); - FOR_RETS(i) + Push(pPlayer); + Push(msg); + Push(type); + Push(lang); + Push(pGroup); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_GROUP_CHAT, 5); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isstring(L, i)) - { - if (const char* c_str = CHECKVAL(L, i, NULL)) - msg = std::string(c_str); - } - else if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(5); return result; } bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Guild* pGuild) { if (lang == LANG_ADDON) - { - OnAddonMessage(pPlayer, type, msg, NULL, pGuild, NULL, NULL); + return OnAddonMessage(pPlayer, type, msg, NULL, pGuild, NULL, NULL); + + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_GUILD_CHAT)) return true; - } + + LOCK_ELUNA; bool result = true; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_GUILD_CHAT, return result); - Push(L, pPlayer); - Push(L, msg); - Push(L, type); - Push(L, lang); - Push(L, pGuild); - EVENT_EXECUTE(2); - FOR_RETS(i) + Push(pPlayer); + Push(msg); + Push(type); + Push(lang); + Push(pGuild); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_GUILD_CHAT, 5); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isstring(L, i)) - { - if (const char* c_str = CHECKVAL(L, i, NULL)) - msg = std::string(c_str); - } - else if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(5); return result; } bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Channel* pChannel) { if (lang == LANG_ADDON) - { - OnAddonMessage(pPlayer, type, msg, NULL, NULL, NULL, pChannel); + return OnAddonMessage(pPlayer, type, msg, NULL, NULL, NULL, pChannel); + + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_CHANNEL_CHAT)) return true; - } + + LOCK_ELUNA; bool result = true; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_CHANNEL_CHAT, return result); - Push(L, pPlayer); - Push(L, msg); - Push(L, type); - Push(L, lang); - Push(L, pChannel->GetChannelId()); - EVENT_EXECUTE(2); - FOR_RETS(i) + Push(pPlayer); + Push(msg); + Push(type); + Push(lang); + Push(pChannel->GetChannelId()); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_CHANNEL_CHAT, 5); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isstring(L, i)) - { - if (const char* c_str = CHECKVAL(L, i, NULL)) - msg = std::string(c_str); - } - else if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(5); return result; } bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Player* pReceiver) { if (lang == LANG_ADDON) - { - OnAddonMessage(pPlayer, type, msg, pReceiver, NULL, NULL, NULL); + return OnAddonMessage(pPlayer, type, msg, pReceiver, NULL, NULL, NULL); + + if (!PlayerEventBindings->HasEvents(PLAYER_EVENT_ON_WHISPER)) return true; - } + + LOCK_ELUNA; bool result = true; - EVENT_BEGIN(PlayerEventBindings, PLAYER_EVENT_ON_WHISPER, return result); - Push(L, pPlayer); - Push(L, msg); - Push(L, type); - Push(L, lang); - Push(L, pReceiver); - EVENT_EXECUTE(2); - FOR_RETS(i) + Push(pPlayer); + Push(msg); + Push(type); + Push(lang); + Push(pReceiver); + int n = SetupStack(PlayerEventBindings, PLAYER_EVENT_ON_WHISPER, 5); + + while (n > 0) { - if (lua_isnoneornil(L, i)) - continue; - if (lua_isstring(L, i)) - { - if (const char* c_str = CHECKVAL(L, i, NULL)) - msg = std::string(c_str); - } - else if (lua_isboolean(L, i) && !CHECKVAL(L, i, true)) - { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) result = false; - break; - } + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(5); return result; } @@ -1085,782 +1362,808 @@ bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, // Vehicle void Eluna::OnInstall(Vehicle* vehicle) { - EVENT_BEGIN(VehicleEventBindings, VEHICLE_EVENT_ON_INSTALL, return); - Push(L, vehicle); - EVENT_EXECUTE(0); - ENDCALL(); + if (!VehicleEventBindings->HasEvents(VEHICLE_EVENT_ON_INSTALL)) + return; + + LOCK_ELUNA; + Push(vehicle); + CallAllFunctions(VehicleEventBindings, VEHICLE_EVENT_ON_INSTALL); } void Eluna::OnUninstall(Vehicle* vehicle) { - EVENT_BEGIN(VehicleEventBindings, VEHICLE_EVENT_ON_UNINSTALL, return); - Push(L, vehicle); - EVENT_EXECUTE(0); - ENDCALL(); + if (!VehicleEventBindings->HasEvents(VEHICLE_EVENT_ON_UNINSTALL)) + return; + + LOCK_ELUNA; + Push(vehicle); + CallAllFunctions(VehicleEventBindings, VEHICLE_EVENT_ON_UNINSTALL); } void Eluna::OnInstallAccessory(Vehicle* vehicle, Creature* accessory) { - EVENT_BEGIN(VehicleEventBindings, VEHICLE_EVENT_ON_INSTALL_ACCESSORY, return); - Push(L, vehicle); - Push(L, accessory); - EVENT_EXECUTE(0); - ENDCALL(); + if (!VehicleEventBindings->HasEvents(VEHICLE_EVENT_ON_INSTALL_ACCESSORY)) + return; + + LOCK_ELUNA; + Push(vehicle); + Push(accessory); + CallAllFunctions(VehicleEventBindings, VEHICLE_EVENT_ON_INSTALL_ACCESSORY); } void Eluna::OnAddPassenger(Vehicle* vehicle, Unit* passenger, int8 seatId) { - EVENT_BEGIN(VehicleEventBindings, VEHICLE_EVENT_ON_ADD_PASSENGER, return); - Push(L, vehicle); - Push(L, passenger); - Push(L, seatId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!VehicleEventBindings->HasEvents(VEHICLE_EVENT_ON_ADD_PASSENGER)) + return; + + LOCK_ELUNA; + Push(vehicle); + Push(passenger); + Push(seatId); + CallAllFunctions(VehicleEventBindings, VEHICLE_EVENT_ON_ADD_PASSENGER); } void Eluna::OnRemovePassenger(Vehicle* vehicle, Unit* passenger) { - EVENT_BEGIN(VehicleEventBindings, VEHICLE_EVENT_ON_REMOVE_PASSENGER, return); - Push(L, vehicle); - Push(L, passenger); - EVENT_EXECUTE(0); - ENDCALL(); + if (!VehicleEventBindings->HasEvents(VEHICLE_EVENT_ON_REMOVE_PASSENGER)) + return; + + LOCK_ELUNA; + Push(vehicle); + Push(passenger); + CallAllFunctions(VehicleEventBindings, VEHICLE_EVENT_ON_REMOVE_PASSENGER); } #endif #endif void Eluna::OnAddMember(Guild* guild, Player* player, uint32 plRank) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_ADD_MEMBER, return); - Push(L, guild); - Push(L, player); - Push(L, plRank); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_ADD_MEMBER)) + return; + + LOCK_ELUNA; + Push(guild); + Push(player); + Push(plRank); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_ADD_MEMBER); } void Eluna::OnRemoveMember(Guild* guild, Player* player, bool isDisbanding) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_REMOVE_MEMBER, return); - Push(L, guild); - Push(L, player); - Push(L, isDisbanding); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_REMOVE_MEMBER)) + return; + + LOCK_ELUNA; + Push(guild); + Push(player); + Push(isDisbanding); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_REMOVE_MEMBER); } void Eluna::OnMOTDChanged(Guild* guild, const std::string& newMotd) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_MOTD_CHANGE, return); - Push(L, guild); - Push(L, newMotd); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_MOTD_CHANGE)) + return; + + LOCK_ELUNA; + Push(guild); + Push(newMotd); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_MOTD_CHANGE); } void Eluna::OnInfoChanged(Guild* guild, const std::string& newInfo) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_INFO_CHANGE, return); - Push(L, guild); - Push(L, newInfo); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_INFO_CHANGE)) + return; + + LOCK_ELUNA; + Push(guild); + Push(newInfo); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_INFO_CHANGE); } void Eluna::OnCreate(Guild* guild, Player* leader, const std::string& name) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_CREATE, return); - Push(L, guild); - Push(L, leader); - Push(L, name); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_CREATE)) + return; + + LOCK_ELUNA; + Push(guild); + Push(leader); + Push(name); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_CREATE); } void Eluna::OnDisband(Guild* guild) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_DISBAND, return); - Push(L, guild); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_DISBAND)) + return; + + LOCK_ELUNA; + Push(guild); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_DISBAND); } void Eluna::OnMemberWitdrawMoney(Guild* guild, Player* player, uint32& amount, bool isRepair) // isRepair not a part of Mangos, implement? { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_MONEY_WITHDRAW, return); - Push(L, guild); - Push(L, player); - Push(L, amount); - Push(L, isRepair); // isRepair not a part of Mangos, implement? - EVENT_EXECUTE(1); - FOR_RETS(i) + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_MONEY_WITHDRAW)) + return; + + LOCK_ELUNA; + Push(guild); + Push(player); + Push(amount); + Push(isRepair); // isRepair not a part of Mangos, implement? + int amountIndex = lua_gettop(L) - 1; + int n = SetupStack(GuildEventBindings, GUILD_EVENT_ON_MONEY_WITHDRAW, 4); + + while (n > 0) { - if (lua_isnumber(L, i)) - amount = CHECKVAL(L, i, amount); + int r = CallOneFunction(n--, 4, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(L, r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); } - ENDCALL(); + + CleanUpStack(4); } void Eluna::OnMemberDepositMoney(Guild* guild, Player* player, uint32& amount) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_MONEY_DEPOSIT, return); - Push(L, guild); - Push(L, player); - Push(L, amount); - EVENT_EXECUTE(1); - FOR_RETS(i) + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_MONEY_DEPOSIT)) + return; + + LOCK_ELUNA; + Push(guild); + Push(player); + Push(amount); + int amountIndex = lua_gettop(L); + int n = SetupStack(GuildEventBindings, GUILD_EVENT_ON_MONEY_DEPOSIT, 3); + + while (n > 0) { - if (lua_isnumber(L, i)) - amount = CHECKVAL(L, i, amount); + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(L, r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); } - ENDCALL(); + + CleanUpStack(3); } void Eluna::OnItemMove(Guild* guild, Player* player, Item* pItem, bool isSrcBank, uint8 srcContainer, uint8 srcSlotId, bool isDestBank, uint8 destContainer, uint8 destSlotId) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_ITEM_MOVE, return); - Push(L, guild); - Push(L, player); - Push(L, pItem); - Push(L, isSrcBank); - Push(L, srcContainer); - Push(L, srcSlotId); - Push(L, isDestBank); - Push(L, destContainer); - Push(L, destSlotId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_ITEM_MOVE)) + return; + + LOCK_ELUNA; + Push(guild); + Push(player); + Push(pItem); + Push(isSrcBank); + Push(srcContainer); + Push(srcSlotId); + Push(isDestBank); + Push(destContainer); + Push(destSlotId); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_ITEM_MOVE); } void Eluna::OnEvent(Guild* guild, uint8 eventType, uint32 playerGuid1, uint32 playerGuid2, uint8 newRank) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_EVENT, return); - Push(L, guild); - Push(L, eventType); - Push(L, playerGuid1); - Push(L, playerGuid2); - Push(L, newRank); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_EVENT)) + return; + + LOCK_ELUNA; + Push(guild); + Push(eventType); + Push(playerGuid1); + Push(playerGuid2); + Push(newRank); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_EVENT); } void Eluna::OnBankEvent(Guild* guild, uint8 eventType, uint8 tabId, uint32 playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) { - EVENT_BEGIN(GuildEventBindings, GUILD_EVENT_ON_BANK_EVENT, return); - Push(L, guild); - Push(L, eventType); - Push(L, tabId); - Push(L, playerGuid); - Push(L, itemOrMoney); - Push(L, itemStackCount); - Push(L, destTabId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GuildEventBindings->HasEvents(GUILD_EVENT_ON_BANK_EVENT)) + return; + + LOCK_ELUNA; + Push(guild); + Push(eventType); + Push(tabId); + Push(playerGuid); + Push(itemOrMoney); + Push(itemStackCount); + Push(destTabId); + CallAllFunctions(GuildEventBindings, GUILD_EVENT_ON_BANK_EVENT); } // Group void Eluna::OnAddMember(Group* group, uint64 guid) { - EVENT_BEGIN(GroupEventBindings, GROUP_EVENT_ON_MEMBER_ADD, return); - Push(L, group); - Push(L, guid); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GroupEventBindings->HasEvents(GROUP_EVENT_ON_MEMBER_ADD)) + return; + + LOCK_ELUNA; + Push(group); + Push(guid); + CallAllFunctions(GroupEventBindings, GROUP_EVENT_ON_MEMBER_ADD); } void Eluna::OnInviteMember(Group* group, uint64 guid) { - EVENT_BEGIN(GroupEventBindings, GROUP_EVENT_ON_MEMBER_INVITE, return); - Push(L, group); - Push(L, guid); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GroupEventBindings->HasEvents(GROUP_EVENT_ON_MEMBER_INVITE)) + return; + + LOCK_ELUNA; + Push(group); + Push(guid); + CallAllFunctions(GroupEventBindings, GROUP_EVENT_ON_MEMBER_INVITE); } void Eluna::OnRemoveMember(Group* group, uint64 guid, uint8 method) { - EVENT_BEGIN(GroupEventBindings, GROUP_EVENT_ON_MEMBER_REMOVE, return); - Push(L, group); - Push(L, guid); - Push(L, method); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GroupEventBindings->HasEvents(GROUP_EVENT_ON_MEMBER_REMOVE)) + return; + + LOCK_ELUNA; + Push(group); + Push(guid); + Push(method); + CallAllFunctions(GroupEventBindings, GROUP_EVENT_ON_MEMBER_REMOVE); } void Eluna::OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 oldLeaderGuid) { - EVENT_BEGIN(GroupEventBindings, GROUP_EVENT_ON_LEADER_CHANGE, return); - Push(L, group); - Push(L, newLeaderGuid); - Push(L, oldLeaderGuid); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GroupEventBindings->HasEvents(GROUP_EVENT_ON_LEADER_CHANGE)) + return; + + LOCK_ELUNA; + Push(group); + Push(newLeaderGuid); + Push(oldLeaderGuid); + CallAllFunctions(GroupEventBindings, GROUP_EVENT_ON_LEADER_CHANGE); } void Eluna::OnDisband(Group* group) { - EVENT_BEGIN(GroupEventBindings, GROUP_EVENT_ON_DISBAND, return); - Push(L, group); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GroupEventBindings->HasEvents(GROUP_EVENT_ON_DISBAND)) + return; + + LOCK_ELUNA; + Push(group); + CallAllFunctions(GroupEventBindings, GROUP_EVENT_ON_DISBAND); } void Eluna::OnCreate(Group* group, uint64 leaderGuid, GroupType groupType) { - EVENT_BEGIN(GroupEventBindings, GROUP_EVENT_ON_CREATE, return); - Push(L, group); - Push(L, leaderGuid); - Push(L, groupType); - EVENT_EXECUTE(0); - ENDCALL(); + if (!GroupEventBindings->HasEvents(GROUP_EVENT_ON_CREATE)) + return; + + LOCK_ELUNA; + Push(group); + Push(leaderGuid); + Push(groupType); + CallAllFunctions(GroupEventBindings, GROUP_EVENT_ON_CREATE); } /* Map */ void Eluna::OnCreate(Map* map) { - EVENT_BEGIN(ServerEventBindings, MAP_EVENT_ON_CREATE, return); - Push(L, map); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(MAP_EVENT_ON_CREATE)) + return; + + LOCK_ELUNA; + Push(map); + CallAllFunctions(ServerEventBindings, MAP_EVENT_ON_CREATE); } void Eluna::OnDestroy(Map* map) { - EVENT_BEGIN(ServerEventBindings, MAP_EVENT_ON_DESTROY, return); - Push(L, map); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(MAP_EVENT_ON_DESTROY)) + return; + + LOCK_ELUNA; + Push(map); + CallAllFunctions(ServerEventBindings, MAP_EVENT_ON_DESTROY); } void Eluna::OnPlayerEnter(Map* map, Player* player) { - EVENT_BEGIN(ServerEventBindings, MAP_EVENT_ON_PLAYER_ENTER, return); - Push(L, map); - Push(L, player); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(MAP_EVENT_ON_PLAYER_ENTER)) + return; + + LOCK_ELUNA; + Push(map); + Push(player); + CallAllFunctions(ServerEventBindings, MAP_EVENT_ON_PLAYER_ENTER); } void Eluna::OnPlayerLeave(Map* map, Player* player) { - EVENT_BEGIN(ServerEventBindings, MAP_EVENT_ON_PLAYER_LEAVE, return); - Push(L, map); - Push(L, player); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(MAP_EVENT_ON_PLAYER_LEAVE)) + return; + + LOCK_ELUNA; + Push(map); + Push(player); + CallAllFunctions(ServerEventBindings, MAP_EVENT_ON_PLAYER_LEAVE); } void Eluna::OnUpdate(Map* map, uint32 diff) { + if (!ServerEventBindings->HasEvents(MAP_EVENT_ON_UPDATE)) + return; + + LOCK_ELUNA; // enable this for multithread // eventMgr->globalProcessor->Update(diff); - EVENT_BEGIN(ServerEventBindings, MAP_EVENT_ON_UPDATE, return); - Push(L, map); - Push(L, diff); - EVENT_EXECUTE(0); - ENDCALL(); + Push(map); + Push(diff); + CallAllFunctions(ServerEventBindings, MAP_EVENT_ON_UPDATE); } void Eluna::OnRemove(GameObject* gameobject) { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_DELETE_GAMEOBJECT, return); - Push(L, gameobject); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_DELETE_GAMEOBJECT)) + return; + + LOCK_ELUNA; + Push(gameobject); + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_DELETE_GAMEOBJECT); } void Eluna::OnRemove(Creature* creature) { - EVENT_BEGIN(ServerEventBindings, WORLD_EVENT_ON_DELETE_CREATURE, return); - Push(L, creature); - EVENT_EXECUTE(0); - ENDCALL(); + if (!ServerEventBindings->HasEvents(WORLD_EVENT_ON_DELETE_CREATURE)) + return; + + LOCK_ELUNA; + Push(creature); + CallAllFunctions(ServerEventBindings, WORLD_EVENT_ON_DELETE_CREATURE); } // creature bool Eluna::OnDummyEffect(Unit* pCaster, uint32 spellId, SpellEffIndex effIndex, Creature* pTarget) { - ENTRY_BEGIN(CreatureEventBindings, pTarget->GetEntry(), CREATURE_EVENT_ON_DUMMY_EFFECT, return false); - Push(L, pCaster); - Push(L, spellId); - Push(L, effIndex); - Push(L, pTarget); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pCaster); + Push(spellId); + Push(effIndex); + Push(pTarget); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry()); } bool Eluna::OnGossipHello(Player* pPlayer, Creature* pCreature) { - ENTRY_BEGIN(CreatureGossipBindings, pCreature->GetEntry(), GOSSIP_EVENT_ON_HELLO, return false); + if (!CreatureGossipBindings->HasEvents(GOSSIP_EVENT_ON_HELLO, pCreature->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pCreature); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + Push(pPlayer); + Push(pCreature); + return CallAllFunctionsBool(CreatureGossipBindings, GOSSIP_EVENT_ON_HELLO, pCreature->GetEntry()); } bool Eluna::OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action) { - ENTRY_BEGIN(CreatureGossipBindings, pCreature->GetEntry(), GOSSIP_EVENT_ON_SELECT, return false); + if (!CreatureGossipBindings->HasEvents(GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pCreature); - Push(L, sender); - Push(L, action); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + Push(pPlayer); + Push(pCreature); + Push(sender); + Push(action); + return CallAllFunctionsBool(CreatureGossipBindings, GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry()); } bool Eluna::OnGossipSelectCode(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code) { - ENTRY_BEGIN(CreatureGossipBindings, pCreature->GetEntry(), GOSSIP_EVENT_ON_SELECT, return false); + if (!CreatureGossipBindings->HasEvents(GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pCreature); - Push(L, sender); - Push(L, action); - Push(L, code); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + Push(pPlayer); + Push(pCreature); + Push(sender); + Push(action); + Push(code); + return CallAllFunctionsBool(CreatureGossipBindings, GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry()); } bool Eluna::OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest) { - ENTRY_BEGIN(CreatureEventBindings, pCreature->GetEntry(), CREATURE_EVENT_ON_QUEST_ACCEPT, return false); - Push(L, pPlayer); - Push(L, pCreature); - Push(L, pQuest); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_QUEST_ACCEPT, pCreature->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pCreature); + Push(pQuest); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_QUEST_ACCEPT, pCreature->GetEntry()); } bool Eluna::OnQuestReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt) { - ENTRY_BEGIN(CreatureEventBindings, pCreature->GetEntry(), CREATURE_EVENT_ON_QUEST_REWARD, return false); - Push(L, pPlayer); - Push(L, pCreature); - Push(L, pQuest); - Push(L, opt); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_QUEST_REWARD, pCreature->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pCreature); + Push(pQuest); + Push(opt); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_QUEST_REWARD, pCreature->GetEntry()); } uint32 Eluna::GetDialogStatus(Player* pPlayer, Creature* pCreature) { - ENTRY_BEGIN(CreatureEventBindings, pCreature->GetEntry(), CREATURE_EVENT_ON_DIALOG_STATUS, return 0); - Push(L, pPlayer); - Push(L, pCreature); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_DIALOG_STATUS, pCreature->GetEntry())) + return DIALOG_STATUS_SCRIPTED_NO_STATUS; + + LOCK_ELUNA; + Push(pPlayer); + Push(pCreature); + CallAllFunctions(CreatureEventBindings, CREATURE_EVENT_ON_DIALOG_STATUS, pCreature->GetEntry()); return DIALOG_STATUS_SCRIPTED_NO_STATUS; } void Eluna::OnAddToWorld(Creature* creature) { - ENTRY_BEGIN(CreatureEventBindings, creature->GetEntry(), CREATURE_EVENT_ON_ADD, return); - Push(L, creature); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_ADD, creature->GetEntry())) + return; + + LOCK_ELUNA; + Push(creature); + CallAllFunctions(CreatureEventBindings, CREATURE_EVENT_ON_ADD, creature->GetEntry()); } void Eluna::OnRemoveFromWorld(Creature* creature) { - ENTRY_BEGIN(CreatureEventBindings, creature->GetEntry(), CREATURE_EVENT_ON_REMOVE, return); - Push(L, creature); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_REMOVE, creature->GetEntry())) + return; + + LOCK_ELUNA; + Push(creature); + CallAllFunctions(CreatureEventBindings, CREATURE_EVENT_ON_REMOVE, creature->GetEntry()); } bool Eluna::OnSummoned(Creature* pCreature, Unit* pSummoner) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, pCreature->GetEntry(), CREATURE_EVENT_ON_SUMMONED, return false); - Push(L, pCreature); - Push(L, pSummoner); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_SUMMONED, pCreature->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pCreature); + Push(pSummoner); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_SUMMONED, pCreature->GetEntry()); } bool Eluna::UpdateAI(Creature* me, const uint32 diff) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_AIUPDATE, return false); - Eluna::Push(L, me); - Eluna::Push(L, diff); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_AIUPDATE, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(diff); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_AIUPDATE, me->GetEntry()); } //Called for reaction at enter to combat if not in combat yet (enemy can be NULL) //Called at creature aggro either by MoveInLOS or Attack Start bool Eluna::EnterCombat(Creature* me, Unit* target) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_ENTER_COMBAT, return false); - Eluna::Push(L, me); - Eluna::Push(L, target); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_ENTER_COMBAT, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(target); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_ENTER_COMBAT, me->GetEntry()); } // Called at any Damage from any attacker (before damage apply) bool Eluna::DamageTaken(Creature* me, Unit* attacker, uint32& damage) { + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_DAMAGE_TAKEN, me->GetEntry())) + return false; + + LOCK_ELUNA; bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_DAMAGE_TAKEN, return false); - Eluna::Push(L, me); - Eluna::Push(L, attacker); - Eluna::Push(L, damage); - ENTRY_EXECUTE(2); - FOR_RETS(i) + Push(me); + Push(attacker); + Push(damage); + int damageIndex = lua_gettop(L); + int n = SetupStack(CreatureEventBindings, CREATURE_EVENT_ON_DAMAGE_TAKEN, me->GetEntry(), 3); + + while (n > 0) { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i); - if (lua_isnumber(L, i)) - damage = Eluna::CHECKVAL(L, i, damage); + int r = CallOneFunction(n--, 3, 2); + + if (lua_isboolean(L, r + 0) && lua_toboolean(L, r + 0)) + result = true; + + if (lua_isnumber(L, r + 1)) + { + damage = Eluna::CHECKVAL(L, r + 1); + // Update the stack for subsequent calls. + ReplaceArgument(damage, damageIndex); + } + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(3); return result; } //Called at creature death bool Eluna::JustDied(Creature* me, Unit* killer) { + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_DIED, me->GetEntry())) + return false; + On_Reset(me); - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_DIED, return false); - Eluna::Push(L, me); - Eluna::Push(L, killer); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + LOCK_ELUNA; + Push(me); + Push(killer); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_DIED, me->GetEntry()); } //Called at creature killing another unit bool Eluna::KilledUnit(Creature* me, Unit* victim) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_TARGET_DIED, return false); - Eluna::Push(L, me); - Eluna::Push(L, victim); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_TARGET_DIED, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(victim); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_TARGET_DIED, me->GetEntry()); } // Called when the creature summon successfully other creature bool Eluna::JustSummoned(Creature* me, Creature* summon) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, return false); - Eluna::Push(L, me); - Eluna::Push(L, summon); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(summon); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, me->GetEntry()); } // Called when a summoned creature is despawned bool Eluna::SummonedCreatureDespawn(Creature* me, Creature* summon) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, return false); - Eluna::Push(L, me); - Eluna::Push(L, summon); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(summon); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, me->GetEntry()); } //Called at waypoint reached or PointMovement end bool Eluna::MovementInform(Creature* me, uint32 type, uint32 id) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_REACH_WP, return false); - Eluna::Push(L, me); - Eluna::Push(L, type); - Eluna::Push(L, id); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_REACH_WP, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(type); + Push(id); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_REACH_WP, me->GetEntry()); } // Called before EnterCombat even before the creature is in combat. bool Eluna::AttackStart(Creature* me, Unit* target) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_PRE_COMBAT, return false); - Eluna::Push(L, me); - Eluna::Push(L, target); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_PRE_COMBAT, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(target); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_PRE_COMBAT, me->GetEntry()); } // Called for reaction at stopping attack at no attackers or targets bool Eluna::EnterEvadeMode(Creature* me) { + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_LEAVE_COMBAT, me->GetEntry())) + return false; + On_Reset(me); - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_LEAVE_COMBAT, return false); - Eluna::Push(L, me); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + LOCK_ELUNA; + Push(me); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_LEAVE_COMBAT, me->GetEntry()); } // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) bool Eluna::AttackedBy(Creature* me, Unit* attacker) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_ATTACKED_AT, return false); - Eluna::Push(L, me); - Eluna::Push(L, attacker); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_ATTACKED_AT, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(attacker); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_ATTACKED_AT, me->GetEntry()); } // Called when creature is spawned or respawned (for reseting variables) bool Eluna::JustRespawned(Creature* me) { + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_SPAWN, me->GetEntry())) + return false; + On_Reset(me); - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_SPAWN, return false); - Eluna::Push(L, me); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + LOCK_ELUNA; + Push(me); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_SPAWN, me->GetEntry()); } // Called at reaching home after evade bool Eluna::JustReachedHome(Creature* me) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_REACH_HOME, return false); - Eluna::Push(L, me); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_REACH_HOME, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_REACH_HOME, me->GetEntry()); } // Called at text emote receive from player bool Eluna::ReceiveEmote(Creature* me, Player* player, uint32 emoteId) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_RECEIVE_EMOTE, return false); - Eluna::Push(L, me); - Eluna::Push(L, player); - Eluna::Push(L, emoteId); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_RECEIVE_EMOTE, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(player); + Push(emoteId); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_RECEIVE_EMOTE, me->GetEntry()); } // called when the corpse of this creature gets removed bool Eluna::CorpseRemoved(Creature* me, uint32& respawnDelay) { + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_CORPSE_REMOVED, me->GetEntry())) + return false; + + LOCK_ELUNA; bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_CORPSE_REMOVED, return false); - Eluna::Push(L, me); - Eluna::Push(L, respawnDelay); - ENTRY_EXECUTE(2); - FOR_RETS(i) + Push(me); + Push(respawnDelay); + int respawnDelayIndex = lua_gettop(L); + int n = SetupStack(CreatureEventBindings, CREATURE_EVENT_ON_CORPSE_REMOVED, me->GetEntry(), 2); + + while (n > 0) { - if (lua_isboolean(L, i)) - result = Eluna::CHECKVAL(L, i); - if (lua_isnumber(L, i)) - respawnDelay = Eluna::CHECKVAL(L, i, respawnDelay); + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && lua_toboolean(L, r + 0)) + result = true; + + if (lua_isnumber(L, r + 1)) + { + respawnDelay = Eluna::CHECKVAL(L, r + 1); + // Update the stack for subsequent calls. + ReplaceArgument(respawnDelay, respawnDelayIndex); + } + + lua_pop(L, 2); } - ENDCALL(); + + CleanUpStack(2); return result; } bool Eluna::MoveInLineOfSight(Creature* me, Unit* who) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_MOVE_IN_LOS, return false); - Eluna::Push(L, me); - Eluna::Push(L, who); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_MOVE_IN_LOS, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(who); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_MOVE_IN_LOS, me->GetEntry()); } // Called on creature initial spawn, respawn, death, evade (leave combat) void Eluna::On_Reset(Creature* me) // Not an override, custom { - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_RESET, return); - Eluna::Push(L, me); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_RESET, me->GetEntry())) + return; + + LOCK_ELUNA; + Push(me); + CallAllFunctions(CreatureEventBindings, CREATURE_EVENT_ON_RESET, me->GetEntry()); } // Called when hit by a spell bool Eluna::SpellHit(Creature* me, Unit* caster, SpellInfo const* spell) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_HIT_BY_SPELL, return false); - Eluna::Push(L, me); - Eluna::Push(L, caster); - Eluna::Push(L, spell->Id); // Pass spell object? - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_HIT_BY_SPELL, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(caster); + Push(spell->Id); // Pass spell object? + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_HIT_BY_SPELL, me->GetEntry()); } // Called when spell hits a target bool Eluna::SpellHitTarget(Creature* me, Unit* target, SpellInfo const* spell) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_SPELL_HIT_TARGET, return false); - Eluna::Push(L, me); - Eluna::Push(L, target); - Eluna::Push(L, spell->Id); // Pass spell object? - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_SPELL_HIT_TARGET, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(target); + Push(spell->Id); // Pass spell object? + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_SPELL_HIT_TARGET, me->GetEntry()); } #ifdef TRINITY bool Eluna::SummonedCreatureDies(Creature* me, Creature* summon, Unit* killer) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, return false); - Eluna::Push(L, me); - Eluna::Push(L, summon); - Eluna::Push(L, killer); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(summon); + Push(killer); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, me->GetEntry()); } // Called when owner takes damage bool Eluna::OwnerAttackedBy(Creature* me, Unit* attacker) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_OWNER_ATTACKED_AT, return false); - Eluna::Push(L, me); - Eluna::Push(L, attacker); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_OWNER_ATTACKED_AT, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(attacker); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_OWNER_ATTACKED_AT, me->GetEntry()); } // Called when owner attacks something bool Eluna::OwnerAttacked(Creature* me, Unit* target) { - bool result = false; - ENTRY_BEGIN(CreatureEventBindings, me->GetEntry(), CREATURE_EVENT_ON_OWNER_ATTACKED, return false); - Eluna::Push(L, me); - Eluna::Push(L, target); - ENTRY_EXECUTE(1); - FOR_RETS(i) - { - if (lua_isboolean(L, i)) - result = CHECKVAL(L, i, false); - } - ENDCALL(); - return result; + if (!CreatureEventBindings->HasEvents(CREATURE_EVENT_ON_OWNER_ATTACKED, me->GetEntry())) + return false; + + LOCK_ELUNA; + Push(me); + Push(target); + return CallAllFunctionsBool(CreatureEventBindings, CREATURE_EVENT_ON_OWNER_ATTACKED, me->GetEntry()); } #endif @@ -2060,94 +2363,104 @@ struct ElunaCreatureAI : ScriptedAI // gameobject bool Eluna::OnDummyEffect(Unit* pCaster, uint32 spellId, SpellEffIndex effIndex, GameObject* pTarget) { - ENTRY_BEGIN(GameObjectEventBindings, pTarget->GetEntry(), GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, return false); - Push(L, pCaster); - Push(L, spellId); - Push(L, effIndex); - Push(L, pTarget); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pCaster); + Push(spellId); + Push(effIndex); + Push(pTarget); + return CallAllFunctionsBool(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry()); } bool Eluna::OnGossipHello(Player* pPlayer, GameObject* pGameObject) { - ENTRY_BEGIN(GameObjectGossipBindings, pGameObject->GetEntry(), GOSSIP_EVENT_ON_HELLO, return false); + if (!GameObjectGossipBindings->HasEvents(GOSSIP_EVENT_ON_HELLO, pGameObject->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pGameObject); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + Push(pPlayer); + Push(pGameObject); + return CallAllFunctionsBool(GameObjectGossipBindings, GOSSIP_EVENT_ON_HELLO, pGameObject->GetEntry()); } bool Eluna::OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action) { - ENTRY_BEGIN(GameObjectGossipBindings, pGameObject->GetEntry(), GOSSIP_EVENT_ON_SELECT, return false); + if (!GameObjectGossipBindings->HasEvents(GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pGameObject); - Push(L, sender); - Push(L, action); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + Push(pPlayer); + Push(pGameObject); + Push(sender); + Push(action); + return CallAllFunctionsBool(GameObjectGossipBindings, GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry()); } bool Eluna::OnGossipSelectCode(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code) { - ENTRY_BEGIN(GameObjectGossipBindings, pGameObject->GetEntry(), GOSSIP_EVENT_ON_SELECT, return false); + if (!GameObjectGossipBindings->HasEvents(GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry())) + return false; + + LOCK_ELUNA; pPlayer->PlayerTalkClass->ClearMenus(); - Push(L, pPlayer); - Push(L, pGameObject); - Push(L, sender); - Push(L, action); - Push(L, code); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + Push(pPlayer); + Push(pGameObject); + Push(sender); + Push(action); + Push(code); + return CallAllFunctionsBool(GameObjectGossipBindings, GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry()); } bool Eluna::OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, return false); - Push(L, pPlayer); - Push(L, pGameObject); - Push(L, pQuest); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, pGameObject->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pGameObject); + Push(pQuest); + return CallAllFunctionsBool(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, pGameObject->GetEntry()); } void Eluna::UpdateAI(GameObject* pGameObject, uint32 diff) { + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_AIUPDATE, pGameObject->GetEntry())) + return; + + LOCK_ELUNA; pGameObject->elunaEvents->Update(diff); - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_AIUPDATE, return); - Push(L, pGameObject); - Push(L, diff); - ENTRY_EXECUTE(0); - ENDCALL(); + Push(pGameObject); + Push(diff); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_AIUPDATE, pGameObject->GetEntry()); } bool Eluna::OnQuestReward(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest, uint32 opt) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_QUEST_REWARD, return false); - Push(L, pPlayer); - Push(L, pGameObject); - Push(L, pQuest); - Push(L, opt); - ENTRY_EXECUTE(0); - ENDCALL(); - return true; + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_QUEST_REWARD, pGameObject->GetEntry())) + return false; + + LOCK_ELUNA; + Push(pPlayer); + Push(pGameObject); + Push(pQuest); + Push(opt); + return CallAllFunctionsBool(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_QUEST_REWARD, pGameObject->GetEntry()); } uint32 Eluna::GetDialogStatus(Player* pPlayer, GameObject* pGameObject) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_DIALOG_STATUS, return 0); - Push(L, pPlayer); - Push(L, pGameObject); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_DIALOG_STATUS, pGameObject->GetEntry())) + return DIALOG_STATUS_SCRIPTED_NO_STATUS; + + LOCK_ELUNA; + Push(pPlayer); + Push(pGameObject); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_DIALOG_STATUS, pGameObject->GetEntry()); return DIALOG_STATUS_SCRIPTED_NO_STATUS; // DIALOG_STATUS_UNDEFINED } @@ -2155,64 +2468,78 @@ uint32 Eluna::GetDialogStatus(Player* pPlayer, GameObject* pGameObject) #ifndef TBC void Eluna::OnDestroyed(GameObject* pGameObject, Player* pPlayer) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_DESTROYED, return); - Push(L, pGameObject); - Push(L, pPlayer); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_DESTROYED, pGameObject->GetEntry())) + return; + + LOCK_ELUNA; + Push(pGameObject); + Push(pPlayer); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_DESTROYED, pGameObject->GetEntry()); } void Eluna::OnDamaged(GameObject* pGameObject, Player* pPlayer) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_DAMAGED, return); - Push(L, pGameObject); - Push(L, pPlayer); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_DAMAGED, pGameObject->GetEntry())) + return; + + LOCK_ELUNA; + Push(pGameObject); + Push(pPlayer); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_DAMAGED, pGameObject->GetEntry()); } #endif #endif void Eluna::OnLootStateChanged(GameObject* pGameObject, uint32 state) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, return); - Push(L, pGameObject); - Push(L, state); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, pGameObject->GetEntry())) + return; + + LOCK_ELUNA; + Push(pGameObject); + Push(state); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, pGameObject->GetEntry()); } void Eluna::OnGameObjectStateChanged(GameObject* pGameObject, uint32 state) { - ENTRY_BEGIN(GameObjectEventBindings, pGameObject->GetEntry(), GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, return); - Push(L, pGameObject); - Push(L, state); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, pGameObject->GetEntry())) + return; + + LOCK_ELUNA; + Push(pGameObject); + Push(state); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, pGameObject->GetEntry()); } void Eluna::OnSpawn(GameObject* gameobject) { - ENTRY_BEGIN(GameObjectEventBindings, gameobject->GetEntry(), GAMEOBJECT_EVENT_ON_SPAWN, return); - Push(L, gameobject); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_SPAWN, gameobject->GetEntry())) + return; + + LOCK_ELUNA; + Push(gameobject); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_SPAWN, gameobject->GetEntry()); } void Eluna::OnAddToWorld(GameObject* gameobject) { - ENTRY_BEGIN(GameObjectEventBindings, gameobject->GetEntry(), GAMEOBJECT_EVENT_ON_ADD, return); - Push(L, gameobject); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_ADD, gameobject->GetEntry())) + return; + + LOCK_ELUNA; + Push(gameobject); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_ADD, gameobject->GetEntry()); } void Eluna::OnRemoveFromWorld(GameObject* gameobject) { - ENTRY_BEGIN(GameObjectEventBindings, gameobject->GetEntry(), GAMEOBJECT_EVENT_ON_REMOVE, return); - Push(L, gameobject); - ENTRY_EXECUTE(0); - ENDCALL(); + if (!GameObjectEventBindings->HasEvents(GAMEOBJECT_EVENT_ON_REMOVE, gameobject->GetEntry())) + return; + + LOCK_ELUNA; + Push(gameobject); + CallAllFunctions(GameObjectEventBindings, GAMEOBJECT_EVENT_ON_REMOVE, gameobject->GetEntry()); } CreatureAI* Eluna::GetAI(Creature* creature) @@ -2224,41 +2551,49 @@ CreatureAI* Eluna::GetAI(Creature* creature) void Eluna::OnBGStart(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId) { - EVENT_BEGIN(BGEventBindings, BG_EVENT_ON_START, return); - Push(L, bg); - Push(L, bgId); - Push(L, instanceId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!BGEventBindings->HasEvents(BG_EVENT_ON_START)) + return; + + LOCK_ELUNA; + Push(bg); + Push(bgId); + Push(instanceId); + CallAllFunctions(BGEventBindings, BG_EVENT_ON_START); } void Eluna::OnBGEnd(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId, Team winner) { - EVENT_BEGIN(BGEventBindings, BG_EVENT_ON_END, return); - Push(L, bg); - Push(L, bgId); - Push(L, instanceId); - Push(L, winner); - EVENT_EXECUTE(0); - ENDCALL(); + if (!BGEventBindings->HasEvents(BG_EVENT_ON_END)) + return; + + LOCK_ELUNA; + Push(bg); + Push(bgId); + Push(instanceId); + Push(winner); + CallAllFunctions(BGEventBindings, BG_EVENT_ON_END); } void Eluna::OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId) { - EVENT_BEGIN(BGEventBindings, BG_EVENT_ON_CREATE, return); - Push(L, bg); - Push(L, bgId); - Push(L, instanceId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!BGEventBindings->HasEvents(BG_EVENT_ON_CREATE)) + return; + + LOCK_ELUNA; + Push(bg); + Push(bgId); + Push(instanceId); + CallAllFunctions(BGEventBindings, BG_EVENT_ON_CREATE); } void Eluna::OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId) { - EVENT_BEGIN(BGEventBindings, BG_EVENT_ON_PRE_DESTROY, return); - Push(L, bg); - Push(L, bgId); - Push(L, instanceId); - EVENT_EXECUTE(0); - ENDCALL(); + if (!BGEventBindings->HasEvents(BG_EVENT_ON_PRE_DESTROY)) + return; + + LOCK_ELUNA; + Push(bg); + Push(bgId); + Push(instanceId); + CallAllFunctions(BGEventBindings, BG_EVENT_ON_PRE_DESTROY); } diff --git a/LuaEngine.cpp b/LuaEngine.cpp index 36195af..29a7f9b 100644 --- a/LuaEngine.cpp +++ b/LuaEngine.cpp @@ -36,6 +36,7 @@ std::string Eluna::lua_requirepath; Eluna* Eluna::GEluna = NULL; bool Eluna::reload = false; bool Eluna::initialized = false; +Eluna::LockType Eluna::lock; extern void RegisterFunctions(Eluna* E); @@ -88,13 +89,13 @@ void Eluna::ReloadEluna() EventMgr::ProcessorSet oldProcessors; { - EventMgr::ReadGuard lock(sEluna->eventMgr->GetLock()); + EventMgr::ReadGuard guard(sEluna->eventMgr->GetLock()); oldProcessors = sEluna->eventMgr->processors; } Uninitialize(); Initialize(); { - EventMgr::WriteGuard lock(sEluna->eventMgr->GetLock()); + EventMgr::WriteGuard guard(sEluna->eventMgr->GetLock()); sEluna->eventMgr->processors.insert(oldProcessors.begin(), oldProcessors.end()); } @@ -118,6 +119,7 @@ Eluna::Eluna() : L(luaL_newstate()), event_level(0), +push_counter(0), eventMgr(NULL), @@ -431,19 +433,31 @@ void Eluna::report(lua_State* luastate) void Eluna::ExecuteCall(int params, int res) { int top = lua_gettop(L); - int type = lua_type(L, top - params); + // Expected: function, [parameters] + ASSERT(top > params); + + // Check function type + int type = lua_type(L, top - params); if (type != LUA_TFUNCTION) { - lua_pop(L, params + 1); // Cleanup the stack. ELUNA_LOG_ERROR("[Eluna]: Cannot execute call: registered value is %s, not a function.", lua_typename(L, type)); - return; + ASSERT(false); } + // Objects are invalidated when event level hits 0 ++event_level; - if (lua_pcall(L, params, res, 0)) - report(L); + int result = lua_pcall(L, params, res, 0); --event_level; + + // lua_pcall returns 0 on success. + // On error we report errors and push nils for expected amount of returned values + if (result) + { + report(L); + for (int i = 0; i < res; ++i) + lua_pushnil(L); + } } void Eluna::Push(lua_State* luastate) diff --git a/LuaEngine.h b/LuaEngine.h index 77d40d7..2b3ef7f 100644 --- a/LuaEngine.h +++ b/LuaEngine.h @@ -109,6 +109,44 @@ private: Eluna(Eluna const&); Eluna& operator=(const Eluna&); + // Some helpers for hooks to call event handlers. + template int SetupStack(EventBind* event_bindings, EntryBind* entry_bindings, T event_id, uint32 entry, int number_of_arguments); + void CleanUpStack(int number_of_arguments); + template void ReplaceArgument(T value, uint8 index); + int CallOneFunction(int number_of_functions, int number_of_arguments, int number_of_results); + template void CallAllFunctions(EventBind* event_bindings, EntryBind* entry_bindings, T event_id, uint32 entry); + template bool CallAllFunctionsBool(EventBind* event_bindings, EntryBind* entry_bindings, T event_id, uint32 entry, bool default_value); + + // Convenient overloads for Setup. Use these in hooks instead of original. + template int SetupStack(EventBind* event_bindings, T event_id, int number_of_arguments) + { + return SetupStack(event_bindings, (EntryBind*)NULL, event_id, 0, number_of_arguments); + } + template int SetupStack(EntryBind* entry_bindings, T event_id, uint32 entry, int number_of_arguments) + { + return SetupStack((EventBind*)NULL, entry_bindings, event_id, entry, number_of_arguments); + } + + // Convenient overloads for CallAllFunctions. Use these in hooks instead of original. + template void CallAllFunctions(EventBind* event_bindings, T event_id) + { + CallAllFunctions(event_bindings, (EntryBind*)NULL, event_id, 0); + } + template void CallAllFunctions(EntryBind* entry_bindings, T event_id, uint32 entry) + { + CallAllFunctions((EventBind*)NULL, entry_bindings, event_id, entry); + } + + // Convenient overloads for CallAllFunctionsBool. Use these in hooks instead of original. + template bool CallAllFunctionsBool(EventBind* event_bindings, T event_id, bool default_value = false) + { + return CallAllFunctionsBool(event_bindings, (EntryBind*)NULL, event_id, 0, default_value); + } + template bool CallAllFunctionsBool(EntryBind* entry_bindings, T event_id, uint32 entry, bool default_value = false) + { + return CallAllFunctionsBool((EventBind*)NULL, entry_bindings, event_id, entry, default_value); + } + public: typedef std::list ScriptList; @@ -118,13 +156,13 @@ public: #ifdef TRINITY typedef std::recursive_mutex LockType; - typedef std::lock_guard ElunaGuard; + typedef std::lock_guard Guard; #else typedef ACE_Recursive_Thread_Mutex LockType; - typedef ACE_Guard ElunaGuard; + typedef ACE_Guard Guard; #endif - LockType elunaLock; + static LockType lock; lua_State* L; uint32 event_level; @@ -168,7 +206,7 @@ public: void RunScripts(); void InvalidateObjects(); - // Pushes + // Static pushes, can be used by anything, including methods. static void Push(lua_State* luastate); // nil static void Push(lua_State* luastate, const long long); static void Push(lua_State* luastate, const unsigned long long); @@ -191,6 +229,26 @@ public: static void Push(lua_State* luastate, Pet const* pet); static void Push(lua_State* luastate, TempSummon const* summon); + // When a hook pushes arguments to be passed to event handlers + // this is used to keep track of how many arguments were pushed. + uint8 push_counter; + + // Non-static pushes, to be used in hooks. + // These just call the correct static version with the main thread's Lua state. + void Push() { Push(L); ++push_counter; } + void Push(const long long value) { Push(L, value); ++push_counter; } + void Push(const unsigned long long value) { Push(L, value); ++push_counter; } + void Push(const long value) { Push(L, value); ++push_counter; } + void Push(const unsigned long value) { Push(L, value); ++push_counter; } + void Push(const int value) { Push(L, value); ++push_counter; } + void Push(const unsigned int value) { Push(L, value); ++push_counter; } + void Push(const bool value) { Push(L, value); ++push_counter; } + void Push(const float value) { Push(L, value); ++push_counter; } + void Push(const double value) { Push(L, value); ++push_counter; } + void Push(const std::string& value) { Push(L, value); ++push_counter; } + void Push(const char* value) { Push(L, value); ++push_counter; } + template void Push(T const* ptr){ Push(L, ptr); ++push_counter; } + // Checks template static T CHECKVAL(lua_State* luastate, int narg); template static T CHECKVAL(lua_State* luastate, int narg, T def) @@ -401,5 +459,5 @@ template<> WorldObject* Eluna::CHECKOBJ(lua_State* L, int narg, boo template<> ElunaObject* Eluna::CHECKOBJ(lua_State* L, int narg, bool error); #define sEluna Eluna::GEluna -#define ELUNA_LOCK(E) Eluna::ElunaGuard elunaGuard((E)->elunaLock); +#define LOCK_ELUNA Eluna::Guard __guard(Eluna::lock) #endif