unreal

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SpacetimeDB Unreal Engine Integration

SpacetimeDB Unreal Engine 集成

This skill covers Unreal Engine-specific patterns for connecting to SpacetimeDB. For server-side module development, see the
rust-server
or
csharp-server
skills.

此 skill 介绍连接 SpacetimeDB 的 Unreal Engine 专属模式。对于服务端模块开发,请参阅
rust-server
或
csharp-server
skills。

Installation

安装

Add the SpacetimeDB Unreal SDK as a plugin:
  1. Create a
    Plugins
    folder in your Unreal project root if it does not exist.
  2. Copy the
    SpacetimeDbSdk
    folder into
    Plugins/
    .
  3. Right-click your
    .uproject
    file and select Generate Visual Studio project files.
  4. Add
    "SpacetimeDbSdk"
    to your module's
    Build.cs
    :
csharp
PublicDependencyModuleNames.AddRange(new string[] { "SpacetimeDbSdk" });

将 SpacetimeDB Unreal SDK 作为 plugin 添加:
  1. 如果 Unreal 项目根目录中不存在
    Plugins
    文件夹,请创建。
  2. 将
    SpacetimeDbSdk
    文件夹复制到
    Plugins/
    。
  3. 右键点击
    .uproject
    文件并选择 生成 Visual Studio 项目文件。
  4. 将
    "SpacetimeDbSdk"
    添加到模块的
    Build.cs
    :
csharp
PublicDependencyModuleNames.AddRange(new string[] { "SpacetimeDbSdk" });

Generate Module Bindings

生成模块绑定

bash
spacetime generate --lang unrealcpp \
  --uproject-dir <path_to_uproject_directory> \
  --module-path <path_to_spacetimedb_module> \
  --unreal-module-name <your_unreal_module_name>
This generates C++ bindings in
ModuleBindings/
inside your project. Include the generated header:
cpp
#include "ModuleBindings/SpacetimeDBClient.g.h"
Regenerate whenever you change module tables, reducers, or types.

bash
spacetime generate --lang unrealcpp \
  --uproject-dir <path_to_uproject_directory> \
  --module-path <path_to_spacetimedb_module> \
  --unreal-module-name <your_unreal_module_name>
这会在项目的
ModuleBindings/
中生成 C++ 绑定。请包含生成的头文件:
cpp
#include "ModuleBindings/SpacetimeDBClient.g.h"
每次更改模块 tables、reducers 或 types 后,都要重新生成。

GameManager Actor Pattern

GameManager Actor 模式

The recommended pattern is a singleton Actor that owns the connection. Enable ticking so
FrameTick
is called every frame.
推荐模式是使用一个拥有连接的单例 Actor。启用 ticking,使
FrameTick
每帧被调用。

Header (GameManager.h)

头文件(GameManager.h)

cpp
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ModuleBindings/SpacetimeDBClient.g.h"
#include "GameManager.generated.h"

class UDbConnection;

UCLASS()
class AGameManager : public AActor
{
    GENERATED_BODY()
public:
    AGameManager();
    static AGameManager* Instance;

    UPROPERTY(EditAnywhere, Category="SpacetimeDB")
    FString ServerUri = TEXT("127.0.0.1:3000");

    UPROPERTY(EditAnywhere, Category="SpacetimeDB")
    FString DatabaseName = TEXT("my-module");

    UPROPERTY(BlueprintReadOnly, Category="SpacetimeDB")
    UDbConnection* Conn = nullptr;

    UPROPERTY(BlueprintReadOnly, Category="SpacetimeDB")
    FSpacetimeDBIdentity LocalIdentity;

protected:
    virtual void BeginPlay() override;
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
public:
    virtual void Tick(float DeltaTime) override;

private:
    UFUNCTION() void HandleConnect(UDbConnection* InConn, FSpacetimeDBIdentity Identity, const FString& Token);
    UFUNCTION() void HandleConnectError(const FString& Error);
    UFUNCTION() void HandleDisconnect(UDbConnection* InConn, const FString& Error);
    UFUNCTION() void HandleSubscriptionApplied(FSubscriptionEventContext& Context);
};
cpp
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ModuleBindings/SpacetimeDBClient.g.h"
#include "GameManager.generated.h"

