/*
* Copyright (C) 2010 - 2014 Eluna Lua Engine
* This program is free software licensed under GPL version 3
* Please see the included DOCS/LICENSE.md for more information
*/
#include
#include
#include "HookMgr.h"
#include "LuaEngine.h"
#include "Includes.h"
Eluna::ScriptPaths Eluna::scripts;
Eluna* Eluna::GEluna = NULL;
bool Eluna::reload = false;
extern void RegisterFunctions(lua_State* L);
void Eluna::Initialize()
{
uint32 oldMSTime = GetCurrTime();
scripts.clear();
std::string folderpath = eConfigMgr->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());
GetScripts(folderpath, scripts);
GetScripts(folderpath + "/extensions", scripts);
ELUNA_LOG_INFO("[Eluna]: Loaded %u scripts in %u ms", uint32(scripts.size()), GetTimeDiff(oldMSTime));
// Create global eluna
new Eluna();
}
void Eluna::Uninitialize()
{
delete GEluna;
scripts.clear();
}
void Eluna::ReloadEluna()
{
eWorld->SendServerMessage(SERVER_MSG_STRING, "Reloading Eluna...");
Uninitialize();
Initialize();
reload = false;
}
Eluna::Eluna():
L(luaL_newstate()),
m_EventMgr(new EventMgr(*this)),
ServerEventBindings(new EventBind("ServerEvents", *this)),
PlayerEventBindings(new EventBind("PlayerEvents", *this)),
GuildEventBindings(new EventBind("GuildEvents", *this)),
GroupEventBindings(new EventBind("GroupEvents", *this)),
VehicleEventBindings(new EventBind("VehicleEvents", *this)),
PacketEventBindings(new EntryBind("PacketEvents", *this)),
CreatureEventBindings(new EntryBind("CreatureEvents", *this)),
CreatureGossipBindings(new EntryBind("GossipEvents (creature)", *this)),
GameObjectEventBindings(new EntryBind("GameObjectEvents", *this)),
GameObjectGossipBindings(new EntryBind("GossipEvents (gameobject)", *this)),
ItemEventBindings(new EntryBind("ItemEvents", *this)),
ItemGossipBindings(new EntryBind("GossipEvents (item)", *this)),
playerGossipBindings(new EntryBind("GossipEvents (player)", *this))
{
// open base lua
luaL_openlibs(L);
RegisterFunctions(L);
// Create hidden table with weak values
lua_newtable(L);
lua_newtable(L);
lua_pushstring(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
userdata_table = luaL_ref(L, LUA_REGISTRYINDEX);
// Replace this with map insert if making multithread version
ASSERT(!Eluna::GEluna);
Eluna::GEluna = this;
// run scripts
RunScripts(scripts);
}
Eluna::~Eluna()
{
OnLuaStateClose();
// Replace this with map remove if making multithread version
Eluna::GEluna = NULL;
delete m_EventMgr;
delete ServerEventBindings;
delete PlayerEventBindings;
delete GuildEventBindings;
delete GroupEventBindings;
delete VehicleEventBindings;
delete PacketEventBindings;
delete CreatureEventBindings;
delete CreatureGossipBindings;
delete GameObjectEventBindings;
delete GameObjectGossipBindings;
delete ItemEventBindings;
delete ItemGossipBindings;
delete playerGossipBindings;
// Must close lua state after deleting stores and mgr
lua_close(L);
}
// 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_DEBUG("[Eluna]: Loaded %u Lua scripts", count);
}
void Eluna::RemoveRef(const void* obj)
{
lua_rawgeti(sEluna->L, LUA_REGISTRYINDEX, sEluna->userdata_table);
lua_pushfstring(sEluna->L, "%p", obj);
lua_gettable(sEluna->L, -2);
if (!lua_isnoneornil(sEluna->L, -1))
{
lua_pushfstring(sEluna->L, "%p", obj);
lua_pushnil(sEluna->L);
lua_settable(sEluna->L, -4);
}
lua_pop(sEluna->L, 2);
}
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::ExecuteCall(lua_State* L, int params, int res)
{
int top = lua_gettop(L);
luaL_checktype(L, top - params, LUA_TFUNCTION);
if (lua_pcall(L, params, res, 0))
report(L);
}
void Eluna::Push(lua_State* L)
{
lua_pushnil(L);
}
void Eluna::Push(lua_State* L, const uint64 l)
{
std::ostringstream ss;
ss << l;
Push(L, ss.str());
}
void Eluna::Push(lua_State* L, const int64 l)
{
std::ostringstream ss;
ss << l;
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::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::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