mirror of
https://github.com/azerothcore/mod-ale
synced 2025-11-29 15:38:17 +08:00
Note that this will still need the core side destructors to have the ref remove function used in order to have invalid pointer errors.
871 lines
25 KiB
C++
871 lines
25 KiB
C++
/*
|
|
* Copyright (C) 2010 - 2014 Eluna Lua Engine <http://emudevs.com/>
|
|
* This program is free software licensed under GPL version 3
|
|
* Please see the included DOCS/LICENSE.md for more information
|
|
*/
|
|
|
|
#include <ace/Dirent.h>
|
|
#include <ace/OS_NS_sys_stat.h>
|
|
#include "LuaEngine.h"
|
|
|
|
#ifdef MANGOS
|
|
INSTANTIATE_SINGLETON_1(Eluna);
|
|
#endif
|
|
|
|
extern void RegisterFunctions(lua_State* L);
|
|
extern void AddElunaScripts();
|
|
|
|
// Start or restart eluna. Returns true if started
|
|
bool StartEluna()
|
|
{
|
|
#ifndef ELUNA
|
|
#ifndef MANGOS
|
|
{
|
|
ELUNA_LOG_ERROR("[Eluna]: LuaEngine is Disabled. (If you want to use it please enable in cmake)");
|
|
return false;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
ELUNA_GUARD();
|
|
bool restart = false;
|
|
if (sEluna->L)
|
|
{
|
|
restart = true;
|
|
sHookMgr->OnEngineRestart();
|
|
ELUNA_LOG_INFO("[Eluna]: Stopping Lua Engine");
|
|
|
|
// Unregisters and stops all timed events
|
|
sEluna->m_EventMgr.RemoveEvents();
|
|
|
|
// Remove bindings
|
|
sEluna->PacketEventBindings.Clear();
|
|
sEluna->ServerEventBindings.Clear();
|
|
sEluna->PlayerEventBindings.Clear();
|
|
sEluna->GuildEventBindings.Clear();
|
|
sEluna->GroupEventBindings.Clear();
|
|
|
|
sEluna->CreatureEventBindings.Clear();
|
|
sEluna->CreatureGossipBindings.Clear();
|
|
sEluna->GameObjectEventBindings.Clear();
|
|
sEluna->GameObjectGossipBindings.Clear();
|
|
sEluna->ItemEventBindings.Clear();
|
|
sEluna->ItemGossipBindings.Clear();
|
|
sEluna->playerGossipBindings.Clear();
|
|
sEluna->VehicleEventBindings.Clear();
|
|
|
|
lua_close(sEluna->L);
|
|
}
|
|
else
|
|
AddElunaScripts();
|
|
|
|
#ifdef MANGOS
|
|
// Check config file for eluna is enabled or disabled
|
|
if (!sWorld->getConfig(CONFIG_BOOL_ELUNA_ENABLED))
|
|
{
|
|
ELUNA_LOG_ERROR("[Eluna]: LuaEngine is Disabled. (If you want to use it please set config in 'mangosd.conf')");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
ELUNA_LOG_INFO("[Eluna]: Starting Lua Engine");
|
|
|
|
sEluna->L = luaL_newstate();
|
|
luaL_openlibs(sEluna->L);
|
|
RegisterFunctions(sEluna->L);
|
|
|
|
// Create hidden table with weak values
|
|
lua_newtable(sEluna->L);
|
|
lua_newtable(sEluna->L);
|
|
lua_pushstring(sEluna->L, "v");
|
|
lua_setfield(sEluna->L, -2, "__mode");
|
|
lua_setmetatable(sEluna->L, -2);
|
|
sHookMgr->userdata_table = luaL_ref(sEluna->L, LUA_REGISTRYINDEX);
|
|
|
|
ScriptPaths scripts;
|
|
std::string folderpath = sConfigMgr->GetStringDefault("Eluna.ScriptPath", "lua_scripts");
|
|
#if PLATFORM == PLATFORM_UNIX || PLATFORM == PLATFORM_APPLE
|
|
if (folderpath[0] == '~')
|
|
if (const char* home = getenv("HOME"))
|
|
folderpath.replace(0, 1, home);
|
|
#endif
|
|
ELUNA_LOG_INFO("[Eluna]: Searching scripts from `%s`", folderpath.c_str());
|
|
sEluna->GetScripts(folderpath, scripts);
|
|
sEluna->GetScripts(folderpath + "/extensions", scripts);
|
|
sEluna->RunScripts(scripts);
|
|
|
|
/*
|
|
if (restart)
|
|
{
|
|
//! Iterate over every supported source type (creature and gameobject)
|
|
//! Not entirely sure how this will affect units in non-loaded grids.
|
|
{
|
|
HashMapHolder<Creature>::ReadGuard g(HashMapHolder<Creature>::GetLock());
|
|
HashMapHolder<Creature>::MapType& m = HashMapHolder<Creature>::GetContainer();
|
|
for (HashMapHolder<Creature>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
|
|
{
|
|
if (itr->second->IsInWorld()) // must check?
|
|
// if(sEluna->CreatureEventBindings->GetBindMap(iter->second->GetEntry())) // update all AI or just Eluna?
|
|
itr->second->AIM_Initialize();
|
|
}
|
|
}
|
|
|
|
{
|
|
HashMapHolder<GameObject>::ReadGuard g(HashMapHolder<GameObject>::GetLock());
|
|
HashMapHolder<GameObject>::MapType& m = HashMapHolder<GameObject>::GetContainer();
|
|
for (HashMapHolder<GameObject>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
|
|
{
|
|
if (itr->second->IsInWorld()) // must check?
|
|
// if(sEluna->GameObjectEventBindings->GetBindMap(iter->second->GetEntry())) // update all AI or just Eluna?
|
|
itr->second->AIM_Initialize();
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
// Finds lua script files from given path (including subdirectories) and pushes them to scripts
|
|
void Eluna::GetScripts(std::string path, ScriptPaths& scripts)
|
|
{
|
|
ELUNA_LOG_DEBUG("[Eluna]: GetScripts from path `%s`", path.c_str());
|
|
|
|
ACE_Dirent dir;
|
|
if (dir.open(path.c_str()) == -1)
|
|
{
|
|
ELUNA_LOG_ERROR("[Eluna]: Error No `%s` directory found, creating it", path.c_str());
|
|
ACE_OS::mkdir(path.c_str());
|
|
return;
|
|
}
|
|
|
|
ACE_DIRENT *directory = 0;
|
|
while (directory = dir.read())
|
|
{
|
|
// Skip the ".." and "." files.
|
|
if (ACE::isdotdir(directory->d_name))
|
|
continue;
|
|
|
|
std::string fullpath = path + "/" + directory->d_name;
|
|
|
|
ACE_stat stat_buf;
|
|
if (ACE_OS::lstat(fullpath.c_str(), &stat_buf) == -1)
|
|
continue;
|
|
|
|
// load subfolder
|
|
if ((stat_buf.st_mode & S_IFMT) == (S_IFDIR))
|
|
{
|
|
GetScripts(fullpath, scripts);
|
|
continue;
|
|
}
|
|
|
|
// was file, check extension
|
|
ELUNA_LOG_DEBUG("[Eluna]: GetScripts Checking file `%s`", fullpath.c_str());
|
|
std::string ext = fullpath.substr(fullpath.length() - 4, 4);
|
|
if (ext != ".lua" && ext != ".dll")
|
|
continue;
|
|
|
|
// was correct, add path to scripts to load
|
|
ELUNA_LOG_DEBUG("[Eluna]: GetScripts add path `%s`", fullpath.c_str());
|
|
scripts.erase(fullpath);
|
|
scripts.insert(fullpath);
|
|
}
|
|
}
|
|
|
|
void Eluna::RunScripts(ScriptPaths& scripts)
|
|
{
|
|
uint32 count = 0;
|
|
// load last first to load extensions first
|
|
for (ScriptPaths::const_reverse_iterator it = scripts.rbegin(); it != scripts.rend(); ++it)
|
|
{
|
|
if (!luaL_loadfile(L, it->c_str()) && !lua_pcall(L, 0, 0, 0))
|
|
{
|
|
// successfully loaded and ran file
|
|
ELUNA_LOG_DEBUG("[Eluna]: Successfully loaded `%s`", it->c_str());
|
|
++count;
|
|
continue;
|
|
}
|
|
ELUNA_LOG_ERROR("[Eluna]: Error loading file `%s`", it->c_str());
|
|
report(L);
|
|
}
|
|
ELUNA_LOG_INFO("[Eluna]: Loaded %u Lua scripts", count);
|
|
}
|
|
|
|
void Eluna::report(lua_State* L)
|
|
{
|
|
const char* msg = lua_tostring(L, -1);
|
|
while (msg)
|
|
{
|
|
lua_pop(L, 1);
|
|
ELUNA_LOG_ERROR("%s", msg);
|
|
msg = lua_tostring(L, -1);
|
|
}
|
|
}
|
|
|
|
void Eluna::BeginCall(int fReference)
|
|
{
|
|
lua_settop(L, 0); // stack should be empty
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, (fReference));
|
|
}
|
|
|
|
bool Eluna::ExecuteCall(int params, int res)
|
|
{
|
|
bool ret = true;
|
|
int top = lua_gettop(L);
|
|
|
|
if (lua_type(L, top - params) == LUA_TFUNCTION) // is function
|
|
{
|
|
if (lua_pcall(L, params, res, 0))
|
|
{
|
|
report(L);
|
|
ret = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = false;
|
|
if (params > 0)
|
|
{
|
|
for (int i = top; i >= (top - params); i--)
|
|
{
|
|
if (!lua_isnone(L, i))
|
|
lua_remove(L, i);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void Eluna::EndCall(int res)
|
|
{
|
|
for (int i = res; i > 0; i--)
|
|
{
|
|
if (!lua_isnone(L, res))
|
|
lua_remove(L, res);
|
|
}
|
|
}
|
|
|
|
void Eluna::Push(lua_State* L)
|
|
{
|
|
lua_pushnil(L);
|
|
}
|
|
void Eluna::Push(lua_State* L, const uint64 l)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << l;
|
|
sEluna->Push(L, ss.str());
|
|
}
|
|
void Eluna::Push(lua_State* L, const int64 l)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << l;
|
|
sEluna->Push(L, ss.str());
|
|
}
|
|
void Eluna::Push(lua_State* L, const uint32 u)
|
|
{
|
|
lua_pushunsigned(L, u);
|
|
}
|
|
void Eluna::Push(lua_State* L, const int32 i)
|
|
{
|
|
lua_pushinteger(L, i);
|
|
}
|
|
void Eluna::Push(lua_State* L, const double d)
|
|
{
|
|
lua_pushnumber(L, d);
|
|
}
|
|
void Eluna::Push(lua_State* L, const float f)
|
|
{
|
|
lua_pushnumber(L, f);
|
|
}
|
|
void Eluna::Push(lua_State* L, const bool b)
|
|
{
|
|
lua_pushboolean(L, b);
|
|
}
|
|
void Eluna::Push(lua_State* L, const std::string str)
|
|
{
|
|
lua_pushstring(L, str.c_str());
|
|
}
|
|
void Eluna::Push(lua_State* L, const char* str)
|
|
{
|
|
lua_pushstring(L, str);
|
|
}
|
|
void Eluna::Push(lua_State* L, Pet const* pet)
|
|
{
|
|
Push(L, pet->ToCreature());
|
|
}
|
|
void Eluna::Push(lua_State* L, TempSummon const* summon)
|
|
{
|
|
Push(L, summon->ToCreature());
|
|
}
|
|
void Eluna::Push(lua_State* L, Unit const* unit)
|
|
{
|
|
if (!unit)
|
|
{
|
|
Push(L);
|
|
return;
|
|
}
|
|
switch (unit->GetTypeId())
|
|
{
|
|
case TYPEID_UNIT:
|
|
Push(L, unit->ToCreature());
|
|
break;
|
|
case TYPEID_PLAYER:
|
|
Push(L, unit->ToPlayer());
|
|
break;
|
|
default:
|
|
ElunaTemplate<Unit>::push(L, unit);
|
|
}
|
|
}
|
|
void Eluna::Push(lua_State* L, WorldObject const* obj)
|
|
{
|
|
if (!obj)
|
|
{
|
|
Push(L);
|
|
return;
|
|
}
|
|
switch (obj->GetTypeId())
|
|
{
|
|
case TYPEID_UNIT:
|
|
Push(L, obj->ToCreature());
|
|
break;
|
|
case TYPEID_PLAYER:
|
|
Push(L, obj->ToPlayer());
|
|
break;
|
|
case TYPEID_GAMEOBJECT:
|
|
Push(L, obj->ToGameObject());
|
|
break;
|
|
case TYPEID_CORPSE:
|
|
Push(L, obj->ToCorpse());
|
|
break;
|
|
default:
|
|
ElunaTemplate<WorldObject>::push(L, obj);
|
|
}
|
|
}
|
|
void Eluna::Push(lua_State* L, Object const* obj)
|
|
{
|
|
if (!obj)
|
|
{
|
|
Push(L);
|
|
return;
|
|
}
|
|
switch (obj->GetTypeId())
|
|
{
|
|
case TYPEID_UNIT:
|
|
Push(L, obj->ToCreature());
|
|
break;
|
|
case TYPEID_PLAYER:
|
|
Push(L, obj->ToPlayer());
|
|
break;
|
|
case TYPEID_GAMEOBJECT:
|
|
Push(L, obj->ToGameObject());
|
|
break;
|
|
case TYPEID_CORPSE:
|
|
Push(L, obj->ToCorpse());
|
|
break;
|
|
default:
|
|
ElunaTemplate<Object>::push(L, obj);
|
|
}
|
|
}
|
|
template<> bool Eluna::CHECKVAL<bool>(lua_State* L, int narg)
|
|
{
|
|
return lua_isnumber(L, narg) ? luaL_optnumber(L, narg, 1) ? true : false : lua_toboolean(L, narg);
|
|
}
|
|
template<> bool Eluna::CHECKVAL<bool>(lua_State* L, int narg, bool def)
|
|
{
|
|
return lua_isnone(L, narg) ? def : lua_isnumber(L, narg) ? luaL_optnumber(L, narg, 1) ? true : false : lua_toboolean(L, narg);
|
|
}
|
|
template<> float Eluna::CHECKVAL<float>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checknumber(L, narg);
|
|
}
|
|
template<> float Eluna::CHECKVAL<float>(lua_State* L, int narg, float def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optnumber(L, narg, def);
|
|
}
|
|
template<> double Eluna::CHECKVAL<double>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checknumber(L, narg);
|
|
}
|
|
template<> double Eluna::CHECKVAL<double>(lua_State* L, int narg, double def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optnumber(L, narg, def);
|
|
}
|
|
template<> int8 Eluna::CHECKVAL<int8>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkint(L, narg);
|
|
}
|
|
template<> int8 Eluna::CHECKVAL<int8>(lua_State* L, int narg, int8 def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optint(L, narg, def);
|
|
}
|
|
template<> uint8 Eluna::CHECKVAL<uint8>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkunsigned(L, narg);
|
|
}
|
|
template<> uint8 Eluna::CHECKVAL<uint8>(lua_State* L, int narg, uint8 def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optunsigned(L, narg, def);
|
|
}
|
|
template<> int16 Eluna::CHECKVAL<int16>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkint(L, narg);
|
|
}
|
|
template<> int16 Eluna::CHECKVAL<int16>(lua_State* L, int narg, int16 def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optint(L, narg, def);
|
|
}
|
|
template<> uint16 Eluna::CHECKVAL<uint16>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkunsigned(L, narg);
|
|
}
|
|
template<> uint16 Eluna::CHECKVAL<uint16>(lua_State* L, int narg, uint16 def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optunsigned(L, narg, def);
|
|
}
|
|
template<> uint32 Eluna::CHECKVAL<uint32>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkunsigned(L, narg);
|
|
}
|
|
template<> uint32 Eluna::CHECKVAL<uint32>(lua_State* L, int narg, uint32 def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optunsigned(L, narg, def);
|
|
}
|
|
template<> int32 Eluna::CHECKVAL<int32>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checklong(L, narg);
|
|
}
|
|
template<> int32 Eluna::CHECKVAL<int32>(lua_State* L, int narg, int32 def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isnumber(L, narg))
|
|
return def;
|
|
return luaL_optlong(L, narg, def);
|
|
}
|
|
template<> const char* Eluna::CHECKVAL<const char*>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkstring(L, narg);
|
|
}
|
|
template<> const char* Eluna::CHECKVAL<const char*>(lua_State* L, int narg, const char* def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isstring(L, narg))
|
|
return def;
|
|
return luaL_optstring(L, narg, def);
|
|
}
|
|
template<> std::string Eluna::CHECKVAL<std::string>(lua_State* L, int narg)
|
|
{
|
|
return luaL_checkstring(L, narg);
|
|
}
|
|
template<> std::string Eluna::CHECKVAL<std::string>(lua_State* L, int narg, std::string def)
|
|
{
|
|
if (lua_isnoneornil(L, narg) || !lua_isstring(L, narg))
|
|
return def;
|
|
return luaL_optstring(L, narg, def.c_str());
|
|
}
|
|
template<> uint64 Eluna::CHECKVAL<uint64>(lua_State* L, int narg)
|
|
{
|
|
const char* c_str = CHECKVAL<const char*>(L, narg, NULL);
|
|
if (!c_str)
|
|
return luaL_argerror(L, narg, "uint64 (as string) expected");
|
|
uint64 l = 0;
|
|
sscanf(c_str, UI64FMTD, &l);
|
|
return l;
|
|
}
|
|
template<> uint64 Eluna::CHECKVAL<uint64>(lua_State* L, int narg, uint64 def)
|
|
{
|
|
const char* c_str = CHECKVAL<const char*>(L, narg, NULL);
|
|
if (!c_str)
|
|
return def;
|
|
uint64 l = 0;
|
|
sscanf(c_str, UI64FMTD, &l);
|
|
return l;
|
|
}
|
|
template<> int64 Eluna::CHECKVAL<int64>(lua_State* L, int narg)
|
|
{
|
|
const char* c_str = CHECKVAL<const char*>(L, narg, NULL);
|
|
if (!c_str)
|
|
return luaL_argerror(L, narg, "int64 (as string) expected");
|
|
int64 l = 0;
|
|
sscanf(c_str, SI64FMTD, &l);
|
|
return l;
|
|
}
|
|
template<> int64 Eluna::CHECKVAL<int64>(lua_State* L, int narg, int64 def)
|
|
{
|
|
const char* c_str = CHECKVAL<const char*>(L, narg, NULL);
|
|
if (!c_str)
|
|
return def;
|
|
int64 l = 0;
|
|
sscanf(c_str, SI64FMTD, &l);
|
|
return l;
|
|
}
|
|
#define TEST_OBJ(T, O, E, F)\
|
|
{\
|
|
if (!O || !O->F())\
|
|
{\
|
|
if (E)\
|
|
{\
|
|
std::string errmsg(ElunaTemplate<T>::tname);\
|
|
errmsg += " expected";\
|
|
luaL_argerror(L, narg, errmsg.c_str());\
|
|
}\
|
|
return NULL;\
|
|
}\
|
|
return O->F();\
|
|
}
|
|
template<> Unit* Eluna::CHECKOBJ<Unit>(lua_State* L, int narg, bool error)
|
|
{
|
|
WorldObject* obj = CHECKOBJ<WorldObject>(L, narg, false);
|
|
TEST_OBJ(Unit, obj, error, ToUnit);
|
|
}
|
|
template<> Player* Eluna::CHECKOBJ<Player>(lua_State* L, int narg, bool error)
|
|
{
|
|
WorldObject* obj = CHECKOBJ<WorldObject>(L, narg, false);
|
|
TEST_OBJ(Player, obj, error, ToPlayer);
|
|
}
|
|
template<> Creature* Eluna::CHECKOBJ<Creature>(lua_State* L, int narg, bool error)
|
|
{
|
|
WorldObject* obj = CHECKOBJ<WorldObject>(L, narg, false);
|
|
TEST_OBJ(Creature, obj, error, ToCreature);
|
|
}
|
|
template<> GameObject* Eluna::CHECKOBJ<GameObject>(lua_State* L, int narg, bool error)
|
|
{
|
|
WorldObject* obj = CHECKOBJ<WorldObject>(L, narg, false);
|
|
TEST_OBJ(GameObject, obj, error, ToGameObject);
|
|
}
|
|
template<> Corpse* Eluna::CHECKOBJ<Corpse>(lua_State* L, int narg, bool error)
|
|
{
|
|
WorldObject* obj = CHECKOBJ<WorldObject>(L, narg, false);
|
|
TEST_OBJ(Corpse, obj, error, ToCorpse);
|
|
}
|
|
#undef TEST_OBJ
|
|
|
|
// Saves the function reference ID given to the register type's store for given entry under the given event
|
|
void Eluna::Register(uint8 regtype, uint32 id, uint32 evt, int functionRef)
|
|
{
|
|
switch (regtype)
|
|
{
|
|
case REGTYPE_PACKET:
|
|
if (evt < NUM_MSG_TYPES)
|
|
{
|
|
PacketEventBindings.Insert(evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_SERVER:
|
|
if (evt < SERVER_EVENT_COUNT)
|
|
{
|
|
ServerEventBindings.Insert(evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_PLAYER:
|
|
if (evt < PLAYER_EVENT_COUNT)
|
|
{
|
|
PlayerEventBindings.Insert(evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_GUILD:
|
|
if (evt < GUILD_EVENT_COUNT)
|
|
{
|
|
GuildEventBindings.Insert(evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_GROUP:
|
|
if (evt < GROUP_EVENT_COUNT)
|
|
{
|
|
GroupEventBindings.Insert(evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_VEHICLE:
|
|
if (evt < VEHICLE_EVENT_COUNT)
|
|
{
|
|
VehicleEventBindings.Insert(evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_CREATURE:
|
|
if (evt < CREATURE_EVENT_COUNT)
|
|
{
|
|
if (!sObjectMgr->GetCreatureTemplate(id))
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id);
|
|
return;
|
|
}
|
|
|
|
sEluna->CreatureEventBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_CREATURE_GOSSIP:
|
|
if (evt < GOSSIP_EVENT_COUNT)
|
|
{
|
|
if (!sObjectMgr->GetCreatureTemplate(id))
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Couldn't find a creature with (ID: %d)!", id);
|
|
return;
|
|
}
|
|
|
|
sEluna->CreatureGossipBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_GAMEOBJECT:
|
|
if (evt < GAMEOBJECT_EVENT_COUNT)
|
|
{
|
|
if (!sObjectMgr->GetGameObjectTemplate(id))
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", id);
|
|
return;
|
|
}
|
|
|
|
sEluna->GameObjectEventBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_GAMEOBJECT_GOSSIP:
|
|
if (evt < GOSSIP_EVENT_COUNT)
|
|
{
|
|
if (!sObjectMgr->GetGameObjectTemplate(id))
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", id);
|
|
return;
|
|
}
|
|
|
|
sEluna->GameObjectGossipBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_ITEM:
|
|
if (evt < ITEM_EVENT_COUNT)
|
|
{
|
|
if (!sObjectMgr->GetItemTemplate(id))
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Couldn't find a item with (ID: %d)!", id);
|
|
return;
|
|
}
|
|
|
|
sEluna->ItemEventBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_ITEM_GOSSIP:
|
|
if (evt < GOSSIP_EVENT_COUNT)
|
|
{
|
|
if (!sObjectMgr->GetItemTemplate(id))
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Couldn't find a item with (ID: %d)!", id);
|
|
return;
|
|
}
|
|
|
|
sEluna->ItemGossipBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case REGTYPE_PLAYER_GOSSIP:
|
|
if (evt < GOSSIP_EVENT_COUNT)
|
|
{
|
|
sEluna->playerGossipBindings.Insert(id, evt, functionRef);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, functionRef);
|
|
luaL_error(L, "Unknown event type (regtype %d, id %d, event %d)", regtype, id, evt);
|
|
}
|
|
|
|
void Eluna::EventBind::Clear()
|
|
{
|
|
for (ElunaEntryMap::iterator itr = Bindings.begin(); itr != Bindings.end(); ++itr)
|
|
{
|
|
for (ElunaBindingMap::iterator it = itr->second.begin(); it != itr->second.end(); ++it)
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, (*it));
|
|
itr->second.clear();
|
|
}
|
|
Bindings.clear();
|
|
}
|
|
|
|
void Eluna::EventBind::Insert(int eventId, int funcRef)
|
|
{
|
|
Bindings[eventId].push_back(funcRef);
|
|
}
|
|
|
|
bool Eluna::EventBind::HasEvents(int eventId) const
|
|
{
|
|
if (Bindings.empty())
|
|
return false;
|
|
if (Bindings.find(eventId) == Bindings.end())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void Eluna::EventBind::BeginCall(int eventId) const
|
|
{
|
|
lua_settop(sEluna->L, 0); // stack should be empty
|
|
sEluna->Push(sEluna->L, eventId);
|
|
}
|
|
|
|
void Eluna::EventBind::ExecuteCall()
|
|
{
|
|
int eventId = sEluna->CHECKVAL<int>(sEluna->L, 1);
|
|
int params = lua_gettop(sEluna->L);
|
|
for (ElunaBindingMap::const_iterator it = Bindings[eventId].begin(); it != Bindings[eventId].end(); ++it)
|
|
{
|
|
lua_rawgeti(sEluna->L, LUA_REGISTRYINDEX, (*it)); // Fetch function
|
|
for (int i = 1; i <= params; ++i) // Copy original pushed params
|
|
lua_pushvalue(sEluna->L, i);
|
|
sEluna->ExecuteCall(params, LUA_MULTRET); // Do call and leave results to stack
|
|
}
|
|
for (int i = params; i > 0; --i) // Remove original pushed params
|
|
if (!lua_isnone(sEluna->L, i))
|
|
lua_remove(sEluna->L, i);
|
|
// Results in stack, otherwise stack clean
|
|
}
|
|
|
|
void Eluna::EventBind::EndCall() const
|
|
{
|
|
lua_settop(sEluna->L, 0); // stack should be empty
|
|
};
|
|
|
|
void Eluna::EntryBind::Clear()
|
|
{
|
|
for (ElunaEntryMap::iterator itr = Bindings.begin(); itr != Bindings.end(); ++itr)
|
|
{
|
|
for (ElunaBindingMap::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it)
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, it->second);
|
|
itr->second.clear();
|
|
}
|
|
Bindings.clear();
|
|
}
|
|
|
|
void Eluna::EntryBind::Insert(uint32 entryId, int eventId, int funcRef)
|
|
{
|
|
if (Bindings[entryId][eventId])
|
|
{
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, funcRef); // free the unused ref
|
|
luaL_error(sEluna->L, "A function is already registered for entry (%d) event (%d)", entryId, eventId);
|
|
}
|
|
else
|
|
Bindings[entryId][eventId] = funcRef;
|
|
}
|
|
|
|
EventMgr::LuaEvent::LuaEvent(EventProcessor* _events, int _funcRef, uint32 _delay, uint32 _calls, Object* _obj):
|
|
events(_events), funcRef(_funcRef), delay(_delay), calls(_calls), obj(_obj)
|
|
{
|
|
if (_events)
|
|
sEluna->m_EventMgr.LuaEvents[_events].insert(this); // Able to access the event if we have the processor
|
|
}
|
|
|
|
EventMgr::LuaEvent::~LuaEvent()
|
|
{
|
|
if (events)
|
|
{
|
|
// Attempt to remove the pointer from LuaEvents
|
|
EventMgr::EventMap::const_iterator it = sEluna->m_EventMgr.LuaEvents.find(events); // Get event set
|
|
if (it != sEluna->m_EventMgr.LuaEvents.end())
|
|
sEluna->m_EventMgr.LuaEvents[events].erase(this);// Remove pointer
|
|
}
|
|
luaL_unref(sEluna->L, LUA_REGISTRYINDEX, funcRef); // Free lua function ref
|
|
}
|
|
|
|
bool EventMgr::LuaEvent::Execute(uint64 time, uint32 diff)
|
|
{
|
|
ELUNA_GUARD();
|
|
bool remove = (calls == 1);
|
|
if (!remove)
|
|
events->AddEvent(this, events->CalculateTime(delay)); // Reschedule before calling incase RemoveEvents used
|
|
sEluna->BeginCall(funcRef);
|
|
sEluna->Push(sEluna->L, funcRef);
|
|
sEluna->Push(sEluna->L, delay);
|
|
sEluna->Push(sEluna->L, calls);
|
|
if (!remove && calls)
|
|
--calls;
|
|
sEluna->Push(sEluna->L, obj);
|
|
sEluna->ExecuteCall(4, 0);
|
|
return remove; // Destory (true) event if not run
|
|
}
|
|
|
|
// Lua taxi helper functions
|
|
uint32 LuaTaxiMgr::nodeId = 500;
|
|
void LuaTaxiMgr::StartTaxi(Player* player, uint32 pathid)
|
|
{
|
|
if (pathid >= sTaxiPathNodesByPath.size())
|
|
return;
|
|
|
|
TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathid];
|
|
if (path.size() < 2)
|
|
return;
|
|
|
|
std::vector<uint32> nodes;
|
|
nodes.resize(2);
|
|
nodes[0] = path[0].index;
|
|
nodes[1] = path[path.size() - 1].index;
|
|
|
|
player->ActivateTaxiPathTo(nodes);
|
|
}
|
|
|
|
uint32 LuaTaxiMgr::AddPath(std::list<TaxiPathNodeEntry> nodes, uint32 mountA, uint32 mountH, uint32 price, uint32 pathId)
|
|
{
|
|
if (nodes.size() < 2)
|
|
return 0;
|
|
if (!pathId)
|
|
pathId = sTaxiPathNodesByPath.size();
|
|
if (sTaxiPathNodesByPath.size() <= pathId)
|
|
sTaxiPathNodesByPath.resize(pathId + 1);
|
|
sTaxiPathNodesByPath[pathId].clear();
|
|
sTaxiPathNodesByPath[pathId].resize(nodes.size());
|
|
uint32 startNode = nodeId;
|
|
uint32 index = 0;
|
|
for (std::list<TaxiPathNodeEntry>::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
|
|
{
|
|
TaxiPathNodeEntry entry = *it;
|
|
entry.path = pathId;
|
|
TaxiNodesEntry* nodeEntry = new TaxiNodesEntry();
|
|
nodeEntry->ID = index;
|
|
nodeEntry->map_id = entry.mapid;
|
|
nodeEntry->MountCreatureID[0] = mountH;
|
|
nodeEntry->MountCreatureID[1] = mountA;
|
|
nodeEntry->x = entry.x;
|
|
nodeEntry->y = entry.y;
|
|
nodeEntry->z = entry.z;
|
|
sTaxiNodesStore.SetEntry(nodeId, nodeEntry);
|
|
entry.index = nodeId++;
|
|
sTaxiPathNodesByPath[pathId].set(index++, TaxiPathNodePtr(new TaxiPathNodeEntry(entry)));
|
|
}
|
|
if (startNode >= nodeId)
|
|
return 0;
|
|
sTaxiPathSetBySource[startNode][nodeId - 1] = TaxiPathBySourceAndDestination(pathId, price);
|
|
return pathId;
|
|
}
|