more scripting features (dynamic fields)

master
Eduardo Bart 13 years ago
parent f7bb044f48
commit 9e90ae0ee4

@ -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

@ -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

@ -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()

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

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

@ -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()

@ -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

@ -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);
}

@ -27,18 +27,18 @@
#include <prerequisites.h>
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<Task*&, Task*&, bool> {
class lessScheduledTask : public std::binary_function<ScheduledTask*&, ScheduledTask*&, bool> {
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<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;

@ -28,6 +28,7 @@
#include <core/engine.h>
#include <core/resources.h>
#include <ui/ui.h>
#include <core/dispatcher.h>
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<UIElement>(g_lua.popClassInstance()))
element->setOnLoad(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction()));
else
g_lua.insert(-2);
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(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<UIElement>(g_lua.popClassInstance()))
element->setOnDestroy(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction()));
g_lua.insert(-2);
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(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<UIElement>(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<UIContainer>(g_lua.popClassInstance()))
@ -174,35 +178,22 @@ int lua_UIContainer_getChildByID()
return 1;
}
int lua_UIContainer_lock()
{
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->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<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);
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<UIButton>(g_lua.popClassInstance()))
button->setOnClick(g_lua.createScriptableSelfFuncCallback(g_lua.popFunction()));
else
g_lua.insert(-2);
if(UIButtonPtr button = boost::dynamic_pointer_cast<UIButton>(g_lua.popClassInstance())) {
int funcRef = g_lua.popFunction();
button->associateLuaRef(__FUNCTION__, funcRef);
button->setOnClick(g_lua.createScriptableSelfFuncCallback(funcRef));
} else
g_lua.pop();
return 1;
}

@ -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();

@ -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<void(ScriptablePtr)> 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

@ -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<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);
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:

@ -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();
}

@ -31,9 +31,15 @@ class Scriptable : public boost::enable_shared_from_this<Scriptable>
{
public:
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::weak_ptr<Scriptable> ScriptableWeakPtr;
#endif

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

@ -64,6 +64,17 @@ void UIElement::internalOnDestroy()
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
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));

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

@ -215,7 +215,7 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
// load events
if(node.FindValue("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) {
g_lua.pushClassInstance(element);
g_lua.pushFunction(funcRef);
@ -226,7 +226,7 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
if(node.FindValue("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) {
g_lua.pushClassInstance(element);
g_lua.pushFunction(funcRef);
@ -314,7 +314,7 @@ void UILoader::loadButton(const UIButtonPtr& button, const YAML::Node& node)
// set on click event
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) {
g_lua.pushClassInstance(button);
g_lua.pushFunction(funcRef);

Loading…
Cancel
Save