From 9e90ae0ee4897ed663949126b121f46a5d85af0c Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Mon, 2 May 2011 01:13:57 -0300 Subject: [PATCH] more scripting features (dynamic fields) --- CMakeLists.txt | 1 + data/modules/mainmenu/entergamewindow.yml | 5 +- data/modules/mainmenu/infowindow.yml | 5 +- data/modules/mainmenu/mainmenu.yml | 2 +- data/modules/mainmenu/menustate.lua | 3 +- data/modules/mainmenu/optionswindow.yml | 5 +- data/modules/messagebox/messagebox.lua | 15 +- src/framework/core/dispatcher.cpp | 19 +- src/framework/core/dispatcher.h | 17 +- src/framework/script/luafunctions.cpp | 89 ++++---- src/framework/script/luafunctions.h | 5 +- src/framework/script/luascript.cpp | 251 +++++++++++++++++----- src/framework/script/luascript.h | 21 +- src/framework/script/scriptable.cpp | 51 +++++ src/framework/script/scriptable.h | 8 +- src/framework/ui/uicontainer.cpp | 3 + src/framework/ui/uielement.cpp | 11 + src/framework/ui/uielement.h | 1 + src/framework/ui/uiloader.cpp | 6 +- 19 files changed, 375 insertions(+), 143 deletions(-) create mode 100644 src/framework/script/scriptable.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5098d910..34bbc3de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ SET(SOURCES src/framework/core/modules.cpp # framework script + src/framework/script/scriptable.cpp src/framework/script/luascript.cpp src/framework/script/luafunctions.cpp diff --git a/data/modules/mainmenu/entergamewindow.yml b/data/modules/mainmenu/entergamewindow.yml index 57ab400d..b07af1e4 100644 --- a/data/modules/mainmenu/entergamewindow.yml +++ b/data/modules/mainmenu/entergamewindow.yml @@ -3,8 +3,7 @@ window#enterGameWindow: size: [236, 178] anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - onLoad: self:getParent():lock(self) - onDestroy: self:getParent():unlock(self) + onLoad: self.locked = true label#accountNameLabel: text: Account name @@ -52,7 +51,7 @@ window#enterGameWindow: anchors.bottom: parent.bottom margin.bottom: 10 margin.right: 13 - onClick: self:getParent():destroy() + onClick: self.parent:destroy() textEdit#accountNameTextEdit: anchors.right: parent.right diff --git a/data/modules/mainmenu/infowindow.yml b/data/modules/mainmenu/infowindow.yml index b1ecd471..bbb86254 100644 --- a/data/modules/mainmenu/infowindow.yml +++ b/data/modules/mainmenu/infowindow.yml @@ -3,8 +3,7 @@ window#infoWindow: size: [244, 221] anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - onLoad: self:getParent():lock(self) - onDestroy: self:getParent():unlock(self) + onLoad: self.locked = true panel#infoPanel: skin: flatPanel @@ -60,4 +59,4 @@ window#infoWindow: anchors.top: parent.top margin.top: 191 margin.left: 188 - onClick: self:getParent():destroy() + onClick: self.parent:destroy() diff --git a/data/modules/mainmenu/mainmenu.yml b/data/modules/mainmenu/mainmenu.yml index 1b568c51..b72d452f 100644 --- a/data/modules/mainmenu/mainmenu.yml +++ b/data/modules/mainmenu/mainmenu.yml @@ -49,4 +49,4 @@ panel#background: anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter margin.top: 136 - onClick: App.exit() + onClick: onApplicationClose() diff --git a/data/modules/mainmenu/menustate.lua b/data/modules/mainmenu/menustate.lua index 061bd831..830df232 100644 --- a/data/modules/mainmenu/menustate.lua +++ b/data/modules/mainmenu/menustate.lua @@ -5,11 +5,12 @@ end function onLeaveMenuState() mainMenu:destroy() + mainMenu = nil end function onApplicationClose() onLeaveMenuState() - exitGame() + App.exit() end -- here is where everything starts diff --git a/data/modules/mainmenu/optionswindow.yml b/data/modules/mainmenu/optionswindow.yml index 45aa789b..411cb564 100644 --- a/data/modules/mainmenu/optionswindow.yml +++ b/data/modules/mainmenu/optionswindow.yml @@ -3,8 +3,7 @@ window#optionsWindow: size: [286, 262] anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - onLoad: self:getParent():lock(self) - onDestroy: self:getParent():unlock(self) + onLoad: self.locked = true # general button#generalButton: @@ -111,4 +110,4 @@ window#optionsWindow: anchors.bottom: parent.bottom margin.right: 10 margin.bottom: 13 - onClick: self:getParent():destroy() + onClick: self.parent:destroy() diff --git a/data/modules/messagebox/messagebox.lua b/data/modules/messagebox/messagebox.lua index a4a3ee7c..abad025d 100644 --- a/data/modules/messagebox/messagebox.lua +++ b/data/modules/messagebox/messagebox.lua @@ -1,12 +1,11 @@ function messageBox(title, text) local messageBoxWindow = UI.load("modules/messagebox/messagebox.yml") - local messageBoxLabel = messageBoxWindow:getChildByID("messageBoxLabel") - local messageBoxOkButton = messageBoxWindow:getChildByID("messageBoxOkButton") - local uiRoot = UI.getRootContainer() - uiRoot:lock(messageBoxWindow) - messageBoxWindow:setTitle(title) - messageBoxLabel:setText(text) + local messageBoxLabel = messageBoxWindow:getChildById("messageBoxLabel") + local messageBoxOkButton = messageBoxWindow:getChildById("messageBoxOkButton") + messageBoxWindow.locked = true + messageBoxWindow.title = title + messageBoxLabel.text = text --messageBoxWindow:setSize(messageBoxLabel:getSize() + Size{20, 20}) - messageBoxWindow:setOnDestroy(function() uiRoot:unlock(self) end) - messageBoxOkButton:setOnClick(function() messageBoxWindow:destroy() end) + messageBoxWindow.onDestroy = function() self.locked = false end + messageBoxOkButton.onClick = function() messageBoxWindow:destroy() end end diff --git a/src/framework/core/dispatcher.cpp b/src/framework/core/dispatcher.cpp index 53153f84..e1f763a1 100644 --- a/src/framework/core/dispatcher.cpp +++ b/src/framework/core/dispatcher.cpp @@ -31,11 +31,15 @@ Dispatcher g_dispatcher; void Dispatcher::poll() { while(!m_taskList.empty()) { - Task *task = m_taskList.top(); + m_taskList.front()(); + m_taskList.pop_front(); + } + + while(!m_scheduledTaskList.empty()) { + ScheduledTask *task = m_scheduledTaskList.top(); if(g_engine.getCurrentFrameTicks() < task->ticks) break; - m_taskList.pop(); - + m_scheduledTaskList.pop(); task->callback(); delete task; } @@ -43,10 +47,13 @@ void Dispatcher::poll() void Dispatcher::scheduleTask(const SimpleCallback& callback, int delay) { - m_taskList.push(new Task(g_engine.getCurrentFrameTicks() + delay, callback)); + m_scheduledTaskList.push(new ScheduledTask(g_engine.getCurrentFrameTicks() + delay, callback)); } -void Dispatcher::addTask(const SimpleCallback& callback) +void Dispatcher::addTask(const SimpleCallback& callback, bool pushFront) { - m_taskList.push(new Task(callback)); + if(pushFront) + m_taskList.push_front(callback); + else + m_taskList.push_back(callback); } diff --git a/src/framework/core/dispatcher.h b/src/framework/core/dispatcher.h index 5287860c..5dbe7710 100644 --- a/src/framework/core/dispatcher.h +++ b/src/framework/core/dispatcher.h @@ -27,18 +27,18 @@ #include -class Task { +class ScheduledTask { public: - inline Task(const SimpleCallback& _callback) : ticks(0), callback(_callback) { } - inline Task(int _ticks, const SimpleCallback& _callback) : ticks(_ticks), callback(_callback) { } - inline bool operator<(const Task& other) const { return ticks > other.ticks; } + inline ScheduledTask(const SimpleCallback& _callback) : ticks(0), callback(_callback) { } + inline ScheduledTask(int _ticks, const SimpleCallback& _callback) : ticks(_ticks), callback(_callback) { } + inline bool operator<(const ScheduledTask& other) const { return ticks > other.ticks; } int ticks; SimpleCallback callback; }; -class lessTask : public std::binary_function { +class lessScheduledTask : public std::binary_function { public: - bool operator()(Task*& t1,Task*& t2) { return (*t1) < (*t2); } + bool operator()(ScheduledTask*& t1,ScheduledTask*& t2) { return (*t1) < (*t2); } }; class Dispatcher @@ -50,13 +50,14 @@ public: void poll(); /// Add an event - void addTask(const SimpleCallback& callback); + void addTask(const SimpleCallback& callback, bool pushFront = false); /// Schedula an event void scheduleTask(const SimpleCallback& callback, int delay); private: - std::priority_queue, lessTask> m_taskList; + std::list m_taskList; + std::priority_queue, lessScheduledTask> m_scheduledTaskList; }; extern Dispatcher g_dispatcher; diff --git a/src/framework/script/luafunctions.cpp b/src/framework/script/luafunctions.cpp index 773a9c42..ef5f2a27 100644 --- a/src/framework/script/luafunctions.cpp +++ b/src/framework/script/luafunctions.cpp @@ -28,6 +28,7 @@ #include #include #include +#include void registerLuaFunctions() { @@ -45,31 +46,28 @@ void registerLuaFunctions() g_lua.registerClass("UILayout"); // UIElement - g_lua.registerClass("UIElement", "UILayout"); - g_lua.registerMemberFunction("UIElement", "setOnLoad", &lua_UIElement_setOnLoad); - g_lua.registerMemberFunction("UIElement", "setOnDestroy", &lua_UIElement_setOnDestroy); - g_lua.registerMemberFunction("UIElement", "getParent", &lua_UIElement_getParent); + g_lua.registerClass("UIElement", "UILayout"); + g_lua.registerMemberField("UIElement", "onLoad", NULL, &lua_UIElement_setOnLoad); + g_lua.registerMemberField("UIElement", "onDestroy", NULL, &lua_UIElement_setOnDestroy); + g_lua.registerMemberField("UIElement", "parent", &lua_UIElement_getParent); g_lua.registerMemberFunction("UIElement", "destroy", &lua_UIElement_destroy); // UIContainer g_lua.registerClass("UIContainer", "UIElement"); - g_lua.registerMemberFunction("UIContainer", "getChildByID", &lua_UIContainer_getChildByID); - g_lua.registerMemberFunction("UIContainer", "lock", &lua_UIContainer_lock); - g_lua.registerMemberFunction("UIContainer", "unlock", &lua_UIContainer_unlock); + g_lua.registerMemberFunction("UIContainer", "getChildById", &lua_UIContainer_getChildById); + g_lua.registerMemberField("UIContainer", "locked", NULL, &lua_UIContainer_setLocked); // UILabel - g_lua.registerClass("UILabel", "UIElement"); - g_lua.registerMemberFunction("UILabel", "setText", &lua_UILabel_setText); - g_lua.registerMemberFunction("UILabel", "getText", &lua_UILabel_getText); + g_lua.registerClass("UILabel", "UIElement"); + g_lua.registerMemberField("UILabel", "text", &lua_UILabel_getText, &lua_UILabel_setText); // UIButton - g_lua.registerClass("UIButton", "UIElement"); - g_lua.registerMemberFunction("UIButton", "setOnClick", &lua_UIButton_setOnClick); + g_lua.registerClass("UIButton", "UIElement"); + g_lua.registerMemberField("UIButton", "onClick", NULL, &lua_UIButton_setOnClick); // UIWindow - g_lua.registerClass("UIWindow", "UIContainer"); - g_lua.registerMemberFunction("UIWindow", "setTitle", &lua_UIWindow_setTitle); - g_lua.registerMemberFunction("UIWindow", "getTitle", &lua_UIWindow_getTitle); + g_lua.registerClass("UIWindow", "UIContainer"); + g_lua.registerMemberField("UIWindow", "title", &lua_UIWindow_getTitle, &lua_UIWindow_setTitle); } @@ -126,19 +124,24 @@ int lua_UI_getRootContainer() int lua_UIElement_setOnLoad() { - g_lua.moveTop(-2); - if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) - element->setOnLoad(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction())); - else + g_lua.insert(-2); + if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) { + int funcRef = g_lua.popFunction(); + element->associateLuaRef(__FUNCTION__, funcRef); + element->setOnLoad(g_lua.createScriptableSelfFuncCallback(funcRef)); + } else g_lua.pop(); return 1; } int lua_UIElement_setOnDestroy() { - g_lua.moveTop(-2); - if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) - element->setOnDestroy(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction())); + g_lua.insert(-2); + if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) { + int funcRef = g_lua.popFunction(); + element->associateLuaRef(__FUNCTION__, funcRef); + element->setOnDestroy(g_lua.createScriptableSelfFuncCallback(funcRef)); + } else g_lua.pop(); return 1; @@ -157,6 +160,7 @@ int lua_UIElement_destroy() { if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) element->destroy(); + g_dispatcher.addTask(boost::bind(&LuaScript::collectGarbage, &g_lua)); return 1; } @@ -164,7 +168,7 @@ int lua_UIElement_destroy() //////////////////////////////////////////////////////////////////////////////// // UIContainer -int lua_UIContainer_getChildByID() +int lua_UIContainer_getChildById() { std::string id = g_lua.popString(); if(UIContainerPtr container = boost::dynamic_pointer_cast(g_lua.popClassInstance())) @@ -174,35 +178,22 @@ int lua_UIContainer_getChildByID() return 1; } -int lua_UIContainer_lock() -{ - if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) { - if(UIContainerPtr container = boost::dynamic_pointer_cast(g_lua.popClassInstance())) - g_lua.pushBoolean(container->lockElement(element)); - else - g_lua.pushBoolean(false); - } else { - g_lua.reportFuncErrorWithTraceback("invalid element"); - g_lua.pushBoolean(false); - } - return 1; -} - -int lua_UIContainer_unlock() +int lua_UIContainer_setLocked() { + bool locked = g_lua.popBoolean(); if(UIElementPtr element = boost::dynamic_pointer_cast(g_lua.popClassInstance())) { - if(UIContainerPtr container = boost::dynamic_pointer_cast(g_lua.popClassInstance())) - g_lua.pushBoolean(container->unlockElement(element)); - else - g_lua.pushBoolean(false); + if(UIContainerPtr container = element->getParent()) { + if(locked) + container->lockElement(element); + else + container->unlockElement(element); + } } else { g_lua.reportFuncErrorWithTraceback("invalid element"); - g_lua.pushBoolean(false); } return 1; } - //////////////////////////////////////////////////////////////////////////////// // UILabel @@ -229,10 +220,12 @@ int lua_UILabel_getText() int lua_UIButton_setOnClick() { - g_lua.moveTop(-2); - if(UIButtonPtr button = boost::dynamic_pointer_cast(g_lua.popClassInstance())) - button->setOnClick(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction())); - else + g_lua.insert(-2); + if(UIButtonPtr button = boost::dynamic_pointer_cast(g_lua.popClassInstance())) { + int funcRef = g_lua.popFunction(); + button->associateLuaRef(__FUNCTION__, funcRef); + button->setOnClick(g_lua.createScriptableSelfFuncCallback(funcRef)); + } else g_lua.pop(); return 1; } diff --git a/src/framework/script/luafunctions.h b/src/framework/script/luafunctions.h index b55e5b1b..65691d44 100644 --- a/src/framework/script/luafunctions.h +++ b/src/framework/script/luafunctions.h @@ -44,9 +44,8 @@ int lua_UIElement_getParent(); int lua_UIElement_destroy(); // UIContainer -int lua_UIContainer_getChildByID(); -int lua_UIContainer_lock(); -int lua_UIContainer_unlock(); +int lua_UIContainer_getChildById(); +int lua_UIContainer_setLocked(); // UILabel int lua_UILabel_setText(); diff --git a/src/framework/script/luascript.cpp b/src/framework/script/luascript.cpp index c3578b29..ba75ffac 100644 --- a/src/framework/script/luascript.cpp +++ b/src/framework/script/luascript.cpp @@ -128,16 +128,37 @@ void LuaScript::reportErrorWithTraceback(const std::string& errorDesc, const cha reportError(popString(), funcName); } +void LuaScript::collectGarbage() +{ + for(int i=0;i<2;i++) + lua_gc(L, LUA_GCCOLLECT, 0); +} + int LuaScript::getStackSize() { return lua_gettop(L); } -void LuaScript::moveTop(int index) +void LuaScript::insert(int index) { lua_insert(L, index); } +int LuaScript::ref() +{ + return luaL_ref(L, LUA_REGISTRYINDEX); +} + +void LuaScript::unref(int ref) +{ + luaL_unref(L, LUA_REGISTRYINDEX, ref); +} + +void LuaScript::getRef(int ref) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); +} + void LuaScript::pop(int n) { lua_pop(L, n); @@ -191,11 +212,16 @@ void LuaScript::pushUserdata(void* ptr) lua_pushlightuserdata(L, ptr); } +void LuaScript::pushValue(int index) +{ + lua_pushvalue(L, index); +} + void LuaScript::pushClassInstance(const ScriptablePtr& object) { if(object) { // create weak_ptr to the scriptable object stored as userdata - new(lua_newuserdata(L, sizeof(ScriptableWeakPtr))) ScriptableWeakPtr(object); + new(lua_newuserdata(L, sizeof(ScriptablePtr))) ScriptablePtr(object); // set object metatable lua_getfield(L, LUA_REGISTRYINDEX, (std::string(object->getScriptableName()) + "_mt").c_str()); lua_setmetatable(L, -2); @@ -208,7 +234,7 @@ ScriptablePtr LuaScript::popClassInstance() { ScriptablePtr object; if(lua_isuserdata(L, -1)) { // instances are store as userdata - object = ((ScriptableWeakPtr *)lua_touserdata(L, -1))->lock(); + object = *((ScriptablePtr *)lua_touserdata(L, -1)); if(!object) reportErrorWithTraceback("attempt to retrive class instance from a object that is already expired"); } else if(!lua_isnil(L, -1)) // we accept nil values @@ -224,18 +250,18 @@ void LuaScript::pushFunction(int functionRef) int LuaScript::popFunction() { - return luaL_ref(L, LUA_REGISTRYINDEX); + return ref(); } void LuaScript::releaseFunction(int functionRef) { - luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + unref(functionRef); } void LuaScript::callFunction(int numArgs) { int size = lua_gettop(L); - int errorIndex = size - numArgs; + int errorIndex = -(numArgs + 2); lua_pushcfunction(L, &LuaScript::luaErrorHandler); lua_insert(L, errorIndex); @@ -244,9 +270,10 @@ void LuaScript::callFunction(int numArgs) reportError(popString()); } - lua_remove(L, errorIndex); + // pop error func + lua_pop(L, 1); - if(lua_gettop(L) + numArgs + 1 != size) + if(lua_gettop(L) != size - numArgs - 1) reportError("stack size changed!"); } @@ -261,18 +288,20 @@ SimpleCallback LuaScript::createSimpleFuncCallback(int funcRef) boost::function LuaScript::createScriptableSelfFuncCallback(int funcRef) { return [this, funcRef](ScriptablePtr scriptable) { + pushClassInstance(scriptable); + setGlobal("self"); + pushFunction(funcRef); - setLocal(scriptable, "self"); callFunction(); + + pushNil(); + setGlobal("self"); }; } -void LuaScript::setLocal(const ScriptablePtr& scriptable, const char *varName, int envIndex) +void LuaScript::setGlobal(const char *varName) { - lua_getfenv(L, envIndex); - pushClassInstance(scriptable); - lua_setfield(L, -2, varName); - lua_pop(L, 1); + lua_setfield(L, LUA_GLOBALSINDEX, varName); } void LuaScript::setupPackageLoader() @@ -295,46 +324,78 @@ void LuaScript::setupPackageLoader() void LuaScript::registerClass(const std::string& klass, const std::string& baseClass) { // klass_mt = {} - lua_newtable(L); // klass metatable - lua_pushvalue(L, -1); // another reference to the metatable - lua_setfield(L, LUA_REGISTRYINDEX, (klass + "_mt").c_str()); // register the metatable at registry index + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, (klass + "_mt").c_str()); - // set __gc metamethod, which collects userdata - lua_pushcfunction(L, &LuaScript::luaCollectClassInstance); - lua_setfield(L, -2, "__gc"); + // set __index metamethod + lua_pushcfunction(L, &LuaScript::luaIndexMetaMethod); + lua_setfield(L, -2, "__index"); + + // set __newindex metamethod + lua_pushcfunction(L, &LuaScript::luaNewIndexMetaMethod); + lua_setfield(L, -2, "__newindex"); // set __eq metamethod - lua_pushcfunction(L, &LuaScript::luaCompareClassInstances); + lua_pushcfunction(L, &LuaScript::luaEqualMetaMethod); lua_setfield(L, -2, "__eq"); - // klass = {} - lua_newtable(L); // klass table - lua_pushvalue(L, -1); // another reference to the table - lua_setfield(L, LUA_GLOBALSINDEX, klass.c_str()); // register at globals index - - // klass.className = "klass" - lua_pushstring(L, klass.c_str()); - lua_setfield(L, -2, "className"); + // set __gc metamethod, which collects userdata + lua_pushcfunction(L, &LuaScript::luaGarbageCollectMetaMethod); + lua_setfield(L, -2, "__gc"); - // klass_mt.__index = klass - lua_setfield(L, -2, "__index"); + // klass_mt.methods = { } + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_GLOBALSINDEX, klass.c_str()); + lua_setfield(L, -2, "methods"); + + // klass_mt.fieldmethods = { } + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_GLOBALSINDEX, (klass + "_fieldmethods").c_str()); + lua_setfield(L, -2, "fieldmethods"); + + if(baseClass.length()) { + // klass_mt.base = baseClass_mt + lua_getfield(L, LUA_REGISTRYINDEX, (baseClass + "_mt").c_str()); + lua_setfield(L, -2, "base"); + } - // pop the class metatable lua_pop(L, 1); +} - if(baseClass.length() > 0) { - // add klass table to the top of the stack - lua_getfield(L, LUA_GLOBALSINDEX, klass.c_str()); - - // redirect = { __index = baseClass } - lua_newtable(L); - lua_getfield(L, LUA_GLOBALSINDEX, baseClass.c_str()); - lua_setfield(L, -2, "__index"); +void LuaScript::registerMemberField(const std::string& klass, const std::string& field, LuaScript::LuaCFunction getFunction, LuaScript::LuaCFunction setFunction) +{ + if(getFunction) { + int functionId = m_functions.size(); + m_functions.push_back(getFunction); - // setmetatable(klass, redirect) - lua_setmetatable(L, -2); + // push the class table + lua_getfield(L, LUA_GLOBALSINDEX, (klass + "_fieldmethods").c_str()); + // push the function id + lua_pushnumber(L, functionId); + // store id in the closure + lua_pushcclosure(L, &LuaScript::luaFunctionCallback, 1); + // store the function at the class field functionName + lua_setfield(L, -2, ("get_" + field).c_str()); + // pop the table + lua_pop(L, 1); + } - // pop the derived class table + if(setFunction) { + int functionId = m_functions.size(); + m_functions.push_back(setFunction); + + // push the class table + lua_getfield(L, LUA_GLOBALSINDEX, (klass + "_fieldmethods").c_str()); + // push the function id + lua_pushnumber(L, functionId); + // store id in the closure + lua_pushcclosure(L, &LuaScript::luaFunctionCallback, 1); + // store the function at the class field functionName + lua_setfield(L, -2, ("set_" + field).c_str()); + // pop the table lua_pop(L, 1); } } @@ -389,24 +450,106 @@ int LuaScript::luaPackageLoader(lua_State* L) return 1; } -int LuaScript::luaCollectClassInstance(lua_State* L) +int LuaScript::luaIndexMetaMethod(lua_State* L) +{ + std::string key = lua_tostring(L, -1); // key, obj + lua_pop(L, 1); // obj + lua_getmetatable(L, -1); // mt, obj + while(!lua_isnil(L, -1)) { + lua_getfield(L, -1, "fieldmethods"); // mt.fieldmethods, mt, obj + lua_getfield(L, -1, ("get_" + key).c_str()); // mt.fieldmethods[get_key], mt.fieldmethods, mt, obj + lua_remove(L, -2); // mt.fieldmethods[get_key], mt, obj + if(!lua_isnil(L, -1)) { + lua_remove(L, -2); // mt.fieldmethods[get_key], obj + lua_insert(L, -2); // obj, mt.fieldmethods[get_key] + lua_pushcfunction(L, &LuaScript::luaErrorHandler); // errorfunc, obj, mt.fieldmethods[get_key] + lua_insert(L, -3); // obj, mt.fieldmethods[get_key], errorfunc + int ret = lua_pcall(L, 1, 1, -3); // ret, errorfunc + if(ret != 0) { + g_lua.reportError(g_lua.popString()); // errofunc + lua_pop(L, 1); // (empty) + lua_pushnil(L); // nil + } else { + lua_remove(L, -2); // ret + } + return 1; + } else { + lua_pop(L, 1); // mt, obj + lua_getfield(L, -1, "methods"); // mt.methods, mt, obj + lua_getfield(L, -1, key.c_str()); // mt.methods[key], mt.methods, mt, obj + lua_remove(L, -2); // mt.methods[key], mt, obj + if(!lua_isnil(L, -1)) { + lua_insert(L, -3); + lua_pop(L, 2); + return 1; + } + lua_pop(L, 1); + } + lua_getfield(L, -1, "base"); // mt.base, mt, obj + lua_remove(L, -2); // mt.base, obj + } + lua_pop(L, 1); + + ScriptablePtr scriptable = g_lua.popClassInstance(); + int refId = scriptable->getLuaRef(key); + if(refId != -1) + g_lua.getRef(refId); + else + g_lua.pushNil(); + + return 1; +} + +int LuaScript::luaNewIndexMetaMethod(lua_State* L) { - ScriptableWeakPtr *objectRef = (ScriptableWeakPtr *)lua_touserdata(L, -1); - objectRef->reset(); + // stack: value, key, obj + lua_insert(L, -2); + std::string key = lua_tostring(L, -1); lua_pop(L, 1); + lua_getmetatable(L, -2); + // stack: mt, value, obj + while(!lua_isnil(L, -1)) { + lua_getfield(L, -1, "fieldmethods"); + lua_getfield(L, -1, ("set_" + key).c_str()); + lua_remove(L, -2); // stack: set method, mt, value, obj + if(!lua_isnil(L, -1)) { + lua_remove(L, -2); // mt.fieldmethods[get_key], value, obj + lua_insert(L, -3); // value, obj, mt.fieldmethods[get_key] + lua_pushcfunction(L, &LuaScript::luaErrorHandler); // errorfunc, value, obj, mt.fieldmethods[get_key] + lua_insert(L, -4); // value, obj, mt.fieldmethods[get_key], errorfunc + int ret = lua_pcall(L, 2, 0, -4); // errorfunc + if(ret != 0) + g_lua.reportError(g_lua.popString()); // errofunc + lua_pop(L, 1); + return 1; + } + lua_pop(L, 1); // mt, value, obj + lua_getfield(L, -1, "base"); // mt.base, mt, value, obj + lua_remove(L, -2); // mt.base, value, obj + } + + // stack: + g_lua.pop(); // value, obj + g_lua.insert(-2); // obj, value + ScriptablePtr scriptable = g_lua.popClassInstance(); + g_lua.pushValue(); // value, value + int refId = g_lua.ref(); + scriptable->associateLuaRef(key, refId); + g_lua.pop(); + return 1; } -int LuaScript::luaCompareClassInstances(lua_State* L) +int LuaScript::luaEqualMetaMethod(lua_State* L) { if(!lua_isuserdata(L, -1) || !lua_isuserdata(L, -2)) { lua_pop(L, 2); lua_pushboolean(L, 0); } else { - ScriptableWeakPtr *objectRef1 = (ScriptableWeakPtr *)lua_touserdata(L, -1); - ScriptableWeakPtr *objectRef2 = (ScriptableWeakPtr *)lua_touserdata(L, -2); + ScriptablePtr *objectRef1 = (ScriptablePtr *)lua_touserdata(L, -1); + ScriptablePtr *objectRef2 = (ScriptablePtr *)lua_touserdata(L, -2); lua_pop(L, 2); - if(objectRef1->lock() == objectRef2->lock()) + if(objectRef1 == objectRef2) lua_pushboolean(L, 1); else lua_pushboolean(L, 0); @@ -414,6 +557,14 @@ int LuaScript::luaCompareClassInstances(lua_State* L) return 1; } +int LuaScript::luaGarbageCollectMetaMethod(lua_State* L) +{ + ScriptablePtr *objectRef = (ScriptablePtr *)lua_touserdata(L, -1); + objectRef->reset(); + lua_pop(L, 1); + return 1; +} + int LuaScript::luaFunctionCallback(lua_State* L) { // look for function id diff --git a/src/framework/script/luascript.h b/src/framework/script/luascript.h index a280d5b6..8aa32d17 100644 --- a/src/framework/script/luascript.h +++ b/src/framework/script/luascript.h @@ -45,14 +45,20 @@ public: void reportError(const std::string& errorDesc, const char *funcName = NULL); void reportErrorWithTraceback(const std::string& errorDesc, const char *funcName = NULL); + void collectGarbage(); int getStackSize(); - void moveTop(int index); + void insert(int index); + + int ref(); + void unref(int ref); + void getRef(int ref); void pushNil(); void pushBoolean(bool b); void pushInteger(int i); void pushString(const std::string& str); void pushUserdata(void* ptr); + void pushValue(int index = -1); void pop(int n = 1); bool popBoolean(); @@ -67,7 +73,7 @@ public: SimpleCallback createSimpleFuncCallback(int funcRef); boost::function createScriptableSelfFuncCallback(int funcRef); - void setLocal(const ScriptablePtr& scriptable, const char *varName, int envIndex = -1); + void setGlobal(const char *varName); void pushClassInstance(const ScriptablePtr& object); ScriptablePtr popClassInstance(); @@ -76,14 +82,19 @@ public: void setupPackageLoader(); void registerClass(const std::string& klass, const std::string& baseClass = ""); + void registerMemberField(const std::string& klass, const std::string& field, LuaCFunction getFunction, LuaCFunction setFunction = NULL); void registerMemberFunction(const std::string& klass, const std::string& functionName, LuaCFunction function); void registerGlobalFunction(const std::string& functionName, LuaCFunction function); void registerModule(const std::string& module); - static int luaFunctionCallback(lua_State* L); static int luaPackageLoader(lua_State* L); - static int luaCollectClassInstance(lua_State* L); - static int luaCompareClassInstances(lua_State* L); + + static int luaIndexMetaMethod(lua_State* L); + static int luaNewIndexMetaMethod(lua_State* L); + static int luaEqualMetaMethod(lua_State* L); + static int luaGarbageCollectMetaMethod(lua_State* L); + + static int luaFunctionCallback(lua_State* L); static int luaErrorHandler(lua_State *L); private: diff --git a/src/framework/script/scriptable.cpp b/src/framework/script/scriptable.cpp new file mode 100644 index 00000000..e7bf6f4b --- /dev/null +++ b/src/framework/script/scriptable.cpp @@ -0,0 +1,51 @@ +/* The MIT License + * + * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include +#include