mcpField
C
cpp-server

cpp-server

github.com/clockworklabs開発25.4k45 回インストール今日更新
リポジトリ

cpp-server について

SpacetimeDBのサーバーモジュールを作成する開発者向けのC++ SDKリファレンススキルです。テーブルマクロ、Reducer、データベース操作、インデックス、実行コンテキストAPIを説明します。

主な機能

  1. 01テーブルとフィールドのマクロパターン
  2. 02Reducerとデータベース操作
  3. 03インデックスと範囲クエリ
  4. 04ライフサイクルフックとスケジュールテーブル

活用シーン

  1. 01C++テーブルと制約の定義
  2. 02Reducerとデータベースアクセスの実装
  3. 03インデックス検索や範囲検索の追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/clockworklabs/spacetimedb --skill cpp-server

インストール後、Claude Code で/cpp-serverを入力して読み込まれたか確認します。

詳細

SpacetimeDB C++ SDK リファレンス

インポート

#include <spacetimedb.h>
using namespace SpacetimeDB;

テーブル

マクロで構造体を登録し、その後テーブルとして宣言します。

struct Entity {
    uint64_t id;
    Identity owner;
    std::string name;
    bool active;
};
SPACETIMEDB_STRUCT(Entity, id, owner, name, active)
SPACETIMEDB_TABLE(Entity, entity, Public)
FIELD_PrimaryKeyAutoInc(entity, id)
FIELD_Index(entity, name)

オプション:

  • SPACETIMEDB_TABLE(Type, accessor, Public|Private): 通常のテーブル
  • SPACETIMEDB_TABLE(Type, accessor, Public|Private, true): イベントテーブル

フィールド制約:

  • FIELD_PrimaryKey(accessor, field): 主キー
  • FIELD_PrimaryKeyAutoInc(accessor, field): 自動インクリメント付き主キー(挿入時は 0 を使用)
  • FIELD_Unique(accessor, field): 一意制約
  • FIELD_Index(accessor, field): B ツリーインデックス(.filter() を有効にします)

列の型

C++ typeNotes
uint8_t / uint16_t / uint32_t / uint64_t符号なし整数
SpacetimeDB::u128 / SpacetimeDB::u256大きな符号なし整数
int8_t / int16_t / int32_t / int64_t符号付き整数
SpacetimeDB::i128 / SpacetimeDB::i256大きな符号付き整数
float / double浮動小数点数
boolブール値
std::stringテキスト
std::vector<T>リスト/配列
std::optional<T>NULL 許容列
Identityユーザー ID
ConnectionId接続ハンドル
Timestampサーバータイムスタンプ(エポックからのマイクロ秒)
TimeDurationマイクロ秒単位の期間
ScheduleAtスケジュールテーブル用

インデックス

// Single-column:
FIELD_Index(entity, name)
// Access: ctx.db[entity_name].filter("Alice")

// Multi-column:
FIELD_NamedMultiColumnIndex(score, by_player_and_level, player_id, level)

範囲クエリ(spacetimedb.h に含まれています。アンブレラヘッダーを使用しない場合に限り、<spacetimedb/range_queries.h> を直接インクルードしてください):

ctx.db[user_age].filter(range_inclusive(uint8_t(18), uint8_t(65)));
ctx.db[user_age].filter(range_from(uint8_t(18)));

リデューサー

すべてのリデューサーは ReducerResult を返します。Ok() または Err(message) を使用してください。

SPACETIMEDB_REDUCER(create_entity, ReducerContext ctx, std::string name) {
    if (name.empty()) {
        return Err("Name cannot be empty");
    }
    ctx.db[entity].insert(Entity{0, ctx.sender(), name, true});
    return Ok();
}

DB 操作

ctx.db[entity].insert(Entity{0, owner, "Sample", true});     // Insert (0 for autoInc)
ctx.db[entity_id].find(entityId);                             // Find by PK → std::optional
ctx.db[entity_identity].find(ctx.sender());                   // Find by unique column
ctx.db[entity_name].filter("Alice");                          // Filter by index → iterable
ctx.db[entity];                                               // All rows → iterable (range-for)
ctx.db[entity].count();                                       // Count rows

// Update: find, mutate, update
if (auto e = ctx.db[entity_id].find(entityId)) {
    e->name = "New Name";
    ctx.db[entity_id].update(*e);
}

// Delete by primary key
ctx.db[entity_id].delete_by_key(entityId);

注:すべてのテーブルアクセスには角括弧表記 ctx.db[accessor] を使用します。アクセサー名は SPACETIMEDB_TABLE マクロおよび FIELD_* マクロに由来します。

ライフサイクルフック