class UDbConnection;

UCLASS()
class AGameManager : public AActor
{
    GENERATED_BODY()
public:
    AGameManager();
    static AGameManager* Instance;

    UPROPERTY(EditAnywhere, Category="SpacetimeDB")
    FString ServerUri = TEXT("127.0.0.1:3000");

    UPROPERTY(EditAnywhere, Category="SpacetimeDB")
    FString DatabaseName = TEXT("my-module");

    UPROPERTY(BlueprintReadOnly, Category="SpacetimeDB")
    UDbConnection* Conn = nullptr;

    UPROPERTY(BlueprintReadOnly, Category="SpacetimeDB")
    FSpacetimeDBIdentity LocalIdentity;

protected:
    virtual void BeginPlay() override;
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
public:
    virtual void Tick(float DeltaTime) override;

private:
    UFUNCTION() void HandleConnect(UDbConnection* InConn, FSpacetimeDBIdentity Identity, const FString& Token);
    UFUNCTION() void HandleConnectError(const FString& Error);
    UFUNCTION() void HandleDisconnect(UDbConnection* InConn, const FString& Error);
    UFUNCTION() void HandleSubscriptionApplied(FSubscriptionEventContext& Context);
};

Source (GameManager.cpp)

源文件(GameManager.cpp)

cpp
#include "GameManager.h"
#include "Connection/Credentials.h"

AGameManager* AGameManager::Instance = nullptr;

AGameManager::AGameManager()
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.bStartWithTickEnabled = true;
}

void AGameManager::BeginPlay()
{
    Super::BeginPlay();
    Instance = this;

    FOnConnectDelegate ConnectDelegate;
    BIND_DELEGATE_SAFE(ConnectDelegate, this, AGameManager, HandleConnect);
    FOnDisconnectDelegate DisconnectDelegate;
    BIND_DELEGATE_SAFE(DisconnectDelegate, this, AGameManager, HandleDisconnect);
    FOnConnectErrorDelegate ConnectErrorDelegate;
    BIND_DELEGATE_SAFE(ConnectErrorDelegate, this, AGameManager, HandleConnectError);

    UCredentials::Init(TEXT(".spacetime_token"));
    FString Token = UCredentials::LoadToken();

    UDbConnectionBuilder* Builder = UDbConnection::Builder()
        ->WithUri(ServerUri)
        ->WithDatabaseName(DatabaseName)
        ->OnConnect(ConnectDelegate)
        ->OnDisconnect(DisconnectDelegate)
        ->OnConnectError(ConnectErrorDelegate);

    if (!Token.IsEmpty())
    {
        Builder->WithToken(Token);
    }

    Conn = Builder->Build();
}

void AGameManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    if (Conn) { Conn->Disconnect(); Conn = nullptr; }
    if (Instance == this) { Instance = nullptr; }
    Super::EndPlay(EndPlayReason);
}

void AGameManager::Tick(float DeltaTime)
{
    if (Conn && Conn->IsActive())
    {
        Conn->FrameTick();
    }
}

void AGameManager::HandleConnect(UDbConnection* InConn, FSpacetimeDBIdentity Identity, const FString& Token)
{
    LocalIdentity = Identity;
    UCredentials::SaveToken(Token);

    FOnSubscriptionApplied AppliedDelegate;
    BIND_DELEGATE_SAFE(AppliedDelegate, this, AGameManager, HandleSubscriptionApplied);
    Conn->SubscriptionBuilder()
        ->OnApplied(AppliedDelegate)
        ->SubscribeToAllTables();
}

void AGameManager::HandleConnectError(const FString& Error)
{
    UE_LOG(LogTemp, Error, TEXT("Connection error: %s"), *Error);
}

void AGameManager::HandleDisconnect(UDbConnection* InConn, const FString& Error)
{
    UE_LOG(LogTemp, Warning, TEXT("Disconnected: %s"), *Error);
}

