From b0119ac89aa566d10c472fa74ff6f565fc910f80 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Sat, 10 May 2025 23:00:33 +0200 Subject: [PATCH] feat(lua): added signal type --- autogen/src/data/analysis.cpp | 19 ++++++- autogen/src/data/analysis.h | 3 +- core/src/datatypes/signal.cpp | 100 ++++++++++++++++++++++++++++++++++ core/src/datatypes/signal.h | 79 +++++++++++++++++++++++++++ core/src/datatypes/vector.h | 2 +- core/src/objects/part.h | 2 + 6 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 core/src/datatypes/signal.cpp create mode 100644 core/src/datatypes/signal.h diff --git a/autogen/src/data/analysis.cpp b/autogen/src/data/analysis.cpp index 51765c6..40107e5 100644 --- a/autogen/src/data/analysis.cpp +++ b/autogen/src/data/analysis.cpp @@ -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; diff --git a/autogen/src/data/analysis.h b/autogen/src/data/analysis.h index 2e2c5b2..a8e55c3 100644 --- a/autogen/src/data/analysis.h +++ b/autogen/src/data/analysis.h @@ -35,7 +35,8 @@ struct ClassAnalysis { std::string name; std::string serializedName; std::string headerPath; - bool hasFromString = false; + bool hasFromString; + bool isSerializable; std::vector properties; std::vector methods; std::vector staticProperties; diff --git a/core/src/datatypes/signal.cpp b/core/src/datatypes/signal.cpp new file mode 100644 index 0000000..d0e503d --- /dev/null +++ b/core/src/datatypes/signal.cpp @@ -0,0 +1,100 @@ +#include "signal.h" +#include "meta.h" +#include "lua.h" +#include +#include + +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 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)> func) { + this->function = func; +} + +void CSignalConnection::Call(std::vector args) { + function(args); +} + +// + +void Signal::Connect(std::function)> callback) { + connections.push_back(std::dynamic_pointer_cast(std::make_shared(callback))); +} + +void Signal::Connect(lua_State* state) { + connections.push_back(std::dynamic_pointer_cast(std::make_shared(state))); +} + +void Signal::Fire(std::vector args) { + for (std::shared_ptr connection : connections) { + connection->Call(args); + } +} + +void Signal::DisconnectAll() { + for (std::shared_ptr 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 = {}; +} \ No newline at end of file diff --git a/core/src/datatypes/signal.h b/core/src/datatypes/signal.h new file mode 100644 index 0000000..18f3192 --- /dev/null +++ b/core/src/datatypes/signal.h @@ -0,0 +1,79 @@ +#pragma once + +#include "base.h" +#include +#include +#include + +// 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 { +protected: + std::weak_ptr parentSignal; + + virtual ~SignalConnection(); + + virtual void Call(std::vector); + friend Signal; +public: + inline bool Connected() { return !parentSignal.expired(); }; + void Disconnect(); +}; + +class CSignalConnection : protected SignalConnection { + std::function)> function; + + CSignalConnection(std::function)>); + + friend Signal; +protected: + void Call(std::vector) override; +}; + +class LuaSignalConnection : protected SignalConnection { + lua_State* thread; + + LuaSignalConnection(lua_State*); + ~LuaSignalConnection(); + + friend Signal; +protected: + void Call(std::vector) override; +}; + +class Signal { + std::vector> connections; + + friend SignalConnection; +public: + Signal(); + virtual ~Signal(); + + void DisconnectAll(); + void Fire(std::vector args); + void Connect(std::function)> callback); + void Connect(lua_State*); +}; + +class SignalSource : public std::shared_ptr { +public: + SignalSource(); + virtual ~SignalSource(); +}; + +namespace Data { + class SignalRef : public Data::Base { + std::weak_ptr signal; + }; + + class SignalConnectionRef : public Data::Base { + std::weak_ptr signal; + }; +} \ No newline at end of file diff --git a/core/src/datatypes/vector.h b/core/src/datatypes/vector.h index 90bca11..78441ae 100644 --- a/core/src/datatypes/vector.h +++ b/core/src/datatypes/vector.h @@ -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; diff --git a/core/src/objects/part.h b/core/src/objects/part.h index fc78602..89a7c60 100644 --- a/core/src/objects/part.h +++ b/core/src/objects/part.h @@ -5,6 +5,7 @@ #include #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;