bool Eluna::ExecuteCall(int params, int res) { int top = lua_gettop(L); int base = top - params; // Expected: function, [parameters] ASSERT(base > 0); // Check function type if (!lua_isfunction(L, base)) { ELUNA_LOG_ERROR("[Eluna]: Cannot execute call: registered value is %s, not a function.", luaL_tolstring(L, base, NULL)); ASSERT(false); // stack probably corrupt } bool usetrace = eConfigMgr->GetBoolDefault("Eluna.TraceBack", false); 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, 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 print the error and push nils for expected amount of returned values if (result) { // 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; }
// Finds lua script files from given path (including subdirectories) and pushes them to scripts void Eluna::GetScripts(std::string path, ScriptList& 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()); // split file name std::string filename = directory->d_name; uint32 extDot = filename.find_last_of('.'); if (extDot == std::string::npos) continue; std::string ext = filename.substr(extDot); filename = filename.substr(0, extDot); // check extension and add path to scripts to load if (ext != ".lua" && ext != ".dll") continue; LuaScript script; script.fileext = ext; script.filename = filename; script.filepath = fullpath; script.modulepath = fullpath.substr(0, fullpath.length() - ext.length()); scripts.push_back(script); ELUNA_LOG_DEBUG("[Eluna]: GetScripts add path `%s`", fullpath.c_str()); } }
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::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); }
// 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() { uint32 oldMSTime = GetCurrTime(); uint32 count = 0; ScriptList scripts; scripts.insert(scripts.end(), lua_extensions.begin(), lua_extensions.end()); scripts.insert(scripts.end(), lua_scripts.begin(), lua_scripts.end()); lua_getglobal(L, "package"); luaL_getsubtable(L, -1, "loaded"); int modules = lua_gettop(L); for (ScriptList::const_iterator it = scripts.begin(); it != scripts.end(); ++it) { lua_getfield(L, modules, it->modulepath.c_str()); if (!lua_isnoneornil(L, -1)) { lua_pop(L, 1); ELUNA_LOG_DEBUG("[Eluna]: Extension was already loaded or required `%s`", it->filepath.c_str()); continue; } lua_pop(L, 1); if (!luaL_loadfile(L, it->filepath.c_str()) && !lua_pcall(L, 0, 1, 0)) { if (!lua_toboolean(L, -1)) { lua_pop(L, 1); Push(L, true); } lua_setfield(L, modules, it->modulepath.c_str()); // successfully loaded and ran file ELUNA_LOG_DEBUG("[Eluna]: Successfully loaded `%s`", it->filepath.c_str()); ++count; continue; } ELUNA_LOG_ERROR("[Eluna]: Error loading extension `%s`", it->filepath.c_str()); report(L); } lua_pop(L, 2); ELUNA_LOG_INFO("[Eluna]: Executed %u Lua scripts in %u ms", count, GetTimeDiff(oldMSTime)); }
const char* ElunaInstanceAI::Save() const { lua_State* L = instance->GetEluna()->L; // Stack: (empty) /* * Need to cheat because this method actually does modify this instance, * even though it's declared as `const`. * * Declaring virtual methods as `const` is BAD! * Don't dictate to children that their methods must be pure. */ ElunaInstanceAI* self = const_cast<ElunaInstanceAI*>(this); lua_pushcfunction(L, mar_encode); instance->GetEluna()->PushInstanceData(L, self, false); // Stack: mar_encode, instance_data if (lua_pcall(L, 1, 1, 0) != 0) { // Stack: error_message ELUNA_LOG_ERROR("Error while saving: %s", lua_tostring(L, -1)); lua_pop(L, 1); return NULL; } // Stack: data size_t dataLength; const unsigned char* data = (const unsigned char*)lua_tolstring(L, -1, &dataLength); ElunaUtil::EncodeData(data, dataLength, self->lastSaveData); lua_pop(L, 1); // Stack: (empty) return lastSaveData.c_str(); }
void ElunaInstanceAI::Load(const char* data) { // If we get passed NULL (i.e. `Reload` was called) then use // the last known save data (or maybe just an empty string). if (!data) { data = lastSaveData.c_str(); } else // Otherwise, copy the new data into our buffer. { lastSaveData.assign(data); } if (data[0] == '\0') { ASSERT(!instance->GetEluna()->HasInstanceData(instance)); // Create a new table for instance data. lua_State* L = instance->GetEluna()->L; lua_newtable(L); instance->GetEluna()->CreateInstanceData(instance); instance->GetEluna()->OnLoad(this); // Stack: (empty) return; } size_t decodedLength; const unsigned char* decodedData = ElunaUtil::DecodeData(data, &decodedLength); lua_State* L = instance->GetEluna()->L; if (decodedData) { // Stack: (empty) lua_pushcfunction(L, mar_decode); lua_pushlstring(L, (const char*)decodedData, decodedLength); // Stack: mar_decode, decoded_data // Call `mar_decode` and check for success. if (lua_pcall(L, 1, 1, 0) == 0) { // Stack: data // Only use the data if it's a table. if (lua_istable(L, -1)) { instance->GetEluna()->CreateInstanceData(instance); // Stack: (empty) instance->GetEluna()->OnLoad(this); // WARNING! lastSaveData might be different after `OnLoad` if the Lua code saved data. } else { ELUNA_LOG_ERROR("Error while loading instance data: Expected data to be a table (type 5), got type %d instead", lua_type(L, -1)); lua_pop(L, 1); // Stack: (empty) Initialize(); } } else { // Stack: error_message ELUNA_LOG_ERROR("Error while parsing instance data with lua-marshal: %s", lua_tostring(L, -1)); lua_pop(L, 1); // Stack: (empty) Initialize(); } delete[] decodedData; } else { ELUNA_LOG_ERROR("Error while decoding instance data: Data is not valid base-64"); Initialize(); } }
// 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; }
void Eluna::Report(lua_State* _L) { const char* msg = lua_tostring(_L, -1); ELUNA_LOG_ERROR("%s", msg); lua_pop(_L, 1); }
void Eluna::RunScripts() { LOCK_ELUNA; if (!IsEnabled()) return; uint32 oldMSTime = ElunaUtil::GetCurrTime(); uint32 count = 0; ScriptList scripts; lua_extensions.sort(ScriptPathComparator); lua_scripts.sort(ScriptPathComparator); scripts.insert(scripts.end(), lua_extensions.begin(), lua_extensions.end()); scripts.insert(scripts.end(), lua_scripts.begin(), lua_scripts.end()); std::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) { // Check that no duplicate names exist if (loaded.find(it->filename) != loaded.end()) { ELUNA_LOG_ERROR("[Eluna]: Error loading `%s`. File with same name already loaded from `%s`, rename either file", it->filepath.c_str(), loaded[it->filename].c_str()); continue; } 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); ELUNA_LOG_DEBUG("[Eluna]: `%s` was already loaded or required", it->filepath.c_str()); continue; } lua_pop(L, 1); // 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; } } // Stack: package, modules lua_pop(L, 2); ELUNA_LOG_INFO("[Eluna]: Executed %u Lua scripts in %u ms", count, ElunaUtil::GetTimeDiff(oldMSTime)); OnLuaStateOpen(); }