Loading...
Loading...
Compare original and translation side by side
ctx.senderctx.senderctx.senderctx.senderspacetime startspacetime publishspacetime generatespacetime logs <db-name>spacetime startspacetime publishspacetime generatespacetime logs <db-name>undefinedundefined
---
---#[spacetimedb::table(name = player, public)]
pub struct Player {
#[primary_key]
#[auto_inc]
id: u32,
#[index(btree)]
name: String,
#[unique]
email: String,
}[SpacetimeDB.Table(Name = "Player", Public = true)]
public partial struct Player
{
[SpacetimeDB.PrimaryKey]
[SpacetimeDB.AutoInc]
public uint Id;
[SpacetimeDB.Index.BTree]
public string Name;
[SpacetimeDB.Unique]
public string Email;
}const players = table(
{ name: 'players', public: true },
{
id: t.u32().primaryKey().autoInc(),
name: t.string().index('btree'),
email: t.string().unique(),
}
);#[spacetimedb::table(name = player, public)]
pub struct Player {
#[primary_key]
#[auto_inc]
id: u32,
#[index(btree)]
name: String,
#[unique]
email: String,
}[SpacetimeDB.Table(Name = "Player", Public = true)]
public partial struct Player
{
[SpacetimeDB.PrimaryKey]
[SpacetimeDB.AutoInc]
public uint Id;
[SpacetimeDB.Index.BTree]
public string Name;
[SpacetimeDB.Unique]
public string Email;
}const players = table(
{ name: 'players', public: true },
{
id: t.u32().primaryKey().autoInc(),
name: t.string().index('btree'),
email: t.string().unique(),
}
);Player PlayerState PlayerStats
id <-- player_id player_id
name position_x total_kills
position_y total_deaths
velocity_x play_time
velocity_yPlayer PlayerState PlayerStats
id <-- player_id player_id
name position_x total_kills
position_y total_deaths
velocity_x play_time
velocity_y#[spacetimedb::reducer]
pub fn create_user(ctx: &ReducerContext, name: String, email: String) -> Result<(), String> {
if name.is_empty() {
return Err("Name cannot be empty".to_string());
}
ctx.db.user().insert(User { id: 0, name, email });
Ok(())
}[SpacetimeDB.Reducer]
public static void CreateUser(ReducerContext ctx, string name, string email)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be empty");
ctx.Db.User.Insert(new User { Id = 0, Name = name, Email = email });
}#[spacetimedb::reducer]
pub fn create_user(ctx: &ReducerContext, name: String, email: String) -> Result<(), String> {
if name.is_empty() {
return Err("Name cannot be empty".to_string());
}
ctx.db.user().insert(User { id: 0, name, email });
Ok(())
}[SpacetimeDB.Reducer]
public static void CreateUser(ReducerContext ctx, string name, string email)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be empty");
ctx.Db.User.Insert(new User { Id = 0, Name = name, Email = email });
}ReducerContextctx.dbctx.senderctx.connection_idctx.timestampReducerContextctx.dbctx.senderctx.connection_idctx.timestamponInsertonDeleteonUpdateonInsertonDeleteonUpdateconst conn = DbConnection.builder()
.withUri('wss://maincloud.spacetimedb.com')
.withModuleName('my_module')
.onConnect((ctx) => {
ctx.subscriptionBuilder()
.onApplied(() => console.log('Subscription ready!'))
.subscribe(['SELECT * FROM user', 'SELECT * FROM message']);
})
.build();
// React to changes
conn.db.user.onInsert((ctx, user) => console.log(`New user: ${user.name}`));
conn.db.user.onDelete((ctx, user) => console.log(`User left: ${user.name}`));
conn.db.user.onUpdate((ctx, old, new_) => console.log(`${old.name} -> ${new_.name}`));const conn = DbConnection.builder()
.withUri('wss://maincloud.spacetimedb.com')
.withModuleName('my_module')
.onConnect((ctx) => {
ctx.subscriptionBuilder()
.onApplied(() => console.log('Subscription ready!'))
.subscribe(['SELECT * FROM user', 'SELECT * FROM message']);
})
.build();
// 响应变化
conn.db.user.onInsert((ctx, user) => console.log(`New user: ${user.name}`));
conn.db.user.onDelete((ctx, user) => console.log(`User left: ${user.name}`));
conn.db.user.onUpdate((ctx, old, new_) => console.log(`${old.name} -> ${new_.name}`));spacetime publishspacetime publish#[spacetimedb::reducer]
pub fn do_something(ctx: &ReducerContext) {
let caller_identity = ctx.sender; // Who is calling this reducer?
// Use identity for authorization checks
// NEVER trust identity passed as a reducer argument
}#[spacetimedb::reducer]
pub fn do_something(ctx: &ReducerContext) {
let caller_identity = ctx.sender; // 谁在调用这个Reducer?
// 使用身份信息进行授权检查
// 永远不要信任作为Reducer参数传递的身份信息
}| Category | Types |
|---|---|
| Primitives | |
| Composite | |
| Collections | |
| Special | |
| 类别 | 类型 |
|---|---|
| 基本类型 | |
| 复合类型 | |
| 集合类型 | |
| 特殊类型 | |
SpacetimeTypeSpacetimeTypectx.reducers.createUser("Alice")ctx.reducers.createUser("Alice")SELECT * FROM userSELECT * FROM user┌─────────────────────────────────────────────────────────┐
│ CLIENT │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Reducers │────>│ Local Cache (Read) │ │
│ │ (Write) │ │ - Tables from subscriptions│ │
│ └─────────────┘ │ - Automatically synced │ │
│ │ └─────────────────────────────┘ │
└─────────│──────────────────────────│───────────────────┘
│ WebSocket │ Updates pushed
v │
┌─────────────────────────────────────────────────────────┐
│ SpacetimeDB │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Module │ │
│ │ - Reducers (transactional logic) │ │
│ │ - Tables (in-memory + persisted) │ │
│ │ - Subscriptions (real-time queries) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│ CLIENT │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Reducers │────>│ Local Cache (Read) │ │
│ │ (Write) │ │ - Tables from subscriptions│ │
│ └─────────────┘ │ - Automatically synced │ │
│ │ └─────────────────────────────┘ │
└─────────│──────────────────────────│───────────────────┘
│ WebSocket │ Updates pushed
v │
┌─────────────────────────────────────────────────────────┐
│ SpacetimeDB │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Module │ │
│ │ - Reducers (transactional logic) │ │
│ │ - Tables (in-memory + persisted) │ │
│ │ - Subscriptions (real-time queries) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘Client
│
v
Load Balancer
│
v
Web/Game Servers (stateless or stateful)
│
├──> Cache (Redis)
│
v
Database (PostgreSQL, MySQL)
│
v
Message Queue (for real-time)Client
│
v
Load Balancer
│
v
Web/Game Servers (stateless or stateful)
│
├──> Cache (Redis)
│
v
Database (PostgreSQL, MySQL)
│
v
Message Queue (for real-time)Client
│
v
SpacetimeDB Host
│
v
Module (your logic + tables)Client
│
v
SpacetimeDB Host
│
v
Module (your logic + tables)#[spacetimedb::reducer]
fn admin_action(ctx: &ReducerContext) -> Result<(), String> {
let admin = ctx.db.admin().identity().find(&ctx.sender)
.ok_or("Not an admin")?;
// ... perform admin action
Ok(())
}#[spacetimedb::reducer]
fn login(ctx: &ReducerContext) -> Result<(), String> {
let player = ctx.db.logged_out_player().identity().find(&ctx.sender)
.ok_or("Not found")?;
ctx.db.player().insert(player.clone());
ctx.db.logged_out_player().identity().delete(&ctx.sender);
Ok(())
}#[spacetimedb::table(name = reminder, scheduled(send_reminder))]
pub struct Reminder {
#[primary_key]
#[auto_inc]
id: u64,
scheduled_at: ScheduleAt,
message: String,
}
#[spacetimedb::reducer]
fn send_reminder(ctx: &ReducerContext, reminder: Reminder) {
// This runs at the scheduled time
log::info!("Reminder: {}", reminder.message);
}#[spacetimedb::reducer]
fn admin_action(ctx: &ReducerContext) -> Result<(), String> {
let admin = ctx.db.admin().identity().find(&ctx.sender)
.ok_or("Not an admin")?;
// ... 执行管理员操作
Ok(())
}#[spacetimedb::reducer]
fn login(ctx: &ReducerContext) -> Result<(), String> {
let player = ctx.db.logged_out_player().identity().find(&ctx.sender)
.ok_or("Not found")?;
ctx.db.player().insert(player.clone());
ctx.db.logged_out_player().identity().delete(&ctx.sender);
Ok(())
}#[spacetimedb::table(name = reminder, scheduled(send_reminder))]
pub struct Reminder {
#[primary_key]
#[auto_inc]
id: u64,
scheduled_at: ScheduleAt,
message: String,
}
#[spacetimedb::reducer]
fn send_reminder(ctx: &ReducerContext, reminder: Reminder) {
// 此函数会在预定时间执行
log::info!("Reminder: {}", reminder.message);
}