void AGameManager::HandleSubscriptionApplied(FSubscriptionEventContext& Context)
{
    UE_LOG(LogTemp, Log, TEXT("Subscription applied - game state loaded"));
}

cpp
#include "GameManager.h"
#include "Connection/Credentials.h"

AGameManager* AGameManager::Instance = nullptr;

AGameManager::AGameManager()
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.bStartWithTickEnabled = true;
}

void AGameManager::BeginPlay()
{
    Super::BeginPlay();
    Instance = this;

    FOnConnectDelegate ConnectDelegate;
    BIND_DELEGATE_SAFE(ConnectDelegate, this, AGameManager, HandleConnect);
    FOnDisconnectDelegate DisconnectDelegate;
    BIND_DELEGATE_SAFE(DisconnectDelegate, this, AGameManager, HandleDisconnect);
    FOnConnectErrorDelegate ConnectErrorDelegate;
    BIND_DELEGATE_SAFE(ConnectErrorDelegate, this, AGameManager, HandleConnectError);

    UCredentials::Init(TEXT(".spacetime_token"));
    FString Token = UCredentials::LoadToken();

    UDbConnectionBuilder* Builder = UDbConnection::Builder()
        ->WithUri(ServerUri)
        ->WithDatabaseName(DatabaseName)
        ->OnConnect(ConnectDelegate)
        ->OnDisconnect(DisconnectDelegate)
        ->OnConnectError(ConnectErrorDelegate);

    if (!Token.IsEmpty())
    {
        Builder->WithToken(Token);
    }

    Conn = Builder->Build();
}

void AGameManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    if (Conn) { Conn->Disconnect(); Conn = nullptr; }
    if (Instance == this) { Instance = nullptr; }
    Super::EndPlay(EndPlayReason);
}

void AGameManager::Tick(float DeltaTime)
{
    if (Conn && Conn->IsActive())
    {
        Conn->FrameTick();
    }
}

void AGameManager::HandleConnect(UDbConnection* InConn, FSpacetimeDBIdentity Identity, const FString& Token)
{
    LocalIdentity = Identity;
    UCredentials::SaveToken(Token);

    FOnSubscriptionApplied AppliedDelegate;
    BIND_DELEGATE_SAFE(AppliedDelegate, this, AGameManager, HandleSubscriptionApplied);
    Conn->SubscriptionBuilder()
        ->OnApplied(AppliedDelegate)
        ->SubscribeToAllTables();
}

void AGameManager::HandleConnectError(const FString& Error)
{
    UE_LOG(LogTemp, Error, TEXT("Connection error: %s"), *Error);
}

void AGameManager::HandleDisconnect(UDbConnection* InConn, const FString& Error)
{
    UE_LOG(LogTemp, Warning, TEXT("Disconnected: %s"), *Error);
}

void AGameManager::HandleSubscriptionApplied(FSubscriptionEventContext& Context)
{
    UE_LOG(LogTemp, Log, TEXT("Subscription applied - game state loaded"));
}

FrameTick -- Critical

FrameTick -- 关键

You must either call
Conn->FrameTick()
every frame in your Actor's
Tick()
, or call
Conn->SetAutoTicking(true)
once at startup.
The SDK queues all network messages and only processes them on tick. Without one of these, no callbacks fire and the client appears frozen.

你必须在 Actor 的
Tick()
中每帧调用
Conn->FrameTick()
,或者在启动时调用一次
Conn->SetAutoTicking(true)
。
SDK 会将所有网络消息加入队列,并且仅在 tick 时处理它们。如果不采用其中任何一种方式,回调将不会触发,客户端看起来会陷入冻结状态。

Connection Builder

连接 Builder

Build a connection with the builder pattern. All builder methods return pointers for chaining with
->
.
cpp
UDbConnection* Conn = UDbConnection::Builder()
    ->WithUri(TEXT("127.0.0.1:3000"))
    ->WithDatabaseName(TEXT("my-module"))
    ->WithToken(SavedToken)                              // optional
    ->WithCompression(ESpacetimeDBCompression::Gzip)     // optional
    ->OnConnect(ConnectDelegate)
    ->OnConnectError(ErrorDelegate)
    ->OnDisconnect(DisconnectDelegate)
    ->Build();