SPACETIMEDB_INIT(init, ReducerContext ctx) {
    LOG_INFO("Database initializing...");
    return Ok();
}

SPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {
    LOG_INFO("Connected: " + ctx.sender().to_string());
    return Ok();
}

SPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {
    LOG_INFO("Disconnected: " + ctx.sender().to_string());
    return Ok();
}

認証とタイムスタンプ

// Auth: ctx.sender() is the caller's Identity
if (row.owner != ctx.sender()) {
    return Err("unauthorized");
}

// Server timestamps
ctx.db[item].insert(Item{0, ctx.sender(), ctx.timestamp});

// Timestamp arithmetic
Timestamp later = ctx.timestamp + TimeDuration::from_seconds(10);

リデューサーコンテキスト

ReducerContext は、リデューサー内で送信者 ID、決定論的な時刻、および決定論的な乱数を取得する唯一の手段です。これらには必ず ctx を使用してください。モジュール内では、標準ライブラリの時計や乱数ソースは使用できません。

ctx.db[table]          // Table access (bracket notation)
ctx.sender()           // Caller's Identity
ctx.timestamp          // Invocation timestamp
ctx.connection_id      // std::optional<ConnectionId>
ctx.identity()         // Module's own identity
ctx.rng()              // Deterministic RNG
ctx.sender_auth()      // AuthCtx with JWT claims

スケジュールテーブル

struct Reminder {
    uint64_t scheduled_id;
    ScheduleAt scheduled_at;
    std::string message;
};
SPACETIMEDB_STRUCT(Reminder, scheduled_id, scheduled_at, message)
SPACETIMEDB_TABLE(Reminder, reminder, Public)
FIELD_PrimaryKeyAutoInc(reminder, scheduled_id)
SPACETIMEDB_SCHEDULE(reminder, 1, send_reminder)  // 1 = scheduled_at field index (0-based)

SPACETIMEDB_REDUCER(send_reminder, ReducerContext ctx, Reminder arg) {
    LOG_INFO("Reminder: " + arg.message);
    return Ok();
}

// One-time: fires at a specific time
ctx.db[reminder].insert(Reminder{0, ScheduleAt::time(ctx.timestamp + TimeDuration::from_seconds(10)), "msg"});
// Repeating: fires on an interval
ctx.db[reminder].insert(Reminder{0, ScheduleAt::interval(TimeDuration::from_seconds(5)), "msg"});

カスタム型

// Struct (product type):
struct Point { float x; float y; };
SPACETIMEDB_STRUCT(Point, x, y)

// Enum (sum type):
SPACETIMEDB_UNIT_TYPE(Active)
SPACETIMEDB_UNIT_TYPE(Inactive)
SPACETIMEDB_ENUM(PlayerStatus,
    (Active, Active),
    (Inactive, Inactive),
    (Suspended, std::string)
)

ロギング

LOG_INFO("Message: " + msg);
LOG_WARN("Warning: " + msg);
LOG_ERROR("Error: " + msg);
LOG_DEBUG("Debug: " + msg);
LOG_PANIC("Fatal: " + msg);   // terminates reducer

完全な例

#include <spacetimedb.h>
using namespace SpacetimeDB;

struct Entity {
    Identity identity;
    std::string name;
    bool active;
};
SPACETIMEDB_STRUCT(Entity, identity, name, active)
SPACETIMEDB_TABLE(Entity, entity, Public)
FIELD_PrimaryKey(entity, identity)

struct Record {
    uint64_t id;
    Identity owner;
    uint32_t value;
    Timestamp created_at;
};
SPACETIMEDB_STRUCT(Record, id, owner, value, created_at)
SPACETIMEDB_TABLE(Record, record, Public)
FIELD_PrimaryKeyAutoInc(record, id)

SPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {
    if (auto existing = ctx.db[entity_identity].find(ctx.sender())) {
        existing->active = true;
        ctx.db[entity_identity].update(*existing);
    }
    return Ok();
}

SPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {
    if (auto existing = ctx.db[entity_identity].find(ctx.sender())) {
        existing->active = false;
        ctx.db[entity_identity].update(*existing);
    }
    return Ok();
}

SPACETIMEDB_REDUCER(create_entity, ReducerContext ctx, std::string name) {
    if (ctx.db[entity_identity].find(ctx.sender())) {
        return Err("already exists");
    }
    ctx.db[entity].insert(Entity{ctx.sender(), name, true});
    return Ok();
}

SPACETIMEDB_REDUCER(add_record, ReducerContext ctx, uint32_t value) {
    if (!ctx.db[entity_identity].find(ctx.sender())) {
        return Err("not found");
    }
    ctx.db[record].insert(Record{0, ctx.sender(), value, ctx.timestamp});
    return Ok();
}

コメント