more scripting features (dynamic fields)

This commit is contained in:
Eduardo Bart 2011-05-02 01:13:57 -03:00
parent f7bb044f48
commit 9e90ae0ee4
19 changed files with 378 additions and 146 deletions

View File

@ -67,6 +67,7 @@ SET(SOURCES
src/framework/core/modules.cpp src/framework/core/modules.cpp
# framework script # framework script
src/framework/script/scriptable.cpp
src/framework/script/luascript.cpp src/framework/script/luascript.cpp
src/framework/script/luafunctions.cpp src/framework/script/luafunctions.cpp

View File

@ -3,8 +3,7 @@ window#enterGameWindow:
size: [236, 178] size: [236, 178]
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onLoad: self:getParent():lock(self) onLoad: self.locked = true
onDestroy: self:getParent():unlock(self)
label#accountNameLabel: label#accountNameLabel:
text: Account name text: Account name
@ -52,7 +51,7 @@ window#enterGameWindow:
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
margin.bottom: 10 margin.bottom: 10
margin.right: 13 margin.right: 13
onClick: self:getParent():destroy() onClick: self.parent:destroy()
textEdit#accountNameTextEdit: textEdit#accountNameTextEdit:
anchors.right: parent.right anchors.right: parent.right

View File

@ -3,8 +3,7 @@ window#infoWindow:
size: [244, 221] size: [244, 221]
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onLoad: self:getParent():lock(self) onLoad: self.locked = true
onDestroy: self:getParent():unlock(self)
panel#infoPanel: panel#infoPanel:
skin: flatPanel skin: flatPanel
@ -60,4 +59,4 @@ window#infoWindow:
anchors.top: parent.top anchors.top: parent.top
margin.top: 191 margin.top: 191
margin.left: 188 margin.left: 188
onClick: self:getParent():destroy() onClick: self.parent:destroy()

View File

@ -49,4 +49,4 @@ panel#background:
anchors.top: parent.top anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
margin.top: 136 margin.top: 136
onClick: App.exit() onClick: onApplicationClose()

View File

@ -5,11 +5,12 @@ end
function onLeaveMenuState() function onLeaveMenuState()
mainMenu:destroy() mainMenu:destroy()
mainMenu = nil
end end
function onApplicationClose() function onApplicationClose()
onLeaveMenuState() onLeaveMenuState()
exitGame() App.exit()
end end
-- here is where everything starts -- here is where everything starts

View File

@ -3,8 +3,7 @@ window#optionsWindow:
size: [286, 262] size: [286, 262]
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onLoad: self:getParent():lock(self) onLoad: self.locked = true
onDestroy: self:getParent():unlock(self)
# general # general
button#generalButton: button#generalButton:
@ -111,4 +110,4 @@ window#optionsWindow:
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
margin.right: 10 margin.right: 10
margin.bottom: 13 margin.bottom: 13
onClick: self:getParent():destroy() onClick: self.parent:destroy()

View File

@ -1,12 +1,11 @@
function messageBox(title, text) function messageBox(title, text)
local messageBoxWindow = UI.load("modules/messagebox/messagebox.yml") local messageBoxWindow = UI.load("modules/messagebox/messagebox.yml")
local messageBoxLabel = messageBoxWindow:getChildByID("messageBoxLabel") local messageBoxLabel = messageBoxWindow:getChildById("messageBoxLabel")
local messageBoxOkButton = messageBoxWindow:getChildByID("messageBoxOkButton") local messageBoxOkButton = messageBoxWindow:getChildById("messageBoxOkButton")
local uiRoot = UI.getRootContainer() messageBoxWindow.locked = true
uiRoot:lock(messageBoxWindow) messageBoxWindow.title = title
messageBoxWindow:setTitle(title) messageBoxLabel.text = text
messageBoxLabel:setText(text)
--messageBoxWindow:setSize(messageBoxLabel:getSize() + Size{20, 20}) --messageBoxWindow:setSize(messageBoxLabel:getSize() + Size{20, 20})
messageBoxWindow:setOnDestroy(function() uiRoot:unlock(self) end) messageBoxWindow.onDestroy = function() self.locked = false end
messageBoxOkButton:setOnClick(function() messageBoxWindow:destroy() end) messageBoxOkButton.onClick = function() messageBoxWindow:destroy() end
end end

View File

@ -31,11 +31,15 @@ Dispatcher g_dispatcher;
void Dispatcher::poll() void Dispatcher::poll()
{ {
while(!m_taskList.empty()) { 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) if(g_engine.getCurrentFrameTicks() < task->ticks)
break; break;
m_taskList.pop(); m_scheduledTaskList.pop();
task->callback(); task->callback();
delete task; delete task;
} }
@ -43,10 +47,13 @@ void Dispatcher::poll()
void Dispatcher::scheduleTask(const SimpleCallback& callback, int delay) 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);
} }

