feat(lua): added signal type

This commit is contained in:
maelstrom 2025-05-10 23:00:33 +02:00
parent 1c70d2956f
commit b0119ac89a
6 changed files with 202 additions and 3 deletions

View file

@ -158,6 +158,22 @@ static void processProperty(CXCursor cur, ClassAnalysis* state) {
state->properties.push_back(anly);
}
static bool hasMethod(CXCursor cur, std::string methodName) {
bool found = false;
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
CXCursorKind kind = clang_getCursorKind(cur);
if (kind != CXCursor_CXXMethod) return CXChildVisit_Continue;
if (x_clang_toString(clang_getCursorSpelling(cur)) == methodName) {
found = true;
return CXChildVisit_Break;
}
return CXChildVisit_Continue;
});
return found;
}
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
ClassAnalysis anly;
@ -166,7 +182,8 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa
anly.name = className;
anly.serializedName = result["name"];
anly.hasFromString = result.count("from_string") > 0;
anly.hasFromString = hasMethod(cur, "FromString");
anly.isSerializable = hasMethod(cur, "Serialize") && hasMethod(cur, "Deserialize");
if (anly.serializedName == "")
anly.serializedName = className;

View file

@ -35,7 +35,8 @@ struct ClassAnalysis {
std::string name;
std::string serializedName;
std::string headerPath;
bool hasFromString = false;
bool hasFromString;
bool isSerializable;
std::vector<PropertyAnalysis> properties;
std::vector<MethodAnalysis> methods;
std::vector<PropertyAnalysis> staticProperties;

View file

@ -0,0 +1,100 @@
#include "signal.h"
#include "meta.h"
#include "lua.h"
#include <luajit-2.1/lua.h>
#include <memory>
SignalSource::SignalSource() {}
SignalSource::~SignalSource() = default;
Signal::Signal() {}
Signal::~Signal() = default;
SignalConnection::~SignalConnection() = default;
// Only used for its address
int __savedThreads;
LuaSignalConnection::LuaSignalConnection(lua_State* L) {
// Create thread from function at top of stack
thread = lua_newthread(L);
lua_xmove(L, thread, 1);
// Save thread so it doesn't get GC'd
lua_pushlightuserdata(thread, &__savedThreads);
lua_gettable(thread, LUA_REGISTRYINDEX);
lua_pushthread(thread); // key
lua_pushboolean(thread, true); // value
lua_rawset(thread, -3); // set
lua_pop(thread, 1); // Pop __savedThreads
}
LuaSignalConnection::~LuaSignalConnection() {
// Remove thread so that it can get properly GC'd
lua_pushlightuserdata(thread, &__savedThreads);
lua_gettable(thread, LUA_REGISTRYINDEX);
lua_pushthread(thread); // key
lua_pushnil(thread); // value
lua_rawset(thread, -3); // set
lua_pop(thread, 1); // Pop __savedThreads
}
void LuaSignalConnection::Call(std::vector<Data::Variant> args) {
for (Data::Variant arg : args) {
arg.PushLuaValue(thread);
}
int status = lua_resume(thread, args.size());
if (status > LUA_YIELD) {
Logger::error(lua_tostring(thread, -1));
lua_pop(thread, 1); // Pop return value
}
}
//
CSignalConnection::CSignalConnection(std::function<void(std::vector<Data::Variant>)> func) {
this->function = func;
}
void CSignalConnection::Call(std::vector<Data::Variant> args) {
function(args);
}
//
void Signal::Connect(std::function<void(std::vector<Data::Variant>)> callback) {
connections.push_back(std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback)));
}
void Signal::Connect(lua_State* state) {
connections.push_back(std::dynamic_pointer_cast<SignalConnection>(std::make_shared<LuaSignalConnection>(state)));
}
void Signal::Fire(std::vector<Data::Variant> args) {
for (std::shared_ptr<SignalConnection> connection : connections) {
connection->Call(args);
}
}
void Signal::DisconnectAll() {
for (std::shared_ptr<SignalConnection> connection : connections) {
connection->parentSignal = {};
}
connections.clear();
}
void SignalConnection::Disconnect() {
if (!Connected()) return;
auto signal = parentSignal.lock();
for(auto it = signal->connections.begin(); it != signal->connections.end();) {
if (*it == shared_from_this())
it = signal->connections.erase(it);
else
it++;
}
parentSignal = {};
}

View file

@ -0,0 +1,79 @@
#pragma once
#include "base.h"
#include <functional>
#include <memory>
#include <vector>
// Ultimately, there's two routes:
// 1. Signals are tied to Instances and are basically just keys that refer to events in the instance
// 2. Signals are independent of Instances, but intermingled.
// I chose 2 because it gives Signals a higher class, and felt easier to implement
// This means that they can be used independently of Instance if need be in the future
class Instance;
class Signal;
class SignalConnection : public std::enable_shared_from_this<SignalConnection> {
protected:
std::weak_ptr<Signal> parentSignal;
virtual ~SignalConnection();
virtual void Call(std::vector<Data::Variant>);
friend Signal;
public:
inline bool Connected() { return !parentSignal.expired(); };
void Disconnect();
};
class CSignalConnection : protected SignalConnection {
std::function<void(std::vector<Data::Variant>)> function;
CSignalConnection(std::function<void(std::vector<Data::Variant>)>);
friend Signal;
protected:
void Call(std::vector<Data::Variant>) override;
};
class LuaSignalConnection : protected SignalConnection {
lua_State* thread;
LuaSignalConnection(lua_State*);
~LuaSignalConnection();
friend Signal;
protected:
void Call(std::vector<Data::Variant>) override;
};
class Signal {
std::vector<std::shared_ptr<SignalConnection>> connections;
friend SignalConnection;
public:
Signal();
virtual ~Signal();
void DisconnectAll();
void Fire(std::vector<Data::Variant> args);
void Connect(std::function<void(std::vector<Data::Variant>)> callback);
void Connect(lua_State*);
};
class SignalSource : public std::shared_ptr<Signal> {
public:
SignalSource();
virtual ~SignalSource();
};
namespace Data {
class SignalRef : public Data::Base {
std::weak_ptr<Signal> signal;
};
class SignalConnectionRef : public Data::Base {
std::weak_ptr<Signal> signal;
};
}

View file

@ -8,7 +8,7 @@
namespace reactphysics3d { class Vector3; };
namespace Data {
class DEF_DATA_(from_string) Vector3 : public Base {
class DEF_DATA Vector3 : public Base {
AUTOGEN_PREAMBLE_DATA
glm::vec3 vector;

View file

@ -5,6 +5,7 @@
#include <glm/ext.hpp>
#include "datatypes/cframe.h"
#include "datatypes/color3.h"
#include "datatypes/signal.h"
#include "datatypes/vector.h"
#include "objects/base/instance.h"
#include "rendering/surface.h"
@ -91,6 +92,7 @@ public:
DEF_PROP float frontParamB = 0.5;
DEF_PROP float backParamB = 0.5;
SignalSource OnParentUpdated;
rp::RigidBody* rigidBody = nullptr;