SpacetimeDB UnrealCPP 代码生成器:Blueprint 兼容性处理与 Unreal 模块绑定生成指南
【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB
导读
本文基于 SpacetimeDB 仓库中的 UnrealCPP-README.md 及对应的 unrealcpp.rs 源码,系统讲解 UnrealCPP 代码生成器如何为 SpacetimeDB 模块生成 Unreal Engine 兼容的 C++ 绑定,以及它如何自动识别 Unreal Blueprint(蓝图)系统不支持的 C++ 类型并调整生成策略。读完本文,你将掌握:哪些 SpacetimeDB 类型无法暴露给蓝图、生成器在表查找函数/Reducer 函数/事件代理/结构体字段等场景下的具体降级行为、Optional 与 Sum Type 等复杂代数类型的生成形态,以及如何用 SpacetimeDB CLI 一步生成整套 Unreal 模块绑定。
一、UnrealCPP 代码生成器概述
UnrealCPP 代码生成器是 SpacetimeDB CLI 代码生成后端之一,实现在 unrealcpp.rs(共 7000 余行,是 codegen crate 中体量最大的后端),并在 lib.rs 中以pub mod unrealcpp注册。它面向 Unreal Engine 开发者,负责把 SpacetimeDB 模块的 schema 定义翻译成一组 Unreal C++ 绑定文件,覆盖五类核心产物:
- Table classes(表类):为每张表生成行结构体(如
FUserType、FMessageType)与表操作类(如UQuickstartChatMessageTable),并提供唯一索引查找、普通 BTree 索引过滤与事件处理入口; - Reducer classes(Reducer 类):为每个 reducer 生成带参调用函数与参数结构体,供 C++ 与蓝图发起事务调用;
- Type definitions(类型定义):为模块中的自定义结构体、Optional、Sum Type(带载荷枚举)、Plain Enum(简单枚举)生成对应的 USTRUCT/UENUM 定义;
- Client connection 管理类:封装连接建立、鉴权与订阅等客户端生命周期逻辑;
- Event delegates(事件代理):通过
DECLARE_DYNAMIC_MULTICAST_DELEGATE_*声明动态多播代理(如FSendMessageHandler),将 reducer 触发的事件实时推送给客户端。
从源码结构看,生成器通过UnrealCpp结构体(unrealcpp.rs)持有module_name、uproject_dir、module_prefix三个关键上下文,其中module_prefix是 Unreal 模块名前缀,直接决定生成的类名、API 宏与目录结构。
二、Blueprint 兼容性的核心规则:is_blueprintable()
Unreal Engine 的 Blueprint 系统并非所有 C++ 类型都能暴露,尤其是内置整型存在明显的白名单限制。UnrealCPP 生成器将这一约束内建在is_blueprintable()函数中(unrealcpp.rs),以递归方式判断某个代数类型能否用于蓝图。
2.1 蓝图不支持的原始类型
以下是无法在 Unreal Blueprint 中使用的类型(is_blueprintable()直接返回false):
| Rust 类型 | C++ 类型 | 说明 |
|---|---|---|
i8 | int8 | 有符号 8 位整数 |
i16 | int16 | 有符号 16 位整数 |
u16 | uint16 | 无符号 16 位整数 |
u32 | uint32 | 无符号 32 位整数 |
u64 | uint64 | 无符号 64 位整数 |
值得注意的细节是,is_blueprintable()中并未把int8列入黑名单——源码只对I8 | I16 | U16 | U32 | U64返回false。也就是说,虽然int8在部分 Unreal 版本中也有反射限制,但当前仓库的生成器判定标准以源码为准:int8(int8)被当作可蓝图类型处理(文档正文中 Blueprint-Compatible 类型列表也把int8、uint8列为可用的类型之一)。
2.2 蓝图兼容的类型
以下类型可以直接在 Blueprint 中使用:
boolint8、uint8、int16、int32、int64float、doubleFString(对应 Rust 的String)- 全部 SpacetimeDB SDK 类型:
Identity、ConnectionId、Timestamp、TimeDuration、Uuid、ScheduleAt等 - 自定义结构体(USTRUCT)与枚举(UENUM)
TArray<T>:当且仅当元素类型T本身可蓝图化
源码中AlgebraicTypeUse::Ref(r)分支进一步印证了"结构体本身可蓝图化"的规则:USTRUCT、Sum Type、Plain Enum 作为字段类型时一律视为可蓝图化(unrealcpp.rs),但结构体内部的字段是否暴露仍需逐字段判断——这正是后续"Struct Fields"章节降级注释的根源。
2.3 两套判定函数的差异
生成器中实际上存在两个判定函数,容易混淆:
is_blueprintable()(unrealcpp.rs):用于函数参数、结构体字段等常规场景。它对数组采取宽松策略——TArray<USTRUCT>永远可蓝图化(即使 USTRUCT 内部含不可蓝图字段),而TArray<基本类型>则递归检查元素类型。is_type_blueprintable_for_delegates()(unrealcpp.rs):专用于 reducer 事件代理参数判定,对自定义结构体一律放行("所有结构体都可作为代理参数"),但对Array分支要求元素类型递归可判定。
此外还有一个面向 Optional 内部类型的名称级判定辅助函数is_type_name_blueprintable()(unrealcpp.rs),用于决定 Optional 包装结构的Value字段是否打UPROPERTY。
三、各生成场景下的降级行为
当检测到不可蓝图类型时,生成器不会报错终止,而是"降级"生成:仍产出完整可用的 C++ 代码,只是省略UFUNCTION(BlueprintCallable)/UPROPERTY(BlueprintAssignable)等反射标记,并插入说明性注释。以下逐一展开。
3.1 表查找函数(Table Find Functions)
当表的主键或唯一索引使用uint32这类不可蓝图类型时,生成的Find函数形如:
// NOTE: Not exposed to Blueprint because uint32 types are not Blueprint-compatible FMessageType Find(uint32 Key) { return IdIndexHelper.FindUniqueIndex(Key); }对应的生成逻辑见 unrealcpp.rs:is_blueprintable()为真时才输出UFUNCTION(BlueprintCallable, Category = "SpacetimeDB|{Table}Index"),否则输出 NOTE 注释。注意源码中注释使用的是{key_type}即实际 C++ 类型名(uint32),与文档示例一致。
行为要点:
- 函数照常生成,只是没有
UFUNCTION标记; - 在 C++ 中完整可用,通过内部模板助手
FUniqueIndexHelper<{RowStruct}, {KeyType}, FTableCache<{RowStruct}>>的FindUniqueIndex(Key)完成查找; - 注释精确列出导致不兼容的类型,方便开发者定位并修改模块定义。
生成器同时也为非唯一 BTree 索引生成Filter方法与对应的UFUNCTION封装(unrealcpp.rs),当多列参数中有任一列不可蓝图化时,会输出// NOTE: Not exposed to Blueprint because some parameter types are not Blueprint-compatible并跳过反射标记。
3.2 Reducer 函数
当 reducer 的参数包含uint32、uint64等类型时:
// NOTE: Not exposed to Blueprint because uint32, uint64 types are not Blueprint-compatible void SendMessage(const FString& Text, const uint32& Priority, const uint64& Timestamp);生成逻辑见 unrealcpp.rs:在生成调用方法前,代码先遍历reducer.params_for_generate.elements收集不可蓝图参数;若列表为空则输出UFUNCTION(BlueprintCallable, Category="SpacetimeDB"),否则输出 NOTE 注释 + 普通成员函数。多个不兼容类型在注释中用,拼接。
行为要点:
- 函数完整生成,可在 C++ 中调用 reducer;
- 多个不可蓝图类型会被一次性列出,注释形如
uint32, uint64 types are not Blueprint-compatible; - 若全部参数可蓝图化,则参数类型会进一步通过
cpp_ty_fmt_blueprint_compatible()映射为蓝图安全类型;若存在不可蓝图参数,则保留原生 C++ 类型。
3.3 Reducer 事件代理(Event Delegates)
当 reducer 事件代理参数含不兼容类型时:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams( FSendMessageHandler, const FReducerEventContext&, Context, const FString&, Text, const uint32&, Priority, const uint64&, Timestamp ); // NOTE: Not exposed to Blueprint because uint32, uint64 types are not Blueprint-compatible FSendMessageHandler OnSendMessage;生成逻辑见 unrealcpp.rs。代理宏按参数个数映射:OneParam到NineParams(见 unrealcpp.rs),Context固定作为第一个参数。当non_blueprintable_types_for_delegate非空时,UPROPERTY(BlueprintAssignable, Category="SpacetimeDB")被替换为 NOTE 注释,成员变量On{ReducerName}依旧声明。
行为要点:
- 代理照常声明,可从 C++ 绑定(
OnSendMessage.AddDynamic(...)); - 蓝图无法访问该事件(无
BlueprintAssignable); - 注意源码里代理参数使用的是
cpp_ty_fmt_blueprint_compatible()映射后的类型,并依据should_pass_by_value_in_delegate()决定按值(仅原生标量)或按const 引用传递(FSpacetimeDB 结构体等复杂类型)。
参数多于 9 个的边界情况:Unreal 动态代理最多支持 9 个参数。当 reducer 参数超过 9 个时,生成器改用DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams把参数整体包装进一个F{Reducer}Args结构体(unrealcpp.rs),此时代理因Args结构体本身是 USTRUCT 而恒可蓝图化,但函数层面仍需逐个参数判定。
3.4 结构体字段(Struct Fields)
当结构体字段含不可蓝图类型时:
USTRUCT(BlueprintType) struct MYMODULE_API FSendMessageArgs { GENERATED_BODY() UPROPERTY(BlueprintReadWrite, Category="SpacetimeDB") FString Text; // NOTE: uint32 types can't be used in blueprints uint32 Priority; // NOTE: uint64 types can't be used in blueprints uint64 Timestamp; };行为要点:
- 可蓝图字段获得
UPROPERTY(BlueprintReadWrite); - 不可蓝图字段保持普通 C++ 成员,并附带说明注释;
- 整个结构体仍是
USTRUCT(BlueprintType),可蓝图字段照常被反射系统识别。
这意味着:包含不可蓝图字段的结构体本身仍能作为函数参数(遵循is_blueprintable()中"USTRUCT 永远可蓝图化"的规则),只是其内部不可蓝图字段在蓝图中不可见、不可编辑。
四、Optional 类型(Option<T>)的生成
SpacetimeDB 的Option<T>在 Unreal 中没有原生对应物,生成器将其翻译为自定义 USTRUCT,存放在Public/ModuleBindings/Optionals/目录下(生成路径见 unrealcpp.rs),并通过bHasValue+Value双字段模拟"有值/无值"语义:
USTRUCT(BlueprintType) struct MYMODULE_API FMyModuleOptionalString { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpacetimeDB", meta = (EditCondition = "bHasValue")) bool bHasValue = false; // Only gets UPROPERTY if the inner type is Blueprint-compatible UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpacetimeDB", meta = (EditCondition = "bHasValue")) FString Value; // Constructors and helper methods... };4.1 命名与映射规则
get_optional_type_name()(unrealcpp.rs)建立了从内部类型到 Optional 名称的完整映射表:
| 内部类型 | Optional 名称 | 说明 |
|---|---|---|
bool | OptionalBool | 基本布尔 |
i8~u256 | OptionalInt8~OptionalUInt256 | 全部整数宽度 |
f32/f64 | OptionalFloat/OptionalDouble | 浮点 |
String | OptionalString | 字符串 |
Identity/ConnectionId | OptionalIdentity/OptionalConnectionId | 身份类 |
Timestamp/TimeDuration | OptionalTimestamp/OptionalTimeDuration | 时间类 |
Uuid/ScheduleAt | OptionalUuid/OptionalScheduleAt | 唯一 ID / 调度 |
TArray<E> | OptionalVec{ElementName} | 数组 |
Ref(自定义类型) | Optional{TypeName} | 自定义结构体 |
Option<Option<T>> | OptionalOptional... | 嵌套 Optional |
Result<Ok, Err> | Optional{ResultName} | Result 包装 |
最终生成的类型名为F{ModuleNamePascal}{OptionalName}(如FQuickstartChatOptionalString),结构体定义由generate_optional_type()(unrealcpp.rs)输出。
4.2 关键行为
bHasValue语义:布尔标记指示值是否存在;IsSet()与Reset()辅助方法分别查询与清除该标记;EditCondition联动:Value字段的UPROPERTY带meta = (EditCondition = "bHasValue"),在编辑器属性面板中只有勾选bHasValue后才能编辑Value;- 按内部类型降级:若内部类型不可蓝图化(如
Option<u32>),Value字段不输出UPROPERTY,仅保留普通成员与// NOTE: ...注释(判断依据is_type_name_blueprintable()); - 相等比较:生成
operator==/operator!=,语义为"标记相等且(无值或值相等)"; - 哈希支持:生成自定义
GetTypeHash(组合bHasValue与Value的哈希),保证 Optional 可安全用于TSet/TMap; - 序列化:生成
UE_SPACETIMEDB_OPTIONAL({StructName}, bHasValue, Value)宏(unrealcpp.rs)与UE_SPACETIMEDB_ENABLE_TARRAY,接入 BSATN 序列化体系,保证与服务器 wire 格式一致。
五、Sum Type(带载荷枚举)的生成
SpacetimeDB 的 sum type(Rust 中带 payload 的 enum,如enum QueryUpdate { Uncompressed(...), Brotli(...), Gzip(...) })无法直接映射为 UE 枚举。生成器采用UENUM 标签 + USTRUCT(TVariant) + UBlueprintFunctionLibrary的组合方案(实现见autogen_cpp_sum(),unrealcpp.rs 起):
// Tag enum for variant identification UENUM(BlueprintType) enum class ECompressableQueryUpdateTag : uint8 { Uncompressed, Brotli, Gzip }; // Main struct USTRUCT(BlueprintType) struct SPACETIMEDBSDK_API FCompressableQueryUpdateType { GENERATED_BODY() public: FCompressableQueryUpdateType() = default; TVariant<FQueryUpdateType, TArray<uint8>> MessageData; UPROPERTY(BlueprintReadOnly) ECompressableQueryUpdateTag Tag; static FCompressableQueryUpdateType Uncompressed(const FQueryUpdateType& Value) { FCompressableQueryUpdateType Obj; Obj.Tag = ECompressableQueryUpdateTag::Uncompressed; Obj.MessageData.Set<FQueryUpdateType>(Value); return Obj; } static FCompressableQueryUpdateType Brotli(const TArray<uint8>& Value) { FCompressableQueryUpdateType Obj; Obj.Tag = ECompressableQueryUpdateTag::Brotli; Obj.MessageData.Set<TArray<uint8>>(Value); return Obj; } static FCompressableQueryUpdateType Gzip(const TArray<uint8>& Value) { FCompressableQueryUpdateType Obj; Obj.Tag = ECompressableQueryUpdateTag::Gzip; Obj.MessageData.Set<TArray<uint8>>(Value); return Obj; } // Is* functions bool IsUncompressed() const { return Tag == ECompressableQueryUpdateTag::Uncompressed; } // GetAs* functions FQueryUpdateType GetAsUncompressed() const { ensureMsgf(IsUncompressed(), TEXT("MessageData does not hold Uncompressed!")); return MessageData.Get<FQueryUpdateType>(); } }; // Corresponding blueprint function library for using the sum types UCLASS() class SPACETIMEDBSDK_API UCompressableQueryUpdateBpLib : public UBlueprintFunctionLibrary { GENERATED_BODY() private: UFUNCTION(BlueprintCallable, Category = "SpacetimeDB|CompressableQueryUpdate") static FCompressableQueryUpdateType Uncompressed(const FQueryUpdateType& InValue) { return FCompressableQueryUpdateType::Uncompressed(InValue); } UFUNCTION(BlueprintPure, Category = "SpacetimeDB|CompressableQueryUpdate") static bool IsUncompressed(const FCompressableQueryUpdateType& InValue) { return InValue.IsUncompressed(); } UFUNCTION(BlueprintPure, Category = "SpacetimeDB|CompressableQueryUpdate") static FQueryUpdateType GetAsUncompressed(const FCompressableQueryUpdateType& InValue) { return InValue.GetAsUncompressed(); } // Rest is the same for other variants... }5.1 关键行为
- UStruct + TVariant 组合:载荷存储于 Unreal
TVariant,相比逐一持有所有成员更节省内存,本质是 C++ union 的封装; - 标签枚举:
E{Name}Tag : uint8标识当前变体,配合UPROPERTY(BlueprintReadOnly)暴露给蓝图读取; - 类型安全:每个变体拥有独立的静态工厂函数(
Uncompressed(...)、Brotli(...))与只读访问器(IsUncompressed()/GetAsUncompressed()),GetAs*内部用ensureMsgf断言当前 Tag 匹配,避免错误访问; - 单位变体:不带载荷的变体使用
FSpacetimeDBUnit类型占位(从类型映射表可见Unit => FSpacetimeDBUnit,unrealcpp.rs); - 蓝图完全可访问:生成对应的
UBlueprintFunctionLibrary,将工厂、判断、取值函数分别包装为BlueprintCallable/BlueprintPure静态函数,且按SpacetimeDB|{SumTypeName}分类,蓝图节点可直接调用; - BSATN 支持:生成带序列化宏,与服务器 wire 格式保持一致;
- 相等与哈希:生成
operator==/operator!=(按 Tag 分支比较各变体载荷)与自定义GetTypeHash。
六、Plain Enum(简单枚举)的生成
Rust 中仅含 unit 变体(无载荷)的 enum 会被识别为 Plain Enum,直接翻译为标准 Unreal 枚举类,实现见autogen_cpp_enum()(unrealcpp.rs)。
Rust 定义:
#[derive(...)] pub enum Status { Pending, Active, Inactive, Suspended, }生成的 C++:
UENUM(BlueprintType) enum class EStatusType : uint8 { Pending, Active, Inactive, Suspended, };6.1 关键行为
- 轻量 UENUM:直接映射为标准
enum class,底层类型固定为uint8,无任何内存开销; - Blueprint 全兼容:
UENUM(BlueprintType)使其可用于蓝图下拉选择、switch 节点等; - 命名约定:
E{Name}Type格式(如EStatusType),变体统一转为 PascalCase(源码中通过.to_case(Case::Pascal)实现,unrealcpp.rs); - 类型安全:强类型
enum class杜绝隐式整数转换; - 序列化:生成
UE_SPACETIMEDB_ENABLE_TARRAY(E{Name}Type)支持 TArray 与 BSATN。
6.2 与 Sum Type 的对比
| 维度 | Plain Enum | Sum Type |
|---|---|---|
| 生成形态 | 简单UENUM | UStruct + TVariant + BPLib |
| 载荷 | 无 | 有(变体可携带数据) |
| 内存开销 | 无 | union 语义(TVariant 占用最大成员空间) |
| 蓝图用法 | 直接使用 | 通过 UBlueprintFunctionLibrary 间接构造/访问 |
| 典型场景 | 状态码、状态机枚举 | Result、QueryUpdate等多形态数据 |
七、使用 CLI 生成模块绑定
7.1 基本命令
cargo run --bin spacetimedb-cli -- generate --lang unrealcpp --uproject-dir <uproject_directory> --module-path <module_path> --unreal-module-name <ModuleName>7.2 仓库内示例
仓库自带的 Unreal 示例工程位于 sdks/unreal/examples/QuickstartChat(含QuickstartChat.uproject与Source/目录),对应生成命令:
cargo run --bin spacetimedb-cli -- generate --lang unrealcpp --uproject-dir crates/sdk-unreal/examples/QuickstartChat --module-path modules/quickstart-chat --unreal-module-name QuickstartChat(注:示例路径在当前仓库为sdks/unreal/examples/QuickstartChat,--module-path指向存放 SpacetimeDB 模块 Rust 源码的目录。)
7.3 参数说明
| 参数 | 必需 | 说明 |
|---|---|---|
--lang unrealcpp | 是 | 选择 UnrealCPP 代码生成后端 |
--uproject-dir | 是 | 包含 Unreal 工程.uproject文件的目录 |
--module-path | 是 | SpacetimeDB 模块源码路径(Rust 模块定义) |
--unreal-module-name | 必填 | 生成类的前缀、API 宏名及绑定文件的落盘模块目录 |
7.4 为什么--unreal-module-name是强制的
UnrealCpp结构体通过get_api_macro()(unrealcpp.rs)生成{MODULE_NAME}_API形式的宏(例如QUICKSTARTCHAT_API)。该参数必不可少,原因有四:
- Unreal API 宏:生成的类均以
MODULENAME_API宏声明(如struct QUICKSTARTCHAT_API FMessageType),保证跨模块的 DLL 导出/导入正确链接; - 类名前缀:所有 Optional 等生成类以模块名做前缀(如
FQuickstartChatOptionalString),避免多模块命名冲突; - 构建系统集成:Unreal 构建系统(UBT)依赖 API 宏完成模块间符号解析,缺失会导致链接失败;
- 落盘位置:生成的绑定文件需写入
Source/{ModuleName}/...对应模块源码目录,确保被目标模块编译进同一 DLL。
⚠️ 若不提供模块名,生成的代码将因缺失 API 宏与命名冲突而无法在 Unreal Engine 中编译。
7.5 工程装配行为
生成器向 Unreal 工程写入绑定文件的同时还会自动完成工程装配,相关逻辑见 unrealcpp.rs 与ensure_module_in_uproject()(unrealcpp.rs):
- 校验/注入
Modules数组:读取--uproject-dir下唯一的.uproject文件并解析 JSON,若缺少Modules字段则自动补一个空数组,若目标模块不在Modules中则注入模块项; - 补齐模块骨架文件:自动创建
Source/{Module}/{Module}.Build.cs、Source/{Module}/{Module}.cpp、Source/{Module}/{Module}.h(模块头中#include "Modules/ModuleManager.h"并实现模块类)。
失败条件:若.uproject文件缺失、不可读、JSON 格式非法或顶层不是对象、Modules字段不是数组,生成会立即失败并给出明确错误(如No .uproject file found in '...')。
八、实现细节与错误信息约定
8.1 兼容性检查的递归实现
is_blueprintable()的判定递归覆盖全部代数类型分支:
Primitive:黑名单(i8/i16/u16/u32/u64)之外的原语一律放行;Array:TArray<USTRUCT/UENUM/PlainEnum>恒可蓝图化;基本类型数组递归判断元素;String/Identity/ConnectionId/Timestamp/TimeDuration/Uuid/ScheduleAt/Unit:均返回true;Ref:结构体、Sum、Plain Enum 引用均视为可蓝图化(字段级仍逐字段判断);Option:递归判定内部类型;Result:要求Ok与Err两侧同时可蓝图化;Never:返回false。
8.2 错误信息的一致性
生成器在遍历 reducer 参数/代理参数/结构体字段时一次性收集所有不兼容类型(避免重复循环),并在注释中列出。错误信息遵循统一格式(实现于 unrealcpp.rs 等处的join(", ")):
- 单类型:
"uint32 types are not Blueprint-compatible" - 多类型:
"uint32, uint64 types are not Blueprint-compatible"
这一约定让开发者能一眼看出需要修改模块中的哪些字段类型才能获得完整的 Blueprint 支持。
九、实践建议
结合上述规则,若希望模块函数、事件与结构体字段获得最大化的 Blueprint 覆盖,可从以下几点入手:
- 优先使用可蓝图整型:模块定义中尽量使用
i32、i64、u8,避免u32/u64/i8/i16/u16;确实需要无符号宽整型时,明确其只能用于 C++ 调用链; - 善用 Optional 与 Sum Type 封装:
Option<T>与 sum type 均被翻译为 USTRUCT 形态,可以在蓝图中传递;对不可蓝图内部类型,Value 字段会失去反射标记,但整个结构体仍可作为参数; - 遵循注释提示修正模块:生成代码中的
// NOTE: ... not Blueprint-compatible注释就是最直接的体检报告,按注释修改模块类型后重新生成即可恢复 Blueprint 暴露; - 确保 Unreal 工程结构完整:
--unreal-module-name必须与工程目标模块一致,--uproject-dir下必须存在合法的.uproject文件,否则生成立即失败; - 保留 SDK 类型不动:
Identity、Timestamp、Uuid等 SpacetimeDB SDK 内建类型已保证 Blueprint 兼容,可作为替代方案减少自定类型的暴露风险。
十、关联资源
- UnrealCPP-README.md:本文所依据的官方说明文档
- unrealcpp.rs:UnrealCPP 代码生成后端完整实现(
is_blueprintable()见 L6286、generate_optional_type()见 L5715、autogen_cpp_sum()见 L6402、autogen_cpp_enum()见 L6373、ensure_module_in_uproject()见 L6962) - lib.rs:代码生成后端注册入口(
pub mod unrealcpp) - sdks/unreal/examples/QuickstartChat:Unreal 示例工程,可作为
--uproject-dir与生成结果的参考 - sdks/unreal:Unreal SDK 源码与文档,包含
ModuleBindings/Optionals等生成物所依赖的运行时基础
【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考