使用 builder 模式创建连接。所有 builder 方法都会返回指针,可通过
->
进行链式调用。
cpp
UDbConnection* Conn = UDbConnection::Builder()
    ->WithUri(TEXT("127.0.0.1:3000"))
    ->WithDatabaseName(TEXT("my-module"))
    ->WithToken(SavedToken)                              // optional
    ->WithCompression(ESpacetimeDBCompression::Gzip)     // optional
    ->OnConnect(ConnectDelegate)
    ->OnConnectError(ErrorDelegate)
    ->OnDisconnect(DisconnectDelegate)
    ->Build();

OnConnect callback signature

OnConnect 回调签名

cpp
UFUNCTION()
void OnConnected(UDbConnection* Connection, FSpacetimeDBIdentity Identity, const FString& Token);
Save the
Token
for future reconnection. The
Identity
is the user's persistent identifier.

cpp
UFUNCTION()
void OnConnected(UDbConnection* Connection, FSpacetimeDBIdentity Identity, const FString& Token);
保存
Token
以供后续重新连接。
Identity
是用户的持久标识符。

Subscribing to Tables

订阅表

After connecting, subscribe to receive table data:
cpp
// Subscribe to all public tables
Conn->SubscriptionBuilder()
    ->OnApplied(AppliedDelegate)
    ->SubscribeToAllTables();

// Subscribe to specific queries
TArray<FString> Queries = { TEXT("SELECT * FROM player"), TEXT("SELECT * FROM entity") };
Conn->SubscriptionBuilder()
    ->OnApplied(AppliedDelegate)
    ->OnError(ErrorDelegate)
    ->Subscribe(Queries);
连接后,订阅以接收表数据:
cpp
// Subscribe to all public tables
Conn->SubscriptionBuilder()
    ->OnApplied(AppliedDelegate)
    ->SubscribeToAllTables();

// Subscribe to specific queries
TArray<FString> Queries = { TEXT("SELECT * FROM player"), TEXT("SELECT * FROM entity") };
Conn->SubscriptionBuilder()
    ->OnApplied(AppliedDelegate)
    ->OnError(ErrorDelegate)
    ->Subscribe(Queries);

Subscription Handle

订阅句柄

Subscribe
and
SubscribeToAllTables
return a
USubscriptionHandle*
:
cpp
USubscriptionHandle* Handle = Conn->SubscriptionBuilder()->...->Subscribe(Queries);
Handle->IsActive();      // true while subscription is live
Handle->Unsubscribe();   // cancel the subscription
Handle->UnsubscribeThen(OnEndDelegate); // cancel with callback
Handle->GetQuerySqls();  // get the SQL queries

For views with arguments (2.13+), use only raw SQL
SELECT * FROM view_name(literal_args)
. Use explicit queries for every table on that connection, not subscribe-all. All argument values share one cache table; unsubscribe from obsolete arguments to avoid combining their rows. Validate/encode dynamic values as SQL literals rather than concatenating untrusted input.
Subscribe
和
SubscribeToAllTables
返回一个
USubscriptionHandle*
:
cpp
USubscriptionHandle* Handle = Conn->SubscriptionBuilder()->...->Subscribe(Queries);
Handle->IsActive();      // true while subscription is live
Handle->Unsubscribe();   // cancel the subscription
Handle->UnsubscribeThen(OnEndDelegate); // cancel with callback
Handle->GetQuerySqls();  // get the SQL queries

对于带参数的 views(2.13+),只能使用原始 SQL
SELECT * FROM view_name(literal_args)
。在该连接上为每张表使用显式查询,不要使用 subscribe-all。所有参数值共享同一个缓存表;请取消订阅不再使用的参数,以避免将其行合并。对动态值进行验证并编码为 SQL 字面量,而不是直接拼接不受信任的输入。

Reading the Client Cache

读取客户端缓存

