From 07daae63b0093ce0afa5e3ec68a0362461aa4734 Mon Sep 17 00:00:00 2001 From: Dylan Hunt Date: Fri, 29 Mar 2024 17:10:36 +0800 Subject: [PATCH] fix(refactor): Reducers, [repeated] scheduling, &: - Better, more-clear, more-realistic scheduler example - Split up the autogen codeblock result - Added a *repeated* scheduler example - Normalized consistency for DbEventArgs arg name (named `dbEvent`; same as the new Unity demo) - Added a warning for Exceptions not logging (known bug in the C# SDK). Discussion: https://discord.com/channels/931210784011321394/1220579367462113351 --- docs/modules/c-sharp/index.md | 51 +++++++++++++++++++----------- docs/modules/c-sharp/quickstart.md | 16 +++++----- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/docs/modules/c-sharp/index.md b/docs/modules/c-sharp/index.md index 36a9618..e72c322 100644 --- a/docs/modules/c-sharp/index.md +++ b/docs/modules/c-sharp/index.md @@ -237,6 +237,9 @@ These attributes are bitflags and can be combined together, but you can also use Attribute `[SpacetimeDB.Reducer]` can be used on any `static void` method to register it as a SpacetimeDB reducer. The method must accept only supported types as arguments. If it throws an exception, those will be caught and reported back to the database runtime. +> [!WARNING] +> Currently, `Exceptions` do not log in C# server modules: For an intentional `throw`, be sure to log your error message before throwing. We are actively working to resolve this issue! + ```csharp [SpacetimeDB.Reducer] public static void Add(string name, int age) @@ -251,38 +254,50 @@ If a reducer has an argument with a type `DbEventArgs` (`SpacetimeDB.Runtime.DbE ```csharp [SpacetimeDB.Reducer] -public static void PrintInfo(DbEventArgs e) +public static void PrintInfo(DbEventArgs dbEvent) { - Log($"Sender identity: {e.Sender}"); - Log($"Sender address: {e.Address}"); - Log($"Time: {e.Time}"); + Log($"Sender identity: {dbEvent.Sender}"); + Log($"Sender address: {dbEvent.Address}"); + Log($"Time: {dbEvent.Time}"); } ``` -`[SpacetimeDB.Reducer]` also generates a function to schedule the given reducer in the future. -Since it's not possible to generate extension methods on existing methods, the codegen will instead add a `Schedule`-prefixed method colocated in the same namespace as the original method instead. The generated method will accept `DateTimeOffset` argument for the time when the reducer should be invoked, followed by all the arguments of the reducer itself, except those that have type `DbEventArgs`. +#### Reducer Scheduling + +`[SpacetimeDB.Reducer]` also generates a function to schedule the given reducer to fire **once** in the future. + +Although schedulers only fire once, you may nest this inside of the function it calls to **repeatedly** invoke the reducer. + +Since it's not possible to generate extension methods on existing methods, the codegen will instead add a `Schedule`-prefixed method colocated in the same namespace as the original method. The generated method will accept `DateTimeOffset` argument for the time when the reducer should be invoked, followed by all the arguments of the reducer itself, except those that have type `DbEventArgs`. + +If we created a Reducer named `SpawnResource(uint resourceId)`, the auto-codegen would create: ```csharp -// Example reducer: -[SpacetimeDB.Reducer] -public static void Add(string name, int age) { ... } +`ScheduleSpawnResource(DateTimeOffset time, int age)` +``` + +Let's consider an **repeating** resource scheduler example: -// Auto-generated by the codegen: -public static void ScheduleAdd(DateTimeOffset time, string name, int age) { ... } +```csharp +/// Cancel this anytime with `.Cancel()` +static ScheduleToken _resourceSpawnerScheduleToken; -// Usage from another reducer: +/// Schedule to call itself every 5m, repeating until cancelled [SpacetimeDB.Reducer] -public static void AddIn5Minutes(DbEventArgs e, string name, int age) +public static void ResourceSpawnerAgent(DbEventArgs dbEvent, string name, int age) { - // Note that we're using `e.Time` instead of `DateTimeOffset.Now` which is not allowed in modules. - var scheduleToken = ScheduleAdd(e.Time.AddMinutes(5), name, age); - - // We can cancel the scheduled reducer by calling `Cancel()` on the returned token. - scheduleToken.Cancel(); + // Note that we're using the sender's `dbEvent.Time` instead of `DateTimeOffset.Now` + DateTimeOffset time = dbEvent.Time.AddMinutes(5); + + // [Re]Schedule to call itself again in 5m + _resourceSpawnerScheduleToken = ScheduleSpawnResource(time, resourceId: 0); } ``` +> [!WARNING] +> When scheduling a Reducer, always use original send time from `dbEvent.time` instead of `DateTimeOffset.Now` to schedule a reducer. + #### Special reducers These are two special kinds of reducers that can be used to respond to module lifecycle events. They're stored in the `SpacetimeDB.Module.ReducerKind` class and can be used as an argument to the `[SpacetimeDB.Reducer]` attribute: diff --git a/docs/modules/c-sharp/quickstart.md b/docs/modules/c-sharp/quickstart.md index fb97c31..d659d82 100644 --- a/docs/modules/c-sharp/quickstart.md +++ b/docs/modules/c-sharp/quickstart.md @@ -190,17 +190,17 @@ In `server/Lib.cs`, add the definition of the connect reducer to the `Module` cl ```csharp [SpacetimeDB.Reducer(ReducerKind.Connect)] -public static void OnConnect(DbEventArgs dbEventArgs) +public static void OnConnect(DbEventArgs dbEvent) { - Log($"Connect {dbEventArgs.Sender}"); - var user = User.FindByIdentity(dbEventArgs.Sender); + Log($"Connect {dbEvent.Sender}"); + var user = User.FindByIdentity(dbEvent.Sender); if (user is not null) { // If this is a returning user, i.e., we already have a `User` with this `Identity`, // set `Online: true`, but leave `Name` and `Identity` unchanged. user.Online = true; - User.UpdateByIdentity(dbEventArgs.Sender, user); + User.UpdateByIdentity(dbEvent.Sender, user); } else { @@ -209,7 +209,7 @@ public static void OnConnect(DbEventArgs dbEventArgs) new User { Name = null, - Identity = dbEventArgs.Sender, + Identity = dbEvent.Sender, Online = true, }.Insert(); } @@ -222,15 +222,15 @@ Add the following code after the `OnConnect` lambda: ```csharp [SpacetimeDB.Reducer(ReducerKind.Disconnect)] -public static void OnDisconnect(DbEventArgs dbEventArgs) +public static void OnDisconnect(DbEventArgs dbEvent) { - var user = User.FindByIdentity(dbEventArgs.Sender); + var user = User.FindByIdentity(dbEvent.Sender); if (user is not null) { // This user should exist, so set `Online: false`. user.Online = false; - User.UpdateByIdentity(dbEventArgs.Sender, user); + User.UpdateByIdentity(dbEvent.Sender, user); } else {