/** * server/src/lua/event.cpp - Lua event wrapper * * This file is part of the traintastic source code. * * Copyright (C) 2021-2022 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "event.hpp" #include "to.hpp" #include "checkarguments.hpp" #include "error.hpp" #include "eventhandler.hpp" #include "sandbox.hpp" #include "../core/abstractevent.hpp" #include "../core/object.hpp" namespace Lua { struct EventData { ObjectPtrWeak object; AbstractEvent& event; EventData(AbstractEvent& _event) : object{_event.object().weak_from_this()}, event{_event} { } }; AbstractEvent& Event::check(lua_State* L, int index) { auto& data = *static_cast(luaL_checkudata(L, index, metaTableName)); if(!data.object.expired()) return data.event; errorDeadObject(L); } AbstractEvent* Event::test(lua_State* L, int index) { auto* data = static_cast(luaL_testudata(L, index, metaTableName)); if(!data) return nullptr; if(!data->object.expired()) return &data->event; errorDeadObject(L); } void Event::push(lua_State* L, AbstractEvent& value) { new(lua_newuserdata(L, sizeof(EventData))) EventData(value); luaL_getmetatable(L, metaTableName); lua_setmetatable(L, -2); } void Event::registerType(lua_State* L) { luaL_newmetatable(L, metaTableName); lua_pushcfunction(L, __index); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, __call); lua_setfield(L, -2, "__call"); lua_pushcfunction(L, __gc); lua_setfield(L, -2, "__gc"); lua_pop(L, 1); } int Event::__index(lua_State* L) { auto& event = check(L, 1); auto name = to(L, 2); if(name == "connect") { push(L, event); lua_pushcclosure(L, connect, 1); } else if(name == "disconnect") { push(L, event); lua_pushcclosure(L, disconnect, 1); } else lua_pushnil(L); return 1; } int Event::__call(lua_State* L) { checkArguments(L, 2, 3); auto& event = check(L, 1); auto handler = std::make_shared(event, L, 2); event.connect(handler); lua_pushinteger(L, Sandbox::getStateData(L).registerEventHandler(handler)); return 1; } int Event::__gc(lua_State* L) { static_cast(lua_touserdata(L, 1))->~EventData(); return 0; } int Event::connect(lua_State* L) { checkArguments(L, 1, 2); auto& event = check(L, lua_upvalueindex(1)); auto handler = std::make_shared(event, L); event.connect(handler); lua_pushinteger(L, Sandbox::getStateData(L).registerEventHandler(handler)); return 1; } int Event::disconnect(lua_State* L) { checkArguments(L, 1); auto& event = check(L, lua_upvalueindex(1)); auto handler = Sandbox::getStateData(L).getEventHandler(luaL_checkinteger(L, 1)); lua_pushboolean(L, handler && &handler->event() == &event && handler->disconnect()); return 1; } }