Compare commits
8 commits
626be7107f
...
c5fd1321e5
Author | SHA1 | Date | |
---|---|---|---|
c5fd1321e5 | |||
b168e87e37 | |||
6e1cfcac80 | |||
d86fd754bd | |||
b9dc280311 | |||
7d54e06b06 | |||
b0119ac89a | |||
1c70d2956f |
14 changed files with 634 additions and 7 deletions
|
@ -158,6 +158,22 @@ static void processProperty(CXCursor cur, ClassAnalysis* state) {
|
||||||
state->properties.push_back(anly);
|
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) {
|
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||||
ClassAnalysis anly;
|
ClassAnalysis anly;
|
||||||
|
|
||||||
|
@ -166,7 +182,8 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa
|
||||||
|
|
||||||
anly.name = className;
|
anly.name = className;
|
||||||
anly.serializedName = result["name"];
|
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 == "")
|
if (anly.serializedName == "")
|
||||||
anly.serializedName = className;
|
anly.serializedName = className;
|
||||||
|
|
|
@ -35,7 +35,8 @@ struct ClassAnalysis {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string serializedName;
|
std::string serializedName;
|
||||||
std::string headerPath;
|
std::string headerPath;
|
||||||
bool hasFromString = false;
|
bool hasFromString;
|
||||||
|
bool isSerializable;
|
||||||
std::vector<PropertyAnalysis> properties;
|
std::vector<PropertyAnalysis> properties;
|
||||||
std::vector<MethodAnalysis> methods;
|
std::vector<MethodAnalysis> methods;
|
||||||
std::vector<PropertyAnalysis> staticProperties;
|
std::vector<PropertyAnalysis> staticProperties;
|
||||||
|
|
|
@ -119,6 +119,27 @@ static void processField(CXCursor cur, ClassAnalysis* state) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void processSignal(CXCursor cur, ClassAnalysis* state) {
|
||||||
|
std::optional<std::string> signalDef = findAnnotation(cur, "OB::def_signal");
|
||||||
|
if (!signalDef) return;
|
||||||
|
|
||||||
|
SignalAnalysis anly;
|
||||||
|
|
||||||
|
auto result = parseAnnotationString(signalDef.value());
|
||||||
|
std::string fieldName = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||||
|
|
||||||
|
anly.name = result["name"];
|
||||||
|
anly.sourceFieldName = fieldName;
|
||||||
|
|
||||||
|
// if name field is not provided, use fieldName instead, but capitalize the first character
|
||||||
|
if (anly.name == "") {
|
||||||
|
anly.name = fieldName;
|
||||||
|
anly.name[0] = std::toupper(anly.name[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->signals.push_back(anly);
|
||||||
|
}
|
||||||
|
|
||||||
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||||
ClassAnalysis anly;
|
ClassAnalysis anly;
|
||||||
|
|
||||||
|
@ -159,6 +180,7 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa
|
||||||
if (kind != CXCursor_FieldDecl) return CXChildVisit_Continue;
|
if (kind != CXCursor_FieldDecl) return CXChildVisit_Continue;
|
||||||
|
|
||||||
processField(cur, &anly);
|
processField(cur, &anly);
|
||||||
|
processSignal(cur, &anly);
|
||||||
|
|
||||||
return CXChildVisit_Continue;
|
return CXChildVisit_Continue;
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,6 +36,11 @@ struct PropertyAnalysis {
|
||||||
PropertyFlags flags = (PropertyFlags)0;
|
PropertyFlags flags = (PropertyFlags)0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SignalAnalysis {
|
||||||
|
std::string name;
|
||||||
|
std::string sourceFieldName;
|
||||||
|
};
|
||||||
|
|
||||||
// https://stackoverflow.com/a/1448478/16255372
|
// https://stackoverflow.com/a/1448478/16255372
|
||||||
inline ClassFlags operator|(ClassFlags a, ClassFlags b) {
|
inline ClassFlags operator|(ClassFlags a, ClassFlags b) {
|
||||||
return static_cast<ClassFlags>(static_cast<int>(a) | static_cast<int>(b));
|
return static_cast<ClassFlags>(static_cast<int>(a) | static_cast<int>(b));
|
||||||
|
@ -51,6 +56,7 @@ struct ClassAnalysis {
|
||||||
std::string headerPath;
|
std::string headerPath;
|
||||||
bool abstract = false;
|
bool abstract = false;
|
||||||
std::vector<PropertyAnalysis> properties;
|
std::vector<PropertyAnalysis> properties;
|
||||||
|
std::vector<SignalAnalysis> signals;
|
||||||
ClassFlags flags = (ClassFlags)0;
|
ClassFlags flags = (ClassFlags)0;
|
||||||
std::string explorerIcon;
|
std::string explorerIcon;
|
||||||
};
|
};
|
||||||
|
|
|
@ -146,6 +146,18 @@ static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
out << "\n }";
|
out << "\n }";
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle signals
|
||||||
|
out << "\n ";
|
||||||
|
first = true;
|
||||||
|
for (auto& signal : state.signals) {
|
||||||
|
out << (first ? "" : " else ") << "if (name == \"" << signal.name << "\") {";
|
||||||
|
|
||||||
|
out << "\n return Data::Variant(Data::SignalRef(" << signal.sourceFieldName << "));";
|
||||||
|
|
||||||
|
out << "\n }";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
out << "\n return " << state.baseClass << "::InternalGetPropertyValue(name);";
|
out << "\n return " << state.baseClass << "::InternalGetPropertyValue(name);";
|
||||||
// out << "\n return MemberNotFound(\"" << state.name << "\", name);";
|
// out << "\n return MemberNotFound(\"" << state.name << "\", name);";
|
||||||
|
@ -199,6 +211,23 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
out << "\n }";
|
out << "\n }";
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle signals
|
||||||
|
out << "\n ";
|
||||||
|
first = true;
|
||||||
|
for (auto& signal : state.signals) {
|
||||||
|
out << (first ? "" : " else ") << "if (name == \"" << signal.name << "\") {";
|
||||||
|
|
||||||
|
std::string strFlags;
|
||||||
|
strFlags += "PROP_READONLY";
|
||||||
|
strFlags += "| PROP_HIDDEN";
|
||||||
|
strFlags += "| PROP_NOSAVE";
|
||||||
|
|
||||||
|
out << "\n return PropertyMeta { &SignalRef::TYPE, " << strFlags << " };";
|
||||||
|
|
||||||
|
out << "\n }";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
out << "\n return " << state.baseClass << "::InternalGetPropertyMeta(name);";
|
out << "\n return " << state.baseClass << "::InternalGetPropertyMeta(name);";
|
||||||
// out << "\n return MemberNotFound(\"" << state.name << "\", name);";
|
// out << "\n return MemberNotFound(\"" << state.name << "\", name);";
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "datatypes/color3.h"
|
#include "datatypes/color3.h"
|
||||||
#include "datatypes/ref.h"
|
#include "datatypes/ref.h"
|
||||||
|
#include "datatypes/signal.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "cframe.h"
|
#include "cframe.h"
|
||||||
|
|
||||||
|
@ -26,7 +27,9 @@ namespace Data {
|
||||||
Vector3,
|
Vector3,
|
||||||
CFrame,
|
CFrame,
|
||||||
Color3,
|
Color3,
|
||||||
InstanceRef
|
InstanceRef,
|
||||||
|
SignalRef,
|
||||||
|
SignalConnectionRef
|
||||||
> __VARIANT_TYPE;
|
> __VARIANT_TYPE;
|
||||||
|
|
||||||
class Variant {
|
class Variant {
|
||||||
|
|
411
core/src/datatypes/signal.cpp
Normal file
411
core/src/datatypes/signal.cpp
Normal file
|
@ -0,0 +1,411 @@
|
||||||
|
#include "signal.h"
|
||||||
|
#include "datatypes/base.h"
|
||||||
|
#include "meta.h"
|
||||||
|
#include "lua.h"
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
SignalSource::SignalSource() : std::shared_ptr<Signal>(std::make_shared<Signal>()) {}
|
||||||
|
SignalSource::~SignalSource() = default;
|
||||||
|
|
||||||
|
Signal::Signal() {}
|
||||||
|
Signal::~Signal() = default;
|
||||||
|
|
||||||
|
SignalConnection::SignalConnection(std::weak_ptr<Signal> parent) : parentSignal(parent) {}
|
||||||
|
SignalConnection::~SignalConnection() = default;
|
||||||
|
|
||||||
|
// Only used for its address
|
||||||
|
int __savedCallbacks = 0;
|
||||||
|
LuaSignalConnection::LuaSignalConnection(lua_State* L, std::weak_ptr<Signal> parent) : SignalConnection(parent) {
|
||||||
|
state = L;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/31952046/16255372
|
||||||
|
// Create the table
|
||||||
|
if (__savedCallbacks == 0) {
|
||||||
|
lua_newtable(L);
|
||||||
|
__savedCallbacks = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save function so it doesn't get GC'd
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, __savedCallbacks);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
function = luaL_ref(L, -2);
|
||||||
|
lua_pop(L, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaSignalConnection::~LuaSignalConnection() {
|
||||||
|
// Remove LuaSignalConnectionthread so that it can get properly GC'd
|
||||||
|
lua_rawgeti(state, LUA_REGISTRYINDEX, __savedCallbacks);
|
||||||
|
luaL_unref(state, -1, function);
|
||||||
|
lua_pop(state, 1); // Pop __savedCallbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSignalConnection::Call(std::vector<Data::Variant> args) {
|
||||||
|
lua_State* thread = lua_newthread(state);
|
||||||
|
|
||||||
|
// Push function
|
||||||
|
lua_rawgeti(thread, LUA_REGISTRYINDEX, __savedCallbacks);
|
||||||
|
lua_rawgeti(thread, -1, function);
|
||||||
|
lua_remove(thread, -2);
|
||||||
|
|
||||||
|
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, std::weak_ptr<Signal> parent) : SignalConnection(parent) {
|
||||||
|
this->function = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSignalConnection::Call(std::vector<Data::Variant> args) {
|
||||||
|
function(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
SignalConnectionRef Signal::Connect(std::function<void(std::vector<Data::Variant>)> callback) {
|
||||||
|
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
||||||
|
connections.push_back(conn);
|
||||||
|
return SignalConnectionRef(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalConnectionRef Signal::Connect(lua_State* state) {
|
||||||
|
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<LuaSignalConnection>(state, weak_from_this()));
|
||||||
|
connections.push_back(conn);
|
||||||
|
return SignalConnectionRef(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalConnectionRef Signal::Once(std::function<void(std::vector<Data::Variant>)> callback) {
|
||||||
|
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
||||||
|
onceConnections.push_back(conn);
|
||||||
|
return SignalConnectionRef(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalConnectionRef Signal::Once(lua_State* state) {
|
||||||
|
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<LuaSignalConnection>(state, weak_from_this()));
|
||||||
|
onceConnections.push_back(conn);
|
||||||
|
return SignalConnectionRef(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __waitingThreads = 0;
|
||||||
|
int Signal::Wait(lua_State* thread) {
|
||||||
|
// If the table hasn't been constructed yet, make it
|
||||||
|
if (__waitingThreads == 0) {
|
||||||
|
lua_newtable(thread);
|
||||||
|
__waitingThreads = luaL_ref(thread, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get waitingThreads table
|
||||||
|
lua_rawgeti(thread, LUA_REGISTRYINDEX, __waitingThreads);
|
||||||
|
lua_pushthread(thread);
|
||||||
|
int threadId = luaL_ref(thread, -2);
|
||||||
|
lua_pop(thread, -1); // pop __waitingThreads
|
||||||
|
waitingThreads.push_back(std::make_pair(threadId, thread));
|
||||||
|
|
||||||
|
// Yield and return results
|
||||||
|
return lua_yield(thread, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Fire(std::vector<Data::Variant> args) {
|
||||||
|
for (std::shared_ptr<SignalConnection> connection : connections) {
|
||||||
|
connection->Call(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call once connections
|
||||||
|
auto prevOnceConns = std::move(onceConnections);
|
||||||
|
onceConnections = std::vector<std::shared_ptr<SignalConnection>>();
|
||||||
|
for (std::shared_ptr<SignalConnection> connection : prevOnceConns) {
|
||||||
|
connection->Call(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call waiting threads
|
||||||
|
auto prevThreads = std::move(waitingThreads);
|
||||||
|
waitingThreads = std::vector<std::pair<int, lua_State*>>();
|
||||||
|
for (auto& [threadId, thread] : prevThreads) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove thread from registry
|
||||||
|
lua_rawgeti(thread, LUA_REGISTRYINDEX, __waitingThreads);
|
||||||
|
luaL_unref(thread, -1, threadId);
|
||||||
|
lua_pop(thread, 1); // pop __waitingThreads
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Fire() {
|
||||||
|
return Fire(std::vector<Data::Variant> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::DisconnectAll() {
|
||||||
|
for (std::shared_ptr<SignalConnection> connection : connections) {
|
||||||
|
connection->parentSignal = {};
|
||||||
|
}
|
||||||
|
connections.clear();
|
||||||
|
|
||||||
|
for (std::shared_ptr<SignalConnection> connection : onceConnections) {
|
||||||
|
connection->parentSignal = {};
|
||||||
|
}
|
||||||
|
onceConnections.clear();
|
||||||
|
|
||||||
|
for (auto& [threadId, thread] : waitingThreads) {
|
||||||
|
lua_rawgeti(thread, LUA_REGISTRYINDEX, __waitingThreads);
|
||||||
|
luaL_unref(thread, -1, threadId);
|
||||||
|
lua_pop(thread, 1);
|
||||||
|
}
|
||||||
|
waitingThreads.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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto it = signal->onceConnections.begin(); it != signal->onceConnections.end();) {
|
||||||
|
if (*it == shared_from_this())
|
||||||
|
it = signal->onceConnections.erase(it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
parentSignal = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
static int signal_Connect(lua_State*);
|
||||||
|
static int signal_Once(lua_State*);
|
||||||
|
static int signal_Wait(lua_State*);
|
||||||
|
|
||||||
|
static int signal_gc(lua_State*);
|
||||||
|
static int signal_index(lua_State*);
|
||||||
|
static int signal_tostring(lua_State*);
|
||||||
|
static const struct luaL_Reg signal_metatable [] = {
|
||||||
|
{"__gc", signal_gc},
|
||||||
|
{"__index", signal_index},
|
||||||
|
{"__tostring", signal_tostring},
|
||||||
|
{NULL, NULL} /* end of array */
|
||||||
|
};
|
||||||
|
|
||||||
|
Data::SignalRef::SignalRef(std::weak_ptr<Signal> ref) : signal(ref) {}
|
||||||
|
Data::SignalRef::~SignalRef() = default;
|
||||||
|
|
||||||
|
const Data::TypeInfo Data::SignalRef::TYPE = {
|
||||||
|
.name = "Signal",
|
||||||
|
.fromLuaValue = &Data::SignalRef::FromLuaValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Data::TypeInfo& Data::SignalRef::GetType() const { return Data::SignalRef::TYPE; };
|
||||||
|
|
||||||
|
const Data::String Data::SignalRef::ToString() const {
|
||||||
|
return Data::String("Signal");
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::SignalRef::operator std::weak_ptr<Signal>() {
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::SignalRef::Serialize(pugi::xml_node node) const {
|
||||||
|
// Not serializable
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::SignalRef::PushLuaValue(lua_State* L) const {
|
||||||
|
int n = lua_gettop(L);
|
||||||
|
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)lua_newuserdata(L, sizeof(std::weak_ptr<Signal>));
|
||||||
|
*userdata = new std::weak_ptr<Signal>(signal);
|
||||||
|
|
||||||
|
// Create the instance's metatable
|
||||||
|
luaL_newmetatable(L, "__mt_signal");
|
||||||
|
luaL_register(L, NULL, signal_metatable);
|
||||||
|
|
||||||
|
lua_setmetatable(L, n+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::SignalRef::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return Data::Variant(Data::SignalRef(**userdata));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signal_gc(lua_State* L) {
|
||||||
|
// Destroy the contained shared_ptr
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
|
delete *userdata;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// __index(t,k)
|
||||||
|
static int signal_index(lua_State* L) {
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
|
std::weak_ptr<Signal> signal = **userdata;
|
||||||
|
std::string key(lua_tostring(L, 2));
|
||||||
|
lua_pop(L, 2);
|
||||||
|
|
||||||
|
if (key == "Connect") {
|
||||||
|
lua_pushcfunction(L, signal_Connect);
|
||||||
|
return 1;
|
||||||
|
} else if (key == "Once") {
|
||||||
|
lua_pushcfunction(L, signal_Once);
|
||||||
|
return 1;
|
||||||
|
} else if (key == "Wait") {
|
||||||
|
lua_pushcfunction(L, signal_Wait);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return luaL_error(L, "'%s' is not a valid member of %s", key.c_str(), "Signal");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signal_tostring(lua_State* L) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_pushstring(L, "Signal");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signal_Connect(lua_State* L) {
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
|
std::shared_ptr<Signal> signal = (**userdata).lock();
|
||||||
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||||
|
|
||||||
|
SignalConnectionRef ref = signal->Connect(L);
|
||||||
|
ref.PushLuaValue(L);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signal_Once(lua_State* L) {
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
|
std::shared_ptr<Signal> signal = (**userdata).lock();
|
||||||
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||||
|
|
||||||
|
SignalConnectionRef ref = signal->Once(L);
|
||||||
|
ref.PushLuaValue(L);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signal_Wait(lua_State* L) {
|
||||||
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
|
// TODO: Add expiry check here and everywhere else
|
||||||
|
std::shared_ptr<Signal> signal = (**userdata).lock();
|
||||||
|
|
||||||
|
return signal->Wait(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
static int signalconnection_Disconnect(lua_State*);
|
||||||
|
|
||||||
|
static int signalconnection_gc(lua_State*);
|
||||||
|
static int signalconnection_index(lua_State*);
|
||||||
|
static int signalconnection_tostring(lua_State*);
|
||||||
|
static const struct luaL_Reg signalconnection_metatable [] = {
|
||||||
|
{"__gc", signalconnection_gc},
|
||||||
|
{"__index", signalconnection_index},
|
||||||
|
{"__tostring", signalconnection_tostring},
|
||||||
|
{NULL, NULL} /* end of array */
|
||||||
|
};
|
||||||
|
|
||||||
|
Data::SignalConnectionRef::SignalConnectionRef(std::weak_ptr<SignalConnection> ref) : signalConnection(ref) {}
|
||||||
|
Data::SignalConnectionRef::~SignalConnectionRef() = default;
|
||||||
|
|
||||||
|
const Data::TypeInfo Data::SignalConnectionRef::TYPE = {
|
||||||
|
.name = "Signal",
|
||||||
|
.fromLuaValue = &Data::SignalConnectionRef::FromLuaValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Data::TypeInfo& Data::SignalConnectionRef::GetType() const { return Data::SignalConnectionRef::TYPE; };
|
||||||
|
|
||||||
|
const Data::String Data::SignalConnectionRef::ToString() const {
|
||||||
|
return Data::String("Connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::SignalConnectionRef::operator std::weak_ptr<SignalConnection>() {
|
||||||
|
return signalConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::SignalConnectionRef::Serialize(pugi::xml_node node) const {
|
||||||
|
// Not serializable
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::SignalConnectionRef::PushLuaValue(lua_State* L) const {
|
||||||
|
int n = lua_gettop(L);
|
||||||
|
|
||||||
|
auto userdata = (std::weak_ptr<SignalConnection>**)lua_newuserdata(L, sizeof(std::weak_ptr<SignalConnection>));
|
||||||
|
*userdata = new std::weak_ptr<SignalConnection>(signalConnection);
|
||||||
|
|
||||||
|
// Create the instance's metatable
|
||||||
|
luaL_newmetatable(L, "__mt_signalconnection");
|
||||||
|
luaL_register(L, NULL, signalconnection_metatable);
|
||||||
|
|
||||||
|
lua_setmetatable(L, n+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::SignalConnectionRef::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return Data::Variant(Data::SignalConnectionRef(**userdata));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signalconnection_tostring(lua_State* L) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_pushstring(L, "SignalConnection");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signalconnection_gc(lua_State* L) {
|
||||||
|
// Destroy the contained shared_ptr
|
||||||
|
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
||||||
|
delete *userdata;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// __index(t,k)
|
||||||
|
static int signalconnection_index(lua_State* L) {
|
||||||
|
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
||||||
|
std::weak_ptr<SignalConnection> signalConnection = **userdata;
|
||||||
|
std::string key(lua_tostring(L, 2));
|
||||||
|
lua_pop(L, 2);
|
||||||
|
|
||||||
|
if (key == "Disconnect") {
|
||||||
|
lua_pushcfunction(L, signalconnection_Disconnect);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return luaL_error(L, "'%s' is not a valid member of %s", key.c_str(), "SignalConnection");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int signalconnection_Disconnect(lua_State* L) {
|
||||||
|
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
||||||
|
std::shared_ptr<SignalConnection> signal = (**userdata).lock();
|
||||||
|
|
||||||
|
signal->Disconnect();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
126
core/src/datatypes/signal.h
Normal file
126
core/src/datatypes/signal.h
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
#include "datatypes/annotation.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;
|
||||||
|
|
||||||
|
namespace Data { class SignalConnectionRef; }
|
||||||
|
|
||||||
|
class SignalConnection : public std::enable_shared_from_this<SignalConnection> {
|
||||||
|
protected:
|
||||||
|
std::weak_ptr<Signal> parentSignal;
|
||||||
|
|
||||||
|
SignalConnection(std::weak_ptr<Signal> parent);
|
||||||
|
|
||||||
|
virtual void Call(std::vector<Data::Variant>) = 0;
|
||||||
|
friend Signal;
|
||||||
|
public:
|
||||||
|
inline bool Connected() { return !parentSignal.expired(); };
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
virtual ~SignalConnection();
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSignalConnection : public SignalConnection {
|
||||||
|
std::function<void(std::vector<Data::Variant>)> function;
|
||||||
|
|
||||||
|
friend Signal;
|
||||||
|
protected:
|
||||||
|
void Call(std::vector<Data::Variant>) override;
|
||||||
|
public:
|
||||||
|
CSignalConnection(std::function<void(std::vector<Data::Variant>)>, std::weak_ptr<Signal> parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LuaSignalConnection : public SignalConnection {
|
||||||
|
lua_State* state;
|
||||||
|
int function;
|
||||||
|
|
||||||
|
friend Signal;
|
||||||
|
protected:
|
||||||
|
void Call(std::vector<Data::Variant>) override;
|
||||||
|
public:
|
||||||
|
LuaSignalConnection(lua_State*, std::weak_ptr<Signal> parent);
|
||||||
|
LuaSignalConnection (const LuaSignalConnection&) = delete;
|
||||||
|
LuaSignalConnection& operator= (const LuaSignalConnection&) = delete;
|
||||||
|
~LuaSignalConnection();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Signal : public std::enable_shared_from_this<Signal> {
|
||||||
|
std::vector<std::shared_ptr<SignalConnection>> connections;
|
||||||
|
std::vector<std::shared_ptr<SignalConnection>> onceConnections;
|
||||||
|
std::vector<std::pair<int, lua_State*>> waitingThreads;
|
||||||
|
|
||||||
|
friend SignalConnection;
|
||||||
|
public:
|
||||||
|
Signal();
|
||||||
|
virtual ~Signal();
|
||||||
|
Signal (const Signal&) = delete;
|
||||||
|
Signal& operator= (const Signal&) = delete;
|
||||||
|
|
||||||
|
void DisconnectAll();
|
||||||
|
void Fire(std::vector<Data::Variant> args);
|
||||||
|
void Fire();
|
||||||
|
Data::SignalConnectionRef Connect(std::function<void(std::vector<Data::Variant>)> callback);
|
||||||
|
Data::SignalConnectionRef Connect(lua_State*);
|
||||||
|
Data::SignalConnectionRef Once(std::function<void(std::vector<Data::Variant>)> callback);
|
||||||
|
Data::SignalConnectionRef Once(lua_State*);
|
||||||
|
int Wait(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;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SignalRef(std::weak_ptr<Signal>);
|
||||||
|
~SignalRef();
|
||||||
|
|
||||||
|
virtual const TypeInfo& GetType() const override;
|
||||||
|
static const TypeInfo TYPE;
|
||||||
|
|
||||||
|
operator std::weak_ptr<Signal>();
|
||||||
|
|
||||||
|
virtual const Data::String ToString() const override;
|
||||||
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
|
virtual void PushLuaValue(lua_State*) const override;
|
||||||
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SignalConnectionRef : public Data::Base {
|
||||||
|
std::weak_ptr<SignalConnection> signalConnection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SignalConnectionRef(std::weak_ptr<SignalConnection>);
|
||||||
|
~SignalConnectionRef();
|
||||||
|
|
||||||
|
virtual const TypeInfo& GetType() const override;
|
||||||
|
static const TypeInfo TYPE;
|
||||||
|
|
||||||
|
operator std::weak_ptr<SignalConnection>();
|
||||||
|
|
||||||
|
virtual const Data::String ToString() const override;
|
||||||
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
|
virtual void PushLuaValue(lua_State*) const override;
|
||||||
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using Data::SignalRef;
|
||||||
|
using Data::SignalConnectionRef;
|
|
@ -8,7 +8,7 @@
|
||||||
namespace reactphysics3d { class Vector3; };
|
namespace reactphysics3d { class Vector3; };
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class DEF_DATA_(from_string) Vector3 : public Base {
|
class DEF_DATA Vector3 : public Base {
|
||||||
AUTOGEN_PREAMBLE_DATA
|
AUTOGEN_PREAMBLE_DATA
|
||||||
glm::vec3 vector;
|
glm::vec3 vector;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
#ifdef __AUTOGEN__
|
#ifdef __AUTOGEN__
|
||||||
#define def_inst(...) clang::annotate("OB::def_inst", #__VA_ARGS__)
|
#define def_inst(...) clang::annotate("OB::def_inst", #__VA_ARGS__)
|
||||||
#define def_prop(...) clang::annotate("OB::def_prop", #__VA_ARGS__)
|
#define def_prop(...) clang::annotate("OB::def_prop", #__VA_ARGS__)
|
||||||
|
#define def_signal(...) clang::annotate("OB::def_signal", #__VA_ARGS__)
|
||||||
#define def_prop_category(...) clang::annotate("OB::def_prop_category", #__VA_ARGS__)
|
#define def_prop_category(...) clang::annotate("OB::def_prop_category", #__VA_ARGS__)
|
||||||
#define cframe_position_prop(...) clang::annotate("OB::cframe_position_prop", #__VA_ARGS__)
|
#define cframe_position_prop(...) clang::annotate("OB::cframe_position_prop", #__VA_ARGS__)
|
||||||
#define cframe_rotation_prop(...) clang::annotate("OB::cframe_rotation_prop", #__VA_ARGS__)
|
#define cframe_rotation_prop(...) clang::annotate("OB::cframe_rotation_prop", #__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define def_inst(...)
|
#define def_inst(...)
|
||||||
#define def_prop(...)
|
#define def_prop(...)
|
||||||
|
#define def_signal(...)
|
||||||
#define def_prop_category(...)
|
#define def_prop_category(...)
|
||||||
#define cframe_position_prop(...)
|
#define cframe_position_prop(...)
|
||||||
#define cframe_rotation_prop(...)
|
#define cframe_rotation_prop(...)
|
||||||
|
@ -26,6 +28,8 @@
|
||||||
#define DEF_INST_SERVICE_(...) [[ def_inst(__VA_ARGS__, service) ]]
|
#define DEF_INST_SERVICE_(...) [[ def_inst(__VA_ARGS__, service) ]]
|
||||||
#define DEF_PROP [[ def_prop() ]]
|
#define DEF_PROP [[ def_prop() ]]
|
||||||
#define DEF_PROP_(...) [[ def_prop(__VA_ARGS__) ]]
|
#define DEF_PROP_(...) [[ def_prop(__VA_ARGS__) ]]
|
||||||
|
#define DEF_SIGNAL [[ def_signal() ]]
|
||||||
|
#define DEF_SIGNAL_(...) [[ def_signal(__VA_ARGS__) ]]
|
||||||
|
|
||||||
// Categories
|
// Categories
|
||||||
#define DEF_PROP_CATEGORY(CATEGORY) [[ def_prop_category(category=CATEGORY) ]]
|
#define DEF_PROP_CATEGORY(CATEGORY) [[ def_prop_category(category=CATEGORY) ]]
|
||||||
|
|
|
@ -60,6 +60,8 @@ void Part::onUpdated(std::string property) {
|
||||||
// When position/rotation/size is manually edited, break all joints, they don't apply anymore
|
// When position/rotation/size is manually edited, break all joints, they don't apply anymore
|
||||||
if (property != "Anchored")
|
if (property != "Anchored")
|
||||||
BreakJoints();
|
BreakJoints();
|
||||||
|
|
||||||
|
OnParentUpdated->Fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expands provided extents to fit point
|
// Expands provided extents to fit point
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <glm/ext.hpp>
|
#include <glm/ext.hpp>
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
#include "datatypes/color3.h"
|
#include "datatypes/color3.h"
|
||||||
|
#include "datatypes/signal.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "rendering/surface.h"
|
#include "rendering/surface.h"
|
||||||
|
@ -91,6 +92,7 @@ public:
|
||||||
DEF_PROP float frontParamB = 0.5;
|
DEF_PROP float frontParamB = 0.5;
|
||||||
DEF_PROP float backParamB = 0.5;
|
DEF_PROP float backParamB = 0.5;
|
||||||
|
|
||||||
|
DEF_SIGNAL SignalSource OnParentUpdated;
|
||||||
|
|
||||||
rp::RigidBody* rigidBody = nullptr;
|
rp::RigidBody* rigidBody = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ int script_wait(lua_State*);
|
||||||
int script_delay(lua_State*);
|
int script_delay(lua_State*);
|
||||||
|
|
||||||
Script::Script(): Instance(&TYPE) {
|
Script::Script(): Instance(&TYPE) {
|
||||||
source = "print \"Hello, world!\"\nwait(1)print \"Wait success! :D\"";
|
source = "workspace.Part.OnParentUpdated:Connect(function()\n\
|
||||||
|
print(\"Yeux d'enfants\")\n\
|
||||||
|
end)";
|
||||||
}
|
}
|
||||||
|
|
||||||
Script::~Script() {
|
Script::~Script() {
|
||||||
|
@ -68,9 +70,9 @@ void Script::Stop() {
|
||||||
int script_wait(lua_State* L) {
|
int script_wait(lua_State* L) {
|
||||||
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
||||||
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
|
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
|
||||||
|
if (lua_gettop(L) > 0) lua_pop(L, 1); // pop secs
|
||||||
|
|
||||||
scriptContext->PushThreadSleep(L, secs);
|
scriptContext->PushThreadSleep(L, secs);
|
||||||
lua_pop(L, 1); // pop secs
|
|
||||||
|
|
||||||
// Yield
|
// Yield
|
||||||
return lua_yield(L, 0);
|
return lua_yield(L, 0);
|
||||||
|
|
4
deps.txt
4
deps.txt
|
@ -6,4 +6,6 @@ sdl2
|
||||||
stb
|
stb
|
||||||
qt6
|
qt6
|
||||||
reactphysics3d
|
reactphysics3d
|
||||||
pugixml
|
pugixml
|
||||||
|
luajit
|
||||||
|
qscintilla
|
Loading…
Add table
Reference in a new issue