Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计通过

spacetimedb-csharpspacetimedb csharp 搜索

Agent Skill

spacetimedb-csharp 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

865

周安装

35

GitHub Stars

24,576

下载量

272
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:spacetimedb-csharp(spacetimedb csharp 搜索)
来源仓库:https://github.com/clockworklabs/spacetimedb
仓库路径:skills/spacetimedb-csharp
安装命令:
npx skills add https://github.com/clockworklabs/spacetimedb --skill spacetimedb-csharp
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/clockworklabs/spacetimedb --skill spacetimedb-csharp

简介

spacetimedb-csharp 用于查找、检索和筛选相关信息。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中根据关键词、任务场景或来源线索快速定位候选结果。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装,需结合原始 README 核验具体用法。
  • 安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

SpacetimeDB C# SDK

This skill provides guidance for building C# server-side modules and C# clients that connect to SpacetimeDB 2.0.


HALLUCINATED APIs — DO NOT USE

These APIs DO NOT EXIST. LLMs frequently hallucinate them.

// WRONG — these table access patterns do not exist
ctx.db.tableName                    // Wrong casing — use ctx.Db
ctx.Db.tableName                    // Wrong casing — accessor must match exactly
ctx.Db.TableName.Get(id)            // Use Find, not Get
ctx.Db.TableName.FindById(id)       // Use index accessor: ctx.Db.TableName.Id.Find(id)
ctx.Db.table.field_name.Find(x)     // Wrong! Use PascalCase: ctx.Db.Table.FieldName.Find(x)
Optional<string> field;             // Use C# nullable: string? field

// WRONG — missing partial keyword
public struct MyTable { }           // Must be "partial struct"
public class Module { }             // Must be "static partial class"

// WRONG — non-partial types
[SpacetimeDB.Table(Accessor = "Player")]
public struct Player { }            // WRONG — missing partial!

// WRONG — sum type syntax (VERY COMMON MISTAKE)
public partial struct Shape : TaggedEnum<(Circle, Rectangle)> { }     // WRONG: struct, missing names
public partial record Shape : TaggedEnum<(Circle, Rectangle)> { }     // WRONG: missing variant names
public partial class Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }  // WRONG: class

// WRONG — Index attribute without full qualification
[Index.BTree(Accessor = "idx", Columns = new[] { "Col" })]    // Ambiguous with System.Index!
[SpacetimeDB.Index.BTree(Accessor = "idx", Columns = ["Col"])]  // Valid with modern C# collection expressions

// WRONG — old 1.0 patterns
[SpacetimeDB.Table(Name = "Player")]        // Use Accessor, not Name (2.0)
<PackageReference Include="SpacetimeDB.ServerSdk" />  // Use SpacetimeDB.Runtime
.WithModuleName("my-db")                    // Use .WithDatabaseName() (2.0)
ScheduleAt.Time(futureTime)                 // Use new ScheduleAt.Time(futureTime)

// WRONG — lifecycle hooks starting with "On"
[SpacetimeDB.Reducer(ReducerKind.ClientConnected)]
public static void OnClientConnected(ReducerContext ctx) { }  // STDB0010 error!

// WRONG — non-deterministic code in reducers
var random = new Random();          // Use ctx.Rng
var guid = Guid.NewGuid();          // Not allowed
var now = DateTime.Now;             // Use ctx.Timestamp

// WRONG — collection parameters
int[] itemIds = { 1, 2, 3 };
_conn.Reducers.ProcessItems(itemIds);  // Generated code expects List<T>!

CORRECT PATTERNS

using SpacetimeDB;

// CORRECT TABLE — must be partial struct, use Accessor
[SpacetimeDB.Table(Accessor = "Player", Public = true)]
public partial struct Player
{
    [SpacetimeDB.PrimaryKey]
    [SpacetimeDB.AutoInc]
    public ulong Id;
    [SpacetimeDB.Index.BTree]
    public Identity OwnerId;
    public string Name;
}

// CORRECT MODULE — must be static partial class
public static partial class Module
{
    [SpacetimeDB.Reducer]
    public static void CreatePlayer(ReducerContext ctx, string name)
    {
        ctx.Db.Player.Insert(new Player { Id = 0, OwnerId = ctx.Sender, Name = name });
    }
}