View File

@ -27,18 +27,18 @@
#include <prerequisites.h> #include <prerequisites.h>
class Task { class ScheduledTask {
public: public:
inline Task(const SimpleCallback& _callback) : ticks(0), callback(_callback) { } inline ScheduledTask(const SimpleCallback& _callback) : ticks(0), callback(_callback) { }
inline Task(int _ticks, const SimpleCallback& _callback) : ticks(_ticks), callback(_callback) { } inline ScheduledTask(int _ticks, const SimpleCallback& _callback) : ticks(_ticks), callback(_callback) { }
inline bool operator<(const Task& other) const { return ticks > other.ticks; } inline bool operator<(const ScheduledTask& other) const { return ticks > other.ticks; }
int ticks; int ticks;
SimpleCallback callback; SimpleCallback callback;
}; };
class lessTask : public std::binary_function<Task*&, Task*&, bool> { class lessScheduledTask : public std::binary_function<ScheduledTask*&, ScheduledTask*&, bool> {
public: public:
bool operator()(Task*& t1,Task*& t2) { return (*t1) < (*t2); } bool operator()(ScheduledTask*& t1,ScheduledTask*& t2) { return (*t1) < (*t2); }
}; };
class Dispatcher class Dispatcher
@ -50,13 +50,14 @@ public:
void poll(); void poll();
/// Add an event /// Add an event
void addTask(const SimpleCallback& callback); void addTask(const SimpleCallback& callback, bool pushFront = false);
/// Schedula an event /// Schedula an event
void scheduleTask(const SimpleCallback& callback, int delay); void scheduleTask(const SimpleCallback& callback, int delay);
private: private:
std::priority_queue<Task*, std::vector<Task*>, lessTask> m_taskList; std::list<SimpleCallback> m_taskList;
std::priority_queue<ScheduledTask*, std::vector<ScheduledTask*>, lessScheduledTask> m_scheduledTaskList;
}; };
extern Dispatcher g_dispatcher; extern Dispatcher g_dispatcher;

View File

