/** * @brief Attempts to executes a Lua script. * If it has not been compiled already, it will compile the script * and cache it in the internal script map. * * @param pUser The user running the script. * @param pNpc The NPC attached to the script. * @param nEventID Identifier for the event. * @param bSelectedReward The reward selected, if applicable. * @param filename The script's filename. * * @return true if it succeeds, false if it fails. */ bool CLuaEngine::ExecuteScript(CUser * pUser, CNpc * pNpc, int32 nEventID, int8 bSelectedReward, const char * filename) { ScriptBytecodeMap::iterator itr; bool result = false; m_lock->AcquireReadLock(); itr = m_scriptMap.find(filename); if (itr == m_scriptMap.end()) { // Build full path to script std::string szPath = LUA_SCRIPT_DIRECTORY; szPath += filename; // Release the read lock (we're not reading anymore) m_lock->ReleaseReadLock(); // Attempt to compile BytecodeBuffer bytecode; bytecode.reserve(LUA_SCRIPT_BUFFER_SIZE); if (!SelectAvailableScript()->CompileScript(szPath.c_str(), bytecode)) { printf("ERROR: Could not compile Lua script.\n"); printf("FILE: %s\n", szPath.c_str()); printf("USER: %s\n", pUser->GetName().c_str()); printf("ZONE: %d\n", pUser->GetZoneID()); printf("NPC ID: %d\n", pNpc->m_sSid); printf("-\n"); return false; } // Acquire the write lock (we're adding the compiled script) m_lock->AcquireWriteLock(); #if !defined(LUA_SCRIPT_CACHE_DISABLED) // Add the script to our map m_scriptMap[filename] = bytecode; #endif // Now that we have the bytecode, we can use it. result = SelectAvailableScript()->ExecuteScript(pUser, pNpc, nEventID, bSelectedReward, filename, bytecode); // Done using the lock. m_lock->ReleaseWriteLock(); } else { // Already have the bytecode, so now we need to use it. result = SelectAvailableScript()->ExecuteScript(pUser, pNpc, nEventID, bSelectedReward, filename, itr->second); // Done using the lock. m_lock->ReleaseReadLock(); } return result; }
/** * @brief Attempts to executes a Lua script. * If it has not been compiled already, it will compile the script * and cache it in the internal script map. * * @param pUser The user running the script. * @param pNpc The NPC attached to the script. * @param nEventID Identifier for the event. * @param bSelectedReward The reward selected, if applicable. * @param filename The script's filename. * * @return true if it succeeds, false if it fails. */ bool CLuaEngine::ExecuteScript(CUser * pUser, CNpc * pNpc, int32 nEventID, int8 bSelectedReward, const char * filename) { ScriptBytecodeMap::iterator itr; bool result = false; m_lock->AcquireReadLock(); itr = m_scriptMap.find(filename); if (itr == m_scriptMap.end()) { // Build full path to script char szPath[_MAX_PATH]; _snprintf(szPath, sizeof(szPath), LUA_SCRIPT_DIRECTORY "%s", filename); // Release the read lock (we're not reading anymore) m_lock->ReleaseReadLock(); // Attempt to compile BytecodeBuffer bytecode; bytecode.reserve(LUA_SCRIPT_BUFFER_SIZE); if (!SelectAvailableScript()->CompileScript(szPath, bytecode)) { printf("ERROR: Could not compile Lua script `%s`.\n", szPath); return false; } // Acquire the write lock (we're adding the compiled script) m_lock->AcquireWriteLock(); #if !defined(LUA_SCRIPT_CACHE_DISABLED) // Add the script to our map m_scriptMap[filename] = bytecode; #endif // Now that we have the bytecode, we can use it. result = SelectAvailableScript()->ExecuteScript(pUser, pNpc, nEventID, bSelectedReward, filename, bytecode); // Done using the lock. m_lock->ReleaseWriteLock(); } else { // Already have the bytecode, so now we need to use it. result = SelectAvailableScript()->ExecuteScript(pUser, pNpc, nEventID, bSelectedReward, filename, itr->second); // Done using the lock. m_lock->ReleaseReadLock(); } return result; }
/** * @brief Attempts to compile a Lua script. * * @param filename Filename of the script. * @param buffer The buffer to store the script's compiled bytecode. * * @return true if it succeeds, false if it fails. */ bool CLuaScript::CompileScript(const char * filename, BytecodeBuffer & buffer) { // ensure that we wait until the last user's done executing their script. Guard lock(m_lock); /* Attempt to load the file */ int err = luaL_loadfile(m_luaState, filename); // If something bad happened, try to find an error. if (err != LUA_OK) { RetrieveLoadError(err, filename); return false; } // Everything's OK so far, the script has been loaded, now we need to start dumping it to bytecode. err = lua_dump(m_luaState, (lua_Writer)LoadBytecodeChunk, &buffer); if (err || buffer.empty()) { printf("ERROR: Failed to dump the Lua script `%s` to bytecode.\n", filename); return false; } // Compiled! return true; }
/** * @brief Attempts to compile a Lua script. * * @param filename Filename of the script. * @param buffer The buffer to store the script's compiled bytecode. * * @return true if it succeeds, false if it fails. */ bool CLuaScript::CompileScript(const char * filename, BytecodeBuffer & buffer) { // ensure that we wait until the last user's done executing their script. FastGuard lock(m_lock); /* Attempt to load the file */ int err = luaL_loadfile(m_luaState, filename); // If something bad happened, try to find an error. if (err != LUA_OK) { RetrieveLoadError(err, filename); return false; } // Everything's OK so far, the script has been loaded, now we need to start dumping it to bytecode. err = lua_dump(m_luaState, (lua_Writer)LoadBytecodeChunk, &buffer); if (err || buffer.empty()) { printf("ERROR: Failed to dump the Lua script `%s` to bytecode.\n", filename); return false; } #if !defined(USE_ORIGINAL_QUESTS) // Load up the script & revert the stack. // This step's only here for cleanup purposes. err = lua_pcall(m_luaState, 0, LUA_MULTRET, 0); if (err != LUA_OK) { RetrieveLoadError(err, filename); return false; } #endif // Compiled! return true; }
/** * @brief Executes the Lua script from bytecode. * * @param pUser The user running the script. * @param pNpc The NPC attached to the script. * @param nEventID Identifier for the event. * @param bSelectedReward The reward selected, if applicable. * @param filename The script's filename for debugging purposes. * @param bytecode The script's compiled bytecode. * * @return true if it succeeds, false if it fails. */ bool CLuaScript::ExecuteScript(CUser * pUser, CNpc * pNpc, int32 nEventID, int8 bSelectedReward, const char * filename, BytecodeBuffer & bytecode) { // Ensure that we wait until the last user's done executing their script. FastGuard lock(m_lock); /* Attempt to run the script. */ // Load the buffer with our bytecode. int err = luaL_loadbuffer(m_luaState, reinterpret_cast<const char *>(&bytecode[0]), bytecode.size(), nullptr); if (err != LUA_OK) { RetrieveLoadError(err, filename); return false; } #if !defined(USE_ORIGINAL_QUESTS) // our quest implementation // The user & NPC instances are globals. As is the selected quest reward. lua_tsetglobal(m_luaState, LUA_SCRIPT_GLOBAL_USER, pUser); lua_tsetglobal(m_luaState, LUA_SCRIPT_GLOBAL_NPC, pNpc); lua_tsetglobal(m_luaState, LUA_SCRIPT_GLOBAL_SELECTED_REWARD, bSelectedReward); // Find & assign script's entry point to the stack lua_getglobal(m_luaState, LUA_SCRIPT_ENTRY_POINT); // Entry point requires 1 arguments: the event ID. lua_tpush(m_luaState, nEventID); // Try calling the script's entry point (Main()). err = lua_pcall(m_luaState, 1, // 1 arguments 0, // 0 returned values 0); // no error handler #else lua_tsetglobal(m_luaState, "UID", pUser->GetID()); lua_tsetglobal(m_luaState, "STEP", bSelectedReward); lua_tsetglobal(m_luaState, "EVENT", nEventID); // Try calling the script's entry point err = lua_pcall(m_luaState, 0, // no arguments 0, // 0 returned values 0); // no error handler #endif // Nothing returned, so we can finish up here. if (err == LUA_OK) { #if defined(USE_ORIGINAL_QUESTS) lua_settop(m_luaState, 0); #endif return true; } // Attempt to provide somewhat informative errors to help the user figure out what's wrong. switch (err) { case LUA_ERRRUN: printf("ERROR: A runtime error occurred within Lua script `%s`.\n", filename); break; case LUA_ERRMEM: printf("ERROR: Unable to allocate memory during execution of Lua script `%s`.\n", filename); break; case LUA_ERRERR: printf("ERROR: An error occurred during Lua script `%s`. Error handler failed.\n", filename); break; default: printf("ERROR: An unknown error occurred in Lua script `%s`.\n", filename); break; } // Is there an error set? That can be more useful than our generic error. if (lua_isstring(m_luaState, -1)) { printf("ERROR: [%s] The following error was provided:\n%s\n", filename, lua_to<const char *>(m_luaState, -1)); } #if defined(USE_ORIGINAL_QUESTS) lua_settop(m_luaState, 0); #endif return false; }
/** * @brief Executes the Lua script from bytecode. * * @param pUser The user running the script. * @param pNpc The NPC attached to the script. * @param nEventID Identifier for the event. * @param bSelectedReward The reward selected, if applicable. * @param filename The script's filename for debugging purposes. * @param bytecode The script's compiled bytecode. * * @return true if it succeeds, false if it fails. */ bool CLuaScript::ExecuteScript(CUser * pUser, CNpc * pNpc, int32 nEventID, int8 bSelectedReward, const char * filename, BytecodeBuffer & bytecode) { // Ensure that we wait until the last user's done executing their script. Guard lock(m_lock); /* Attempt to run the script. */ // Load the buffer with our bytecode. int err = luaL_loadbuffer(m_luaState, reinterpret_cast<const char *>(&bytecode[0]), bytecode.size(), nullptr); if (err != LUA_OK) { RetrieveLoadError(err, filename); return false; } lua_tsetglobal(m_luaState, "UID", pUser->GetID()); lua_tsetglobal(m_luaState, "STEP", bSelectedReward); lua_tsetglobal(m_luaState, "EVENT", nEventID); // Try calling the script's entry point err = lua_pcall(m_luaState, 0, // no arguments 0, // 0 returned values 0); // no error handler // Nothing returned, so we can finish up here. if (err == LUA_OK) { lua_settop(m_luaState, 0); return true; } // Attempt to provide somewhat informative errors to help the user figure out what's wrong. switch (err) { case LUA_ERRRUN: printf("ERROR: A runtime error occurred within Lua script.\n"); printf("FILE: %s\n", filename); printf("USER: %s\n", pUser->GetName().c_str()); printf("ZONE: %d\n", pUser->GetZoneID()); printf("NPC ID: %d\n", pNpc->m_sSid); printf("-\n"); break; case LUA_ERRMEM: printf("ERROR: Unable to allocate memory during execution of Lua script.\n"); printf("FILE: %s\n", filename); printf("USER: %s\n", pUser->GetName().c_str()); printf("ZONE: %d\n", pUser->GetZoneID()); printf("NPC ID: %d\n", pNpc->m_sSid); printf("-\n"); break; case LUA_ERRERR: printf("ERROR: An error occurred during Lua script, Error handler failed.\n"); printf("FILE: %s\n", filename); printf("USER: %s\n", pUser->GetName().c_str()); printf("ZONE: %d\n", pUser->GetZoneID()); printf("NPC ID: %d\n", pNpc->m_sSid); printf("-\n"); break; default: printf("ERROR: An unknown error occurred in Lua script.\n"); printf("FILE: %s\n", filename); printf("USER: %s\n", pUser->GetName().c_str()); printf("ZONE: %d\n", pUser->GetZoneID()); printf("NPC ID: %d\n", pNpc->m_sSid); printf("-\n"); break; } // Is there an error set? That can be more useful than our generic error. if (lua_isstring(m_luaState, -1)) { printf("ERROR: [%s] The following error was provided.\n",filename); printf("MESSAGE: %s\n", lua_to<const char *>(m_luaState, -1)); printf("-\n"); } lua_settop(m_luaState, 0); return false; }