// CORRECT DATABASE ACCESS — PascalCase, index-based lookups
var player = ctx.Db.Player.Id.Find(playerId);           // Unique/PK: returns nullable
foreach (var p in ctx.Db.Player.OwnerId.Filter(ctx.Sender)) { }  // BTree: returns IEnumerable

// CORRECT SUM TYPE — partial record with named tuple elements
[SpacetimeDB.Type]
public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }

// CORRECT — collection parameters use List<T>
_conn.Reducers.ProcessItems(new List<int> { 1, 2, 3 });

Common Mistakes Table

WrongRightError
Wrong.csproj nameStdbModule.csprojPublish fails silently
.NET 9 SDK.NET 8 SDK onlyWASI compilation fails
Missing WASI workloaddotnet workload install wasi-experimentalBuild fails
async/await in reducersSynchronous onlyNot supported
table.Name.Update(...)table.Id.Update(...)Update only via primary key (2.0)
Not calling FrameTick()conn.FrameTick() in Update loopNo callbacks fire
Accessing conn.Db from background threadCopy data in callbackData races

Hard Requirements

  1. Tables and Module MUST be partial — required for code generation
  2. Use Accessor = in table attributesName = is only for SQL compatibility (2.0)
  3. Project file MUST be named StdbModule.csproj — CLI requirement
  4. Requires.NET 8 SDK —.NET 9 and newer not yet supported
  5. Install WASI workloaddotnet workload install wasi-experimental
  6. Procedures are supported — use [SpacetimeDB.Procedure] with ProcedureContext when needed
  7. Reducers must be deterministic — no filesystem, network, timers, or Random
  8. Add Public = true — if clients need to subscribe to a table
  9. Use T? for nullable fields — not Optional<T>
  10. Pass 0 for auto-increment — to trigger ID generation on insert
  11. Sum types must be partial record — not struct or class
  12. Fully qualify Index attribute[SpacetimeDB.Index.BTree] to avoid System.Index ambiguity
  13. Update only via primary key — use delete+insert for non-PK changes (2.0)
  14. Use SpacetimeDB.Runtime package — not ServerSdk (2.0)
  15. Use List<T> for collection parameters — not arrays
  16. Identity is in SpacetimeDB namespace — not SpacetimeDB.Types

Server-Side Module Development

Table Definition

using SpacetimeDB;

[SpacetimeDB.Table(Accessor = "Player", Public = true)]
public partial struct Player
{
    [SpacetimeDB.PrimaryKey]
    [SpacetimeDB.AutoInc]
    public ulong Id;

    [SpacetimeDB.Index.BTree]
    public Identity OwnerId;

    public string Name;
    public Timestamp CreatedAt;
}

// Multi-column index (use fully-qualified attribute!)
[SpacetimeDB.Table(Accessor = "Score", Public = true)]
[SpacetimeDB.Index.BTree(Accessor = "by_player_game", Columns = new[] { "PlayerId", "GameId" })]
public partial struct Score
{
    [SpacetimeDB.PrimaryKey]
    [SpacetimeDB.AutoInc]
    public ulong Id;
    public Identity PlayerId;
    public string GameId;
    public int Points;
}

Field Attributes

[SpacetimeDB.PrimaryKey]     // Exactly one per table (required)
[SpacetimeDB.AutoInc]        // Auto-increment (integer fields only)
[SpacetimeDB.Unique]         // Unique constraint
[SpacetimeDB.Index.BTree]    // Single-column B-tree index
[SpacetimeDB.Default(value)] // Default value for new columns

SpacetimeDB Column Types

Identity                     // User identity (SpacetimeDB namespace, not SpacetimeDB.Types)
Timestamp                    // Timestamp (use ctx.Timestamp server-side, never DateTime.Now)
ScheduleAt                   // For scheduled tables
T?                           // Nullable (e.g., string?)
List<T>                      // Collections (use List, not arrays)

Standard C# primitives (bool, byte..ulong, float, double, string) are all supported.

