summaryrefslogtreecommitdiff
path: root/src/scripting/luascript.cpp
diff options
context:
space:
mode:
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);
}