Access tables through
Conn->Db
:
cpp
// Find by unique/primary key (returns by value; default-constructed if not found)
FUserType User = Conn->Db->User->Identity->Find(SomeIdentity);

// Filter by BTree index
TArray<FPlayerType> LevelFive = Conn->Db->Player->Level->Filter(5);

// Iterate all rows
TArray<FEntityType> AllEntities = Conn->Db->Entity->Iter();

// Count
int32 Total = Conn->Db->Player->Count();

通过
Conn->Db
访问表:
cpp
// Find by unique/primary key (returns by value; default-constructed if not found)
FUserType User = Conn->Db->User->Identity->Find(SomeIdentity);

// Filter by BTree index
TArray<FPlayerType> LevelFive = Conn->Db->Player->Level->Filter(5);

// Iterate all rows
TArray<FEntityType> AllEntities = Conn->Db->Entity->Iter();

// Count
int32 Total = Conn->Db->Player->Count();

Row Callbacks

行回调

Register callbacks on table objects. Callbacks use Unreal dynamic multicast delegates.
cpp
// OnInsert
Conn->Db->User->OnInsert.AddDynamic(this, &AMyActor::OnUserInsert);

// OnDelete
Conn->Db->User->OnDelete.AddDynamic(this, &AMyActor::OnUserDelete);

// OnUpdate (only fires for rows with a primary key)
Conn->Db->User->OnUpdate.AddDynamic(this, &AMyActor::OnUserUpdate);
在表对象上注册回调。回调使用 Unreal 动态多播 delegates。
cpp
// OnInsert
Conn->Db->User->OnInsert.AddDynamic(this, &AMyActor::OnUserInsert);

// OnDelete
Conn->Db->User->OnDelete.AddDynamic(this, &AMyActor::OnUserDelete);

// OnUpdate (only fires for rows with a primary key)
Conn->Db->User->OnUpdate.AddDynamic(this, &AMyActor::OnUserUpdate);

Callback signatures (must be UFUNCTION)

回调签名(必须是 UFUNCTION)

cpp
UFUNCTION()
void OnUserInsert(const FEventContext& Context, const FUserType& NewRow);

UFUNCTION()
void OnUserDelete(const FEventContext& Context, const FUserType& DeletedRow);

UFUNCTION()
void OnUserUpdate(const FEventContext& Context, const FUserType& OldRow, const FUserType& NewRow);
Register callbacks before connecting or in
HandleSubscriptionApplied
.

cpp
UFUNCTION()
void OnUserInsert(const FEventContext& Context, const FUserType& NewRow);

UFUNCTION()
void OnUserDelete(const FEventContext& Context, const FUserType& DeletedRow);

UFUNCTION()
void OnUserUpdate(const FEventContext& Context, const FUserType& OldRow, const FUserType& NewRow);
在连接前或
HandleSubscriptionApplied
中注册回调。

Calling Reducers

调用 Reducers

Invoke reducers through
Conn->Reducers
:
cpp
Conn->Reducers->SendMessage(TEXT("Hello!"));
Conn->Reducers->SetName(TEXT("Alice"));
Conn->Reducers->MovePlayer(1.0f, 0.0f);
通过
Conn->Reducers
调用 reducers:
cpp
Conn->Reducers->SendMessage(TEXT("Hello!"));
Conn->Reducers->SetName(TEXT("Alice"));
Conn->Reducers->MovePlayer(1.0f, 0.0f);

Reducer Result Callbacks

Reducer 结果回调

Observe when a reducer you called completes:
cpp
Conn->Reducers->OnSendMessage.AddDynamic(this, &AMyActor::OnSendMessageResult);
cpp
UFUNCTION()
void OnSendMessageResult(const FReducerEventContext& Context, const FString& Text)
{
    UE_LOG(LogTemp, Log, TEXT("SendMessage result for: %s"), *Text);
}
These delegates fire only for reducer calls made by this connection, not for other clients' calls.