Insert with Auto-Increment

var player = ctx.Db.Player.Insert(new Player
{
    Id = 0,  // Pass 0 to trigger auto-increment
    OwnerId = ctx.Sender,
    Name = name,
    CreatedAt = ctx.Timestamp
});
ulong newId = player.Id;  // Insert returns the row with generated ID

Module and Reducers

using SpacetimeDB;

public static partial class Module
{
    [SpacetimeDB.Reducer]
    public static void CreateTask(ReducerContext ctx, string title)
    {
        if (string.IsNullOrEmpty(title))
            throw new Exception("Title cannot be empty");

        ctx.Db.Task.Insert(new Task
        {
            Id = 0,
            OwnerId = ctx.Sender,
            Title = title,
            Completed = false
        });
    }

    [SpacetimeDB.Reducer]
    public static void CompleteTask(ReducerContext ctx, ulong taskId)
    {
        if (ctx.Db.Task.Id.Find(taskId) is not Task task)
            throw new Exception("Task not found");
        if (task.OwnerId != ctx.Sender)
            throw new Exception("Not authorized");

        ctx.Db.Task.Id.Update(task with { Completed = true });
    }

    [SpacetimeDB.Reducer]
    public static void DeleteTask(ReducerContext ctx, ulong taskId)
    {
        ctx.Db.Task.Id.Delete(taskId);
    }
}

Lifecycle Reducers

public static partial class Module
{
    [SpacetimeDB.Reducer(ReducerKind.Init)]
    public static void Init(ReducerContext ctx)
    {
        Log.Info("Module initialized");
    }

    // CRITICAL: no "On" prefix!
    [SpacetimeDB.Reducer(ReducerKind.ClientConnected)]
    public static void ClientConnected(ReducerContext ctx)
    {
        Log.Info($"Client connected: {ctx.Sender}");
        if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
        {
            ctx.Db.User.Identity.Update(user with { Online = true });
        }
        else
        {
            ctx.Db.User.Insert(new User { Identity = ctx.Sender, Online = true });
        }
    }

    [SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)]
    public static void ClientDisconnected(ReducerContext ctx)
    {
        if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
        {
            ctx.Db.User.Identity.Update(user with { Online = false });
        }
    }
}

Event Tables (2.0)

Reducer callbacks are removed in 2.0. Use event tables + OnInsert instead.

[SpacetimeDB.Table(Accessor = "DamageEvent", Public = true, Event = true)]
public partial struct DamageEvent
{
    public Identity Target;
    public uint Amount;
}

[SpacetimeDB.Reducer]
public static void DealDamage(ReducerContext ctx, Identity target, uint amount)
{
    ctx.Db.DamageEvent.Insert(new DamageEvent { Target = target, Amount = amount });
}

Client subscribes and uses OnInsert:

conn.Db.DamageEvent.OnInsert += (ctx, evt) => {
    PlayDamageAnimation(evt.Target, evt.Amount);
};

Event tables must be subscribed explicitly — they are excluded from SubscribeToAllTables().

Database Access

// Find by primary key — returns nullable, use pattern matching
if (ctx.Db.Task.Id.Find(taskId) is Task task) { /* use task */ }

// Update by primary key (2.0: only primary key has .Update)
ctx.Db.Task.Id.Update(task with { Title = newTitle });

// Delete by primary key
ctx.Db.Task.Id.Delete(taskId);

// Find by unique index — returns nullable
if (ctx.Db.Player.Username.Find("alice") is Player player) { }

// Filter by B-tree index — returns iterator
foreach (var task in ctx.Db.Task.OwnerId.Filter(ctx.Sender)) { }

// Full table scan — avoid for large tables
foreach (var task in ctx.Db.Task.Iter()) { }
var count = ctx.Db.Task.Count;

Custom Types and Sum Types

[SpacetimeDB.Type]
public partial struct Position { public int X; public int Y; }

// Sum types MUST be partial record with named tuple
[SpacetimeDB.Type]
public partial struct Circle { public int Radius; }
[SpacetimeDB.Type]
public partial struct Rectangle { public int Width; public int Height; }
[SpacetimeDB.Type]
public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }

