16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 00:09:18 +02:00

Merge pull request #91 from jakubg1/lua-refactor

Refactor Lua bindings to make it work natively on Linux
This commit is contained in:
2026-04-15 18:43:26 +02:00
committed by GitHub
5 changed files with 392 additions and 299 deletions

View File

@@ -4,7 +4,6 @@
#include "utilities/Logs.h" #include "utilities/Logs.h"
#include "world/MemCell.h" #include "world/MemCell.h"
#include "vehicle/Driver.h" #include "vehicle/Driver.h"
#include "scripting/lua_ffi.h"
#include "simulation/simulation.h" #include "simulation/simulation.h"
lua::lua() lua::lua()
@@ -15,12 +14,33 @@ lua::lua()
lua_atpanic(state, atpanic); lua_atpanic(state, atpanic);
luaL_openlibs(state); luaL_openlibs(state);
lua_getglobal(state, "package"); static constexpr luaL_Reg api[] =
lua_pushstring(state, "preload"); {
lua_gettable(state, -2); {"event_create", scriptapi_event_create},
lua_pushcclosure(state, openffi, 0); {"event_find", scriptapi_event_find},
lua_setfield(state, -2, "eu07.events"); {"event_exists", scriptapi_event_exists},
lua_settop(state, 0); {"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() lua::~lua()
@@ -29,184 +49,324 @@ lua::~lua()
state = nullptr; 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())) { if (luaL_dofile(state, file.c_str())) {
const char *str = lua_tostring(state, -1); std::string str = lua_tostring(state, -1);
ErrorLog(std::string(str), logtype::lua); ErrorLog("lua: Runtime error: " + str, logtype::lua);
} }
} }
// NOTE: we cannot throw exceptions in callbacks
// because it is not supported by LuaJIT on x86 windows
int lua::atpanic(lua_State *s) int lua::atpanic(lua_State *s)
{ {
std::string err(lua_tostring(s, -1)); std::string err = lua_tostring(s, -1);
ErrorLog(err, logtype::lua); ErrorLog("lua: Runtime error: " + err, logtype::lua);
return 0; 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)) luaL_unref(L, LUA_REGISTRYINDEX, ref);
{
ErrorLog(std::string(lua_tostring(s, -1)), logtype::lua);
return 0;
}
return 1;
} }
#if defined _WIN32 /// Executes the callback function of an event created with `scriptapi_event_create`.
# if defined __GNUC__ void lua::dispatch_event(lua_State *L, const int handler, basic_event *event, const TDynamicObject *activator)
# define EXPORT __attribute__ ((dllexport))
# else
# define EXPORT __declspec(dllexport)
# endif
#elif defined __GNUC__
# define EXPORT __attribute__ ((visibility ("default")))
#else
# define EXPORT
#endif
extern "C"
{ {
EXPORT basic_event* scriptapi_event_create(const char* name, double delay, double randomdelay, lua::eventhandler_t handler) lua_rawgeti(L, LUA_REGISTRYINDEX, handler);
{ lua_pushlightuserdata(L, event);
basic_event *event = new lua_event(handler); lua_pushlightuserdata(L, const_cast<TDynamicObject *>(activator));
event->m_name = std::string(name); lua_call(L, 2, 0);
event->m_delay = delay; }
event->m_delayrandom = randomdelay;
void lua::push_memcell_values(lua_State *L, const TMemCell *mc)
if (simulation::Events.insert(event)) {
return event; lua_createtable(L, 0, 3);
else lua_pushstring(L, "str");
return nullptr; lua_pushstring(L, mc ? mc->Text().c_str() : nullptr);
} lua_settable(L, -3);
lua_pushstring(L, "num1");
EXPORT basic_event* scriptapi_event_find(const char* name) lua_pushnumber(L, mc ? mc->Value1() : 0.0);
{ lua_settable(L, -3);
std::string str(name); lua_pushstring(L, "num2");
basic_event *e = simulation::Events.FindEvent(str); lua_pushnumber(L, mc ? mc->Value2() : 0.0);
if (e) lua_settable(L, -3);
return e; }
else
WriteLog("lua: missing event: " + str); memcell_values lua::get_memcell_values(lua_State *L, int idx)
return nullptr; {
} // For negative indices, we need to account for the extra item that is the key name/value extracted from the table.
if (idx < 0)
EXPORT TTrack* scriptapi_track_find(const char* name) idx--;
{ memcell_values mc{nullptr, 0.0, 0.0};
std::string str(name); lua_pushstring(L, "str");
TTrack *track = simulation::Paths.find(str); lua_gettable(L, idx);
if (track) if (lua_isstring(L, -1))
return track; mc.str = lua_tostring(L, -1);
else lua_pop(L, 1);
WriteLog("lua: missing track: " + str); lua_pushstring(L, "num1");
return nullptr; lua_gettable(L, idx);
} if (lua_isnumber(L, -1))
mc.num1 = lua_tonumber(L, -1);
EXPORT bool scriptapi_track_isoccupied(TTrack* track) lua_pop(L, 1);
{ lua_pushstring(L, "num2");
if (track) lua_gettable(L, idx);
return !track->IsEmpty(); if (lua_isnumber(L, -1))
return false; mc.num2 = lua_tonumber(L, -1);
} lua_pop(L, 1);
return mc;
EXPORT TIsolated* scriptapi_isolated_find(const char* name) }
{
std::string str(name); int lua::scriptapi_event_create(lua_State *L)
TIsolated *isolated = TIsolated::Find(name); {
if (isolated) std::string name = lua_tostring(L, 1);
return isolated; double delay = lua_tonumber(L, 2);
else double randomdelay = lua_tonumber(L, 3);
WriteLog("lua: missing isolated: " + str); lua_pushvalue(L, 4);
return nullptr; int funcRef = luaL_ref(L, LUA_REGISTRYINDEX);
}
basic_event *event = new lua_event(L, funcRef);
EXPORT bool scriptapi_isolated_isoccupied(TIsolated* isolated) event->m_name = name;
{ event->m_delay = delay;
if (isolated) event->m_delayrandom = randomdelay;
return isolated->Busy(); if (simulation::Events.insert(event))
return false; {
} lua_pushlightuserdata(L, event);
return 1;
EXPORT const char* scriptapi_event_getname(basic_event *e) }
{ return 0;
if (e) }
return e->m_name.c_str();
return nullptr; int lua::scriptapi_event_find(lua_State *L)
} {
std::string name = lua_tostring(L, 1);
EXPORT const char* scriptapi_train_getname(TDynamicObject *dyn) basic_event *event = simulation::Events.FindEvent(name);
{ if (event)
if (dyn && dyn->Mechanik) {
return dyn->Mechanik->TrainName().c_str(); lua_pushlightuserdata(L, event);
return nullptr; return 1;
} }
ErrorLog("lua: missing event: " + name);
EXPORT void scriptapi_event_dispatch(basic_event *e, TDynamicObject *activator, double delay) return 0;
{ }
if (e)
simulation::Events.AddToQuery(e, activator, delay); int lua::scriptapi_event_exists(lua_State *L)
} {
std::string name = lua_tostring(L, 1);
EXPORT double scriptapi_random(double a, double b) basic_event *event = simulation::Events.FindEvent(name);
{ lua_pushboolean(L, event != nullptr);
return Random(a, b); return 1;
} }
EXPORT void scriptapi_writelog(const char* txt) int lua::scriptapi_event_getname(lua_State *L)
{ {
WriteLog("lua: log: " + std::string(txt), logtype::lua); auto *event = static_cast<basic_event *>(lua_touserdata(L, 1));
} if (event)
{
EXPORT void scriptapi_writeerrorlog(const char* txt) lua_pushstring(L, event->m_name.c_str());
{ return 1;
ErrorLog("lua: log: " + std::string(txt), logtype::lua); }
} return 0;
}
struct memcell_values { const char *str; double num1; double num2; };
int lua::scriptapi_event_dispatch(lua_State *L)
EXPORT TMemCell* scriptapi_memcell_find(const char *name) {
{ auto *event = static_cast<basic_event *>(lua_touserdata(L, 1));
std::string str(name); auto *activator = static_cast<TDynamicObject *>(lua_touserdata(L, 2));
TMemCell *mc = simulation::Memory.find(str); double delay = lua_tonumber(L, 3);
if (mc) if (event)
return mc; simulation::Events.AddToQuery(event, activator, delay);
else return 0;
WriteLog("lua: missing memcell: " + str); }
return nullptr;
} int lua::scriptapi_event_dispatch_n(lua_State *L)
{
EXPORT memcell_values scriptapi_memcell_read(TMemCell *mc) std::string name = lua_tostring(L, 1);
{ auto *activator = static_cast<TDynamicObject *>(lua_touserdata(L, 2));
if (!mc) double delay = lua_tonumber(L, 3);
return { nullptr, 0.0, 0.0 }; basic_event *event = simulation::Events.FindEvent(name);
return { mc->Text().c_str(), mc->Value1(), mc->Value2() }; if (event)
} simulation::Events.AddToQuery(event, activator, delay);
else
EXPORT void scriptapi_memcell_update(TMemCell *mc, const char *str, double num1, double num2) ErrorLog("lua: missing event: " + name);
{ return 0;
if (!mc) }
return;
mc->UpdateValues(std::string(str), num1, num2, int lua::scriptapi_track_find(lua_State *L)
basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); {
} std::string name = lua_tostring(L, 1);
TTrack *track = simulation::Paths.find(name);
EXPORT void scriptapi_dynobj_putvalues(TDynamicObject *dyn, const char *str, double num1, double num2) if (track)
{ {
if (!dyn) lua_pushlightuserdata(L, track);
return; return 1;
TLocation loc; }
if (dyn->Mechanik) ErrorLog("lua: missing track: " + name);
dyn->Mechanik->PutCommand(std::string(str), num1, num2, loc); return 0;
else }
dyn->MoverParameters->PutCommand(std::string(str), num1, num2, loc);
} int lua::scriptapi_track_isoccupied(lua_State *L)
{
auto *track = static_cast<TTrack *>(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<TIsolated *>(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<TDynamicObject *>(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<TDynamicObject *>(lua_touserdata(L, 1));
auto [str, num1, num2] = get_memcell_values(L, 2);
if (!dyn)
return 0;
TLocation loc{};
if (dyn->Mechanik)
dyn->Mechanik->PutCommand(str, num1, num2, loc);
else
dyn->MoverParameters->PutCommand(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<TMemCell *>(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<TMemCell *>(lua_touserdata(L, 1));
auto [str, num1, num2] = get_memcell_values(L, 2);
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);
auto [str, num1, num2] = get_memcell_values(L, 2);
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: " + mstr);
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;
} }

View File

@@ -1,22 +1,49 @@
#pragma once #pragma once
#include <lua.hpp> #include <lua.hpp>
#include <stdafx.h>
class basic_event; class basic_event;
class TMemCell;
class TDynamicObject; class TDynamicObject;
struct memcell_values { const char *str; double num1; double num2; };
class lua class lua
{ {
lua_State *state; lua_State *state;
static int atpanic(lua_State *s); 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: public:
lua(); lua();
~lua(); ~lua();
std::string get_error(); std::string get_error() const;
void interpret(std::string file); void interpret(const std::string& file) const;
static void unref(lua_State *L, int ref);
typedef void (*eventhandler_t)(basic_event*, const TDynamicObject*); 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);
static memcell_values get_memcell_values(lua_State *L, int idx);
}; };

View File

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

View File

@@ -2110,44 +2110,44 @@ friction_event::export_as_text_( std::ostream &Output ) const {
} }
#ifdef WITH_LUA #ifdef WITH_LUA
lua_event::lua_event(lua::eventhandler_t func) { lua_event::lua_event(lua_State *L, const int ref) {
lua_func = func; lua_state = L;
lua_func = ref;
}
lua_event::~lua_event() {
lua::unref(lua_state, lua_func);
} }
// prepares event for use // prepares event for use
void void lua_event::init() {
lua_event::init() { // nothing to do here
// nothing to do here
} }
// event type string // event type string
std::string std::string lua_event::type() const {
lua_event::type() const { return "lua";
return "lua";
} }
// deserialize() subclass details // deserialize() subclass details
void void lua_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) {
lua_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) { // preload next token
// preload next token Input.getTokens();
Input.getTokens();
} }
// run() subclass details // run() subclass details
void void lua_event::run_() {
lua_event::run_() {
try { try {
if (lua_func) if (lua_func != LUA_NOREF)
lua_func(this, m_activator); lua::dispatch_event(lua_state, lua_func, this, m_activator);
} catch (...) { } catch (...) {
ErrorLog(simulation::Lua.get_error()); ErrorLog("lua: Runtime error: " + simulation::Lua.get_error());
} }
} }
// export_as_text() subclass details // export_as_text() subclass details
void void lua_event::export_as_text_( std::ostream &Output ) const {
lua_event::export_as_text_( std::ostream &Output ) const { // nothing to do here
// nothing to do here
} }
bool lua_event::is_instant() const { bool lua_event::is_instant() const {

View File

@@ -591,17 +591,19 @@ private:
#ifdef WITH_LUA #ifdef WITH_LUA
class lua_event : public basic_event { class lua_event : public basic_event {
public: public:
lua_event(lua::eventhandler_t func); lua_event(lua_State *L, int ref);
void init() override; ~lua_event() override;
void init() override;
private: private:
std::string type() const override; std::string type() const override;
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override; void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
void run_() override; void run_() override;
void export_as_text_( std::ostream &Output ) const override; void export_as_text_( std::ostream &Output ) const override;
bool is_instant() const override; bool is_instant() const override;
lua::eventhandler_t lua_func = nullptr; lua_State *lua_state = nullptr;
int lua_func = LUA_NOREF;
}; };
#endif #endif