diff --git a/crates/cli/src/subcommands/dev.rs b/crates/cli/src/subcommands/dev.rs index 8badb72773a..70acd76eadc 100644 --- a/crates/cli/src/subcommands/dev.rs +++ b/crates/cli/src/subcommands/dev.rs @@ -81,7 +81,7 @@ pub fn cli() -> Command { Arg::new("client-lang") .long("client-lang") .value_parser(clap::value_parser!(Language)) - .help("The programming language for the generated client module bindings (e.g., typescript, csharp, python). If not specified, it will be detected from the project."), + .help("The programming language for the generated client module bindings (e.g., typescript, csharp, rust, unrealcpp). If not specified, it will be detected from the project."), ) .arg(common_args::server().help("The nickname, host name or URL of the server to publish to")) .arg(common_args::yes()) diff --git a/crates/cli/src/subcommands/generate.rs b/crates/cli/src/subcommands/generate.rs index 6e5378fede5..94686939cba 100644 --- a/crates/cli/src/subcommands/generate.rs +++ b/crates/cli/src/subcommands/generate.rs @@ -168,7 +168,7 @@ fn get_filtered_generate_configs<'a>( pub fn cli() -> clap::Command { clap::Command::new("generate") .about("Generate client files for a spacetime module.") - .override_usage("generate [DATABASE] --lang --out-dir [--module-path | --bin-path | --unreal-module-name | --uproject-dir | --include-private]") + .override_usage("generate [DATABASE] --lang [--module-path | --bin-path | --js-path ] [--out-dir | --uproject-dir ] [--unreal-module-name ] [OPTIONS]") .arg( Arg::new("database") .help("Database name or glob pattern to filter which databases to generate for"), diff --git a/crates/update/src/cli/self_install.rs b/crates/update/src/cli/self_install.rs index 06af737756b..671173e3c46 100644 --- a/crates/update/src/cli/self_install.rs +++ b/crates/update/src/cli/self_install.rs @@ -96,7 +96,7 @@ following line to your shell configuration and open a new shell session: eprintln!( "\ The install process is complete; check out our quickstart guide to get started! - " + " ); Ok(ExitCode::SUCCESS) diff --git a/docs/docs/00100-intro/00100-getting-started/00250-zen-of-spacetimedb.md b/docs/docs/00100-intro/00100-getting-started/00250-zen-of-spacetimedb.md index 1f268be7ccc..800e91b692f 100644 --- a/docs/docs/00100-intro/00100-getting-started/00250-zen-of-spacetimedb.md +++ b/docs/docs/00100-intro/00100-getting-started/00250-zen-of-spacetimedb.md @@ -80,7 +80,7 @@ Perfect consistency, always. ## Everything is Programmable -SpacetimeDB doesn't limit you to declarative rules or configuration files. Your module is real code (Rust, C#, or TypeScript) running inside the database. You have the full power of a procedural, normal programming language at your disposal. +SpacetimeDB doesn't limit you to declarative rules or configuration files. Your module is real code (Rust, C#, TypeScript, or C++) running inside the database. You have the full power of a procedural, normal programming language at your disposal. Need custom authorization logic? Write a function. Need to validate complex business rules? Write a function. Need to transform data before storing it? Write a function. diff --git a/docs/docs/00100-intro/00100-getting-started/00300-language-support.md b/docs/docs/00100-intro/00100-getting-started/00300-language-support.md index 6f5ab3a9f5e..f5ead25e91d 100644 --- a/docs/docs/00100-intro/00100-getting-started/00300-language-support.md +++ b/docs/docs/00100-intro/00100-getting-started/00300-language-support.md @@ -6,11 +6,12 @@ slug: /intro/language-support ## Server Database Modules -SpacetimeDB modules define your database schema and server-side business logic. Modules can be written in three languages: +SpacetimeDB modules define your database schema and server-side business logic. Modules can be written in four languages: - **[Rust](../../00200-core-concepts/00100-databases.md)** - High performance, compiled to WebAssembly [(Quickstart)](../00200-quickstarts/00500-rust.md) - **[C#](../../00200-core-concepts/00100-databases.md)** - Great for Unity developers, compiled to WebAssembly [(Quickstart)](../00200-quickstarts/00600-c-sharp.md) - **[TypeScript](../../00200-core-concepts/00100-databases.md)** - Ideal for web developers, runs on V8 [(Quickstart)](../00200-quickstarts/00400-typescript.md) +- **[C++](../../00200-core-concepts/00100-databases.md)** - Fits Unreal and C++ workflows, compiled to WebAssembly [(Quickstart)](../00200-quickstarts/00700-cpp.md) ## Client SDKs diff --git a/docs/docs/00100-intro/00100-getting-started/00500-faq.md b/docs/docs/00100-intro/00100-getting-started/00500-faq.md index 2552dd1b630..907e09dac38 100644 --- a/docs/docs/00100-intro/00100-getting-started/00500-faq.md +++ b/docs/docs/00100-intro/00100-getting-started/00500-faq.md @@ -60,7 +60,7 @@ SpacetimeDB replaces the entire server. Your game state lives in tables, your ga Firebase and Supabase are Backend-as-a-Service platforms. They give you a database with an API layer on top, but your application logic still runs elsewhere (cloud functions, edge functions, or your own server). Complex business logic is awkward to express as database triggers or serverless functions. -SpacetimeDB lets you write your entire application as a module in a real programming language (Rust, C#, TypeScript) that runs inside the database. You get full transactional guarantees, direct table access, and real-time subscriptions without the cold starts, execution limits, or awkward abstractions of serverless functions. +SpacetimeDB lets you write your entire application as a module in a real programming language (Rust, C#, TypeScript, or C++) that runs inside the database. You get full transactional guarantees, direct table access, and real-time subscriptions without the cold starts, execution limits, or awkward abstractions of serverless functions. ### How is SpacetimeDB different from a regular database (PostgreSQL, MySQL)? @@ -149,7 +149,7 @@ SpacetimeDB 2.0 also includes a **type-safe query builder** for client-side subs 1. Install the CLI: `curl -sSf https://install.spacetimedb.com | sh` 2. Start a local instance: `spacetime start` -3. Create a project: `spacetime init --lang rust` (or `csharp`, `typescript`) +3. Create a project: `spacetime init --lang rust` (or `csharp`, `typescript`, `cpp`) 4. Write your module, publish it: `spacetime publish my-app` 5. Generate client bindings: `spacetime generate --lang typescript --out-dir src/module_bindings` 6. Connect from your client using the generated code diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md index 1ea168d46d9..033bd7ed2ea 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md @@ -743,7 +743,21 @@ public class GameManager : MonoBehaviour void HandleConnect(DbConnection _conn, Identity identity, string token) { Debug.Log("Connected."); + // Only the WebGL player has the browser WebSocket header limitation. + // The Unity Editor uses the normal desktop transport even when the + // selected build target is WebGL, so keep the normal behavior there. +#if UNITY_WEBGL && !UNITY_EDITOR + if (AuthToken.Token == "") + { + // No token was supplied to the connection, so this is the + // long-lived server-issued token for the new identity. Save it. + // If a token already exists, this connect may have used a + // short-lived WebSocket token, which should not overwrite it. + AuthToken.SaveToken(token); + } +#else AuthToken.SaveToken(token); +#endif LocalIdentity = identity; OnConnected?.Invoke(); @@ -787,6 +801,8 @@ public class GameManager : MonoBehaviour } ``` +> Unity WebGL needs one extra precaution here. Browser WebSocket APIs cannot set an `Authorization` header, so reconnecting with a saved server-issued token may yield a short-lived WebSocket token in `HandleConnect`. The `#if UNITY_WEBGL` guard keeps the original saved token instead of overwriting it during reconnect. + Here we configure the connection to the database, by passing it some callbacks in addition to providing the `SERVER_URI` and `MODULE_NAME` to the connection. When the client connects, the SpacetimeDB SDK will call the `HandleConnect` method, allowing us to start up the game. In our `HandleConnect` callback we build a subscription and call `Subscribe`, subscribing to all data in the database. This causes SpacetimeDB to synchronize the state of all your tables with your Unity client's SDK client cache. diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index 612bdb4030f..ee1161f4774 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -1351,7 +1351,21 @@ Next lets add some callbacks when rows change in the database. Modify the `Handl void HandleConnect(DbConnection conn, Identity identity, string token) { Debug.Log("Connected."); + // Only the WebGL player has the browser WebSocket header limitation. + // The Unity Editor uses the normal desktop transport even when the + // selected build target is WebGL, so keep the normal behavior there. +#if UNITY_WEBGL && !UNITY_EDITOR + if (AuthToken.Token == "") + { + // No token was supplied to the connection, so this is the + // long-lived server-issued token for the new identity. Save it. + // If a token already exists, this connect may have used a + // short-lived WebSocket token, which should not overwrite it. + AuthToken.SaveToken(token); + } +#else AuthToken.SaveToken(token); +#endif LocalIdentity = identity; conn.Db.Circle.OnInsert += CircleOnInsert; @@ -1370,6 +1384,8 @@ void HandleConnect(DbConnection conn, Identity identity, string token) } ``` +Keep the same WebGL guard from Part 2 here as well. On Unity WebGL, a reconnect can surface a short-lived WebSocket token in `HandleConnect`, so you should not overwrite an already-saved long-lived server-issued token. + Next add the following implementations for those callbacks to the `GameManager` class. ```csharp diff --git a/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md b/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md index 6bc9fe40194..02587b095c1 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md +++ b/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md @@ -8,7 +8,7 @@ import { CppModuleVersionNotice } from "@site/src/components/CppModuleVersionNot import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Quick reference for SpacetimeDB module syntax across Rust, C#, and TypeScript. +Quick reference for SpacetimeDB module syntax across Rust, C#, TypeScript, and C++. ## Project Setup diff --git a/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md b/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md index 17fbc870676..fa7161d280c 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md +++ b/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md @@ -31,7 +31,7 @@ Consider a game inventory with ordered pockets. A `Vec` preserves pocket o ## Binary Data and Files -SpacetimeDB includes optimizations for storing binary data as `Vec` (Rust), `List` (C#), or `t.array(t.u8())` (TypeScript). You can store files, images, serialized data, or other binary blobs directly in table columns. +SpacetimeDB includes optimizations for storing binary data as `Vec` (Rust), `List` (C#), `t.array(t.u8())` (TypeScript), or `std::vector` (C++). You can store files, images, serialized data, or other binary blobs directly in table columns. This approach works well when: - The binary data is associated with a specific row (e.g., a user's avatar image) diff --git a/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md index df8ae1e3c32..febbdaad245 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md @@ -178,7 +178,7 @@ export const schedule_periodic_tasks = spacetimedb.reducer((ctx) => { ```csharp -public partial class Module +public static partial class Module { [SpacetimeDB.Reducer] public static void SchedulePeriodicTasks(ReducerContext ctx) @@ -291,10 +291,11 @@ public static partial class Module public static void ScheduleTimedTasks(ReducerContext ctx) { // Schedule for 10 seconds from now + var tenSecondsFromNow = ctx.Timestamp + new TimeDuration(10_000_000); ctx.Db.Reminder.Insert(new Reminder { Message = "Your auction has ended", - ScheduledAt = new ScheduleAt.Time(DateTimeOffset.UtcNow.AddSeconds(10)) + ScheduledAt = new ScheduleAt.Time(tenSecondsFromNow) }); // Schedule for a specific time diff --git a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md index e0bf1e4daac..3f2936ca7ea 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md @@ -5,6 +5,7 @@ slug: /tables/event-tables import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import { CppModuleVersionNotice } from "@site/src/components/CppModuleVersionNotice"; In many applications, particularly games and real-time systems, modules need to notify clients about things that happened without storing that information permanently. A combat system might need to tell clients "entity X took 50 damage" so they can display a floating damage number, but there is no reason to keep that record in the database after the moment has passed. @@ -60,6 +61,21 @@ pub struct DamageEvent { } ``` + + + + + +```cpp +struct DamageEvent { + Identity entity_id; + uint32_t damage; + std::string source; +}; +SPACETIMEDB_STRUCT(DamageEvent, entity_id, damage, source) +SPACETIMEDB_TABLE(DamageEvent, damage_event, Public, true) +``` + @@ -128,6 +144,21 @@ fn attack(ctx: &ReducerContext, target_id: Identity, damage: u32) { } ``` + + + + + +```cpp +SPACETIMEDB_REDUCER(attack, ReducerContext ctx, Identity target_id, uint32_t damage) { + // Game logic... + + // Publish the event + ctx.db[damage_event].insert(DamageEvent{target_id, damage, "melee_attack"}); + return Ok(); +} +``` + diff --git a/docs/docs/00200-core-concepts/00500-authentication.md b/docs/docs/00200-core-concepts/00500-authentication.md index 54fc79a95a0..c118245c946 100644 --- a/docs/docs/00200-core-concepts/00500-authentication.md +++ b/docs/docs/00200-core-concepts/00500-authentication.md @@ -11,6 +11,28 @@ needs, or even implement your own. If you're new to OIDC, check out our [blog post about OIDC](https://spacetimedb.com/blog/who-are-you) to learn more about how OIDC works and why it's a great choice for authentication. +## Server-issued tokens and reconnects + +If a client connects without supplying a token, SpacetimeDB creates a new +identity and returns a server-issued token for that identity. If you want later +connections to keep using that same identity, persist that returned token and +pass it back into the SDK on reconnect. + +On platforms that can send an `Authorization` header during the WebSocket +handshake, the SDK can reuse that saved token directly. On platforms that cannot +set custom WebSocket headers, the SDK first exchanges the saved token for a +short-lived WebSocket token through +[`POST /v1/identity/websocket-token`](/docs/http/identity#post-v1identitywebsocket-token), +then sends only that short-lived token in the WebSocket URL. + +This matters when you persist tokens on the client: + +- Save the long-lived server-issued token that establishes the user's identity. +- Do not overwrite an already-saved long-lived token with a short-lived + WebSocket token received during reconnect. +- Expect this distinction on browser-style transports where WebSocket headers + are unavailable, such as Unity WebGL builds. + ## SpacetimeAuth To make it easier to get started with authentication, SpacetimeDB offers diff --git a/docs/docs/00200-core-concepts/00600-clients.md b/docs/docs/00200-core-concepts/00600-clients.md index e149b3b63a0..e3389c3f23a 100644 --- a/docs/docs/00200-core-concepts/00600-clients.md +++ b/docs/docs/00200-core-concepts/00600-clients.md @@ -83,11 +83,11 @@ New to SpacetimeDB client development? Follow this progression: 1. **[Generate Client Bindings](./00600-clients/00200-codegen.md)** - Create type-safe interfaces from your module 2. **[Connect to SpacetimeDB](./00600-clients/00300-connection.md)** - Establish a connection and understand the lifecycle 3. **[Use the SDK API](./00600-clients/00400-sdk-api.md)** - Learn about subscriptions, reducers, and callbacks -4. **Language Reference** - Dive into language-specific details: [Rust](./00600-clients/00500-rust-reference.md), [C#](./00600-clients/00600-csharp-reference.md), [TypeScript](./00600-clients/00700-typescript-reference.md) +4. **Language Reference** - Dive into language-specific details: [Rust](./00600-clients/00500-rust-reference.md), [C#](./00600-clients/00600-csharp-reference.md), [TypeScript](./00600-clients/00700-typescript-reference.md), and [Unreal Engine](./00600-clients/00800-unreal-reference.md) ## Next Steps -- Follow a **Quickstart guide** [Rust](../00100-intro/00200-quickstarts/00500-rust.md), [C#](../00100-intro/00200-quickstarts/00600-c-sharp.md), or [TypeScript](../00100-intro/00200-quickstarts/00400-typescript.md) to build your first client +- To build your first client, follow a **Quickstart guide** for [Rust](../00100-intro/00200-quickstarts/00500-rust.md), [C#](../00100-intro/00200-quickstarts/00600-c-sharp.md), or [TypeScript](../00100-intro/00200-quickstarts/00400-typescript.md), or use the [Unreal tutorial](../00100-intro/00300-tutorials/00400-unreal-tutorial/index.md) - Learn about [Databases](./00100-databases.md) to understand what you're connecting to - Explore [Subscriptions](./00400-subscriptions.md) for efficient data synchronization - Review [Reducers](./00200-functions/00300-reducers/00300-reducers.md) to understand server-side state changes diff --git a/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md b/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md index 9d9e537d66d..91e651d2f24 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md +++ b/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md @@ -45,7 +45,7 @@ Replace **PATH-TO-MODULE-DIRECTORY** with the path to your module's directory, w ```bash mkdir -p module_bindings -spacetime generate --lang cs --out-dir module_bindings --module-path PATH-TO-MODULE-DIRECTORY +spacetime generate --lang csharp --out-dir module_bindings --module-path PATH-TO-MODULE-DIRECTORY ``` This generates C# files in `module_bindings/`. The generated files are automatically included in your project. diff --git a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md index 270d6901570..8882aa3c80d 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md +++ b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md @@ -38,7 +38,7 @@ const conn = new DbConnection.builder() using SpacetimeDB; var conn = DbConnection.Builder() - .WithUri(new Uri("https://maincloud.spacetimedb.com")) + .WithUri("https://maincloud.spacetimedb.com") .WithDatabaseName("my_database") .Build(); ``` @@ -90,7 +90,7 @@ const conn = new DbConnection.builder() ```csharp var conn = DbConnection.Builder() - .WithUri(new Uri("https://maincloud.spacetimedb.com")) + .WithUri("https://maincloud.spacetimedb.com") .WithDatabaseName("my_database") .Build(); ``` @@ -137,7 +137,7 @@ const conn = new DbConnection.builder() ```csharp var conn = DbConnection.Builder() - .WithUri(new Uri("https://maincloud.spacetimedb.com")) + .WithUri("https://maincloud.spacetimedb.com") .WithDatabaseName("my_database") .WithToken("your_auth_token_here") .Build(); @@ -174,9 +174,9 @@ The token is sent to the server during connection and validates your identity. S :::danger[Critical: C#, Unity, and Unreal Users] -In C# (including Unity) and Unreal Engine, you **must** manually advance the connection to process incoming messages. The connection does not process messages automatically! +In C# (including Unity), you **must** manually advance the connection to process incoming messages. In Unreal Engine, you must either manually advance the connection or enable automatic ticking. If the connection is not advanced, it will not process messages. -Call `DbConnection.FrameTick()` in your game loop or update method: +Call `FrameTick()` in your game loop or update method: @@ -200,7 +200,7 @@ while (running) ```cpp -// In your Actor's Tick() method +// Option 1: call FrameTick() from your Actor's Tick() method void AMyActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); @@ -210,6 +210,10 @@ void AMyActor::Tick(float DeltaTime) Conn->FrameTick(); } } + +// Option 2: enable automatic ticking once after building the connection +Conn = Builder->Build(); +Conn->SetAutoTicking(true); ``` @@ -256,7 +260,7 @@ const conn = DbConnection.builder() ```csharp var conn = DbConnection.Builder() - .WithUri(new Uri("https://maincloud.spacetimedb.com")) + .WithUri("https://maincloud.spacetimedb.com") .WithDatabaseName("my_database") .OnConnect((conn, identity, token) => { diff --git a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md index 34ec9a8fa7b..a6a456d007a 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md @@ -39,7 +39,7 @@ Before diving into the reference, you may want to review: | [`EventContext` type](#type-eventcontext) | Implements [`IDbContext`](#interface-idbcontext) for [row callbacks](#callback-oninsert). | | [`ReducerEventContext` type](#type-reducereventcontext) | Implements [`IDbContext`](#interface-idbcontext) for [reducer callbacks](#observe-and-invoke-reducers). | | [`SubscriptionEventContext` type](#type-subscriptioneventcontext) | Implements [`IDbContext`](#interface-idbcontext) for [subscription callbacks](#subscribe-to-queries). | -| [`ErrorContext` type](#type-errorcontext) | Implements [`IDbContext`](#interface-idbcontext) for error-related callbacks. | +| [`ErrorContext` type](#type-errorcontext) | Implements [`IDbContext`](#interface-idbcontext) for subscription error callbacks. | | [Query Builder API](#query-builder-api) | Type-safe query builder for typed subscription queries. | | [Access the client cache](#access-the-client-cache) | Access to your local view of the database. | | [Observe and invoke reducers](#observe-and-invoke-reducers) | Send requests to the database to run reducers, and register callbacks to run when notified of reducers. | @@ -73,7 +73,7 @@ Each SpacetimeDB client depends on some bindings specific to your module. Create ```bash mkdir -p module_bindings -spacetime generate --lang cs --out-dir module_bindings --module-path PATH-TO-MODULE-DIRECTORY +spacetime generate --lang csharp --out-dir module_bindings --module-path PATH-TO-MODULE-DIRECTORY ``` Replace `PATH-TO-MODULE-DIRECTORY` with the path to your SpacetimeDB module. @@ -115,7 +115,7 @@ Construct a `DbConnection` by calling `DbConnection.Builder()`, chaining configu ```csharp class DbConnectionBuilder { - public DbConnectionBuilder WithUri(Uri uri); + public DbConnectionBuilder WithUri(string uri); } ``` @@ -163,20 +163,18 @@ Chain a call to `.OnConnect(callback)` to your builder to register a callback to ```csharp class DbConnectionBuilder { - public DbConnectionBuilder OnConnectError(Action callback); + public DbConnectionBuilder OnConnectError(Action callback); } ``` Chain a call to `.OnConnectError(callback)` to your builder to register a callback to run when your connection fails. -A known bug in the SpacetimeDB Rust client SDK currently causes this callback never to be invoked. [`OnDisconnect`](#callback-ondisconnect) callbacks are invoked instead. - #### Callback `OnDisconnect` ```csharp class DbConnectionBuilder { - public DbConnectionBuilder OnDisconnect(Action callback); + public DbConnectionBuilder OnDisconnect(Action callback); } ``` @@ -187,11 +185,11 @@ Chain a call to `.OnDisconnect(callback)` to your builder to register a callback ```csharp class DbConnectionBuilder { - public DbConnectionBuilder WithToken(string token = null); + public DbConnectionBuilder WithToken(string? token); } ``` -Chain a call to `.WithToken(token)` to your builder to provide an OpenID Connect compliant JSON Web Token to authenticate with, or to explicitly select an anonymous connection. If this method is not called or `None` is passed, SpacetimeDB will generate a new `Identity` and sign a new private access token for the connection. +Chain a call to `.WithToken(token)` to your builder to provide an OpenID Connect compliant JSON Web Token to authenticate with, or to explicitly select an anonymous connection. If this method is not called or `null` is passed, SpacetimeDB will generate a new `Identity` and sign a new private access token for the connection. #### Method `Build` @@ -894,7 +892,7 @@ The `Reducers` property of the context provides access to reducers exposed by th ## Type `ErrorContext` -An `ErrorContext` is an [`IDbContext`](#interface-idbcontext) augmented with an `Event` property. `ErrorContext`s are to connections' [`OnDisconnect`](#callback-ondisconnect) and [`OnConnectError`](#callback-onconnecterror) callbacks, and to subscriptions' [`OnError`](#callback-onerror) callbacks. +An `ErrorContext` is an [`IDbContext`](#interface-idbcontext) augmented with an `Event` property. `ErrorContext`s are passed to subscriptions' [`OnError`](#callback-onerror) callbacks. | Name | Description | | ----------------------------------------- | ------------------------------------------------------ | diff --git a/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md b/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md index 906d62f3b0a..57063d8f08a 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md @@ -58,7 +58,7 @@ A connection to a remote database is represented by the `UDbConnection` class. T | Name | Description | | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | [Connect to a database](#connect-to-a-database) | Construct a UDbConnection instance. | -| [Advance the connection](#advance-the-connection-and-process-messages) | The connection processes messages automatically via WebSocket callbacks. | +| [Advance the connection](#advance-the-connection-and-process-messages) | Process queued WebSocket messages and dispatch callbacks. | | [Access tables and reducers](#access-tables-and-reducers) | Access the client cache, request reducer invocations, and register callbacks. | ### Connect to a database @@ -186,7 +186,50 @@ Finalize configuration and open the connection. This creates a WebSocket connect ### Advance the connection and process messages -The Unreal SDK processes messages automatically via WebSocket callbacks and with UDbConnection which ultimately inherits from FTickableGameObject. No manual polling or advancement is required. Events are dispatched through the registered delegates. +The Unreal SDK receives WebSocket messages asynchronously, then queues them for processing on the game thread. You must either call `FrameTick()` regularly, typically from an Actor's `Tick()`, or enable automatic ticking once with `SetAutoTicking(true)`. Without one of these, queued messages are not processed and table callbacks, reducer callbacks, and subscription callbacks will not fire. + +You can either call `FrameTick()` yourself from an Actor or component tick, or enable the SDK's automatic ticker once after building the connection: + +```cpp +void AGameManager::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + if (Conn && Conn->IsActive()) + { + Conn->FrameTick(); + } +} +``` + +```cpp +Conn = Builder->Build(); +Conn->SetAutoTicking(true); +``` + +#### Method `FrameTick` + +```cpp +class UDbConnection +{ + UFUNCTION(BlueprintCallable, Category = "SpacetimeDB") + void FrameTick(); +}; +``` + +Process any queued server messages and dispatch the corresponding callbacks. Call this regularly from game-thread code if you manage ticking yourself. + +#### Method `SetAutoTicking` + +```cpp +class UDbConnection +{ + UFUNCTION(BlueprintCallable, Category = "SpacetimeDB") + void SetAutoTicking(bool bAutoTick); +}; +``` + +Enable or disable the SDK's automatic ticker. When enabled, the connection registers with Unreal's core ticker and calls `FrameTick()` for you. ### Access tables and reducers diff --git a/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md b/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md index 472494f17d0..35b03022b61 100644 --- a/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md +++ b/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md @@ -60,15 +60,15 @@ Your client may have failed to connect to the remote server, or may have been di You may need to advance your connection by calling one of the following methods: -| Client SDK | Method | Description | -|---------------------|------------------------------|------------------------------------------------------------------------------------| -| Rust (native only) | `conn.run_threaded()` | Spawn a thread to continuously advance the connection. | -| Rust (browser only) | `conn.run_background_task()` | Spawn a task to continuously advance the connection. | -| Rust | `conn.run_async()` | A `Future` which you can `await` or poll to advance the connection. | -| Rust | `conn.frame_tick()` | In single-threaded games, call this every frame to advance the connection. | -| C# | `Conn.FrameTick()` | Call this every frame to advance the connection, or call it in a loop on a thread. | -| Unreal | `Conn.FrameTick()` | Call this every frame to advance the connection. | -| TypeScript | N/a | The TypeScript client SDK advances connections automatically. | +| Client SDK | Method | Description | +|---------------------|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| +| Rust (native only) | `conn.run_threaded()` | Spawn a thread to continuously advance the connection. | +| Rust (browser only) | `conn.run_background_task()` | Spawn a task to continuously advance the connection. | +| Rust | `conn.run_async()` | A `Future` which you can `await` or poll to advance the connection. | +| Rust | `conn.frame_tick()` | In single-threaded games, call this every frame to advance the connection. | +| C# | `Conn.FrameTick()` | Call this from your game or application update loop. If you use a separate loop, keep `Conn.Db` access on that same thread or synchronize access. | +| Unreal | `Conn->FrameTick()` or `Conn->SetAutoTicking(true)` | Call `FrameTick()` every frame, or enable auto-ticking once after building the connection. | +| TypeScript | N/a | The TypeScript client SDK advances connections automatically. | ### Rows never appear diff --git a/docs/docs/00300-resources/00200-reference/00100-cli-reference/00100-cli-reference.md b/docs/docs/00300-resources/00200-reference/00100-cli-reference/00100-cli-reference.md index 0ac1cd74511..95f2c3a0598 100644 --- a/docs/docs/00300-resources/00200-reference/00100-cli-reference/00100-cli-reference.md +++ b/docs/docs/00300-resources/00200-reference/00100-cli-reference/00100-cli-reference.md @@ -249,7 +249,7 @@ Start development mode with auto-regenerate client module bindings, auto-rebuild Default value: `src/module_bindings` * `--module-path ` — Path to the SpacetimeDB server module, relative to current directory. Defaults to `/spacetimedb`. -* `--client-lang ` — The programming language for the generated client module bindings (e.g., typescript, csharp, python). If not specified, it will be detected from the project. +* `--client-lang ` — The programming language for the generated client module bindings (e.g., typescript, csharp, rust, unrealcpp). If not specified, it will be detected from the project. Possible values: `csharp`, `typescript`, `rust`, `unrealcpp` @@ -318,7 +318,7 @@ Run `spacetime rename --help` for more detailed information. Generate client files for a spacetime module. -**Usage:** `spacetime generate [DATABASE] --lang --out-dir [--module-path | --bin-path | --unreal-module-name | --uproject-dir | --include-private]` +**Usage:** `spacetime generate [DATABASE] --lang [--module-path | --bin-path | --js-path ] [--out-dir | --uproject-dir ] [--unreal-module-name ] [OPTIONS]` Run `spacetime help generate` for more detailed information. diff --git a/docs/static/llms.md b/docs/static/llms.md index 2f17b8fd21a..b107d689f20 100644 --- a/docs/static/llms.md +++ b/docs/static/llms.md @@ -60,7 +60,7 @@ A module is a collection of functions and schema definitions, which can be writt - [The Database Module](/docs/databases): A module is a collection of functions and schema definitions, which can be written in TypeScript, C#, Rust, or C++. Modules define the structure of your database and the server-side logic that processes and handles client requests. - [Automatic Migrations](/docs/databases/automatic-migrations): When you republish a module to an existing database using spacetime publish , SpacetimeDB attempts to automatically migrate your database schema to match the new module definition. This allows you to update your module code and redeploy without losing existing data, as long as the changes are compatible. - [spacetime publish](/docs/databases/building-publishing): This guide covers how to build and publish your SpacetimeDB module. -- [Cheat Sheet](/docs/databases/cheat-sheet): Quick reference for SpacetimeDB module syntax across Rust, C#, and TypeScript. +- [Cheat Sheet](/docs/databases/cheat-sheet): Quick reference for SpacetimeDB module syntax across Rust, C#, TypeScript, and C++. - [spacetime dev](/docs/databases/developing): This guide covers how to create a new SpacetimeDB database module project. - [Incremental Migrations](/docs/databases/incremental-migrations): SpacetimeDB does not provide built-in support for general schema-modifying migrations. It does, however, allow adding new tables, and changing reducers' definitions in arbitrary ways. It's possible to run general migrations using an external tool, but this is tedious, necessitates downtime, and imposes the requirement that you update all your clients at the same time as publishing your new module version. - [Transactions and Atomicity](/docs/databases/transactions-atomicity): SpacetimeDB provides strong transactional guarantees for all database operations. Every reducer runs inside a database transaction, ensuring your data remains consistent and reliable even under concurrent load. diff --git a/skills/cpp-server/SKILL.md b/skills/cpp-server/SKILL.md index 8a920c2e3e3..8d270e93080 100644 --- a/skills/cpp-server/SKILL.md +++ b/skills/cpp-server/SKILL.md @@ -37,7 +37,9 @@ FIELD_PrimaryKeyAutoInc(entity, id) FIELD_Index(entity, name) ``` -Options: `SPACETIMEDB_TABLE(Type, accessor, Public|Private)` +Options: +- `SPACETIMEDB_TABLE(Type, accessor, Public|Private)`: regular table +- `SPACETIMEDB_TABLE(Type, accessor, Public|Private, true)`: event table Field constraints: - `FIELD_PrimaryKey(accessor, field)`: primary key