观察你调用的 reducer 何时完成:
cpp
Conn->Reducers->OnSendMessage.AddDynamic(this, &AMyActor::OnSendMessageResult);
cpp
UFUNCTION()
void OnSendMessageResult(const FReducerEventContext& Context, const FString& Text)
{
    UE_LOG(LogTemp, Log, TEXT("SendMessage result for: %s"), *Text);
}
这些 delegates 仅针对由此连接发起的 reducer 调用触发,而不会针对其他客户端的调用触发。

Delegate Binding with BIND_DELEGATE_SAFE

使用 BIND_DELEGATE_SAFE 绑定 Delegate

Use the
BIND_DELEGATE_SAFE
macro to safely bind delegates to member functions:
cpp
FOnConnectDelegate ConnectDelegate;
BIND_DELEGATE_SAFE(ConnectDelegate, this, AMyActor, HandleConnect);
This is the recommended pattern for all SpacetimeDB delegate bindings in C++.

使用
BIND_DELEGATE_SAFE
宏将 delegates 安全绑定到成员函数:
cpp
FOnConnectDelegate ConnectDelegate;
BIND_DELEGATE_SAFE(ConnectDelegate, this, AMyActor, HandleConnect);
这是在 C++ 中所有 SpacetimeDB delegate 绑定的推荐模式。

Identity and ConnectionId

Identity 与 ConnectionId

cpp
// FSpacetimeDBIdentity -- 256-bit unique user identifier, persists across connections
FSpacetimeDBIdentity Identity;
Identity.ToHex();

// FSpacetimeDBConnectionId -- 128-bit per-session connection identifier
FSpacetimeDBConnectionId ConnId = Conn->GetConnectionId();

// From any context
FSpacetimeDBIdentity Id;
bool Found = Context.TryGetIdentity(Id);
FSpacetimeDBConnectionId CId = Context.GetConnectionId();

cpp
// FSpacetimeDBIdentity -- 256-bit unique user identifier, persists across connections
FSpacetimeDBIdentity Identity;
Identity.ToHex();

// FSpacetimeDBConnectionId -- 128-bit per-session connection identifier
FSpacetimeDBConnectionId ConnId = Conn->GetConnectionId();

// From any context
FSpacetimeDBIdentity Id;
bool Found = Context.TryGetIdentity(Id);
FSpacetimeDBConnectionId CId = Context.GetConnectionId();

Token Persistence

Token 持久化

Use the built-in
UCredentials
helper to save and load tokens. Passing the saved server-issued token back on reconnect keeps the same identity. This token does not expire and a lost one can't be recovered, so self-issued identities are for development. For production, authenticate with an OIDC provider such as SpacetimeAuth, which handles token lifecycle.
cpp
UCredentials::Init(TEXT(".spacetime_token"));
FString Token = UCredentials::LoadToken();
// ... after connect:
UCredentials::SaveToken(Token);

使用内置的
UCredentials
helper 保存和加载 token。将服务器签发的已保存 token 在重新连接时传回,可保持相同的 identity。该 token 不会过期,丢失后也无法恢复,因此自行签发的 identity 仅用于开发。生产环境应使用 OIDC provider(例如 SpacetimeAuth)进行身份验证,由其管理 token 生命周期。
cpp
UCredentials::Init(TEXT(".spacetime_token"));
FString Token = UCredentials::LoadToken();
// ... after connect:
UCredentials::SaveToken(Token);

Context Types

Context 类型

All callbacks receive a context struct that provides access to
Db
and
Reducers
:
TypeUsed In
FEventContext
Table row callbacks (OnInsert, OnDelete, OnUpdate)
FReducerEventContext
Reducer result callbacks
FSubscriptionEventContext
Subscription lifecycle callbacks (OnApplied, OnError)
FErrorContext
Error callbacks
All inherit from
FContextBase
which provides:
cpp
Context.Db          // URemoteTables* -- client cache
Context.Reducers    // URemoteReducers* -- invoke reducers
Context.SubscriptionBuilder()  // start a new subscription

