Add StackTracePlus, Rewrite not to recreate eluna on reload
This commit is contained in:
305
LuaEngine.cpp
305
LuaEngine.cpp
@@ -43,8 +43,35 @@ extern void RegisterFunctions(Eluna* E);
|
||||
|
||||
void Eluna::Initialize()
|
||||
{
|
||||
ASSERT(!initialized);
|
||||
LOCK_ELUNA;
|
||||
ASSERT(!IsInitialized());
|
||||
|
||||
LoadScriptPaths();
|
||||
|
||||
// Must be before creating GEluna
|
||||
// This is checked on Eluna creation
|
||||
initialized = true;
|
||||
|
||||
// Create global eluna
|
||||
GEluna = new Eluna();
|
||||
}
|
||||
|
||||
void Eluna::Uninitialize()
|
||||
{
|
||||
LOCK_ELUNA;
|
||||
ASSERT(IsInitialized());
|
||||
|
||||
delete GEluna;
|
||||
GEluna = NULL;
|
||||
|
||||
lua_scripts.clear();
|
||||
lua_extensions.clear();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void Eluna::LoadScriptPaths()
|
||||
{
|
||||
uint32 oldMSTime = ElunaUtil::GetCurrTime();
|
||||
|
||||
lua_scripts.clear();
|
||||
@@ -64,43 +91,28 @@ void Eluna::Initialize()
|
||||
lua_requirepath.erase(lua_requirepath.end() - 1);
|
||||
|
||||
ELUNA_LOG_DEBUG("[Eluna]: Loaded %u scripts in %u ms", uint32(lua_scripts.size() + lua_extensions.size()), ElunaUtil::GetTimeDiff(oldMSTime));
|
||||
|
||||
initialized = true;
|
||||
|
||||
// Create global eluna
|
||||
GEluna = new Eluna();
|
||||
}
|
||||
|
||||
void Eluna::Uninitialize()
|
||||
void Eluna::_ReloadEluna()
|
||||
{
|
||||
ASSERT(initialized);
|
||||
LOCK_ELUNA;
|
||||
ASSERT(IsInitialized());
|
||||
|
||||
delete GEluna;
|
||||
GEluna = NULL;
|
||||
|
||||
lua_scripts.clear();
|
||||
lua_extensions.clear();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void Eluna::ReloadEluna()
|
||||
{
|
||||
eWorld->SendServerMessage(SERVER_MSG_STRING, "Reloading Eluna...");
|
||||
|
||||
EventMgr::ProcessorSet oldProcessors;
|
||||
{
|
||||
EventMgr::ReadGuard guard(sEluna->eventMgr->GetLock());
|
||||
oldProcessors = sEluna->eventMgr->processors;
|
||||
}
|
||||
Uninitialize();
|
||||
Initialize();
|
||||
{
|
||||
EventMgr::WriteGuard guard(sEluna->eventMgr->GetLock());
|
||||
sEluna->eventMgr->processors.insert(oldProcessors.begin(), oldProcessors.end());
|
||||
}
|
||||
// Remove all timed events
|
||||
sEluna->eventMgr->RemoveEvents();
|
||||
|
||||
// in multithread foreach: run scripts
|
||||
// Close lua
|
||||
sEluna->CloseLua();
|
||||
|
||||
// Reload script paths
|
||||
LoadScriptPaths();
|
||||
|
||||
// Open new lua and libaraies
|
||||
sEluna->OpenLua();
|
||||
|
||||
// Run scripts from laoded paths
|
||||
sEluna->RunScripts();
|
||||
|
||||
#ifdef TRINITY
|
||||
@@ -117,31 +129,76 @@ void Eluna::ReloadEluna()
|
||||
}
|
||||
|
||||
Eluna::Eluna() :
|
||||
L(luaL_newstate()),
|
||||
|
||||
event_level(0),
|
||||
push_counter(0),
|
||||
enabled(false),
|
||||
|
||||
L(NULL),
|
||||
eventMgr(NULL),
|
||||
|
||||
ServerEventBindings(new EventBind<Hooks::ServerEvents>("ServerEvents", *this)),
|
||||
PlayerEventBindings(new EventBind<Hooks::PlayerEvents>("PlayerEvents", *this)),
|
||||
GuildEventBindings(new EventBind<Hooks::GuildEvents>("GuildEvents", *this)),
|
||||
GroupEventBindings(new EventBind<Hooks::GroupEvents>("GroupEvents", *this)),
|
||||
VehicleEventBindings(new EventBind<Hooks::VehicleEvents>("VehicleEvents", *this)),
|
||||
BGEventBindings(new EventBind<Hooks::BGEvents>("BGEvents", *this)),
|
||||
ServerEventBindings(NULL),
|
||||
PlayerEventBindings(NULL),
|
||||
GuildEventBindings(NULL),
|
||||
GroupEventBindings(NULL),
|
||||
VehicleEventBindings(NULL),
|
||||
BGEventBindings(NULL),
|
||||
|
||||
PacketEventBindings(new EntryBind<Hooks::PacketEvents>("PacketEvents", *this)),
|
||||
CreatureEventBindings(new EntryBind<Hooks::CreatureEvents>("CreatureEvents", *this)),
|
||||
CreatureGossipBindings(new EntryBind<Hooks::GossipEvents>("GossipEvents (creature)", *this)),
|
||||
GameObjectEventBindings(new EntryBind<Hooks::GameObjectEvents>("GameObjectEvents", *this)),
|
||||
GameObjectGossipBindings(new EntryBind<Hooks::GossipEvents>("GossipEvents (gameobject)", *this)),
|
||||
ItemEventBindings(new EntryBind<Hooks::ItemEvents>("ItemEvents", *this)),
|
||||
ItemGossipBindings(new EntryBind<Hooks::GossipEvents>("GossipEvents (item)", *this)),
|
||||
playerGossipBindings(new EntryBind<Hooks::GossipEvents>("GossipEvents (player)", *this)),
|
||||
PacketEventBindings(NULL),
|
||||
CreatureEventBindings(NULL),
|
||||
CreatureGossipBindings(NULL),
|
||||
GameObjectEventBindings(NULL),
|
||||
GameObjectGossipBindings(NULL),
|
||||
ItemEventBindings(NULL),
|
||||
ItemGossipBindings(NULL),
|
||||
playerGossipBindings(NULL),
|
||||
|
||||
CreatureUniqueBindings(new UniqueBind<Hooks::CreatureEvents>("CreatureEvents", *this))
|
||||
CreatureUniqueBindings(NULL)
|
||||
{
|
||||
ASSERT(IsInitialized());
|
||||
|
||||
OpenLua();
|
||||
|
||||
// Replace this with map insert if making multithread version
|
||||
//
|
||||
|
||||
// Set event manager. Must be after setting sEluna
|
||||
// on multithread have a map of state pointers and here insert this pointer to the map and then save a pointer of that pointer to the EventMgr
|
||||
eventMgr = new EventMgr(&Eluna::GEluna);
|
||||
}
|
||||
|
||||
Eluna::~Eluna()
|
||||
{
|
||||
CloseLua();
|
||||
|
||||
delete eventMgr;
|
||||
eventMgr = NULL;
|
||||
}
|
||||
|
||||
void Eluna::CloseLua()
|
||||
{
|
||||
OnLuaStateClose();
|
||||
|
||||
DestroyBindStores();
|
||||
|
||||
// Must close lua state after deleting stores and mgr
|
||||
if (L)
|
||||
lua_close(L);
|
||||
L = NULL;
|
||||
}
|
||||
|
||||
void Eluna::OpenLua()
|
||||
{
|
||||
CreateBindStores();
|
||||
|
||||
enabled = eConfigMgr->GetBoolDefault("Eluna.Enabled", true);
|
||||
if (!IsEnabled())
|
||||
{
|
||||
ELUNA_LOG_INFO("[Eluna]: Eluna is disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
L = luaL_newstate();
|
||||
|
||||
// open base lua libraries
|
||||
luaL_openlibs(L);
|
||||
|
||||
@@ -165,25 +222,33 @@ CreatureUniqueBindings(new UniqueBind<Hooks::CreatureEvents>("CreatureEvents", *
|
||||
lua_pushstring(L, ""); // erase cpath
|
||||
lua_setfield(L, -2, "cpath");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Replace this with map insert if making multithread version
|
||||
//
|
||||
|
||||
// Set event manager. Must be after setting sEluna
|
||||
// on multithread have a map of state pointers and here insert this pointer to the map and then save a pointer of that pointer to the EventMgr
|
||||
eventMgr = new EventMgr(&Eluna::GEluna);
|
||||
}
|
||||
|
||||
Eluna::~Eluna()
|
||||
void Eluna::CreateBindStores()
|
||||
{
|
||||
OnLuaStateClose();
|
||||
DestroyBindStores();
|
||||
|
||||
delete eventMgr;
|
||||
eventMgr = NULL;
|
||||
ServerEventBindings = new EventBind<Hooks::ServerEvents>("ServerEvents", *this);
|
||||
PlayerEventBindings = new EventBind<Hooks::PlayerEvents>("PlayerEvents", *this);
|
||||
GuildEventBindings = new EventBind<Hooks::GuildEvents>("GuildEvents", *this);
|
||||
GroupEventBindings = new EventBind<Hooks::GroupEvents>("GroupEvents", *this);
|
||||
VehicleEventBindings = new EventBind<Hooks::VehicleEvents>("VehicleEvents", *this);
|
||||
BGEventBindings = new EventBind<Hooks::BGEvents>("BGEvents", *this);
|
||||
|
||||
// Replace this with map remove if making multithread version
|
||||
//
|
||||
PacketEventBindings = new EntryBind<Hooks::PacketEvents>("PacketEvents", *this);
|
||||
CreatureEventBindings = new EntryBind<Hooks::CreatureEvents>("CreatureEvents", *this);
|
||||
CreatureGossipBindings = new EntryBind<Hooks::GossipEvents>("GossipEvents (creature)", *this);
|
||||
GameObjectEventBindings = new EntryBind<Hooks::GameObjectEvents>("GameObjectEvents", *this);
|
||||
GameObjectGossipBindings = new EntryBind<Hooks::GossipEvents>("GossipEvents (gameobject)", *this);
|
||||
ItemEventBindings = new EntryBind<Hooks::ItemEvents>("ItemEvents", *this);
|
||||
ItemGossipBindings = new EntryBind<Hooks::GossipEvents>("GossipEvents (item)", *this);
|
||||
playerGossipBindings = new EntryBind<Hooks::GossipEvents>("GossipEvents (player)", *this);
|
||||
|
||||
CreatureUniqueBindings = new UniqueBind<Hooks::CreatureEvents>("CreatureEvents (unique)", *this);
|
||||
}
|
||||
|
||||
void Eluna::DestroyBindStores()
|
||||
{
|
||||
delete ServerEventBindings;
|
||||
delete PlayerEventBindings;
|
||||
delete GuildEventBindings;
|
||||
@@ -200,6 +265,8 @@ Eluna::~Eluna()
|
||||
delete playerGossipBindings;
|
||||
delete BGEventBindings;
|
||||
|
||||
delete CreatureUniqueBindings;
|
||||
|
||||
ServerEventBindings = NULL;
|
||||
PlayerEventBindings = NULL;
|
||||
GuildEventBindings = NULL;
|
||||
@@ -216,8 +283,7 @@ Eluna::~Eluna()
|
||||
playerGossipBindings = NULL;
|
||||
BGEventBindings = NULL;
|
||||
|
||||
// Must close lua state after deleting stores and mgr
|
||||
lua_close(L);
|
||||
CreatureUniqueBindings = NULL;
|
||||
}
|
||||
|
||||
void Eluna::AddScriptPath(std::string filename, const std::string& fullpath)
|
||||
@@ -350,11 +416,15 @@ void Eluna::GetScripts(std::string path)
|
||||
|
||||
static bool ScriptPathComparator(const LuaScript& first, const LuaScript& second)
|
||||
{
|
||||
return first.filepath.compare(second.filepath) < 0;
|
||||
return first.filepath < second.filepath;
|
||||
}
|
||||
|
||||
void Eluna::RunScripts()
|
||||
{
|
||||
LOCK_ELUNA;
|
||||
if (!IsEnabled())
|
||||
return;
|
||||
|
||||
uint32 oldMSTime = ElunaUtil::GetCurrTime();
|
||||
uint32 count = 0;
|
||||
|
||||
@@ -367,7 +437,9 @@ void Eluna::RunScripts()
|
||||
UNORDERED_MAP<std::string, std::string> loaded; // filename, path
|
||||
|
||||
lua_getglobal(L, "package");
|
||||
// Stack: package
|
||||
luaL_getsubtable(L, -1, "loaded");
|
||||
// Stack: package, modules
|
||||
int modules = lua_gettop(L);
|
||||
for (ScriptList::const_iterator it = scripts.begin(); it != scripts.end(); ++it)
|
||||
{
|
||||
@@ -380,6 +452,7 @@ void Eluna::RunScripts()
|
||||
loaded[it->filename] = it->filepath;
|
||||
|
||||
lua_getfield(L, modules, it->filename.c_str());
|
||||
// Stack: package, modules, module
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
@@ -387,25 +460,38 @@ void Eluna::RunScripts()
|
||||
continue;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
if (!luaL_loadfile(L, it->filepath.c_str()) && !lua_pcall(L, 0, 1, 0))
|
||||
// Stack: package, modules
|
||||
|
||||
if (luaL_loadfile(L, it->filepath.c_str()))
|
||||
{
|
||||
// Stack: package, modules, errmsg
|
||||
ELUNA_LOG_ERROR("[Eluna]: Error loading `%s`", it->filepath.c_str());
|
||||
Report(L);
|
||||
// Stack: package, modules
|
||||
continue;
|
||||
}
|
||||
// Stack: package, modules, filefunc
|
||||
|
||||
if (ExecuteCall(0, 1))
|
||||
{
|
||||
// Stack: package, modules, result
|
||||
if (lua_isnoneornil(L, -1) || (lua_isboolean(L, -1) && !lua_toboolean(L, -1)))
|
||||
{
|
||||
// if result evaluates to false, change it to true
|
||||
lua_pop(L, 1);
|
||||
Push(L, true);
|
||||
}
|
||||
lua_setfield(L, modules, it->filename.c_str());
|
||||
// Stack: package, modules
|
||||
|
||||
// successfully loaded and ran file
|
||||
ELUNA_LOG_DEBUG("[Eluna]: Successfully loaded `%s`", it->filepath.c_str());
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
ELUNA_LOG_ERROR("[Eluna]: Error loading `%s`", it->filepath.c_str());
|
||||
report(L);
|
||||
}
|
||||
// Stack: package, modules
|
||||
lua_pop(L, 2);
|
||||
|
||||
ELUNA_LOG_INFO("[Eluna]: Executed %u Lua scripts in %u ms", count, ElunaUtil::GetTimeDiff(oldMSTime));
|
||||
|
||||
OnLuaStateOpen();
|
||||
@@ -426,41 +512,98 @@ void Eluna::InvalidateObjects()
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void Eluna::report(lua_State* luastate)
|
||||
void Eluna::Report(lua_State* _L)
|
||||
{
|
||||
const char* msg = lua_tostring(luastate, -1);
|
||||
const char* msg = lua_tostring(_L, -1);
|
||||
ELUNA_LOG_ERROR("%s", msg);
|
||||
lua_pop(luastate, 1);
|
||||
lua_pop(_L, 1);
|
||||
}
|
||||
|
||||
void Eluna::ExecuteCall(int params, int res)
|
||||
// Borrowed from http://stackoverflow.com/questions/12256455/print-stacktrace-from-c-code-with-embedded-lua
|
||||
int Eluna::StackTrace(lua_State *_L)
|
||||
{
|
||||
// Stack: errmsg
|
||||
if (!lua_isstring(_L, -1)) /* 'message' not a string? */
|
||||
return 1; /* keep it intact */
|
||||
// Stack: errmsg, debug
|
||||
lua_getglobal(_L, "debug");
|
||||
if (!lua_istable(_L, -1))
|
||||
{
|
||||
lua_pop(_L, 1);
|
||||
return 1;
|
||||
}
|
||||
// Stack: errmsg, debug, traceback
|
||||
lua_getfield(_L, -1, "traceback");
|
||||
if (!lua_isfunction(_L, -1))
|
||||
{
|
||||
lua_pop(_L, 2);
|
||||
return 1;
|
||||
}
|
||||
lua_pushvalue(_L, -3); /* pass error message */
|
||||
lua_pushinteger(_L, 1); /* skip this function and traceback */
|
||||
// Stack: errmsg, debug, traceback, errmsg, 2
|
||||
lua_call(_L, 2, 1); /* call debug.traceback */
|
||||
|
||||
// dirty stack?
|
||||
// Stack: errmsg, debug, tracemsg
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Eluna::ExecuteCall(int params, int res)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
int base = top - params;
|
||||
|
||||
// Expected: function, [parameters]
|
||||
ASSERT(top > params);
|
||||
ASSERT(base > 0);
|
||||
|
||||
// Check function type
|
||||
int type = lua_type(L, top - params);
|
||||
if (type != LUA_TFUNCTION)
|
||||
if (!lua_isfunction(L, base))
|
||||
{
|
||||
ELUNA_LOG_ERROR("[Eluna]: Cannot execute call: registered value is %s, not a function.", lua_typename(L, type));
|
||||
ASSERT(false);
|
||||
ELUNA_LOG_ERROR("[Eluna]: Cannot execute call: registered value is %s, not a function.", luaL_tolstring(L, base, NULL));
|
||||
ASSERT(false); // stack probably corrupt
|
||||
}
|
||||
|
||||
// Objects are invalidated when event level hits 0
|
||||
bool usetrace = eConfigMgr->GetBoolDefault("Eluna.TraceBack", true);
|
||||
if (usetrace)
|
||||
{
|
||||
lua_pushcfunction(L, &StackTrace);
|
||||
// Stack: function, [parameters], traceback
|
||||
lua_insert(L, base);
|
||||
// Stack: traceback, function, [parameters]
|
||||
}
|
||||
|
||||
// Objects are invalidated when event_level hits 0
|
||||
++event_level;
|
||||
int result = lua_pcall(L, params, res, 0);
|
||||
int result = lua_pcall(L, params, res, usetrace ? base : 0);
|
||||
--event_level;
|
||||
|
||||
if (usetrace)
|
||||
{
|
||||
// Stack: traceback, [results or errmsg]
|
||||
lua_remove(L, base);
|
||||
}
|
||||
// Stack: [results or errmsg]
|
||||
|
||||
// lua_pcall returns 0 on success.
|
||||
// On error we report errors and push nils for expected amount of returned values
|
||||
// On error print the error and push nils for expected amount of returned values
|
||||
if (result)
|
||||
{
|
||||
report(L);
|
||||
// Stack: errmsg
|
||||
Report(L);
|
||||
|
||||
// Force garbage collect
|
||||
lua_gc(L, LUA_GCCOLLECT, 0);
|
||||
|
||||
// Push nils for expected amount of results
|
||||
for (int i = 0; i < res; ++i)
|
||||
lua_pushnil(L);
|
||||
// Stack: [nils]
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stack: [results]
|
||||
return true;
|
||||
}
|
||||
|
||||
void Eluna::Push(lua_State* luastate)
|
||||
|
||||
Reference in New Issue
Block a user