// Creating sum type values
var circle = new Shape.Circle(new Circle { Radius = 10 });

Scheduled Tables

[SpacetimeDB.Table(Accessor = "Reminder", Scheduled = nameof(Module.SendReminder))]
public partial struct Reminder
{
    [SpacetimeDB.PrimaryKey]
    [SpacetimeDB.AutoInc]
    public ulong Id;
    public string Message;
    public ScheduleAt ScheduledAt;
}

public static partial class Module
{
    [SpacetimeDB.Reducer]
    public static void SendReminder(ReducerContext ctx, Reminder reminder)
    {
        Log.Info($"Reminder: {reminder.Message}");
    }

    [SpacetimeDB.Reducer]
    public static void CreateReminder(ReducerContext ctx, string message, ulong delaySecs)
    {
        ctx.Db.Reminder.Insert(new Reminder
        {
            Id = 0,
            Message = message,
            ScheduledAt = new ScheduleAt.Time(ctx.Timestamp + TimeSpan.FromSeconds(delaySecs))
        });
    }
}

Logging

Log.Debug("Debug message");
Log.Info("Information");
Log.Warn("Warning");
Log.Error("Error occurred");
Log.Exception("Critical failure");  // Logs at error level

ReducerContext API

ctx.Sender          // Identity of the caller
ctx.Timestamp       // Current timestamp
ctx.Db              // Database access
ctx.Identity        // Module's own identity
ctx.ConnectionId    // Connection ID (nullable)
ctx.SenderAuth      // Authorization context (JWT claims, internal call detection)
ctx.Rng             // Deterministic random number generator

Error Handling

Throwing an exception in a reducer rolls back the entire transaction:

[SpacetimeDB.Reducer]
public static void TransferCredits(ReducerContext ctx, Identity toUser, uint amount)
{
    if (ctx.Db.User.Identity.Find(ctx.Sender) is not User sender)
        throw new Exception("Sender not found");

    if (sender.Credits < amount)
        throw new Exception("Insufficient credits");

    ctx.Db.User.Identity.Update(sender with { Credits = sender.Credits - amount });

    if (ctx.Db.User.Identity.Find(toUser) is User receiver)
        ctx.Db.User.Identity.Update(receiver with { Credits = receiver.Credits + amount });
}

Project Setup

Required.csproj (MUST be named StdbModule.csproj)

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="SpacetimeDB.Runtime" Version="1.*" />
  </ItemGroup>
</Project>

Prerequisites

# Install .NET 8 SDK (required, not .NET 9)
# Install WASI workload
dotnet workload install wasi-experimental

Client SDK

Installation

dotnet add package SpacetimeDB.ClientSDK

Generate Module Bindings

spacetime generate --lang csharp --out-dir module_bindings --module-path PATH_TO_MODULE