@ -28,6 +28,7 @@
#include <core/engine.h> #include <core/engine.h>
#include <core/resources.h> #include <core/resources.h>
#include <ui/ui.h> #include <ui/ui.h>
#include <core/dispatcher.h>
void registerLuaFunctions() void registerLuaFunctions()
{ {
@ -45,31 +46,28 @@ void registerLuaFunctions()
g_lua.registerClass("UILayout"); g_lua.registerClass("UILayout");
// UIElement // UIElement
g_lua.registerClass("UIElement", "UILayout"); g_lua.registerClass("UIElement", "UILayout");
g_lua.registerMemberFunction("UIElement", "setOnLoad", &lua_UIElement_setOnLoad); g_lua.registerMemberField("UIElement", "onLoad", NULL, &lua_UIElement_setOnLoad);
g_lua.registerMemberFunction("UIElement", "setOnDestroy", &lua_UIElement_setOnDestroy); g_lua.registerMemberField("UIElement", "onDestroy", NULL, &lua_UIElement_setOnDestroy);
g_lua.registerMemberFunction("UIElement", "getParent", &lua_UIElement_getParent); g_lua.registerMemberField("UIElement", "parent", &lua_UIElement_getParent);
g_lua.registerMemberFunction("UIElement", "destroy", &lua_UIElement_destroy); g_lua.registerMemberFunction("UIElement", "destroy", &lua_UIElement_destroy);
// UIContainer // UIContainer
g_lua.registerClass("UIContainer", "UIElement"); g_lua.registerClass("UIContainer", "UIElement");
g_lua.registerMemberFunction("UIContainer", "getChildByID", &lua_UIContainer_getChildByID); g_lua.registerMemberFunction("UIContainer", "getChildById", &lua_UIContainer_getChildById);
g_lua.registerMemberFunction("UIContainer", "lock", &lua_UIContainer_lock); g_lua.registerMemberField("UIContainer", "locked", NULL, &lua_UIContainer_setLocked);
g_lua.registerMemberFunction("UIContainer", "unlock", &lua_UIContainer_unlock);
// UILabel // UILabel
g_lua.registerClass("UILabel", "UIElement"); g_lua.registerClass("UILabel", "UIElement");
g_lua.registerMemberFunction("UILabel", "setText", &lua_UILabel_setText); g_lua.registerMemberField("UILabel", "text", &lua_UILabel_getText, &lua_UILabel_setText);
g_lua.registerMemberFunction("UILabel", "getText", &lua_UILabel_getText);
// UIButton // UIButton
g_lua.registerClass("UIButton", "UIElement"); g_lua.registerClass("UIButton", "UIElement");
g_lua.registerMemberFunction("UIButton", "setOnClick", &lua_UIButton_setOnClick); g_lua.registerMemberField("UIButton", "onClick", NULL, &lua_UIButton_setOnClick);
// UIWindow // UIWindow
g_lua.registerClass("UIWindow", "UIContainer"); g_lua.registerClass("UIWindow", "UIContainer");
g_lua.registerMemberFunction("UIWindow", "setTitle", &lua_UIWindow_setTitle); g_lua.registerMemberField("UIWindow", "title", &lua_UIWindow_getTitle, &lua_UIWindow_setTitle);
g_lua.registerMemberFunction("UIWindow", "getTitle", &lua_UIWindow_getTitle);
} }
@ -126,19 +124,24 @@ int lua_UI_getRootContainer()
int lua_UIElement_setOnLoad() int lua_UIElement_setOnLoad()
{ {
g_lua.moveTop(-2); g_lua.insert(-2);
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) {
element->setOnLoad(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction())); int funcRef = g_lua.popFunction();
else element->associateLuaRef(__FUNCTION__, funcRef);
element->setOnLoad(g_lua.createScriptableSelfFuncCallback(funcRef));
} else
g_lua.pop(); g_lua.pop();
return 1; return 1;
} }
int lua_UIElement_setOnDestroy() int lua_UIElement_setOnDestroy()
{ {
g_lua.moveTop(-2); g_lua.insert(-2);
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) {
element->setOnDestroy(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction())); int funcRef = g_lua.popFunction();
element->associateLuaRef(__FUNCTION__, funcRef);
element->setOnDestroy(g_lua.createScriptableSelfFuncCallback(funcRef));
}
else else
g_lua.pop(); g_lua.pop();
return 1; return 1;
@ -157,6 +160,7 @@ int lua_UIElement_destroy()
{ {
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance()))
element->destroy(); element->destroy();
g_dispatcher.addTask(boost::bind(&LuaScript::collectGarbage, &g_lua));
return 1; return 1;
} }
@ -164,7 +168,7 @@ int lua_UIElement_destroy()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// UIContainer // UIContainer
int lua_UIContainer_getChildByID() int lua_UIContainer_getChildById()
{ {
std::string id = g_lua.popString(); std::string id = g_lua.popString();
if(UIContainerPtr container = boost::dynamic_pointer_cast<UIContainer>(g_lua.popClassInstance())) if(UIContainerPtr container = boost::dynamic_pointer_cast<UIContainer>(g_lua.popClassInstance()))
@ -174,35 +178,22 @@ int lua_UIContainer_getChildByID()
return 1; return 1;
} }
int lua_UIContainer_lock() int lua_UIContainer_setLocked()
{ {
bool locked = g_lua.popBoolean();
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) { if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) {
if(UIContainerPtr container = boost::dynamic_pointer_cast<UIContainer>(g_lua.popClassInstance())) if(UIContainerPtr container = element->getParent()) {
g_lua.pushBoolean(container->lockElement(element)); if(locked)
else container->lockElement(element);
g_lua.pushBoolean(false); else
container->unlockElement(element);
}
} else { } else {
g_lua.reportFuncErrorWithTraceback("invalid element"); g_lua.reportFuncErrorWithTraceback("invalid element");
g_lua.pushBoolean(false);
} }
return 1; return 1;
} }
int lua_UIContainer_unlock()
{
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance())) {
if(UIContainerPtr container = boost::dynamic_pointer_cast<UIContainer>(g_lua.popClassInstance()))
g_lua.pushBoolean(container->unlockElement(element));
else
g_lua.pushBoolean(false);
} else {
g_lua.reportFuncErrorWithTraceback("invalid element");
g_lua.pushBoolean(false);
}
return 1;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// UILabel // UILabel
@ -229,10 +220,12 @@ int lua_UILabel_getText()
int lua_UIButton_setOnClick() int lua_UIButton_setOnClick()
{ {
g_lua.moveTop(-2); g_lua.insert(-2);
if(UIButtonPtr button = boost::dynamic_pointer_cast<UIButton>(g_lua.popClassInstance())) if(UIButtonPtr button = boost::dynamic_pointer_cast<UIButton>(g_lua.popClassInstance())) {
button->setOnClick(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction())); int funcRef = g_lua.popFunction();
else button->associateLuaRef(__FUNCTION__, funcRef);
button->setOnClick(g_lua.createScriptableSelfFuncCallback(funcRef));
} else
g_lua.pop(); g_lua.pop();
return 1; return 1;
} }

