summaryrefslogtreecommitdiff
path: root/src/scripting/luascript.cpp
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2012-03-09 21:30:42 +0100
committerThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2012-03-10 18:07:29 +0100
commit78c912fb4007c3e5f0b43de02646772acb21ecf2 (patch)
treee45409ff061de75b0e4273763a87f5a25de6a65b /src/scripting/luascript.cpp
parent2fa7d1f39b24714ee6dc72b6b9e61ec5a1997724 (diff)
downloadmanaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.tar.gz
manaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.tar.bz2
manaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.tar.xz
manaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.zip
Moved the managing of NPC script coroutines into C++
Rather than wrapping NPC functions up in coroutines in the Lua side, they are now managed on the C++ side as "script threads", which are essentially the same thing. The main purpose is that the server can now know whether any of these long running script interactions are still active, which will probably be useful when adding the ability to reload scripts. Reviewed-by: Erik Schilling
Diffstat (limited to 'src/scripting/luascript.cpp')
-rw-r--r--src/scripting/luascript.cpp183
1 files changed, 135 insertions, 48 deletions
diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp
index c9337d59..dfa64d0f 100644
--- a/src/scripting/luascript.cpp
+++ b/src/scripting/luascript.cpp
@@ -21,8 +21,8 @@
#include "luascript.h"
-
#include "scripting/luautil.h"
+#include "scripting/scriptmanager.h"
#include "game-server/character.h"
#include "utils/logger.h"
@@ -30,8 +30,6 @@
#include <cassert>
#include <cstring>
-Script::Ref LuaScript::mQuestReplyCallback;
-Script::Ref LuaScript::mPostReplyCallback;
Script::Ref LuaScript::mDeathNotificationCallback;
Script::Ref LuaScript::mRemoveNotificationCallback;
@@ -39,36 +37,59 @@ const char LuaScript::registryKey = 0;
LuaScript::~LuaScript()
{
- lua_close(mState);
+ lua_close(mRootState);
}
void LuaScript::prepare(Ref function)
{
assert(nbArgs == -1);
+
assert(function.isValid());
- lua_rawgeti(mState, LUA_REGISTRYINDEX, function.value);
- assert(lua_isfunction(mState, -1));
+ lua_rawgeti(mCurrentState, LUA_REGISTRYINDEX, function.value);
+ assert(lua_isfunction(mCurrentState, -1));
+ nbArgs = 0;
+}
+
+Script::Thread *LuaScript::newThread()
+{
+ assert(nbArgs == -1);
+ assert(!mCurrentThread);
+
+ LuaThread *thread = new LuaThread(this);
+
+ mCurrentThread = thread;
+ mCurrentState = thread->mState;
+ return thread;
+}
+
+void LuaScript::prepareResume(Thread *thread)
+{
+ assert(nbArgs == -1);
+ assert(!mCurrentThread);
+
+ mCurrentThread = thread;
+ mCurrentState = static_cast<LuaThread*>(thread)->mState;
nbArgs = 0;
}
void LuaScript::push(int v)
{
assert(nbArgs >= 0);
- lua_pushinteger(mState, v);
+ lua_pushinteger(mCurrentState, v);
++nbArgs;
}
void LuaScript::push(const std::string &v)
{
assert(nbArgs >= 0);
- lua_pushstring(mState, v.c_str());
+ lua_pushstring(mCurrentState, v.c_str());
++nbArgs;
}
void LuaScript::push(Thing *v)
{
assert(nbArgs >= 0);
- lua_pushlightuserdata(mState, v);
+ lua_pushlightuserdata(mCurrentState, v);
++nbArgs;
}
@@ -77,8 +98,8 @@ void LuaScript::push(const std::list<InventoryItem> &itemList)
assert(nbArgs >= 0);
int position = 0;
- lua_createtable(mState, itemList.size(), 0);
- int itemTable = lua_gettop(mState);
+ lua_createtable(mCurrentState, itemList.size(), 0);
+ int itemTable = lua_gettop(mCurrentState);
for (std::list<InventoryItem>::const_iterator i = itemList.begin();
i != itemList.end();
@@ -88,8 +109,8 @@ void LuaScript::push(const std::list<InventoryItem> &itemList)
std::map<std::string, int> item;
item["id"] = i->itemId;
item["amount"] = i->amount;
- pushSTLContainer<std::string, int>(mState, item);
- lua_rawseti(mState, itemTable, ++position);
+ pushSTLContainer<std::string, int>(mCurrentState, item);
+ lua_rawseti(mCurrentState, itemTable, ++position);
}
++nbArgs;
}
@@ -97,56 +118,106 @@ void LuaScript::push(const std::list<InventoryItem> &itemList)
int LuaScript::execute()
{
assert(nbArgs >= 0);
- int res = lua_pcall(mState, nbArgs, 1, 1);
+ assert(!mCurrentThread);
+
+ int res = lua_pcall(mCurrentState, nbArgs, 1, 1);
nbArgs = -1;
- if (res || !(lua_isnil(mState, -1) || lua_isnumber(mState, -1)))
+
+ if (res || !(lua_isnil(mCurrentState, -1) || lua_isnumber(mCurrentState, -1)))
{
- const char *s = lua_tostring(mState, -1);
+ const char *s = lua_tostring(mCurrentState, -1);
LOG_WARN("Lua Script Error" << std::endl
<< " Script : " << mScriptFile << std::endl
<< " Error : " << (s ? s : "") << std::endl);
- lua_pop(mState, 1);
+ lua_pop(mCurrentState, 1);
return 0;
}
- res = lua_tointeger(mState, -1);
- lua_pop(mState, 1);
+ res = lua_tointeger(mCurrentState, -1);
+ lua_pop(mCurrentState, 1);
return res;
}
+bool LuaScript::resume()
+{
+ assert(nbArgs >= 0);
+ assert(mCurrentThread);
+
+ setMap(mCurrentThread->mMap);
+ int result = lua_resume(mCurrentState, nbArgs);
+ nbArgs = -1;
+ setMap(0);
+
+ if (result == 0) // Thread is done
+ {
+ if (lua_gettop(mCurrentState) > 0)
+ LOG_WARN("Ignoring values returned by script thread!");
+ }
+ else if (result == LUA_YIELD) // Thread has yielded
+ {
+ if (lua_gettop(mCurrentState) > 0)
+ LOG_WARN("Ignoring values passed to yield!");
+ }
+ else // Thread encountered an error
+ {
+ // Make a traceback using the debug.traceback function
+ lua_getglobal(mCurrentState, "debug");
+ lua_getfield(mCurrentState, -1, "traceback");
+ lua_pushvalue(mCurrentState, -3); // error string as first parameter
+ lua_pcall(mCurrentState, 1, 1, 0);
+
+ LOG_WARN("Lua Script Error:" << std::endl
+ << lua_tostring(mCurrentState, -1));
+ }
+
+ lua_settop(mCurrentState, 0);
+ const bool done = result != LUA_YIELD;
+
+ if (done)
+ {
+ // Clean up the current thread (not sure if this is the best place)
+ delete mCurrentThread;
+ }
+
+ mCurrentThread = 0;
+ mCurrentState = mRootState;
+
+ return done;
+}
+
void LuaScript::assignCallback(Script::Ref &function)
{
- assert(lua_isfunction(mState, -1));
+ assert(lua_isfunction(mRootState, -1));
// If there is already a callback set, replace it
if (function.isValid())
- luaL_unref(mState, LUA_REGISTRYINDEX, function.value);
+ luaL_unref(mRootState, LUA_REGISTRYINDEX, function.value);
- function.value = luaL_ref(mState, LUA_REGISTRYINDEX);
+ function.value = luaL_ref(mRootState, LUA_REGISTRYINDEX);
}
void LuaScript::load(const char *prog, const char *name)
{
- int res = luaL_loadbuffer(mState, prog, std::strlen(prog), name);
+ int res = luaL_loadbuffer(mRootState, prog, std::strlen(prog), name);
if (res)
{
switch (res) {
case LUA_ERRSYNTAX:
LOG_ERROR("Syntax error while loading Lua script: "
- << lua_tostring(mState, -1));
+ << lua_tostring(mRootState, -1));
break;
case LUA_ERRMEM:
LOG_ERROR("Memory allocation error while loading Lua script");
break;
}
- lua_pop(mState, 1);
+ lua_pop(mRootState, 1);
}
- else if (lua_pcall(mState, 0, 0, 1))
+ else if (lua_pcall(mRootState, 0, 0, 1))
{
LOG_ERROR("Failure while initializing Lua script: "
- << lua_tostring(mState, -1));
- lua_pop(mState, 1);
+ << lua_tostring(mRootState, -1));
+ lua_pop(mRootState, 1);
}
}
@@ -179,31 +250,47 @@ void LuaScript::processRemoveEvent(Thing *being)
/**
* Called when the server has recovered the value of a quest variable.
*/
-void LuaScript::getQuestCallback(Character *q, const std::string &name,
- const std::string &value, Script *script)
+void LuaScript::getQuestCallback(Character *q,
+ const std::string &value,
+ Script *script)
{
- if (mQuestReplyCallback.isValid())
- {
- script->prepare(mQuestReplyCallback);
- script->push(q);
- script->push(name);
- script->push(value);
- script->execute();
- }
+ Script::Thread *thread = q->getNpcThread();
+ if (!thread || thread->mState != Script::ThreadExpectingString)
+ return;
+
+ script->prepareResume(thread);
+ script->push(value);
+ script->resume();
}
/**
* Called when the server has recovered the post for a user.
*/
-void LuaScript::getPostCallback(Character *q, const std::string &sender,
- const std::string &letter, Script *script)
+void LuaScript::getPostCallback(Character *q,
+ const std::string &sender,
+ const std::string &letter,
+ Script *script)
{
- if (mPostReplyCallback.isValid())
- {
- script->prepare(mPostReplyCallback);
- script->push(q);
- script->push(sender);
- script->push(letter);
- script->execute();
- }
+ Script::Thread *thread = q->getNpcThread();
+ if (!thread || thread->mState != Script::ThreadExpectingTwoStrings)
+ return;
+
+ script->prepareResume(thread);
+ script->push(sender);
+ script->push(letter);
+ script->resume();
+}
+
+
+LuaScript::LuaThread::LuaThread(LuaScript *script) :
+ Thread(script)
+{
+ mState = lua_newthread(script->mRootState);
+ mRef = luaL_ref(script->mRootState, LUA_REGISTRYINDEX);
+}
+
+LuaScript::LuaThread::~LuaThread()
+{
+ LuaScript *luaScript = static_cast<LuaScript*>(mScript);
+ luaL_unref(luaScript->mRootState, LUA_REGISTRYINDEX, mRef);
}