Add StackTracePlus, Rewrite not to recreate eluna on reload

This commit is contained in:
Rochet2
2015-01-14 01:03:16 +02:00
parent e8d840ace9
commit 3d645d8048
13 changed files with 924 additions and 198 deletions

View File

@@ -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)