From ca8e4aae718a44bdfc4f81d34d353f96bcdc0d65 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Wed, 1 Apr 2026 01:42:54 +0200 Subject: [PATCH] Refactor Lua bindings to not use FFI anymore - Lua bindings are no longer using FFI in favor of the "classic" Lua C API. - This makes it work properly on native Linux builds. - Changed the Lua API slightly: - Removed the `api.event_preparefunc()` function because it is no longer necessary. Instead, you can pass any function you want without preparing it first. - Added an `api.event_exists()` function. Takes a single `name` argument. - Any `api.find_*()` functions return `nil` instead of an userdata representing a null pointer when the requested object is not found. --- scripting/lua.cpp | 477 ++++++++++++++++++++++++++++---------------- scripting/lua.h | 35 +++- scripting/lua_ffi.h | 96 --------- world/Event.cpp | 38 ++-- world/Event.h | 18 +- 5 files changed, 370 insertions(+), 294 deletions(-) delete mode 100644 scripting/lua_ffi.h diff --git a/scripting/lua.cpp b/scripting/lua.cpp index c6585e76..e9932f19 100644 --- a/scripting/lua.cpp +++ b/scripting/lua.cpp @@ -4,7 +4,6 @@ #include "utilities/Logs.h" #include "world/MemCell.h" #include "vehicle/Driver.h" -#include "scripting/lua_ffi.h" #include "simulation/simulation.h" lua::lua() @@ -15,12 +14,33 @@ lua::lua() lua_atpanic(state, atpanic); luaL_openlibs(state); - lua_getglobal(state, "package"); - lua_pushstring(state, "preload"); - lua_gettable(state, -2); - lua_pushcclosure(state, openffi, 0); - lua_setfield(state, -2, "eu07.events"); - lua_settop(state, 0); + static constexpr luaL_Reg api[] = + { + {"event_create", scriptapi_event_create}, + {"event_find", scriptapi_event_find}, + {"event_exists", scriptapi_event_exists}, + {"event_getname", scriptapi_event_getname}, + {"event_dispatch", scriptapi_event_dispatch}, + {"event_dispatch_n", scriptapi_event_dispatch_n}, + {"track_find", scriptapi_track_find}, + {"track_isoccupied", scriptapi_track_isoccupied}, + {"track_isoccupied_n", scriptapi_track_isoccupied_n}, + {"isolated_find", scriptapi_isolated_find}, + {"isolated_isoccupied", scriptapi_isolated_isoccupied}, + {"isolated_isoccupied_n", scriptapi_isolated_isoccupied_n}, + {"train_getname", scriptapi_train_getname}, + {"dynobj_putvalues", scriptapi_dynobj_putvalues}, + {"memcell_find", scriptapi_memcell_find}, + {"memcell_read", scriptapi_memcell_read}, + {"memcell_read_n", scriptapi_memcell_read_n}, + {"memcell_update", scriptapi_memcell_update}, + {"memcell_update_n", scriptapi_memcell_update_n}, + {"random", scriptapi_random}, + {"writelog", scriptapi_writelog}, + {"writeerrorlog", scriptapi_writeerrorlog}, + {nullptr, nullptr} + }; + luaL_register(state, "eu07.events", api); } lua::~lua() @@ -29,12 +49,12 @@ lua::~lua() state = nullptr; } -std::string lua::get_error() +std::string lua::get_error() const { - return std::string(lua_tostring(state, -1)); + return lua_tostring(state, -1); } -void lua::interpret(std::string file) +void lua::interpret(const std::string& file) const { if (luaL_dofile(state, file.c_str())) { const char *str = lua_tostring(state, -1); @@ -42,171 +62,294 @@ void lua::interpret(std::string file) } } -// NOTE: we cannot throw exceptions in callbacks -// because it is not supported by LuaJIT on x86 windows - int lua::atpanic(lua_State *s) { - std::string err(lua_tostring(s, -1)); + std::string err = lua_tostring(s, 1); ErrorLog(err, logtype::lua); return 0; } -int lua::openffi(lua_State *s) +/// Dereferences the function handler from the Lua registry. +void lua::unref(lua_State *L, const int ref) { - if (luaL_dostring(s, lua_ffi)) - { - ErrorLog(std::string(lua_tostring(s, -1)), logtype::lua); - return 0; - } - return 1; + luaL_unref(L, LUA_REGISTRYINDEX, ref); } -#if defined _WIN32 -# if defined __GNUC__ -# define EXPORT __attribute__ ((dllexport)) -# else -# define EXPORT __declspec(dllexport) -# endif -#elif defined __GNUC__ -# define EXPORT __attribute__ ((visibility ("default"))) -#else -# define EXPORT -#endif - -extern "C" +/// Executes the callback function of an event created with `scriptapi_event_create`. +void lua::dispatch_event(lua_State *L, const int handler, basic_event *event, const TDynamicObject *activator) { - EXPORT basic_event* scriptapi_event_create(const char* name, double delay, double randomdelay, lua::eventhandler_t handler) - { - basic_event *event = new lua_event(handler); - event->m_name = std::string(name); - event->m_delay = delay; - event->m_delayrandom = randomdelay; - - if (simulation::Events.insert(event)) - return event; - else - return nullptr; - } - - EXPORT basic_event* scriptapi_event_find(const char* name) - { - std::string str(name); - basic_event *e = simulation::Events.FindEvent(str); - if (e) - return e; - else - WriteLog("lua: missing event: " + str); - return nullptr; - } - - EXPORT TTrack* scriptapi_track_find(const char* name) - { - std::string str(name); - TTrack *track = simulation::Paths.find(str); - if (track) - return track; - else - WriteLog("lua: missing track: " + str); - return nullptr; - } - - EXPORT bool scriptapi_track_isoccupied(TTrack* track) - { - if (track) - return !track->IsEmpty(); - return false; - } - - EXPORT TIsolated* scriptapi_isolated_find(const char* name) - { - std::string str(name); - TIsolated *isolated = TIsolated::Find(name); - if (isolated) - return isolated; - else - WriteLog("lua: missing isolated: " + str); - return nullptr; - } - - EXPORT bool scriptapi_isolated_isoccupied(TIsolated* isolated) - { - if (isolated) - return isolated->Busy(); - return false; - } - - EXPORT const char* scriptapi_event_getname(basic_event *e) - { - if (e) - return e->m_name.c_str(); - return nullptr; - } - - EXPORT const char* scriptapi_train_getname(TDynamicObject *dyn) - { - if (dyn && dyn->Mechanik) - return dyn->Mechanik->TrainName().c_str(); - return nullptr; - } - - EXPORT void scriptapi_event_dispatch(basic_event *e, TDynamicObject *activator, double delay) - { - if (e) - simulation::Events.AddToQuery(e, activator, delay); - } - - EXPORT double scriptapi_random(double a, double b) - { - return Random(a, b); - } - - EXPORT void scriptapi_writelog(const char* txt) - { - WriteLog("lua: log: " + std::string(txt), logtype::lua); - } - - EXPORT void scriptapi_writeerrorlog(const char* txt) - { - ErrorLog("lua: log: " + std::string(txt), logtype::lua); - } - - struct memcell_values { const char *str; double num1; double num2; }; - - EXPORT TMemCell* scriptapi_memcell_find(const char *name) - { - std::string str(name); - TMemCell *mc = simulation::Memory.find(str); - if (mc) - return mc; - else - WriteLog("lua: missing memcell: " + str); - return nullptr; - } - - EXPORT memcell_values scriptapi_memcell_read(TMemCell *mc) - { - if (!mc) - return { nullptr, 0.0, 0.0 }; - return { mc->Text().c_str(), mc->Value1(), mc->Value2() }; - } - - EXPORT void scriptapi_memcell_update(TMemCell *mc, const char *str, double num1, double num2) - { - if (!mc) - return; - mc->UpdateValues(std::string(str), num1, num2, - basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); - } - - EXPORT void scriptapi_dynobj_putvalues(TDynamicObject *dyn, const char *str, double num1, double num2) - { - if (!dyn) - return; - TLocation loc; - if (dyn->Mechanik) - dyn->Mechanik->PutCommand(std::string(str), num1, num2, loc); - else - dyn->MoverParameters->PutCommand(std::string(str), num1, num2, loc); - } + lua_rawgeti(L, LUA_REGISTRYINDEX, handler); + lua_pushlightuserdata(L, event); + lua_pushlightuserdata(L, const_cast(activator)); + lua_call(L, 2, 0); +} + +void lua::push_memcell_values(lua_State *L, const TMemCell *mc) +{ + lua_createtable(L, 0, 3); + lua_pushstring(L, "str"); + lua_pushstring(L, mc ? mc->Text().c_str() : nullptr); + lua_settable(L, -3); + lua_pushstring(L, "num1"); + lua_pushnumber(L, mc ? mc->Value1() : 0.0); + lua_settable(L, -3); + lua_pushstring(L, "num2"); + lua_pushnumber(L, mc ? mc->Value2() : 0.0); + lua_settable(L, -3); +} + +int lua::scriptapi_event_create(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + double delay = lua_tonumber(L, 2); + double randomdelay = lua_tonumber(L, 3); + lua_pushvalue(L, 4); + int funcRef = luaL_ref(L, LUA_REGISTRYINDEX); + + basic_event *event = new lua_event(L, funcRef); + event->m_name = name; + event->m_delay = delay; + event->m_delayrandom = randomdelay; + if (simulation::Events.insert(event)) + { + lua_pushlightuserdata(L, event); + return 1; + } + return 0; +} + +int lua::scriptapi_event_find(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + basic_event *event = simulation::Events.FindEvent(name); + if (event) + { + lua_pushlightuserdata(L, event); + return 1; + } + ErrorLog("lua: missing event: " + name); + return 0; +} + +int lua::scriptapi_event_exists(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + basic_event *event = simulation::Events.FindEvent(name); + lua_pushboolean(L, event != nullptr); + return 1; +} + +int lua::scriptapi_event_getname(lua_State *L) +{ + auto *event = static_cast(lua_touserdata(L, 1)); + if (event) + { + lua_pushstring(L, event->m_name.c_str()); + return 1; + } + return 0; +} + +int lua::scriptapi_event_dispatch(lua_State *L) +{ + auto *event = static_cast(lua_touserdata(L, 1)); + auto *activator = static_cast(lua_touserdata(L, 2)); + double delay = lua_tonumber(L, 3); + if (event) + simulation::Events.AddToQuery(event, activator, delay); + return 0; +} + +int lua::scriptapi_event_dispatch_n(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + auto *activator = static_cast(lua_touserdata(L, 2)); + double delay = lua_tonumber(L, 3); + basic_event *event = simulation::Events.FindEvent(name); + if (event) + simulation::Events.AddToQuery(event, activator, delay); + else + ErrorLog("lua: missing event: " + name); + return 0; +} + +int lua::scriptapi_track_find(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TTrack *track = simulation::Paths.find(name); + if (track) + { + lua_pushlightuserdata(L, track); + return 1; + } + ErrorLog("lua: missing track: " + name); + return 0; +} + +int lua::scriptapi_track_isoccupied(lua_State *L) +{ + auto *track = static_cast(lua_touserdata(L, 1)); + if (track) + { + lua_pushboolean(L, !track->IsEmpty()); + return 1; + } + return 0; +} + +int lua::scriptapi_track_isoccupied_n(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TTrack *track = simulation::Paths.find(name); + if (track) + { + lua_pushboolean(L, !track->IsEmpty()); + return 1; + } + ErrorLog("lua: missing track: " + name); + return 0; +} + +int lua::scriptapi_isolated_find(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TIsolated *isolated = TIsolated::Find(name); + if (isolated) + { + lua_pushlightuserdata(L, isolated); + return 1; + } + ErrorLog("lua: missing isolated: " + name); + return 0; +} + +int lua::scriptapi_isolated_isoccupied(lua_State *L) +{ + auto *isolated = static_cast(lua_touserdata(L, 1)); + if (isolated) + { + lua_pushboolean(L, isolated->Busy()); + return 1; + } + return 0; +} + +int lua::scriptapi_isolated_isoccupied_n(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TIsolated *isolated = TIsolated::Find(name); + if (isolated) + { + lua_pushboolean(L, isolated->Busy()); + return 1; + } + ErrorLog("lua: missing isolated: " + name); + return 0; +} + +int lua::scriptapi_train_getname(lua_State *L) +{ + auto *dyn = static_cast(lua_touserdata(L, 1)); + if (dyn && dyn->Mechanik) + { + lua_pushstring(L, dyn->Mechanik->TrainName().c_str()); + return 1; + } + return 0; +} + +int lua::scriptapi_dynobj_putvalues(lua_State *L) +{ + auto *dyn = static_cast(lua_touserdata(L, 1)); + auto *mc = static_cast(lua_touserdata(L, 2)); + std::string str = mc->str; + double num1 = mc->num1; + double num2 = mc->num2; + if (!dyn) + return 0; + TLocation loc{}; + if (dyn->Mechanik) + dyn->Mechanik->PutCommand(std::string(str), num1, num2, loc); + else + dyn->MoverParameters->PutCommand(std::string(str), num1, num2, loc); + return 0; +} + +int lua::scriptapi_memcell_find(lua_State *L) +{ + std::string str = lua_tostring(L, 1); + TMemCell *mc = simulation::Memory.find(str); + if (mc) + { + lua_pushlightuserdata(L, mc); + return 1; + } + ErrorLog("lua: missing memcell: " + str); + return 0; +} + +int lua::scriptapi_memcell_read(lua_State *L) +{ + auto *mc = static_cast(lua_touserdata(L, 1)); + push_memcell_values(L, mc); + return 1; +} + +int lua::scriptapi_memcell_read_n(lua_State *L) +{ + std::string str = lua_tostring(L, 1); + TMemCell *mc = simulation::Memory.find(str); + push_memcell_values(L, mc); + if (!mc) + ErrorLog("lua: missing memcell: " + str); + return 1; +} + +int lua::scriptapi_memcell_update(lua_State *L) +{ + auto *mc = static_cast(lua_touserdata(L, 1)); + std::string str = lua_tostring(L, 2); + double num1 = lua_tonumber(L, 3); + double num2 = lua_tonumber(L, 4); + if (mc) + mc->UpdateValues(str, num1, num2, + basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); + return 0; +} + +int lua::scriptapi_memcell_update_n(lua_State *L) +{ + std::string mstr = lua_tostring(L, 1); + std::string str = lua_tostring(L, 2); + double num1 = lua_tonumber(L, 3); + double num2 = lua_tonumber(L, 4); + TMemCell *mc = simulation::Memory.find(mstr); + if (mc) + mc->UpdateValues(str, num1, num2, + basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); + else + ErrorLog("lua: missing memcell: " + str); + return 0; +} + +int lua::scriptapi_random(lua_State *L) +{ + double a = lua_tonumber(L, 1); + double b = lua_tonumber(L, 2); + lua_pushnumber(L, Random(a, b)); + return 1; +} + +int lua::scriptapi_writelog(lua_State *L) +{ + std::string txt = lua_tostring(L, 1); + WriteLog("lua: log: " + txt, logtype::lua); + return 0; +} + +int lua::scriptapi_writeerrorlog(lua_State *L) +{ + std::string txt = lua_tostring(L, 1); + ErrorLog("lua: log: " + txt, logtype::lua); + return 0; } diff --git a/scripting/lua.h b/scripting/lua.h index 0e781279..bb5359f9 100644 --- a/scripting/lua.h +++ b/scripting/lua.h @@ -1,7 +1,9 @@ #pragma once #include +#include class basic_event; +class TMemCell; class TDynamicObject; class lua @@ -9,14 +11,39 @@ class lua lua_State *state; static int atpanic(lua_State *s); - static int openffi(lua_State *s); + + static int scriptapi_event_create(lua_State *L); + static int scriptapi_event_find(lua_State *L); + static int scriptapi_event_exists(lua_State *L); + static int scriptapi_event_getname(lua_State *L); + static int scriptapi_event_dispatch(lua_State *L); + static int scriptapi_event_dispatch_n(lua_State *L); + static int scriptapi_track_find(lua_State *L); + static int scriptapi_track_isoccupied(lua_State *L); + static int scriptapi_track_isoccupied_n(lua_State *L); + static int scriptapi_isolated_find(lua_State *L); + static int scriptapi_isolated_isoccupied(lua_State *L); + static int scriptapi_isolated_isoccupied_n(lua_State *L); + static int scriptapi_train_getname(lua_State *L); + static int scriptapi_dynobj_putvalues(lua_State *L); + static int scriptapi_memcell_find(lua_State *L); + static int scriptapi_memcell_read(lua_State *L); + static int scriptapi_memcell_read_n(lua_State *L); + static int scriptapi_memcell_update(lua_State *L); + static int scriptapi_memcell_update_n(lua_State *L); + static int scriptapi_random(lua_State *L); + static int scriptapi_writelog(lua_State *L); + static int scriptapi_writeerrorlog(lua_State *L); public: lua(); ~lua(); - std::string get_error(); - void interpret(std::string file); + std::string get_error() const; + void interpret(const std::string& file) const; + static void unref(lua_State *L, int ref); + static void dispatch_event(lua_State *L, int handler, basic_event *event, const TDynamicObject *activator); + static void push_memcell_values(lua_State *L, const TMemCell *mc); - typedef void (*eventhandler_t)(basic_event*, const TDynamicObject*); + struct memcell_values { const char *str; double num1; double num2; }; }; diff --git a/scripting/lua_ffi.h b/scripting/lua_ffi.h deleted file mode 100644 index 94105bd6..00000000 --- a/scripting/lua_ffi.h +++ /dev/null @@ -1,96 +0,0 @@ -const char lua_ffi[] = R"STRING( -local ffi = require("ffi") -ffi.cdef[[ -struct memcell_values { const char *str; double num1; double num2; }; - -typedef struct TEvent TEvent; -typedef struct TTrack TTrack; -typedef struct TIsolated TIsolated; -typedef struct TDynamicObject TDynamicObject; -typedef struct TMemCell TMemCell; -typedef struct memcell_values memcell_values; - -TEvent* scriptapi_event_create(const char* name, double delay, double randomdelay, void (*handler)(TEvent*, TDynamicObject*)); -TEvent* scriptapi_event_find(const char* name); -const char* scriptapi_event_getname(TEvent *e); -void scriptapi_event_dispatch(TEvent *e, TDynamicObject *activator, double delay); - -TTrack* scriptapi_track_find(const char* name); -bool scriptapi_track_isoccupied(TTrack *track); - -TIsolated* scriptapi_isolated_find(const char* name); -bool scriptapi_isolated_isoccupied(TIsolated *isolated); - -const char* scriptapi_train_getname(TDynamicObject *dyn); -void scriptapi_dynobj_putvalues(TDynamicObject *dyn, const char *str, double num1, double num2); - -TMemCell* scriptapi_memcell_find(const char *name); -memcell_values scriptapi_memcell_read(TMemCell *mc); -void scriptapi_memcell_update(TMemCell *mc, const char *str, double num1, double num2); - -double scriptapi_random(double a, double b); -void scriptapi_writelog(const char* txt); -void scriptapi_writeerrorlog(const char* txt); -]] - -local ns = ffi.C - -local module = {} - -module.event_create = ns.scriptapi_event_create -function module.event_preparefunc(f) - return ffi.cast("void (*)(TEvent*, TDynamicObject*)", f) -end -module.event_find = ns.scriptapi_event_find -function module.event_getname(a) - return ffi.string(ns.scriptapi_event_getname(a)) -end -module.event_dispatch = ns.scriptapi_event_dispatch -function module.event_dispatch_n(a, b, c) - ns.scriptapi_event_dispatch(ns.scriptapi_event_find(a), b, c) -end - -module.track_find = ns.scriptapi_track_find -module.track_isoccupied = ns.scriptapi_track_isoccupied -function module.track_isoccupied_n(a) - return ns.scriptapi_track_isoccupied(ns.scriptapi_track_find(a)) -end - -module.isolated_find = ns.scriptapi_isolated_find -module.isolated_isoccupied = ns.scriptapi_isolated_isoccupied -function module.isolated_isoccupied_n(a) - return ns.scriptapi_isolated_isoccupied(ns.scriptapi_isolated_find(a)) -end - -function module.train_getname(a) - return ffi.string(ns.scriptapi_train_getname(a)) -end -function module.dynobj_putvalues(a, b) - ns.scriptapi_dynobj_putvalues(a, b.str, b.num1, b.num2) -end - -module.memcell_find = ns.scriptapi_memcell_find -function module.memcell_read(a) - native = ns.scriptapi_memcell_read(a) - mc = { } - mc.str = ffi.string(native.str) - mc.num1 = native.num1 - mc.num2 = native.num2 - return mc -end -function module.memcell_read_n(a) - return module.memcell_read(ns.scriptapi_memcell_find(a)) -end -function module.memcell_update(a, b) - ns.scriptapi_memcell_update(a, b.str, b.num1, b.num2) -end -function module.memcell_update_n(a, b) - ns.scriptapi_memcell_update(ns.scriptapi_memcell_find(a), b.str, b.num1, b.num2) -end - -module.random = ns.scriptapi_random -module.writelog = ns.scriptapi_writelog -module.writeerrorlog = ns.scriptapi_writeerrorlog - -return module; -)STRING"; diff --git a/world/Event.cpp b/world/Event.cpp index f7513825..fc85af79 100644 --- a/world/Event.cpp +++ b/world/Event.cpp @@ -2110,44 +2110,44 @@ friction_event::export_as_text_( std::ostream &Output ) const { } #ifdef WITH_LUA -lua_event::lua_event(lua::eventhandler_t func) { - lua_func = func; +lua_event::lua_event(lua_State *L, const int ref) { + lua_state = L; + lua_func = ref; +} + +lua_event::~lua_event() { + lua::unref(lua_state, lua_func); } // prepares event for use -void -lua_event::init() { - // nothing to do here +void lua_event::init() { + // nothing to do here } // event type string -std::string -lua_event::type() const { - return "lua"; +std::string lua_event::type() const { + return "lua"; } // deserialize() subclass details -void -lua_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) { - // preload next token - Input.getTokens(); +void lua_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) { + // preload next token + Input.getTokens(); } // run() subclass details -void -lua_event::run_() { +void lua_event::run_() { try { - if (lua_func) - lua_func(this, m_activator); + if (lua_func != LUA_NOREF) + lua::dispatch_event(lua_state, lua_func, this, m_activator); } catch (...) { ErrorLog(simulation::Lua.get_error()); } } // export_as_text() subclass details -void -lua_event::export_as_text_( std::ostream &Output ) const { - // nothing to do here +void lua_event::export_as_text_( std::ostream &Output ) const { + // nothing to do here } bool lua_event::is_instant() const { diff --git a/world/Event.h b/world/Event.h index 2f67d8bb..d04b4c87 100644 --- a/world/Event.h +++ b/world/Event.h @@ -591,17 +591,19 @@ private: #ifdef WITH_LUA class lua_event : public basic_event { public: - lua_event(lua::eventhandler_t func); - void init() override; + lua_event(lua_State *L, int ref); + ~lua_event() override; + void init() override; private: - std::string type() const override; - void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override; - void run_() override; - void export_as_text_( std::ostream &Output ) const override; - bool is_instant() const override; + std::string type() const override; + void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override; + void run_() override; + void export_as_text_( std::ostream &Output ) const override; + bool is_instant() const override; - lua::eventhandler_t lua_func = nullptr; + lua_State *lua_state = nullptr; + int lua_func = LUA_NOREF; }; #endif