View File

@ -44,9 +44,8 @@ int lua_UIElement_getParent();
int lua_UIElement_destroy(); int lua_UIElement_destroy();
// UIContainer // UIContainer
int lua_UIContainer_getChildByID(); int lua_UIContainer_getChildById();
int lua_UIContainer_lock(); int lua_UIContainer_setLocked();
int lua_UIContainer_unlock();
// UILabel // UILabel
int lua_UILabel_setText(); int lua_UILabel_setText();

View File

@ -128,16 +128,37 @@ void LuaScript::reportErrorWithTraceback(const std::string& errorDesc, const cha
reportError(popString(), funcName); reportError(popString(), funcName);
} }
void LuaScript::collectGarbage()
{
for(int i=0;i<2;i++)
lua_gc(L, LUA_GCCOLLECT, 0);
}
int LuaScript::getStackSize() int LuaScript::getStackSize()
{ {
return lua_gettop(L); return lua_gettop(L);
} }
void LuaScript::moveTop(int index) void LuaScript::insert(int index)
{ {
lua_insert(L, 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) void LuaScript::pop(int n)
{ {
lua_pop(L, n); lua_pop(L, n);
@ -191,11 +212,16 @@ void LuaScript::pushUserdata(void* ptr)
lua_pushlightuserdata(L, ptr); lua_pushlightuserdata(L, ptr);
} }
void LuaScript::pushValue(int index)
{
lua_pushvalue(L, index);
}
void LuaScript::pushClassInstance(const ScriptablePtr& object) void LuaScript::pushClassInstance(const ScriptablePtr& object)
{ {
if(object) { if(object) {
// create weak_ptr to the scriptable object stored as userdata // 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 // set object metatable
lua_getfield(L, LUA_REGISTRYINDEX, (std::string(object->getScriptableName()) + "_mt").c_str()); lua_getfield(L, LUA_REGISTRYINDEX, (std::string(object->getScriptableName()) + "_mt").c_str());
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
@ -208,7 +234,7 @@ ScriptablePtr LuaScript::popClassInstance()
{ {
ScriptablePtr object; ScriptablePtr object;
if(lua_isuserdata(L, -1)) { // instances are store as userdata 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) if(!object)
reportErrorWithTraceback("attempt to retrive class instance from a object that is already expired"); reportErrorWithTraceback("attempt to retrive class instance from a object that is already expired");
} else if(!lua_isnil(L, -1)) // we accept nil values } else if(!lua_isnil(L, -1)) // we accept nil values
@ -224,18 +250,18 @@ void LuaScript::pushFunction(int functionRef)
int LuaScript::popFunction() int LuaScript::popFunction()
{ {
return luaL_ref(L, LUA_REGISTRYINDEX); return ref();
} }
void LuaScript::releaseFunction(int functionRef) void LuaScript::releaseFunction(int functionRef)
{ {
luaL_unref(L, LUA_REGISTRYINDEX, functionRef); unref(functionRef);
} }
void LuaScript::callFunction(int numArgs) void LuaScript::callFunction(int numArgs)
{ {
int size = lua_gettop(L); int size = lua_gettop(L);
int errorIndex = size - numArgs; int errorIndex = -(numArgs + 2);
lua_pushcfunction(L, &LuaScript::luaErrorHandler); lua_pushcfunction(L, &LuaScript::luaErrorHandler);
lua_insert(L, errorIndex); lua_insert(L, errorIndex);
@ -244,9 +270,10 @@ void LuaScript::callFunction(int numArgs)
reportError(popString()); 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!"); reportError("stack size changed!");
} }
@ -261,18 +288,20 @@ SimpleCallback LuaScript::createSimpleFuncCallback(int funcRef)
boost::function<void(ScriptablePtr)> LuaScript::createScriptableSelfFuncCallback(int funcRef) boost::function<void(ScriptablePtr)> LuaScript::createScriptableSelfFuncCallback(int funcRef)
{ {
return [this, funcRef](ScriptablePtr scriptable) { return [this, funcRef](ScriptablePtr scriptable) {
pushClassInstance(scriptable);
setGlobal("self");
pushFunction(funcRef); pushFunction(funcRef);
setLocal(scriptable, "self");
callFunction(); 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); lua_setfield(L, LUA_GLOBALSINDEX, varName);
pushClassInstance(scriptable);
lua_setfield(L, -2, varName);
lua_pop(L, 1);
} }
void LuaScript::setupPackageLoader() void LuaScript::setupPackageLoader()
@ -295,46 +324,78 @@ void LuaScript::setupPackageLoader()
void LuaScript::registerClass(const std::string& klass, const std::string& baseClass) void LuaScript::registerClass(const std::string& klass, const std::string& baseClass)
{ {
// klass_mt = {} // klass_mt = {}
lua_newtable(L); // klass metatable lua_newtable(L);
lua_pushvalue(L, -1); // another reference to the metatable lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, (klass + "_mt").c_str()); // register the metatable at registry index lua_setfield(L, LUA_REGISTRYINDEX, (klass + "_mt").c_str());
// set __gc metamethod, which collects userdata // set __index metamethod
lua_pushcfunction(L, &LuaScript::luaCollectClassInstance); lua_pushcfunction(L, &LuaScript::luaIndexMetaMethod);
lua_setfield(L, -2, "__gc");
// set __eq metamethod
lua_pushcfunction(L, &LuaScript::luaCompareClassInstances);
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");
// klass_mt.__index = klass
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
// pop the class metatable // set __newindex metamethod
lua_pushcfunction(L, &LuaScript::luaNewIndexMetaMethod);
lua_setfield(L, -2, "__newindex");
// set __eq metamethod
lua_pushcfunction(L, &LuaScript::luaEqualMetaMethod);
lua_setfield(L, -2, "__eq");
// set __gc metamethod, which collects userdata
lua_pushcfunction(L, &LuaScript::luaGarbageCollectMetaMethod);
lua_setfield(L, -2, "__gc");
// 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");
}
lua_pop(L, 1); lua_pop(L, 1);
}
if(baseClass.length() > 0) { void LuaScript::registerMemberField(const std::string& klass, const std::string& field, LuaScript::LuaCFunction getFunction, LuaScript::LuaCFunction setFunction)
// add klass table to the top of the stack {
lua_getfield(L, LUA_GLOBALSINDEX, klass.c_str()); if(getFunction) {
int functionId = m_functions.size();
m_functions.push_back(getFunction);
// redirect = { __index = baseClass } // push the class table
lua_newtable(L); lua_getfield(L, LUA_GLOBALSINDEX, (klass + "_fieldmethods").c_str());
lua_getfield(L, LUA_GLOBALSINDEX, baseClass.c_str()); // push the function id
lua_setfield(L, -2, "__index"); 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);
}
// setmetatable(klass, redirect) if(setFunction) {
lua_setmetatable(L, -2); int functionId = m_functions.size();
m_functions.push_back(setFunction);
// pop the derived class table // 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); lua_pop(L, 1);
} }
} }
@ -389,24 +450,106 @@ int LuaScript::luaPackageLoader(lua_State* L)
return 1; return 1;
} }
int LuaScript::luaCollectClassInstance(lua_State* L) int LuaScript::luaIndexMetaMethod(lua_State* L)
{ {
ScriptableWeakPtr *objectRef = (ScriptableWeakPtr *)lua_touserdata(L, -1); std::string key = lua_tostring(L, -1); // key, obj
objectRef->reset(); 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); 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; return 1;
} }
int LuaScript::luaCompareClassInstances(lua_State* L) int LuaScript::luaNewIndexMetaMethod(lua_State* L)
{
// 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::luaEqualMetaMethod(lua_State* L)
{ {
if(!lua_isuserdata(L, -1) || !lua_isuserdata(L, -2)) { if(!lua_isuserdata(L, -1) || !lua_isuserdata(L, -2)) {
lua_pop(L, 2); lua_pop(L, 2);
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
} else { } else {
ScriptableWeakPtr *objectRef1 = (ScriptableWeakPtr *)lua_touserdata(L, -1); ScriptablePtr *objectRef1 = (ScriptablePtr *)lua_touserdata(L, -1);
ScriptableWeakPtr *objectRef2 = (ScriptableWeakPtr *)lua_touserdata(L, -2); ScriptablePtr *objectRef2 = (ScriptablePtr *)lua_touserdata(L, -2);
lua_pop(L, 2); lua_pop(L, 2);
if(objectRef1->lock() == objectRef2->lock()) if(objectRef1 == objectRef2)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
@ -414,6 +557,14 @@ int LuaScript::luaCompareClassInstances(lua_State* L)
return 1; 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) int LuaScript::luaFunctionCallback(lua_State* L)
{ {
// look for function id // look for function id

View File

@ -45,14 +45,20 @@ public:
void reportError(const std::string& errorDesc, const char *funcName = NULL); void reportError(const std::string& errorDesc, const char *funcName = NULL);
void reportErrorWithTraceback(const std::string& errorDesc, const char *funcName = NULL); void reportErrorWithTraceback(const std::string& errorDesc, const char *funcName = NULL);
void collectGarbage();
int getStackSize(); int getStackSize();
void moveTop(int index); void insert(int index);
int ref();
void unref(int ref);
void getRef(int ref);
void pushNil(); void pushNil();
void pushBoolean(bool b); void pushBoolean(bool b);
void pushInteger(int i); void pushInteger(int i);
void pushString(const std::string& str); void pushString(const std::string& str);
void pushUserdata(void* ptr); void pushUserdata(void* ptr);
void pushValue(int index = -1);
void pop(int n = 1); void pop(int n = 1);
bool popBoolean(); bool popBoolean();
@ -67,7 +73,7 @@ public:
SimpleCallback createSimpleFuncCallback(int funcRef); SimpleCallback createSimpleFuncCallback(int funcRef);
boost::function<void(ScriptablePtr)> createScriptableSelfFuncCallback(int funcRef); boost::function<void(ScriptablePtr)> createScriptableSelfFuncCallback(int funcRef);
void setLocal(const ScriptablePtr& scriptable, const char *varName, int envIndex = -1); void setGlobal(const char *varName);
void pushClassInstance(const ScriptablePtr& object); void pushClassInstance(const ScriptablePtr& object);
ScriptablePtr popClassInstance(); ScriptablePtr popClassInstance();
@ -76,14 +82,19 @@ public:
void setupPackageLoader(); void setupPackageLoader();
void registerClass(const std::string& klass, const std::string& baseClass = ""); 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 registerMemberFunction(const std::string& klass, const std::string& functionName, LuaCFunction function);
void registerGlobalFunction(const std::string& functionName, LuaCFunction function); void registerGlobalFunction(const std::string& functionName, LuaCFunction function);
void registerModule(const std::string& module); void registerModule(const std::string& module);
static int luaFunctionCallback(lua_State* L);
static int luaPackageLoader(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); static int luaErrorHandler(lua_State *L);
private: private:

View File

@ -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 <prerequisites.h>
#include <script/scriptable.h>
#include <script/luascript.h>
#include <core/dispatcher.h>
void Scriptable::associateLuaRef(const std::string& refName, int refId)
{
// check if there is already a ref with this name
if(m_luaRefs.find(refName) != m_luaRefs.end())
g_lua.unref(m_luaRefs[refName]);
m_luaRefs[refName] = refId;
}
int Scriptable::getLuaRef(const std::string& refName)
{
if(m_luaRefs.find(refName) != m_luaRefs.end())
return m_luaRefs[refName];
return -1;
}
void Scriptable::clearLuaRefs()
{
foreach(auto pair, m_luaRefs) {
g_lua.unref(pair.second);
}
m_luaRefs.clear();
}

View File

@ -31,9 +31,15 @@ class Scriptable : public boost::enable_shared_from_this<Scriptable>
{ {
public: public:
virtual const char *getScriptableName() const { return NULL; } virtual const char *getScriptableName() const { return NULL; }
void associateLuaRef(const std::string& refName, int refId);
int getLuaRef(const std::string& refName);
void clearLuaRefs();
private:
std::map<std::string, int> m_luaRefs;
}; };
typedef boost::shared_ptr<Scriptable> ScriptablePtr; typedef boost::shared_ptr<Scriptable> ScriptablePtr;
typedef boost::weak_ptr<Scriptable> ScriptableWeakPtr;
#endif #endif

View File

@ -66,6 +66,9 @@ void UIContainer::removeChild(UIElementPtr child)
if(m_focusedElement == child) if(m_focusedElement == child)
setFocusedElement(UIElementPtr()); setFocusedElement(UIElementPtr());
// try to unlock
unlockElement(child);
// remove from children list // remove from children list
m_children.remove(child); m_children.remove(child);

View File

@ -64,6 +64,17 @@ void UIElement::internalOnDestroy()
getParent()->removeChild(me); getParent()->removeChild(me);
} }
// free script stuff
clearLuaRefs();
g_dispatcher.addTask(boost::bind(&UIElement::internalDestroyCheck, asUIElement()));
}
void UIElement::internalDestroyCheck()
{
//logTraceDebug(getId());
UIElementPtr me = asUIElement();
// check for leaks, the number of references must be always 2 here // check for leaks, the number of references must be always 2 here
if(me.use_count() != 2 && me != UIContainer::getRoot()) { if(me.use_count() != 2 && me != UIContainer::getRoot()) {
flogWarning("destroyed element with id '%s', but it still have %d references left", getId() % (me.use_count()-2)); flogWarning("destroyed element with id '%s', but it still have %d references left", getId() % (me.use_count()-2));

View File

@ -52,6 +52,7 @@ public:
/// Destroy this element by removing it from its parent /// Destroy this element by removing it from its parent
void destroy(); void destroy();
virtual void internalOnDestroy(); virtual void internalOnDestroy();
virtual void internalDestroyCheck();
/// Draw element /// Draw element
virtual void render(); virtual void render();

View File

@ -215,7 +215,7 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
// load events // load events
if(node.FindValue("onLoad")) { if(node.FindValue("onLoad")) {
const YAML::Node& cnode = node["onLoad"]; const YAML::Node& cnode = node["onLoad"];
int funcRef = g_lua.loadBufferAsFunction(cnode.Read<std::string>()); int funcRef = g_lua.loadBufferAsFunction(cnode.Read<std::string>(), element->getId() + ":onLoad");
if(funcRef != LUA_REFNIL) { if(funcRef != LUA_REFNIL) {
g_lua.pushClassInstance(element); g_lua.pushClassInstance(element);
g_lua.pushFunction(funcRef); g_lua.pushFunction(funcRef);
@ -226,7 +226,7 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
if(node.FindValue("onDestroy")) { if(node.FindValue("onDestroy")) {
const YAML::Node& cnode = node["onDestroy"]; const YAML::Node& cnode = node["onDestroy"];
int funcRef = g_lua.loadBufferAsFunction(cnode.Read<std::string>()); int funcRef = g_lua.loadBufferAsFunction(cnode.Read<std::string>(), element->getId() + ":onDestroy");
if(funcRef != LUA_REFNIL) { if(funcRef != LUA_REFNIL) {
g_lua.pushClassInstance(element); g_lua.pushClassInstance(element);
g_lua.pushFunction(funcRef); g_lua.pushFunction(funcRef);
@ -314,7 +314,7 @@ void UILoader::loadButton(const UIButtonPtr& button, const YAML::Node& node)
// set on click event // set on click event
if(node.FindValue("onClick")) { if(node.FindValue("onClick")) {
int funcRef = g_lua.loadBufferAsFunction(node["onClick"].Read<std::string>()); int funcRef = g_lua.loadBufferAsFunction(node["onClick"].Read<std::string>(), button->getId() + ":onClick");
if(funcRef != LUA_REFNIL) { if(funcRef != LUA_REFNIL) {
g_lua.pushClassInstance(button); g_lua.pushClassInstance(button);
g_lua.pushFunction(funcRef); g_lua.pushFunction(funcRef);