所有回调都会接收一个 context struct,该结构体提供对
Db
和
Reducers
的访问:
类型使用场景
FEventContext
表行回调(OnInsert、OnDelete、OnUpdate)
FReducerEventContext
Reducer 结果回调
FSubscriptionEventContext
订阅生命周期回调(OnApplied、OnError)
FErrorContext
错误回调
它们都继承自
FContextBase
,后者提供:
cpp
Context.Db          // URemoteTables* -- client cache
Context.Reducers    // URemoteReducers* -- invoke reducers
Context.SubscriptionBuilder()  // start a new subscription

Blueprint Integration

Blueprint 集成

All core classes are Blueprint-accessible via
UFUNCTION(BlueprintCallable)
and
UPROPERTY(BlueprintReadOnly/BlueprintAssignable)
:
  • UDbConnection::Builder()
    and all builder methods are
    BlueprintCallable
    .
  • Table callbacks (
    OnInsert
    ,
    OnDelete
    ,
    OnUpdate
    ) are
    BlueprintAssignable
    delegates.
  • Reducer invoke methods and result delegates are Blueprint-accessible.
  • Conn->Db
    and
    Conn->Reducers
    are
    BlueprintReadOnly
    properties.
  • Generated row types are
    BlueprintType
    USTRUCTs with
    BlueprintReadWrite
    properties.
This means you can build the entire connection and callback flow in Blueprints without writing C++.

所有核心类都可通过
UFUNCTION(BlueprintCallable)
和
UPROPERTY(BlueprintReadOnly/BlueprintAssignable)
供 Blueprint 访问:
  • UDbConnection::Builder()
    及所有 builder 方法均为
    BlueprintCallable
    。
  • 表 callbacks(
    OnInsert
    、
    OnDelete
    、
    OnUpdate
    )是
    BlueprintAssignable
    delegates。
  • Reducer 调用方法和结果 delegates 可供 Blueprint 访问。
  • Conn->Db
    和
    Conn->Reducers
    是
    BlueprintReadOnly
    properties。
  • 生成的行类型是带有
    BlueprintReadWrite
    properties 的
    BlueprintType
    USTRUCT。
这意味着你可以在 Blueprints 中构建完整的连接和回调流程,而无需编写 C++。

Unreal-Specific Considerations

Unreal 专属注意事项

Auto Ticking Alternative

自动 Ticking 替代方案

UDbConnection
inherits from
FTickableGameObject
, but auto ticking is off by default. You have two options:
cpp
// Option 1: Call FrameTick() manually in your Actor's Tick() (shown in GameManager above)
void Tick(float DeltaTime) { Conn->FrameTick(); }

// Option 2: Enable auto ticking. The SDK then processes messages every frame automatically
Conn->SetAutoTicking(true);
Pick one. Without either, no callbacks fire.
UDbConnection
继承自
FTickableGameObject
,但自动 ticking 默认关闭。你有两个选项:
cpp
// Option 1: Call FrameTick() manually in your Actor's Tick() (shown in GameManager above)
void Tick(float DeltaTime) { Conn->FrameTick(); }

// Option 2: Enable auto ticking. The SDK then processes messages every frame automatically
Conn->SetAutoTicking(true);
选择其中一种。如果两者都不采用,回调将不会触发。

Compression

压缩

cpp
Builder->WithCompression(ESpacetimeDBCompression::Gzip);  // default
Builder->WithCompression(ESpacetimeDBCompression::None);   // no compression
cpp
Builder->WithCompression(ESpacetimeDBCompression::Gzip);  // default
Builder->WithCompression(ESpacetimeDBCompression::None);   // no compression

Generated Types

生成的类型

Codegen produces USTRUCTs prefixed with
F
(e.g.,
FUserType
,
FEntityType
) and table classes prefixed with
U
(e.g.,
UUserTable
). Row types use
GENERATED_BODY()
and
UPROPERTY()
for full reflection support.
Codegen 会生成以
F
为前缀的 USTRUCT(例如
FUserType
、
FEntityType
),以及以
U
为前缀的表类(例如
UUserTable
)。行类型使用
GENERATED_BODY()
和
UPROPERTY()
来提供完整的反射支持。