载入中...
搜索中...
未找到
Rx.cpp
浏览该文件的文档.
1#include "rx/Rx.h"
2
4
5#include <simplesquirrel/simplesquirrel.hpp>
6
7#include <squirrel.h>
8
9#include <unordered_map>
10
11namespace eve::rx {
12
13// ---------------------------------------------------------------------------
14// Value conversion helpers.
15// ---------------------------------------------------------------------------
16int64_t Value::toInt() const {
17 switch (type) {
18 case Type::Int: return i;
19 case Type::Float: return static_cast<int64_t>(f);
20 case Type::Bool: return b ? 1 : 0;
21 case Type::String:
22 try {
23 return static_cast<int64_t>(std::stoll(s));
24 } catch (...) {
25 return 0;
26 }
27 default: return 0;
28 }
29}
30
31double Value::toFloat() const {
32 switch (type) {
33 case Type::Int: return static_cast<double>(i);
34 case Type::Float: return f;
35 case Type::Bool: return b ? 1.0 : 0.0;
36 case Type::String:
37 try {
38 return std::stod(s);
39 } catch (...) {
40 return 0.0;
41 }
42 default: return 0.0;
43 }
44}
45
46bool Value::toBool() const {
47 switch (type) {
48 case Type::Int: return i != 0;
49 case Type::Float: return f != 0.0;
50 case Type::Bool: return b;
51 case Type::String: return !s.empty();
52 case Type::Ptr: return p != nullptr;
53 default: return false;
54 }
55}
56
57std::string Value::toString() const {
58 switch (type) {
59 case Type::Int: return std::to_string(i);
60 case Type::Float: return std::to_string(f);
61 case Type::Bool: return b ? "true" : "false";
62 case Type::String: return s;
63 case Type::Ptr: return "ptr";
64 default: return "";
65 }
66}
67
68bool Value::equals(const Value& o) const {
69 if (type != o.type) return false;
70 switch (type) {
71 case Type::Int: return i == o.i;
72 case Type::Float: return f == o.f;
73 case Type::Bool: return b == o.b;
74 case Type::String: return s == o.s;
75 case Type::Ptr: return p == o.p;
76 default: return true;
77 }
78}
79
80} // namespace eve::rx
81
82// ---------------------------------------------------------------------------
83// ssq::detail specializations so Value can cross the Squirrel boundary in
84// bound method parameters / return values. Declared here so all bindings in
85// this TU instantiate against them.
86// ---------------------------------------------------------------------------
87namespace ssq {
88namespace detail {
89
90template <>
91inline void pushValue(HSQUIRRELVM vm, const eve::rx::Value& v) {
92 switch (v.type) {
93 case eve::rx::Value::Type::Nil: sq_pushnull(vm); break;
94 case eve::rx::Value::Type::Int: sq_pushinteger(vm, static_cast<SQInteger>(v.i)); break;
95 case eve::rx::Value::Type::Float: sq_pushfloat(vm, static_cast<SQFloat>(v.f)); break;
96 case eve::rx::Value::Type::Bool: sq_pushbool(vm, v.b ? SQTrue : SQFalse); break;
98 sq_pushstring(vm, v.s.c_str(), static_cast<SQInteger>(v.s.size()));
99 break;
100 case eve::rx::Value::Type::Ptr: sq_pushuserpointer(vm, v.p); break;
101 }
102}
103
104template <>
106 switch (sq_gettype(vm, index)) {
107 case OT_NULL: return eve::rx::Value::makeNil();
108 case OT_INTEGER: {
109 SQInteger i = 0;
110 sq_getinteger(vm, index, &i);
111 return eve::rx::Value::makeInt(static_cast<int64_t>(i));
112 }
113 case OT_FLOAT: {
114 SQFloat f = 0;
115 sq_getfloat(vm, index, &f);
116 return eve::rx::Value::makeFloat(static_cast<double>(f));
117 }
118 case OT_BOOL: {
119 SQBool b = SQFalse;
120 sq_getbool(vm, index, &b);
121 return eve::rx::Value::makeBool(b == SQTrue);
122 }
123 case OT_STRING: {
124 const SQChar* s = nullptr;
125 sq_getstring(vm, index, &s);
126 return eve::rx::Value::makeString(s ? s : "");
127 }
128 case OT_USERPOINTER: {
129 SQUserPointer p = nullptr;
130 sq_getuserpointer(vm, index, &p);
132 }
133 default: return eve::rx::Value::makeNil();
134 }
135}
136
137} // namespace detail
138} // namespace ssq
139
140namespace eve::rx {
141
142// ---------------------------------------------------------------------------
143// Script-facing aliases (Value-typed).
144// ---------------------------------------------------------------------------
150
151namespace {
152
153// Call a script function with the given Value args; returns its Value result.
154Value callScript(const ssq::Function& fn, const std::vector<Value>& args, bool wantResult) {
155 if (fn.isEmpty()) return Value::makeNil();
156 HSQUIRRELVM raw = fn.getHandle();
157 SQInteger top = sq_gettop(raw);
158 sq_pushobject(raw, fn.getRaw());
159 sq_pushroottable(raw);
160 for (const auto& a : args) ssq::detail::pushValue(raw, a);
161 Value result = Value::makeNil();
162 if (SQ_SUCCEEDED(sq_call(raw, static_cast<SQInteger>(args.size() + 1),
163 wantResult ? SQTrue : SQFalse, SQTrue))) {
164 if (wantResult && sq_gettop(raw) > top) result = ssq::detail::popValue<Value>(raw, -1);
165 }
166 sq_settop(raw, top);
167 return result;
168}
169
170std::function<void()> scriptCompleted(ssq::Function fn) {
171 return [fn]() { callScript(fn, {}, false); };
172}
173std::function<void(const Value&)> scriptNext(ssq::Function fn) {
174 return [fn](const Value& v) { callScript(fn, {v}, false); };
175}
176std::function<void(const std::string&)> scriptError(ssq::Function fn) {
177 return [fn](const std::string& e) { callScript(fn, {Value::makeString(e)}, false); };
178}
179
180bool isClosure(const ssq::Object& obj) {
181 if (obj.isEmpty()) return false;
182 auto t = obj.getType();
183 return t == ssq::Type::CLOSURE || t == ssq::Type::NATIVECLOSURE;
184}
185
186// Return a copy of obj if it is a usable closure, else an empty Object.
187ssq::Object asClosure(const ssq::Object& obj) {
188 if (isClosure(obj)) return obj;
189 return ssq::Object();
190}
191
192Observer<Value> makeObserver(ssq::Object next, ssq::Object error, ssq::Object done) {
194 ssq::Object n = asClosure(next);
195 ssq::Object e = asClosure(error);
196 ssq::Object c = asClosure(done);
197 if (!n.isEmpty()) o.onNext = scriptNext(n.toFunction());
198 if (!e.isEmpty()) o.onError = scriptError(e.toFunction());
199 if (!c.isEmpty()) o.onCompleted = scriptCompleted(c.toFunction());
200 return o;
201}
202
203Subscription* doSubscribe(ObservableV* obs, ssq::Object next, ssq::Object error, ssq::Object done) {
204 if (!obs) throw eve::Exception("Rx.subscribe: null observable");
205 return new Subscription(obs->subscribe(makeObserver(next, error, done)));
206}
207
208// map(fn) : ObservableV -> ObservableV via script function returning Value.
209ObservableV* doMap(ObservableV* self, ssq::Function fn) {
210 if (!self) throw eve::Exception("Rx.map: null observable");
211 return new AnonymousObservable<Value>([self, fn](Observer<Value> out) {
213 in.onNext = [out, fn](const Value& v) { out.next(callScript(fn, {v}, true)); };
214 in.onError = [out](const std::string& e) { out.error(e); };
215 in.onCompleted = [out]() { out.completed(); };
216 return self->subscribe(std::move(in));
217 });
218}
219
220ObservableV* doFilter(ObservableV* self, ssq::Function pred) {
221 if (!self) throw eve::Exception("Rx.filter: null observable");
222 return new AnonymousObservable<Value>([self, pred](Observer<Value> out) {
224 in.onNext = [out, pred](const Value& v) {
225 if (callScript(pred, {v}, true).toBool()) out.next(v);
226 };
227 in.onError = [out](const std::string& e) { out.error(e); };
228 in.onCompleted = [out]() { out.completed(); };
229 return self->subscribe(std::move(in));
230 });
231}
232
233ObservableV* doTake(ObservableV* self, int n) {
234 if (!self) throw eve::Exception("Rx.take: null observable");
235 return new AnonymousObservable<Value>([self, n](Observer<Value> out) {
236 auto remaining = std::make_shared<int>(n);
238 in.onNext = [out, remaining](const Value& v) {
239 if (*remaining <= 0) return;
240 *remaining -= 1;
241 out.next(v);
242 if (*remaining == 0) out.completed();
243 };
244 in.onError = [out](const std::string& e) { out.error(e); };
245 in.onCompleted = [out]() { out.completed(); };
246 return self->subscribe(std::move(in));
247 });
248}
249
250ObservableV* doSkip(ObservableV* self, int n) {
251 if (!self) throw eve::Exception("Rx.skip: null observable");
252 return new AnonymousObservable<Value>([self, n](Observer<Value> out) {
253 auto remaining = std::make_shared<int>(n);
255 in.onNext = [out, remaining](const Value& v) {
256 if (*remaining > 0) {
257 *remaining -= 1;
258 return;
259 }
260 out.next(v);
261 };
262 in.onError = [out](const std::string& e) { out.error(e); };
263 in.onCompleted = [out]() { out.completed(); };
264 return self->subscribe(std::move(in));
265 });
266}
267
268ObservableV* doFirst(ObservableV* self) {
269 if (!self) throw eve::Exception("Rx.first: null observable");
270 return new AnonymousObservable<Value>([self](Observer<Value> out) {
271 auto done = std::make_shared<bool>(false);
273 in.onNext = [out, done](const Value& v) {
274 if (*done) return;
275 *done = true;
276 out.next(v);
277 out.completed();
278 };
279 in.onError = [out](const std::string& e) { out.error(e); };
280 in.onCompleted = [out]() { out.completed(); };
281 return self->subscribe(std::move(in));
282 });
283}
284
285ObservableV* doDistinctUntilChanged(ObservableV* self) {
286 if (!self) throw eve::Exception("Rx.distinctUntilChanged: null observable");
287 return new AnonymousObservable<Value>([self](Observer<Value> out) {
288 auto last = std::make_shared<Value>();
289 auto has = std::make_shared<bool>(false);
291 in.onNext = [out, last, has](const Value& v) {
292 if (*has && last->equals(v)) return;
293 *has = true;
294 *last = v;
295 out.next(v);
296 };
297 in.onError = [out](const std::string& e) { out.error(e); };
298 in.onCompleted = [out]() { out.completed(); };
299 return self->subscribe(std::move(in));
300 });
301}
302
303} // namespace
304
305// ---------------------------------------------------------------------------
306// Rx module.
307// ---------------------------------------------------------------------------
308class Rx : public Module {
309public:
311
312 SubjectV* newSubject() { return new SubjectV(); }
313 BehaviorSubjectV* newBehaviorSubject(const Value& initial) { return new BehaviorSubjectV(initial); }
315 ReactivePropertyV* newProperty(const Value& initial) { return new ReactivePropertyV(initial); }
316
317 // Event bridge: fromEvent(name) returns an Observable that forwards matching
318 // messages pushed by pump() (fed from an eve::platform_event::PlatformEvent queue).
319 ObservableV* fromEvent(const std::string& eventName) {
320 auto subject = std::make_shared<SubjectV>();
321 {
322 std::lock_guard<std::mutex> lock(mu_);
323 bridges_[eventName] = subject;
324 }
325 // Return a fresh observable that subscribes to the shared subject. The
326 // returned AnonymousObservable is owned by the script; the shared subject
327 // stays alive in bridges_ so pump() can keep feeding it.
329 return subject->subscribe(std::move(out));
330 });
331 }
332
334 if (!ev) return;
335 while (auto msg = ev->pollOwned()) {
336 std::string eventName = msg->name;
337 std::string data;
338 for (const auto& a : msg->args) {
340 data = a.s;
341 break;
342 }
343 }
344 std::shared_ptr<SubjectV> subject;
345 {
346 std::lock_guard<std::mutex> lock(mu_);
347 auto it = bridges_.find(eventName);
348 if (it != bridges_.end()) subject = it->second;
349 }
350 if (subject) subject->onNext(Value::makeString(data));
351 }
352 }
353
354private:
355 std::mutex mu_;
356 std::unordered_map<std::string, std::shared_ptr<SubjectV>> bridges_;
357};
358
360
361// ---------------------------------------------------------------------------
362// Squirrel binding.
363// ---------------------------------------------------------------------------
364namespace {
365
366using ObsFn = std::function<ObservableV*(ObservableV*)>;
367
368void bindCommon(ssq::Class& cls) {
369 cls.addFunc("subscribe",
370 std::function<Subscription*(ObservableV*, ssq::Object)>(
371 [](ObservableV* self, ssq::Object n) { return doSubscribe(self, n, {}, {}); }));
372 cls.addFunc("subscribe3",
373 std::function<Subscription*(ObservableV*, ssq::Object, ssq::Object, ssq::Object)>(
374 [](ObservableV* self, ssq::Object n, ssq::Object e, ssq::Object d) {
375 return doSubscribe(self, n, e, d);
376 }));
377 cls.addFunc("map",
378 std::function<ObservableV*(ObservableV*, ssq::Function)>(
379 [](ObservableV* self, ssq::Function f) { return doMap(self, f); }));
380 cls.addFunc("filter",
381 std::function<ObservableV*(ObservableV*, ssq::Function)>(
382 [](ObservableV* self, ssq::Function p) { return doFilter(self, p); }));
383 cls.addFunc("take",
384 std::function<ObservableV*(ObservableV*, int)>(
385 [](ObservableV* self, int n) { return doTake(self, n); }));
386 cls.addFunc("skip",
387 std::function<ObservableV*(ObservableV*, int)>(
388 [](ObservableV* self, int n) { return doSkip(self, n); }));
389 cls.addFunc("first",
390 std::function<ObservableV*(ObservableV*)>([](ObservableV* self) { return doFirst(self); }));
391 cls.addFunc("distinctUntilChanged",
392 std::function<ObservableV*(ObservableV*)>(
393 [](ObservableV* self) { return doDistinctUntilChanged(self); }));
394}
395
396} // namespace
397
398void Rx::expose(ssq::Table& table) {
399 auto cls = table.addClass(name, Rx::create, false);
400 expose(cls);
401
402 auto obs = table.addClass<ObservableV>(
403 "Observable", std::function<ObservableV*()>([]() -> ObservableV* { return nullptr; }), true);
404 bindCommon(obs);
405
406 auto subject = table.addClass<SubjectV>(
407 "Subject", std::function<SubjectV*()>([]() -> SubjectV* { return nullptr; }), true);
408 subject.addFunc("onNext", &SubjectV::onNext);
409 subject.addFunc("onError", &SubjectV::onError);
410 subject.addFunc("onCompleted", &SubjectV::onCompleted);
411 subject.addFunc("hasObservers", &SubjectV::hasObservers);
412 bindCommon(subject);
413
414 auto behavior = table.addClass<BehaviorSubjectV>(
415 "BehaviorSubject", std::function<BehaviorSubjectV*()>([]() -> BehaviorSubjectV* { return nullptr; }), true);
416 behavior.addFunc("onNext", &BehaviorSubjectV::onNext);
417 behavior.addFunc("onError", &BehaviorSubjectV::onError);
418 behavior.addFunc("onCompleted", &BehaviorSubjectV::onCompleted);
419 behavior.addFunc("hasObservers", &BehaviorSubjectV::hasObservers);
420 behavior.addFunc("getValue", &BehaviorSubjectV::getValue);
421 behavior.addFunc("setValue", &BehaviorSubjectV::setValue);
422 bindCommon(behavior);
423
424 auto replay = table.addClass<ReplaySubjectV>(
425 "ReplaySubject", std::function<ReplaySubjectV*()>([]() -> ReplaySubjectV* { return nullptr; }), true);
426 replay.addFunc("onNext", &ReplaySubjectV::onNext);
427 replay.addFunc("onError", &ReplaySubjectV::onError);
428 replay.addFunc("onCompleted", &ReplaySubjectV::onCompleted);
429 replay.addFunc("hasObservers", &ReplaySubjectV::hasObservers);
430 bindCommon(replay);
431
432 auto prop = table.addClass<ReactivePropertyV>(
433 "ReactiveProperty", std::function<ReactivePropertyV*()>([]() -> ReactivePropertyV* { return nullptr; }), true);
434 prop.addFunc("get", &ReactivePropertyV::get);
435 prop.addFunc("set", &ReactivePropertyV::set);
436 prop.addFunc("subscribe",
437 std::function<Subscription*(ReactivePropertyV*, ssq::Object)>(
438 [](ReactivePropertyV* self, ssq::Object n) {
439 if (!self) throw eve::Exception("Rx.subscribe: null property");
440 return doSubscribe(self->asObservable(), n, {}, {});
441 }));
442 prop.addFunc("subscribe3",
443 std::function<Subscription*(ReactivePropertyV*, ssq::Object, ssq::Object, ssq::Object)>(
444 [](ReactivePropertyV* self, ssq::Object n, ssq::Object e, ssq::Object d) {
445 if (!self) throw eve::Exception("Rx.subscribe: null property");
446 return doSubscribe(self->asObservable(), n, e, d);
447 }));
448
449 auto sub = table.addClass<Subscription>(
450 "Subscription", std::function<Subscription*()>([]() -> Subscription* { return nullptr; }), true);
451 sub.addFunc("dispose", &Subscription::dispose);
452 sub.addFunc("isDisposed", &Subscription::isDisposed);
453}
454
455void Rx::expose(ssq::Class& cls) {
456 cls.addFunc("getName", &Rx::getName);
457 cls.addFunc("newSubject", &Rx::newSubject);
458 cls.addFunc("newBehaviorSubject", &Rx::newBehaviorSubject);
459 cls.addFunc("newReplaySubject", &Rx::newReplaySubject);
460 cls.addFunc("newProperty", &Rx::newProperty);
461 cls.addFunc("fromEvent", &Rx::fromEvent);
462 cls.addFunc("pump", &Rx::pump);
463}
464
465} // namespace eve::rx
SQInteger top
int subject
Definition AnimSmr.cpp:163
const std::string & s
struct SQVM * HSQUIRRELVM
glm::vec4 p[6]
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::uint32_t capacity
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::string error
Definition Package.cpp:60
float f
float d
int detail
float t
uint32_t index
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
EVENGINE_API_FOUNDATION public API.
Definition Module.h:46
virtual std::string getName() const =0
Returns the name.
EVENGINE_API_FOUNDATION public API.
std::unique_ptr< Message > pollOwned()
Pops the oldest message into an RAII handle, or returns null.
Internal: observable built directly from a subscribe function.
Definition Rx.h:239
Subject that replays the latest value to every new subscriber. setValue()/onNext() update the stored ...
Definition Rx.h:501
void onCompleted()
Stops replay and delivers a terminal completion.
Definition Rx.h:552
bool hasObservers() const
True while at least one live observer is registered.
Definition Rx.h:561
T getValue() const
Current stored value.
Definition Rx.h:524
void onNext(T v)
Alias of setValue().
Definition Rx.h:541
void onError(const std::string &e)
Stops replay and delivers a terminal error.
Definition Rx.h:543
void setValue(T v)
Stores a new value and pushes it to observers.
Definition Rx.h:530
Push-based stream source. Operators return a new (caller-owned) Observable; subscribe() returns a Sub...
Definition Rx.h:191
virtual Subscription subscribe(Observer< T > obs)=0
Subscribes with a full observer; returns a cancel handle.
Callback bundle pushed to a subscriber. Once error() or completed() fires the observer is stopped and...
Definition Rx.h:146
CompletedFn onCompleted
Completion callback (terminal).
Definition Rx.h:157
void error(const std::string &e) const
Delivers a terminal error and stops.
Definition Rx.h:169
void completed() const
Delivers a terminal completion and stops.
Definition Rx.h:175
NextFn onNext
Value callback.
Definition Rx.h:153
ErrorFn onError
Error callback (terminal).
Definition Rx.h:155
void next(const T &v) const
Delivers a value unless stopped.
Definition Rx.h:165
Observable value backed by a BehaviorSubject. get() returns the current value; set() stores and pushe...
Definition Rx.h:631
T get() const
Current value.
Definition Rx.h:637
void set(T v)
Stores a new value and notifies subscribers.
Definition Rx.h:639
Subject that buffers up to capacity values (0 = unlimited) and replays the buffer to every new subscr...
Definition Rx.h:576
void onNext(T v)
Buffers and pushes a new value to observers.
Definition Rx.h:596
void onCompleted()
Delivers a terminal completion to observers.
Definition Rx.h:612
void onError(const std::string &e)
Delivers a terminal error to observers.
Definition Rx.h:608
bool hasObservers() const
True while at least one live observer is registered.
Definition Rx.h:616
ReactivePropertyV * newProperty(const Value &initial)
Definition Rx.cpp:315
ReplaySubjectV * newReplaySubject(int capacity=0)
Definition Rx.cpp:314
void pump(platform_event::PlatformEvent *ev)
Definition Rx.cpp:333
BehaviorSubjectV * newBehaviorSubject(const Value &initial)
Definition Rx.cpp:313
SubjectV * newSubject()
Definition Rx.cpp:312
ObservableV * fromEvent(const std::string &eventName)
Definition Rx.cpp:319
Multicast push-based stream: both an Observable and a push source. Thread-safe: onNext/onError/onComp...
Definition Rx.h:399
bool hasObservers() const
True while at least one live observer is registered.
Definition Rx.h:443
void onCompleted()
Pushes a terminal completion and stops the subject.
Definition Rx.h:435
void onNext(const T &v)
Pushes a value to every registered observer.
Definition Rx.h:426
void onError(const std::string &e)
Pushes a terminal error and stops the subject.
Definition Rx.h:428
RAII-style dispose handle for an active stream subscription. Disposing unsubscribes from the source; ...
Definition Rx.h:93
void dispose()
Runs the dispose callback once; idempotent.
Definition Rx.h:122
bool isDisposed() const
True once dispose() has run (or the handle was moved from).
Definition Rx.h:132
Runtime variant used by the script-facing (Squirrel) streams. The C++ core is templated (Observer<T>/...
Definition Rx.h:21
Type type
Definition Rx.h:26
bool toBool() const
Definition Rx.cpp:46
double toFloat() const
Definition Rx.cpp:31
static Value makeFloat(double v)
Constructs a floating-point value.
Definition Rx.h:43
static Value makeNil()
Constructs a nil value.
Definition Rx.h:34
bool b
Definition Rx.h:29
static Value makePtr(void *v)
Constructs a pointer value (borrowed, not owned).
Definition Rx.h:64
int64_t toInt() const
Converters (throw eve::Exception on type mismatch).
Definition Rx.cpp:16
std::string s
Definition Rx.h:30
static Value makeInt(int64_t v)
Constructs an integer value.
Definition Rx.h:36
bool equals(const Value &o) const
Value equality across the supported types.
Definition Rx.cpp:68
double f
Definition Rx.h:28
void * p
Definition Rx.h:31
static Value makeString(std::string v)
Constructs a string value (takes ownership).
Definition Rx.h:57
int64_t i
Definition Rx.h:27
static Value makeBool(bool v)
Constructs a boolean value.
Definition Rx.h:50
std::string toString() const
Definition Rx.cpp:57
Result< void > replay(const Trace &trace, IEnvironment &environment, double tolerance)
Reset and replay actual actions, checking all observations and failure evidence.
Definition Agent.cpp:258
eve::Subscription Subscription
Compatibility alias for the common observer lifetime token.
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
Definition Rx.cpp:11
ReactiveProperty< Value > ReactivePropertyV
Definition Rx.cpp:149
Observable< Value > ObservableV
Definition Rx.cpp:148
Subject< Value > SubjectV
Definition Rx.cpp:145
BehaviorSubject< Value > BehaviorSubjectV
Definition Rx.cpp:146
ReplaySubject< Value > ReplaySubjectV
Definition Rx.cpp:147
void pushValue(HSQUIRRELVM vm, const eve::rx::Value &v)
Definition Rx.cpp:91
eve::rx::Value popValue(HSQUIRRELVM vm, SQInteger index)
Definition Rx.cpp:105
EVEngine ECS 集成层:底层实现使用 sunxfancy/ECS.hpp https://github.com/sunxfancy/ECS.hpp
Definition ECS.h:14
SettlementPipeline::Stage fn