diff --git a/README.md b/README.md index af34b88a..0f9998b0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ To make changes to our docs, you can open a pull request in this repository. You git clone ssh://git@github.com//spacetime-docs ``` -3. Make your edits to the docs that you want to make + test them locally (see Testing Your Edits below) +3. Make your edits to the docs that you want to make + test them locally 4. Commit your changes: ```bash diff --git a/docs/getting-started.md b/docs/getting-started.md index 5a0c6041..177a0d25 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,29 +2,33 @@ To develop SpacetimeDB applications locally, you will need to run the Standalone version of the server. -1. [Install](/install) the SpacetimeDB CLI (Command Line Interface). -2. Run the start command +1. [Install](/install) the SpacetimeDB CLI (Command Line Interface) +2. Run the start command: ```bash spacetime start ``` -The server listens on port `3000` by default. You can change this by using the `--listen-addr` option described below. +The server listens on port `3000` by default, customized via `--listen-addr`. -SSL is not supported in standalone mode. +💡 Standalone mode will run in the foreground. +⚠️ SSL is not supported in standalone mode. ## What's Next? -You are ready to start developing SpacetimeDB modules. We have a quickstart guide for each supported server-side language: +You are ready to start developing SpacetimeDB modules. See below for a quickstart guide for both client and server (module) languages/frameworks. + +### Server (Module) - [Rust](/docs/modules/rust/quickstart) - [C#](/docs/modules/c-sharp/quickstart) -Then you can write your client application. We have a quickstart guide for each supported client-side language: +⚡**Note:** Rust is [roughly 2x faster](https://faun.dev/c/links/faun/c-vs-rust-vs-go-a-performance-benchmarking-in-kubernetes/) than C# + +### Client - [Rust](/docs/sdks/rust/quickstart) -- [C#](/docs/sdks/c-sharp/quickstart) +- [C# (Standalone)](/docs/sdks/c-sharp/quickstart) +- [C# (Unity)](/docs/unity/part-1) - [Typescript](/docs/sdks/typescript/quickstart) -- [Python](/docs/sdks/python/quickstart) - -We also have a [step-by-step tutorial](/docs/unity/part-1) for building a multiplayer game in Unity3d. +- [Python](/docs/sdks/python/quickstart) \ No newline at end of file diff --git a/docs/modules/c-sharp/index.md b/docs/modules/c-sharp/index.md index 36a9618a..31ebd1d4 100644 --- a/docs/modules/c-sharp/index.md +++ b/docs/modules/c-sharp/index.md @@ -42,6 +42,7 @@ static partial class Module { // We can skip (or explicitly set to zero) auto-incremented fields when creating new rows. var person = new Person { Name = name, Age = age }; + // `Insert()` method is auto-generated and will insert the given row into the table. person.Insert(); // After insertion, the auto-incremented fields will be populated with their actual values. @@ -211,8 +212,10 @@ public partial struct Person // Finds a row in the table with the given value in the `Id` column and returns it, or `null` if no such row exists. public static Person? FindById(int id); + // Deletes a row in the table with the given value in the `Id` column and returns `true` if the row was found and deleted, or `false` if no such row exists. public static bool DeleteById(int id); + // Updates a row in the table with the given value in the `Id` column and returns `true` if the row was found and updated, or `false` if no such row exists. public static bool UpdateById(int oldId, Person newValue); } diff --git a/docs/modules/c-sharp/quickstart.md b/docs/modules/c-sharp/quickstart.md index fb97c316..f5f73401 100644 --- a/docs/modules/c-sharp/quickstart.md +++ b/docs/modules/c-sharp/quickstart.md @@ -16,7 +16,18 @@ If you haven't already, start by [installing SpacetimeDB](/install). This will i ## Install .NET 8 -Next we need to [install .NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) so that we can build and publish our module. .NET 8.0 is the earliest to have the `wasi-experimental` workload that we rely on. +Next we need to [install .NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) so that we can build and publish our module. + +You may already have .NET 8 and can be checked: +```bash +dotnet --list-sdks +``` + +.NET 8.0 is the earliest to have the `wasi-experimental` workload that we rely on, but requires manual activation: + +```bash +dotnet workload install wasi-experimental +``` ## Project structure @@ -35,7 +46,11 @@ spacetime init --lang csharp server ## Declare imports -`spacetime init` should have pre-populated `server/Lib.cs` with a trivial module. Clear it out, so we can write a module that's still pretty simple: a bare-bones chat server. +`spacetime init` generated a few files: + +1. Open `server/StdbModule.csproj` to generate a .sln file for intellisense/validation support. +2. Open `server/Lib.cs`, a trivial module. +3. Clear it out, so we can write a new module that's still pretty simple: a bare-bones chat server. To the top of `server/Lib.cs`, add some imports we'll be using: @@ -45,8 +60,10 @@ using SpacetimeDB.Module; using static SpacetimeDB.Runtime; ``` -- `System.Runtime.CompilerServices` allows us to use the `ModuleInitializer` attribute, which we'll use to register our `OnConnect` and `OnDisconnect` callbacks. -- `SpacetimeDB.Module` contains the special attributes we'll use to define our module. +- `System.Runtime.CompilerServices` +- `SpacetimeDB.Module` + - Contains the special attributes we'll use to define our module. + - Allows us to use the `ModuleInitializer` attribute, which we'll use to register our `OnConnect` and `OnDisconnect` callbacks. - `SpacetimeDB.Runtime` contains the raw API bindings SpacetimeDB uses to communicate with the database. We also need to create our static module class which all of the module code will live in. In `server/Lib.cs`, add: @@ -184,7 +201,7 @@ You could extend the validation in `ValidateMessage` in similar ways to `Validat In C# modules, you can register for `Connect` and `Disconnect` events by using a special `ReducerKind`. We'll use the `Connect` event to create a `User` record for the client if it doesn't yet exist, and to set its online status. -We'll use `User.FilterByIdentity` to look up a `User` row for `dbEvent.Sender`, if one exists. If we find one, we'll use `User.UpdateByIdentity` to overwrite it with a row that has `Online: true`. If not, we'll use `User.Insert` to insert a new row for our new user. All three of these methods are generated by the `[SpacetimeDB.Table]` attribute, with rows and behavior based on the row attributes. `FilterByIdentity` returns a nullable `User`, because the unique constraint from the `[SpacetimeDB.Column(ColumnAttrs.PrimaryKey)]` attribute means there will be either zero or one matching rows. `Insert` will throw an exception if the insert violates this constraint; if we want to overwrite a `User` row, we need to do so explicitly using `UpdateByIdentity`. +We'll use `User.FindByIdentity` to look up a `User` row for `dbEvent.Sender`, if one exists. If we find one, we'll use `User.UpdateByIdentity` to overwrite it with a row that has `Online: true`. If not, we'll use `User.Insert` to insert a new row for our new user. All three of these methods are generated by the `[SpacetimeDB.Table]` attribute, with rows and behavior based on the row attributes. `FindByIdentity` returns a nullable `User`, because the unique constraint from the `[SpacetimeDB.Column(ColumnAttrs.PrimaryKey)]` attribute means there will be either zero or one matching rows. `Insert` will throw an exception if the insert violates this constraint; if we want to overwrite a `User` row, we need to do so explicitly using `UpdateByIdentity`. In `server/Lib.cs`, add the definition of the connect reducer to the `Module` class: @@ -235,7 +252,7 @@ public static void OnDisconnect(DbEventArgs dbEventArgs) else { // User does not exist, log warning - Log($"Warning: No user found for disconnected client."); + Log("Warning: No user found for disconnected client."); } } ``` @@ -250,12 +267,16 @@ From the `quickstart-chat` directory, run: spacetime publish --project-path server ``` +```bash +npm i wasm-opt -g +``` + ## Call Reducers You can use the CLI (command line interface) to run reducers. The arguments to the reducer are passed in JSON format. ```bash -spacetime call send_message '["Hello, World!"]' +spacetime call send_message "Hello, World!" ``` Once we've called our `send_message` reducer, we can check to make sure it ran by running the `logs` command. @@ -288,4 +309,4 @@ spacetime sql "SELECT * FROM Message" You've just set up your first database in SpacetimeDB! The next step would be to create a client module that interacts with this module. You can use any of SpacetimDB's supported client languages to do this. Take a look at the quick start guide for your client language of choice: [Rust](/docs/languages/rust/rust-sdk-quickstart-guide), [C#](/docs/languages/csharp/csharp-sdk-quickstart-guide), [TypeScript](/docs/languages/typescript/typescript-sdk-quickstart-guide) or [Python](/docs/languages/python/python-sdk-quickstart-guide). -If you are planning to use SpacetimeDB with the Unity3d game engine, you can skip right to the [Unity Comprehensive Tutorial](/docs/unity/part-1) or check out our example game, [BitcraftMini](/docs/unity/part-3). +If you are planning to use SpacetimeDB with the Unity game engine, you can skip right to the [Unity Comprehensive Tutorial](/docs/unity/part-1) or check out our example game, [BitcraftMini](/docs/unity/part-3). diff --git a/docs/modules/rust/quickstart.md b/docs/modules/rust/quickstart.md index e0ff0f5f..e015b881 100644 --- a/docs/modules/rust/quickstart.md +++ b/docs/modules/rust/quickstart.md @@ -269,4 +269,4 @@ You can find the full code for this module [in the SpacetimeDB module examples]( You've just set up your first database in SpacetimeDB! The next step would be to create a client module that interacts with this module. You can use any of SpacetimDB's supported client languages to do this. Take a look at the quickstart guide for your client language of choice: [Rust](/docs/sdks/rust/quickstart), [C#](/docs/sdks/c-sharp/quickstart), [TypeScript](/docs/sdks/typescript/quickstart) or [Python](/docs/sdks/python/quickstart). -If you are planning to use SpacetimeDB with the Unity3d game engine, you can skip right to the [Unity Comprehensive Tutorial](/docs/unity/part-1) or check out our example game, [BitcraftMini](/docs/unity/part-3). +If you are planning to use SpacetimeDB with the Unity game engine, you can skip right to the [Unity Comprehensive Tutorial](/docs/unity/part-1) or check out our example game, [BitcraftMini](/docs/unity/part-3). diff --git a/docs/sdks/c-sharp/index.md b/docs/sdks/c-sharp/index.md index 473ca1ba..7c920cf5 100644 --- a/docs/sdks/c-sharp/index.md +++ b/docs/sdks/c-sharp/index.md @@ -17,9 +17,10 @@ The SpacetimeDB client C# for Rust contains all the tools you need to build nati - [Method `SpacetimeDBClient.Connect`](#method-spacetimedbclientconnect) - [Event `SpacetimeDBClient.onIdentityReceived`](#event-spacetimedbclientonidentityreceived) - [Event `SpacetimeDBClient.onConnect`](#event-spacetimedbclientonconnect) - - [Subscribe to queries](#subscribe-to-queries) + - [Query subscriptions & one-time actions](#subscribe-to-queries) - [Method `SpacetimeDBClient.Subscribe`](#method-spacetimedbclientsubscribe) - [Event `SpacetimeDBClient.onSubscriptionApplied`](#event-spacetimedbclientonsubscriptionapplied) + - [Method `SpacetimeDBClient.OneOffQuery`](#event-spacetimedbclientoneoffquery) - [View rows of subscribed tables](#view-rows-of-subscribed-tables) - [Class `{TABLE}`](#class-table) - [Static Method `{TABLE}.Iter`](#static-method-tableiter) @@ -64,13 +65,11 @@ dotnet add package spacetimedbsdk ### Using Unity -To install the SpacetimeDB SDK into a Unity project, download the SpacetimeDB SDK from the following link. +To install the SpacetimeDB SDK into a Unity project, [download the SpacetimeDB SDK](https://github.com/clockworklabs/com.clockworklabs.spacetimedbsdk/releases/latest), packaged as a `.unitypackage`. -https://sdk.spacetimedb.com/SpacetimeDBUnitySDK.unitypackage +In Unity navigate to the `Assets > Import Package > Custom Package` menu in the menu bar. Select your `SpacetimeDB.Unity.Comprehensive.Tutorial.unitypackage` file and leave all folders checked. -In Unity navigate to the `Assets > Import Package > Custom Package...` menu in the menu bar. Select your `SpacetimeDBUnitySDK.unitypackage` file and leave all folders checked. - -(See also the [Unity Tutorial](/docs/unity/part-1).) +(See also the [Unity Tutorial](/docs/unity/part-1)) ## Generate module bindings @@ -319,6 +318,15 @@ void Main() } ``` +### Method [`OneTimeQuery`](#method-spacetimedbclientsubscribe) + +You may not want to subscribe to a query, but instead want to run a query once and receive the results immediately via a `Task` result: + +```csharp +// Query all Messages from the sender "bob" +SpacetimeDBClient.instance.OneOffQuery("WHERE sender = \"bob\""); +``` + ## View rows of subscribed tables The SDK maintains a local view of the database called the "client cache". This cache contains whatever rows are selected via a call to [`SpacetimeDBClient.Subscribe`](#method-spacetimedbclientsubscribe). These rows are represented in the SpacetimeDB .Net SDK as instances of [`SpacetimeDB.Types.{TABLE}`](#class-table). diff --git a/docs/sdks/c-sharp/quickstart.md b/docs/sdks/c-sharp/quickstart.md index f7565019..07aa6cf6 100644 --- a/docs/sdks/c-sharp/quickstart.md +++ b/docs/sdks/c-sharp/quickstart.md @@ -1,8 +1,8 @@ # C# Client SDK Quick Start -In this guide we'll show you how to get up and running with a simple SpacetimDB app with a client written in C#. +In this guide we'll show you how to get up and running with a simple SpacetimeDB app with a client written in C#. -We'll implement a command-line client for the module created in our Rust or C# Module Quickstart guides. Make sure you follow one of these guides before you start on this one. +We'll implement a command-line client for the module created in our [Rust](../../modules/rust/quickstart.md) or [C# Module](../../modules/c-sharp/quickstart.md) Quickstart guides. Ensure you followed one of these guides before continuing. ## Project structure @@ -12,7 +12,7 @@ Enter the directory `quickstart-chat` you created in the [Rust Module Quickstart cd quickstart-chat ``` -Within it, create a new C# console application project called `client` using either Visual Studio or the .NET CLI: +Within it, create a new C# console application project called `client` using either Visual Studio, Rider or the .NET CLI: ```bash dotnet new console -o client @@ -22,7 +22,7 @@ Open the project in your IDE of choice. ## Add the NuGet package for the C# SpacetimeDB SDK -Add the `SpacetimeDB.ClientSDK` [NuGet package](https://www.nuget.org/packages/spacetimedbsdk) using Visual Studio NuGet package manager or via the .NET CLI +Add the `SpacetimeDB.ClientSDK` [NuGet package](https://www.nuget.org/packages/spacetimedbsdk) using Visual Studio or Rider _NuGet Package Manager_ or via the .NET CLI: ```bash dotnet add package SpacetimeDB.ClientSDK @@ -65,8 +65,10 @@ We will also need to create some global variables that will be explained when we ```csharp // our local client SpacetimeDB identity Identity? local_identity = null; + // declare a thread safe queue to store commands in format (command, args) ConcurrentQueue<(string,string)> input_queue = new ConcurrentQueue<(string, string)>(); + // declare a threadsafe cancel token to cancel the process loop CancellationTokenSource cancel_token = new CancellationTokenSource(); ``` @@ -75,10 +77,10 @@ CancellationTokenSource cancel_token = new CancellationTokenSource(); We'll work outside-in, first defining our `Main` function at a high level, then implementing each behavior it needs. We need `Main` to do several things: -1. Initialize the AuthToken module, which loads and stores our authentication token to/from local storage. -2. Create the SpacetimeDBClient instance. +1. Initialize the `AuthToken` module, which loads and stores our authentication token to/from local storage. +2. Create the `SpacetimeDBClient` instance. 3. Register callbacks on any events we want to handle. These will print to standard output messages received from the database and updates about users' names and online statuses. -4. Start our processing thread, which connects to the SpacetimeDB module, updates the SpacetimeDB client and processes commands that come in from the input loop running in the main thread. +4. Start our processing thread which connects to the SpacetimeDB module, updates the SpacetimeDB client and processes commands that come in from the input loop running in the main thread. 5. Start the input loop, which reads commands from standard input and sends them to the processing thread. 6. When the input loop exits, stop the processing thread and wait for it to exit. @@ -154,7 +156,7 @@ string UserNameOrIdentity(User user) => user.Name ?? user.Identity.ToString()!.S void User_OnInsert(User insertedValue, ReducerEvent? dbEvent) { - if(insertedValue.Online) + if (insertedValue.Online) { Console.WriteLine($"{UserNameOrIdentity(insertedValue)} is online"); } @@ -178,20 +180,21 @@ We'll print an appropriate message in each of these cases. ```csharp void User_OnUpdate(User oldValue, User newValue, ReducerEvent dbEvent) { - if(oldValue.Name != newValue.Name) + if (oldValue.Name != newValue.Name) { Console.WriteLine($"{UserNameOrIdentity(oldValue)} renamed to {newValue.Name}"); } - if(oldValue.Online != newValue.Online) + + if (oldValue.Online == newValue.Online) + return; + + if (newValue.Online) { - if(newValue.Online) - { - Console.WriteLine($"{UserNameOrIdentity(newValue)} connected."); - } - else - { - Console.WriteLine($"{UserNameOrIdentity(newValue)} disconnected."); - } + Console.WriteLine($"{UserNameOrIdentity(newValue)} connected."); + } + else + { + Console.WriteLine($"{UserNameOrIdentity(newValue)} disconnected."); } } ``` @@ -209,7 +212,7 @@ void PrintMessage(Message message) { var sender = User.FilterByIdentity(message.Sender); var senderName = "unknown"; - if(sender != null) + if (sender != null) { senderName = UserNameOrIdentity(sender); } @@ -219,7 +222,7 @@ void PrintMessage(Message message) void Message_OnInsert(Message insertedValue, ReducerEvent? dbEvent) { - if(dbEvent != null) + if (dbEvent != null) { PrintMessage(insertedValue); } @@ -254,7 +257,11 @@ We'll test both that our identity matches the sender and that the status is `Fai ```csharp void Reducer_OnSetNameEvent(ReducerEvent reducerEvent, string name) { - if(reducerEvent.Identity == local_identity && reducerEvent.Status == ClientApi.Event.Types.Status.Failed) + bool localIdentityFailedToChangeName = + reducerEvent.Identity == local_identity && + reducerEvent.Status == ClientApi.Event.Types.Status.Failed; + + if (localIdentityFailedToChangeName) { Console.Write($"Failed to change name to {name}"); } @@ -268,7 +275,11 @@ We handle warnings on rejected messages the same way as rejected names, though t ```csharp void Reducer_OnSendMessageEvent(ReducerEvent reducerEvent, string text) { - if (reducerEvent.Identity == local_identity && reducerEvent.Status == ClientApi.Event.Types.Status.Failed) + bool localIdentityFailedToSendMessage = + reducerEvent.Identity == local_identity && + reducerEvent.Status == ClientApi.Event.Types.Status.Failed; + + if (localIdentityFailedToSendMessage) { Console.Write($"Failed to send message {text}"); } @@ -282,7 +293,10 @@ Once we are connected, we can send our subscription to the SpacetimeDB module. S ```csharp void OnConnect() { - SpacetimeDBClient.instance.Subscribe(new List { "SELECT * FROM User", "SELECT * FROM Message" }); + SpacetimeDBClient.instance.Subscribe(new List + { + "SELECT * FROM User", "SELECT * FROM Message" + }); } ``` @@ -370,12 +384,12 @@ void InputLoop() while (true) { var input = Console.ReadLine(); - if(input == null) + if (input == null) { break; } - if(input.StartsWith("/name ")) + if (input.StartsWith("/name ")) { input_queue.Enqueue(("name", input.Substring(6))); continue; @@ -421,4 +435,4 @@ dotnet run --project client ## What's next? -Congratulations! You've built a simple chat app using SpacetimeDB. You can look at the C# SDK Reference for more information about the client SDK. If you are interested in developing in the Unity3d game engine, check out our Unity3d Comprehensive Tutorial and BitcraftMini game example. +Congratulations! You've built a simple chat app using SpacetimeDB. You can look at the C# SDK Reference for more information about the client SDK. If you are interested in developing in the Unity game engine, check out our Unity3d Comprehensive Tutorial and BitcraftMini game example. diff --git a/docs/unity/part-1.md b/docs/unity/part-1.md index 30bd3137..0e899750 100644 --- a/docs/unity/part-1.md +++ b/docs/unity/part-1.md @@ -12,14 +12,19 @@ This tutorial has been tested against UnityEngine version 2022.3.4f1. This tutor ## Prepare Project Structure -This project is separated into two sub-projects, one for the server (module) code and one for the client code. First we'll create the main directory, this directory name doesn't matter but we'll give you an example: +This project is separated into two sub-projects; + +1. Server (module) code +2. Client code + +First, we'll create a project root directory (you can choose the name): ```bash mkdir SpacetimeDBUnityTutorial cd SpacetimeDBUnityTutorial ``` -In the following sections we'll be adding a client directory and a server directory, which will contain the client files and the module (server) files respectively. We'll start by populating the client directory. +We'll start by populating the client directory. ## Setting up the Tutorial Unity Project @@ -31,9 +36,9 @@ Open Unity and create a new project by selecting "New" from the Unity Hub or goi ![UnityHub-NewProject](/images/unity-tutorial/UnityHub-NewProject.JPG) -For Project Name use `client`. For Project Location make sure that you use your `SpacetimeDBUnityTutorial` directory. This is the directory that we created in a previous step. +**⚠️ Important: Ensure `3D (URP)` is selected** to properly render the materials in the scene! -**Important: Ensure that you have selected the 3D (URP) template for this project.** If you forget to do this then Unity won't be able to properly render the materials in the scene! +For Project Name use `client`. For Project Location make sure that you use your `SpacetimeDBUnityTutorial` directory. This is the directory that we created in a previous step. ![UnityHub-3DURP](/images/unity-tutorial/UnityHub-3DURP.JPG) @@ -77,7 +82,9 @@ Now that we have everything set up, let's run the project and see it in action: ![Unity-OpenSceneMain](/images/unity-tutorial/Unity-OpenSceneMain.JPG) -NOTE: When you open the scene you may get a message saying you need to import TMP Essentials. When it appears, click the "Import TMP Essentials" button. +**NOTE:** When you open the scene you may get a message saying you need to import TMP Essentials. When it appears, click the "Import TMP Essentials" button. + +🧹 Clear any false-positive TMPro errors that may show. ![Unity Import TMP Essentials](/images/unity-tutorial/Unity-ImportTMPEssentials.JPG) @@ -105,6 +112,9 @@ At this point you should have the single player game working. In your CLI, your spacetime start ``` +💡 Standalone mode will run in the foreground. +💡 Below examples Rust language, [but you may also use C#](../modules/c-sharp/index.md). + 3. Run the following command to initialize the SpacetimeDB server project with Rust as the language: ```bash @@ -284,7 +294,6 @@ We use the `connect` and `disconnect` reducers to update the logged in state of // Called when the client connects, we update the logged_in state to true #[spacetimedb(connect)] pub fn client_connected(ctx: ReducerContext) { - // called when the client connects, we update the logged_in state to true update_player_login_state(ctx, true); } @@ -292,7 +301,6 @@ pub fn client_connected(ctx: ReducerContext) { // Called when the client disconnects, we update the logged_in state to false #[spacetimedb(disconnect)] pub fn client_disconnected(ctx: ReducerContext) { - // Called when the client disconnects, we update the logged_in state to false update_player_login_state(ctx, false); } @@ -553,8 +561,8 @@ public class RemotePlayer : MonoBehaviour canvas.worldCamera = Camera.main; // Get the username from the PlayerComponent for this object and set it in the UI - PlayerComponent playerComp = PlayerComponent.FilterByEntityId(EntityId); - Username = playerComp.Username; + // FilterByEntityId is normally nullable, but we'll assume not null for simplicity + PlayerComponent playerComp = PlayerComponent.FilterByEntityId(EntityId).First(); // Get the last location for this player and set the initial position EntityComponent entity = EntityComponent.FilterByEntityId(EntityId); @@ -612,13 +620,16 @@ private void PlayerComponent_OnInsert(PlayerComponent obj, ReducerEvent callInfo { // Spawn the player object and attach the RemotePlayer component var remotePlayer = Instantiate(PlayerPrefab); + // Lookup and apply the position for this new player var entity = EntityComponent.FilterByEntityId(obj.EntityId); var position = new Vector3(entity.Position.X, entity.Position.Y, entity.Position.Z); remotePlayer.transform.position = position; + var movementController = remotePlayer.GetComponent(); movementController.RemoteTargetPosition = position; movementController.RemoteTargetRotation = entity.Direction; + remotePlayer.AddComponent().EntityId = obj.EntityId; } } @@ -639,21 +650,26 @@ using SpacetimeDB; private float? lastUpdateTime; private void FixedUpdate() { - if ((lastUpdateTime.HasValue && Time.time - lastUpdateTime.Value > 1.0f / movementUpdateSpeed) || !SpacetimeDBClient.instance.IsConnected()) - { - return; - } - - lastUpdateTime = Time.time; - var p = PlayerMovementController.Local.GetModelPosition(); - Reducer.UpdatePlayerPosition(new StdbVector3 - { - X = p.x, - Y = p.y, - Z = p.z, - }, - PlayerMovementController.Local.GetModelRotation(), - PlayerMovementController.Local.IsMoving()); + float? deltaTime = Time.time - lastUpdateTime; + bool hasUpdatedRecently = deltaTime.HasValue && deltaTime.Value < 1.0f / movementUpdateSpeed; + bool isConnected = SpacetimeDBClient.instance.IsConnected(); + + if (hasUpdatedRecently || !isConnected) + { + return; + } + + lastUpdateTime = Time.time; + var p = PlayerMovementController.Local.GetModelPosition(); + + Reducer.UpdatePlayerPosition(new StdbVector3 + { + X = p.x, + Y = p.y, + Z = p.z, + }, + PlayerMovementController.Local.GetModelRotation(), + PlayerMovementController.Local.IsMoving()); } ``` @@ -713,13 +729,16 @@ private void OnPlayerComponentChanged(PlayerComponent obj) { // Spawn the player object and attach the RemotePlayer component var remotePlayer = Instantiate(PlayerPrefab); + // Lookup and apply the position for this new player var entity = EntityComponent.FilterByEntityId(obj.EntityId); var position = new Vector3(entity.Position.X, entity.Position.Y, entity.Position.Z); remotePlayer.transform.position = position; + var movementController = remotePlayer.GetComponent(); movementController.RemoteTargetPosition = position; movementController.RemoteTargetRotation = entity.Direction; + remotePlayer.AddComponent().EntityId = obj.EntityId; } }