This creates SpacetimeDBClient.g.cs, Tables/*.g.cs, Reducers/*.g.cs, and Types/*.g.cs.

Connection Setup

using SpacetimeDB;
using SpacetimeDB.Types;

var conn = DbConnection.Builder()
    .WithUri("http://localhost:3000")
    .WithDatabaseName("my-database")
    .WithToken(savedToken)
    .OnConnect(OnConnected)
    .OnConnectError(err => Console.Error.WriteLine($"Failed: {err}"))
    .OnDisconnect((conn, err) => { if (err != null) Console.Error.WriteLine(err); })
    .Build();

void OnConnected(DbConnection conn, Identity identity, string authToken)
{
    // Save authToken to persistent storage for reconnection
    Console.WriteLine($"Connected: {identity}");
    conn.SubscriptionBuilder()
        .OnApplied(OnSubscriptionApplied)
        .SubscribeToAllTables();
}

Critical: FrameTick

The SDK does NOT automatically process messages. You must call FrameTick() regularly.

// Console application
while (running) { conn.FrameTick(); Thread.Sleep(16); }

// Unity: call conn?.FrameTick() in Update()

Warning: Do NOT call FrameTick() from a background thread. It modifies conn.Db and can cause data races.

Subscribing to Tables

// SQL queries
conn.SubscriptionBuilder()
    .OnApplied(OnSubscriptionApplied)
    .OnError((ctx, err) => Console.Error.WriteLine($"Subscription failed: {err}"))
    .Subscribe(new[] {
        "SELECT * FROM player",
        "SELECT * FROM message WHERE sender = :sender"
    });

// Subscribe to all tables (development only)
conn.SubscriptionBuilder()
    .OnApplied(OnSubscriptionApplied)
    .SubscribeToAllTables();

// Subscription handle for later unsubscribe
SubscriptionHandle handle = conn.SubscriptionBuilder()
    .OnApplied(ctx => Console.WriteLine("Applied"))
    .Subscribe(new[] { "SELECT * FROM player" });

handle.UnsubscribeThen(ctx => Console.WriteLine("Unsubscribed"));

Warning: SubscribeToAllTables() cannot be mixed with Subscribe() on the same connection.

Accessing the Client Cache

// Iterate all rows
foreach (var player in ctx.Db.Player.Iter()) { Console.WriteLine(player.Name); }

// Count rows
int playerCount = ctx.Db.Player.Count;

// Find by unique/primary key — returns nullable
Player? player = ctx.Db.Player.Identity.Find(someIdentity);
if (player != null) { Console.WriteLine(player.Name); }

// Filter by BTree index — returns IEnumerable
foreach (var p in ctx.Db.Player.Level.Filter(1)) { }

Row Event Callbacks

ctx.Db.Player.OnInsert += (EventContext ctx, Player player) => {
    Console.WriteLine($"Player joined: {player.Name}");
};

ctx.Db.Player.OnDelete += (EventContext ctx, Player player) => {
    Console.WriteLine($"Player left: {player.Name}");
};

ctx.Db.Player.OnUpdate += (EventContext ctx, Player oldRow, Player newRow) => {
    Console.WriteLine($"Player {oldRow.Name} renamed to {newRow.Name}");
};

// Checking event source
ctx.Db.Player.OnInsert += (EventContext ctx, Player player) => {
    switch (ctx.Event)
    {
        case Event<Reducer>.SubscribeApplied:
            break;  // Initial subscription data
        case Event<Reducer>.Reducer(var reducerEvent):
            Console.WriteLine($"Reducer: {reducerEvent.Reducer}");
            break;
    }
};

Calling Reducers

ctx.Reducers.SendMessage("Hello, world!");
ctx.Reducers.CreatePlayer("NewPlayer");

// Reducer completion callbacks
conn.Reducers.OnSendMessage += (ReducerEventContext ctx, string text) => {
    if (ctx.Event.Status is Status.Committed)
        Console.WriteLine($"Message sent: {text}");
    else if (ctx.Event.Status is Status.Failed(var reason))
        Console.Error.WriteLine($"Send failed: {reason}");
};

// Unhandled reducer errors
conn.OnUnhandledReducerError += (ReducerEventContext ctx, Exception ex) => {
    Console.Error.WriteLine($"Reducer error: {ex.Message}");
};

Identity and Authentication

// In OnConnect callback — save token for reconnection
void OnConnected(DbConnection conn, Identity identity, string authToken)
{
    // Save authToken to persistent storage (file, config, PlayerPrefs, etc.)
    SaveToken(authToken);
}

// Reconnect with saved token
string savedToken = LoadToken();
DbConnection.Builder()
    .WithUri("http://localhost:3000")
    .WithDatabaseName("my-database")
    .WithToken(savedToken)
    .OnConnect(OnConnected)
    .Build();

// Pass null or omit WithToken for anonymous connection

Commands

spacetime start
spacetime publish <module-name> --module-path <backend-dir>
spacetime publish <module-name> --clear-database -y --module-path <backend-dir>
spacetime generate --lang csharp --out-dir <client>/SpacetimeDB --module-path <backend-dir>
spacetime logs <module-name>

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

35.98%
按下载量换算98

Claude

31.83%
按下载量换算87

Cursor

16.94%
按下载量换算46

Gemini CLI

8.98%
按下载量换算24

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills