diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index 90eb00d3eaf..0731b890923 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -1,4 +1,4 @@ -//HintName: FFI.cs +//HintName: FFI.cs // #nullable enable // The runtime already defines SpacetimeDB.Internal.LocalReadOnly in Runtime\Internal\Module.cs as an empty partial type. @@ -17,7 +17,6 @@ namespace SpacetimeDB { public sealed record ReducerContext : DbContext, Internal.IReducerContext { - public readonly Identity Sender; public readonly ConnectionId? ConnectionId; public readonly Random Rng; public readonly Timestamp Timestamp; @@ -29,6 +28,8 @@ public sealed record ReducerContext : DbContext, Internal.IReducerContext // We need this property to be non-static for parity with client SDK. public Identity Identity => Internal.IReducerContext.GetIdentity(); + private readonly Identity _sender; + internal ReducerContext( Identity identity, ConnectionId? connectionId, @@ -37,7 +38,7 @@ internal ReducerContext( AuthCtx? senderAuth = null ) { - Sender = identity; + _sender = identity; ConnectionId = connectionId; Rng = random; Timestamp = time; @@ -45,6 +46,11 @@ internal ReducerContext( CounterUuid = 0; } + /// + /// The identity of the client that invoked the reducer. + /// + public Identity Sender() => _sender; + /// /// Create a new random `v4` using the built-in RNG. /// @@ -209,13 +215,18 @@ public sealed class Local : global::SpacetimeDB.LocalBase public sealed record ViewContext : DbContext, Internal.IViewContext { - public Identity Sender { get; } + private readonly Identity _sender; internal ViewContext(Identity sender, Internal.LocalReadOnly db) : base(db) { - Sender = sender; + _sender = sender; } + + /// + /// The identity of the client that invoked the view. + /// + public Identity Sender() => _sender; } public sealed record AnonymousViewContext diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index f4d86c2ddbc..86a559890c1 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -1,4 +1,4 @@ -//HintName: FFI.cs +//HintName: FFI.cs // #nullable enable // The runtime already defines SpacetimeDB.Internal.LocalReadOnly in Runtime\Internal\Module.cs as an empty partial type. @@ -17,7 +17,6 @@ namespace SpacetimeDB { public sealed record ReducerContext : DbContext, Internal.IReducerContext { - public readonly Identity Sender; public readonly ConnectionId? ConnectionId; public readonly Random Rng; public readonly Timestamp Timestamp; @@ -29,6 +28,8 @@ public sealed record ReducerContext : DbContext, Internal.IReducerContext // We need this property to be non-static for parity with client SDK. public Identity Identity => Internal.IReducerContext.GetIdentity(); + private readonly Identity _sender; + internal ReducerContext( Identity identity, ConnectionId? connectionId, @@ -37,7 +38,7 @@ internal ReducerContext( AuthCtx? senderAuth = null ) { - Sender = identity; + _sender = identity; ConnectionId = connectionId; Rng = random; Timestamp = time; @@ -45,6 +46,11 @@ internal ReducerContext( CounterUuid = 0; } + /// + /// The identity of the client that invoked the reducer. + /// + public Identity Sender() => _sender; + /// /// Create a new random `v4` using the built-in RNG. /// @@ -199,13 +205,18 @@ public sealed class Local : global::SpacetimeDB.LocalBase public sealed record ViewContext : DbContext, Internal.IViewContext { - public Identity Sender { get; } + private readonly Identity _sender; internal ViewContext(Identity sender, Internal.LocalReadOnly db) : base(db) { - Sender = sender; + _sender = sender; } + + /// + /// The identity of the client that invoked the view. + /// + public Identity Sender() => _sender; } public sealed record AnonymousViewContext diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 63331bba5b6..e927f8832a4 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1737,7 +1737,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) namespace SpacetimeDB { public sealed record ReducerContext : DbContext, Internal.IReducerContext { - public readonly Identity Sender; public readonly ConnectionId? ConnectionId; public readonly Random Rng; public readonly Timestamp Timestamp; @@ -1747,16 +1746,24 @@ public sealed record ReducerContext : DbContext, Internal.IReducerContext // We need this property to be non-static for parity with client SDK. public Identity Identity => Internal.IReducerContext.GetIdentity(); + private readonly Identity _sender; + internal ReducerContext(Identity identity, ConnectionId? connectionId, Random random, Timestamp time, AuthCtx? senderAuth = null) { - Sender = identity; + _sender = identity; ConnectionId = connectionId; Rng = random; Timestamp = time; SenderAuth = senderAuth ?? AuthCtx.BuildFromSystemTables(connectionId, identity); CounterUuid = 0; } + + /// + /// The identity of the client that invoked the reducer. + /// + public Identity Sender() => _sender; + /// /// Create a new random `v4` using the built-in RNG. /// @@ -1891,13 +1898,18 @@ public sealed class Local : global::SpacetimeDB.LocalBase { public sealed record ViewContext : DbContext, Internal.IViewContext { - public Identity Sender { get; } + private readonly Identity _sender; internal ViewContext(Identity sender, Internal.LocalReadOnly db) : base(db) { - Sender = sender; + _sender = sender; } + + /// + /// The identity of the client that invoked the view. + /// + public Identity Sender() => _sender; } public sealed record AnonymousViewContext : DbContext, Internal.IAnonymousViewContext diff --git a/crates/bindings-csharp/Runtime/Internal/TxContext.cs b/crates/bindings-csharp/Runtime/Internal/TxContext.cs index d5bea2febd9..f8465e78d56 100644 --- a/crates/bindings-csharp/Runtime/Internal/TxContext.cs +++ b/crates/bindings-csharp/Runtime/Internal/TxContext.cs @@ -9,13 +9,16 @@ public sealed class TxContext( Random rng ) { + private readonly Identity _sender = sender; + public Local Db { get; } = db; - public Identity Sender { get; } = sender; public ConnectionId? ConnectionId { get; } = connectionId; public Timestamp Timestamp { get; } = timestamp; public AuthCtx SenderAuth { get; } = senderAuth; public Random Rng { get; } = rng; + public Identity Sender() => _sender; + public TxContext WithTimestamp(Timestamp ts) => - new(Db, Sender, ConnectionId, ts, SenderAuth, Rng); + new(Db, _sender, ConnectionId, ts, SenderAuth, Rng); } diff --git a/crates/bindings-csharp/Runtime/ProcedureContext.cs b/crates/bindings-csharp/Runtime/ProcedureContext.cs index 4eb8583b7d5..1b878b8e549 100644 --- a/crates/bindings-csharp/Runtime/ProcedureContext.cs +++ b/crates/bindings-csharp/Runtime/ProcedureContext.cs @@ -10,8 +10,11 @@ public abstract class ProcedureContextBase( Timestamp time ) : Internal.IInternalProcedureContext { + private readonly Identity _sender = sender; + + public Identity Sender() => _sender; + public static Identity Identity => Internal.IProcedureContext.GetIdentity(); - public Identity Sender { get; } = sender; public ConnectionId? ConnectionId { get; } = connectionId; public Random Rng { get; } = random; public Timestamp Timestamp { get; private set; } = time; @@ -47,7 +50,7 @@ public Internal.TxContext EnterTxContext(long timestampMicros) txContext?.WithTimestamp(timestamp) ?? new Internal.TxContext( CreateLocal(), - Sender, + _sender, ConnectionId, timestamp, SenderAuth, @@ -229,8 +232,9 @@ public abstract class ProcedureTxContextBase(Internal.TxContext inner) internal void Refresh(Internal.TxContext inner) => Inner = inner; + public Identity Sender() => Inner.Sender(); + public LocalBase Db => (LocalBase)Inner.Db; - public Identity Sender => Inner.Sender; public ConnectionId? ConnectionId => Inner.ConnectionId; public Timestamp Timestamp => Inner.Timestamp; public AuthCtx SenderAuth => Inner.SenderAuth; diff --git a/demo/Blackholio/server-csharp/Lib.cs b/demo/Blackholio/server-csharp/Lib.cs index 20e26cf99e6..542a7040037 100644 --- a/demo/Blackholio/server-csharp/Lib.cs +++ b/demo/Blackholio/server-csharp/Lib.cs @@ -138,7 +138,7 @@ public static void Init(ReducerContext ctx) [Reducer(ReducerKind.ClientConnected)] public static void Connect(ReducerContext ctx) { - var player = ctx.Db.logged_out_player.identity.Find(ctx.Sender); + var player = ctx.Db.logged_out_player.identity.Find(ctx.Sender()); if (player != null) { ctx.Db.player.Insert(player.Value); @@ -157,7 +157,7 @@ public static void Connect(ReducerContext ctx) { ctx.Db.player.Insert(new Player { - identity = ctx.Sender, + identity = ctx.Sender(), name = "", }); } @@ -166,7 +166,7 @@ public static void Connect(ReducerContext ctx) [Reducer(ReducerKind.ClientDisconnected)] public static void Disconnect(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); foreach (var circle in ctx.Db.circle.player_id.Filter(player.player_id)) { var entity = ctx.Db.entity.entity_id.Find(circle.entity_id) ?? throw new Exception("Could not find circle"); @@ -183,7 +183,7 @@ public static void Disconnect(ReducerContext ctx) public static void EnterGame(ReducerContext ctx, string name) { Log.Info($"Creating player with name {name}"); - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); player.name = name; ctx.Db.player.identity.Update(player); SpawnPlayerInitialCircle(ctx, player.player_id); @@ -192,7 +192,7 @@ public static void EnterGame(ReducerContext ctx, string name) [Reducer] public static void Respawn(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("No such player found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("No such player found"); SpawnPlayerInitialCircle(ctx, player.player_id); } @@ -200,7 +200,7 @@ public static void Respawn(ReducerContext ctx) [Reducer] public static void Suicide(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("No such player found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("No such player found"); foreach (var circle in ctx.Db.circle.player_id.Filter(player.player_id)) { @@ -246,7 +246,7 @@ public static Entity SpawnCircleAt(ReducerContext ctx, int player_id, int mass, [Reducer] public static void UpdatePlayerInput(ReducerContext ctx, DbVector2 direction) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); foreach (var c in ctx.Db.circle.player_id.Filter(player.player_id)) { var circle = c; @@ -449,7 +449,7 @@ public static void DestroyEntity(ReducerContext ctx, int entityId) [Reducer] public static void PlayerSplit(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Sender has no player"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Sender has no player"); List circles = ctx.Db.circle.player_id.Filter(player.player_id).ToList(); var circle_count = circles.Count; if (circle_count >= MAX_CIRCLES_PER_PLAYER) diff --git a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md index 63eff91f7b0..59ba40c581f 100644 --- a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md +++ b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md @@ -439,7 +439,7 @@ A view can be written in C# like so: [SpacetimeDB.View(Name = "MyPlayer", Public = true)] public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.Player.Identity.Find(ctx.Sender) as Player; + return ctx.Db.Player.Identity.Find(ctx.Sender()) as Player; } ``` diff --git a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md index 06ef0c039ce..f8dc277f38c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md +++ b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md @@ -322,7 +322,7 @@ public static void SetName(ReducerContext ctx, string name) { name = ValidateName(name); - if (ctx.Db.User.Identity.Find(ctx.Sender) is User user) + if (ctx.Db.User.Identity.Find(ctx.Sender()) is User user) { user.Name = name; ctx.Db.User.Identity.Update(user); @@ -411,7 +411,7 @@ public static void SendMessage(ReducerContext ctx, string text) ctx.Db.Message.Insert( new Message { - Sender = ctx.Sender, + Sender = ctx.Sender(), Text = text, Sent = ctx.Timestamp, } @@ -504,9 +504,9 @@ In `spacetimedb/Lib.cs`, add to the `Module` class: [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { - Log.Info($"Connect {ctx.Sender}"); + Log.Info($"Connect {ctx.Sender()}"); - if (ctx.Db.User.Identity.Find(ctx.Sender) is User user) + if (ctx.Db.User.Identity.Find(ctx.Sender()) is User user) { user.Online = true; ctx.Db.User.Identity.Update(user); @@ -517,7 +517,7 @@ public static void ClientConnected(ReducerContext ctx) new User { Name = null, - Identity = ctx.Sender, + Identity = ctx.Sender(), Online = true, } ); @@ -527,7 +527,7 @@ public static void ClientConnected(ReducerContext ctx) [Reducer(ReducerKind.ClientDisconnected)] public static void ClientDisconnected(ReducerContext ctx) { - if (ctx.Db.User.Identity.Find(ctx.Sender) is User user) + if (ctx.Db.User.Identity.Find(ctx.Sender()) is User user) { user.Online = false; ctx.Db.User.Identity.Update(user); 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 9f06001aa8a..d19945793c3 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 @@ -335,7 +335,7 @@ Add this function to the `Module` class in `Lib.cs`: [Reducer] public static void Debug(ReducerContext ctx) { - Log.Info($"This reducer was called by {ctx.Sender}"); + Log.Info($"This reducer was called by {ctx.Sender()}"); } ``` @@ -444,7 +444,7 @@ Next let's connect our client to our database. Let's start by modifying our `Deb [Reducer(ReducerKind.ClientConnected)] public static void Connect(ReducerContext ctx) { - Log.Info($"{ctx.Sender} just connected."); + Log.Info($"{ctx.Sender()} just connected."); } ``` 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 ab95e71294d..f1a0c99aa0d 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 @@ -359,7 +359,7 @@ Next, modify your `Connect` reducer and add a new `Disconnect` reducer below it: [Reducer(ReducerKind.ClientConnected)] public static void Connect(ReducerContext ctx) { - var player = ctx.Db.logged_out_player.identity.Find(ctx.Sender); + var player = ctx.Db.logged_out_player.identity.Find(ctx.Sender()); if (player != null) { ctx.Db.player.Insert(player.Value); @@ -369,7 +369,7 @@ public static void Connect(ReducerContext ctx) { ctx.Db.player.Insert(new Player { - identity = ctx.Sender, + identity = ctx.Sender(), name = "", }); } @@ -378,7 +378,7 @@ public static void Connect(ReducerContext ctx) [Reducer(ReducerKind.ClientDisconnected)] public static void Disconnect(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); ctx.Db.logged_out_player.Insert(player); ctx.Db.player.identity.Delete(player.identity); } @@ -457,7 +457,7 @@ const int START_PLAYER_MASS = 15; public static void EnterGame(ReducerContext ctx, string name) { Log.Info($"Creating player with name {name}"); - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); player.name = name; ctx.Db.player.identity.Update(player); SpawnPlayerInitialCircle(ctx, player.player_id); @@ -579,7 +579,7 @@ Let's also modify our `disconnect` reducer to remove the circles from the arena [Reducer(ReducerKind.ClientDisconnected)] public static void Disconnect(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); // Remove any circles from the arena foreach (var circle in ctx.Db.circle.player_id.Filter(player.player_id)) { diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md index 45ad7ee8ea8..2f7ae934851 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md @@ -49,7 +49,7 @@ Next, add the following reducer to the `Module` class of your `Lib.cs` file. [Reducer] public static void UpdatePlayerInput(ReducerContext ctx, DbVector2 direction) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); foreach (var c in ctx.Db.circle.player_id.Filter(player.player_id)) { var circle = c; @@ -60,7 +60,7 @@ public static void UpdatePlayerInput(ReducerContext ctx, DbVector2 direction) } ``` -This is a simple reducer that takes the movement input from the client and applies them to all circles that that player controls. Note that it is not possible for a player to move another player's circles using this reducer, because the `ctx.Sender` value is not set by the client. Instead `ctx.Sender` is set by SpacetimeDB after it has authenticated that sender. You can rest assured that the caller has been authenticated as that player by the time this reducer is called. +This is a simple reducer that takes the movement input from the client and applies them to all circles that that player controls. Note that it is not possible for a player to move another player's circles using this reducer, because the `ctx.Sender()` value is not set by the client. Instead `ctx.Sender()` is set by SpacetimeDB after it has authenticated that sender. You can rest assured that the caller has been authenticated as that player by the time this reducer is called. diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md index bde4659ba26..a97a65abec8 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md @@ -333,7 +333,7 @@ Add this function to the `Module` class in `Lib.cs`: [Reducer] public static void Debug(ReducerContext ctx) { - Log.Info($"This reducer was called by {ctx.Sender}"); + Log.Info($"This reducer was called by {ctx.Sender()}"); } ``` @@ -440,7 +440,7 @@ Next let's connect our client to our database. Let's start by modifying our `Deb [Reducer(ReducerKind.ClientConnected)] public static void Connect(ReducerContext ctx) { - Log.Info($"{ctx.Sender} just connected."); + Log.Info($"{ctx.Sender()} just connected."); } ``` diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md index 192c7d3ebc8..d3543050874 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md @@ -352,7 +352,7 @@ Next, modify your `Connect` reducer and add a new `Disconnect` reducer below it: [Reducer(ReducerKind.ClientConnected)] public static void Connect(ReducerContext ctx) { - var player = ctx.Db.logged_out_player.identity.Find(ctx.Sender); + var player = ctx.Db.logged_out_player.identity.Find(ctx.Sender()); if (player != null) { ctx.Db.player.Insert(player.Value); @@ -362,7 +362,7 @@ public static void Connect(ReducerContext ctx) { ctx.Db.player.Insert(new Player { - identity = ctx.Sender, + identity = ctx.Sender(), name = "", }); } @@ -371,7 +371,7 @@ public static void Connect(ReducerContext ctx) [Reducer(ReducerKind.ClientDisconnected)] public static void Disconnect(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); ctx.Db.logged_out_player.Insert(player); ctx.Db.player.identity.Delete(player.identity); } @@ -446,7 +446,7 @@ const int START_PLAYER_MASS = 15; public static void EnterGame(ReducerContext ctx, string name) { Log.Info($"Creating player with name {name}"); - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); player.name = name; ctx.Db.player.identity.Update(player); SpawnPlayerInitialCircle(ctx, player.player_id); @@ -566,7 +566,7 @@ Let's also modify our `disconnect` reducer to remove the circles from the arena [Reducer(ReducerKind.ClientDisconnected)] public static void Disconnect(ReducerContext ctx) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); // Remove any circles from the arena foreach (var circle in ctx.Db.circle.player_id.Filter(player.player_id)) { diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md index ae8eb620633..2acda18aac8 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md @@ -50,7 +50,7 @@ Next, add the following reducer to the `Module` class of your `Lib.cs` file. [Reducer] public static void UpdatePlayerInput(ReducerContext ctx, DbVector2 direction) { - var player = ctx.Db.player.identity.Find(ctx.Sender) ?? throw new Exception("Player not found"); + var player = ctx.Db.player.identity.Find(ctx.Sender()) ?? throw new Exception("Player not found"); foreach (var c in ctx.Db.circle.player_id.Filter(player.player_id)) { var circle = c; @@ -61,7 +61,7 @@ public static void UpdatePlayerInput(ReducerContext ctx, DbVector2 direction) } ``` -This is a simple reducer that takes the movement input from the client and applies them to all circles that that player controls. Note that it is not possible for a player to move another player's circles using this reducer, because the `ctx.Sender` value is not set by the client. Instead `ctx.Sender` is set by SpacetimeDB after it has authenticated that sender. You can rest assured that the caller has been authenticated as that player by the time this reducer is called. +This is a simple reducer that takes the movement input from the client and applies them to all circles that that player controls. Note that it is not possible for a player to move another player's circles using this reducer, because the `ctx.Sender()` value is not set by the client. Instead `ctx.Sender()` is set by SpacetimeDB after it has authenticated that sender. You can rest assured that the caller has been authenticated as that player by the time this reducer is called. Let's start by building out a simple math library to help us do collision calculations. Create a new `math.rs` file in the `blackholio/spacetimedb/src` directory and add the following contents. Let's also move the `DbVector2` type from `lib.rs` into this file. 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 b49afcd4c0b..da6b28f2e02 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 @@ -440,7 +440,7 @@ using SpacetimeDB; [SpacetimeDB.View(Public = true)] public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.Player.Identity.Find(ctx.Sender); + return ctx.Db.Player.Identity.Find(ctx.Sender()); } // Return multiple rows @@ -493,7 +493,7 @@ ctx.identity // Module's identity ```csharp ctx.Db // Database access -ctx.Sender // Identity of caller +ctx.Sender() // Identity of caller ctx.ConnectionId // ConnectionId? ctx.Timestamp // Timestamp ctx.Identity // Module's identity diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index 38f447125d4..d789845ab29 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -145,7 +145,7 @@ public static partial class Module public static void UpdateScore(ReducerContext ctx, uint newScore) { // Get the caller's identity - Identity caller = ctx.Sender; + Identity caller = ctx.Sender(); // Find and update their player record if (ctx.Db.Player.Identity.Find(caller) is Player player) @@ -262,7 +262,7 @@ public static partial class Module public static void SendReminder(ReducerContext ctx, ScheduledTask task) { // Only allow the scheduler (module identity) to call this - if (ctx.Sender != ctx.Identity) + if (ctx.Sender() != ctx.Identity) { throw new Exception("This reducer can only be called by the scheduler"); } diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md index 20f43bc8394..605ad09dd09 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md @@ -110,7 +110,7 @@ spacetimedb.clientConnected((ctx) => { [SpacetimeDB.Reducer(ReducerKind.ClientConnected)] public static void OnConnect(ReducerContext ctx) { - Log.Info($"Client connected: {ctx.Sender}"); + Log.Info($"Client connected: {ctx.Sender()}"); // ctx.ConnectionId is guaranteed to be non-null var connId = ctx.ConnectionId!.Value; @@ -119,7 +119,7 @@ public static void OnConnect(ReducerContext ctx) ctx.Db.Session.Insert(new Session { ConnectionId = connId, - Identity = ctx.Sender, + Identity = ctx.Sender(), ConnectedAt = ctx.Timestamp }); } @@ -182,7 +182,7 @@ spacetimedb.clientDisconnected((ctx) => { [SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)] public static void OnDisconnect(ReducerContext ctx) { - Log.Info($"Client disconnected: {ctx.Sender}"); + Log.Info($"Client disconnected: {ctx.Sender()}"); // ctx.ConnectionId is guaranteed to be non-null var connId = ctx.ConnectionId!.Value; diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md index 714f68d1110..661e5870853 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md @@ -61,7 +61,7 @@ Throw an exception: [SpacetimeDB.Reducer] public static void TransferCredits(ReducerContext ctx, ulong toUser, uint amount) { - var fromUser = ctx.Db.User.Id.Find(ctx.Sender); + var fromUser = ctx.Db.User.Id.Find(ctx.Sender()); if (fromUser == null) { throw new InvalidOperationException("User not found"); diff --git a/docs/docs/00200-core-concepts/00200-functions/00500-views.md b/docs/docs/00200-core-concepts/00200-functions/00500-views.md index 0603fa97dad..7b8c43ea1b7 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00500-views.md +++ b/docs/docs/00200-core-concepts/00200-functions/00500-views.md @@ -130,7 +130,7 @@ public static partial class Module [SpacetimeDB.View(Name = "MyPlayer", Public = true)] public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.Player.Identity.Find(ctx.Sender) as Player; + return ctx.Db.Player.Identity.Find(ctx.Sender()) as Player; } // Multiple rows: return a list @@ -283,7 +283,7 @@ spacetimedb.view( [SpacetimeDB.View(Name = "MyPlayer", Public = true)] public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.Player.Identity.Find(ctx.Sender); + return ctx.Db.Player.Identity.Find(ctx.Sender()); } ``` @@ -485,7 +485,7 @@ public partial class Module [SpacetimeDB.View(Name = "EntitiesInMyChunk", Public = true)] public static List EntitiesInMyChunk(ViewContext ctx) { - if (ctx.Db.Player.Identity.Find(ctx.Sender) is not Player player) + if (ctx.Db.Player.Identity.Find(ctx.Sender()) is not Player player) { return new List(); } diff --git a/docs/docs/00200-core-concepts/00300-tables.md b/docs/docs/00200-core-concepts/00300-tables.md index cb241bc1ae1..e6ca5262ff7 100644 --- a/docs/docs/00200-core-concepts/00300-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables.md @@ -344,7 +344,7 @@ ctx.Db.Player.Insert(new Player { /* ... */ }); ctx.Db.LoggedOutPlayer.Insert(new Player { /* ... */ }); // Move a row between tables -var player = ctx.Db.LoggedOutPlayer.Identity.Find(ctx.Sender); +var player = ctx.Db.LoggedOutPlayer.Identity.Find(ctx.Sender()); if (player != null) { ctx.Db.Player.Insert(player.Value); diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index 0a102b8377f..80b0eb5a19a 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -221,7 +221,7 @@ public static partial class Module ctx.Db.Document.Insert(new Document { Id = 0, // auto-increment - OwnerId = ctx.Sender, + OwnerId = ctx.Sender(), Filename = filename, MimeType = mimeType, SizeBytes = sizeBytes, diff --git a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md index 27ae27f34a6..3293e90e05f 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md +++ b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md @@ -372,8 +372,8 @@ public partial class Module public static List MyMessages(ViewContext ctx) { // Look up messages by index where caller is sender or recipient - var sent = ctx.Db.Message.Sender.Filter(ctx.Sender).ToList(); - var received = ctx.Db.Message.Recipient.Filter(ctx.Sender).ToList(); + var sent = ctx.Db.Message.Sender.Filter(ctx.Sender()).ToList(); + var received = ctx.Db.Message.Recipient.Filter(ctx.Sender()).ToList(); sent.AddRange(received); return sent; } @@ -505,7 +505,7 @@ public partial class Module public static PublicUserProfile? MyProfile(ViewContext ctx) { // Look up the caller's account by their identity (unique index) - if (ctx.Db.UserAccount.Identity.Find(ctx.Sender) is not UserAccount user) + if (ctx.Db.UserAccount.Identity.Find(ctx.Sender()) is not UserAccount user) { return null; } @@ -662,7 +662,7 @@ public partial class Module public static List MyTeam(ViewContext ctx) { // Find the caller's employee record by identity (unique index) - if (ctx.Db.Employee.Identity.Find(ctx.Sender) is not Employee me) + if (ctx.Db.Employee.Identity.Find(ctx.Sender()) is not Employee me) { return new List(); } diff --git a/docs/docs/00300-resources/00100-how-to/00300-logging.md b/docs/docs/00300-resources/00100-how-to/00300-logging.md index 1958b01634e..1b77fd7bcc7 100644 --- a/docs/docs/00300-resources/00100-how-to/00300-logging.md +++ b/docs/docs/00300-resources/00100-how-to/00300-logging.md @@ -69,7 +69,7 @@ public static partial class Module throw new ArgumentException("Value cannot be zero"); } - Log.Debug($"Debug information: ctx.Sender = {ctx.Sender}"); + Log.Debug($"Debug information: ctx.Sender() = {ctx.Sender()}"); } } ``` @@ -202,7 +202,7 @@ Include relevant context in your log messages: [SpacetimeDB.Reducer] public static void TransferCredits(ReducerContext ctx, ulong toUser, uint amount) { - Log.Info($"Credit transfer: from={ctx.Sender}, to={toUser}, amount={amount}"); + Log.Info($"Credit transfer: from={ctx.Sender()}, to={toUser}, amount={amount}"); // ... transfer logic } diff --git a/docs/static/llms.md b/docs/static/llms.md index 876a91b31ad..f35907bf85d 100644 --- a/docs/static/llms.md +++ b/docs/static/llms.md @@ -1449,7 +1449,7 @@ Reducers are the functions within your server module responsible for atomically - **Return Type:** Reducers should typically return `void`. Errors are signaled by throwing exceptions. - **Reducer Context:** The `ReducerContext` (`ctx`) provides access to: - `ctx.Db`: Handles for interacting with database tables. - - `ctx.Sender`: The `Identity` of the caller. + - `ctx.Sender()`: The `Identity` of the caller. - `ctx.Identity`: The `Identity` of the module itself. - `ctx.Timestamp`: The `Timestamp` of the invocation. - `ctx.ConnectionId`: The nullable `ConnectionId` of the caller. @@ -1478,7 +1478,7 @@ public static partial class Module [Reducer] public static void UpdatePlayerData(ReducerContext ctx, string? newName) { - var playerId = ctx.Sender; + var playerId = ctx.Sender(); // Find player by primary key var player = ctx.Db.player_state.PlayerId.Find(playerId); @@ -1521,10 +1521,10 @@ public static partial class Module if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Name cannot be empty."); } - Log.Info($"Attempting to register player: {name} ({ctx.Sender})"); + Log.Info($"Attempting to register player: {name} ({ctx.Sender()})"); // Check if player identity or name already exists - if (ctx.Db.player_state.PlayerId.Find(ctx.Sender) != null || ctx.Db.player_state.Name.Find(name) != null) + if (ctx.Db.player_state.PlayerId.Find(ctx.Sender()) != null || ctx.Db.player_state.Name.Find(name) != null) { throw new Exception("Player already registered or name taken."); } @@ -1532,7 +1532,7 @@ public static partial class Module // Create new player instance var newPlayer = new PlayerState { - PlayerId = ctx.Sender, + PlayerId = ctx.Sender(), Name = name, Health = 100, Level = 1, @@ -1541,14 +1541,14 @@ public static partial class Module // Insert the new player. This will throw on constraint violation. ctx.Db.player_state.Insert(newPlayer); - Log.Info($"Player registered successfully: {ctx.Sender}"); + Log.Info($"Player registered successfully: {ctx.Sender()}"); } // Example: Basic reducer showing deletion [Reducer] public static void DeleteMyItems(ReducerContext ctx) { - var ownerId = ctx.Sender; + var ownerId = ctx.Sender(); int deletedCount = 0; // Find items by owner (Requires an index on OwnerId for efficiency) @@ -1632,13 +1632,13 @@ These reducers cannot take arguments beyond `&ReducerContext`. // Example init reducer is shown in Scheduled Reducers section [Reducer(ReducerKind.ClientConnected)] public static void HandleConnect(ReducerContext ctx) { - Log.Info($"Client connected: {ctx.Sender}"); + Log.Info($"Client connected: {ctx.Sender()}"); // ... setup initial state for ctx.sender ... } [Reducer(ReducerKind.ClientDisconnected)] public static void HandleDisconnect(ReducerContext ctx) { - Log.Info($"Client disconnected: {ctx.Sender}"); + Log.Info($"Client disconnected: {ctx.Sender()}"); // ... cleanup state for ctx.sender ... } ``` @@ -1692,7 +1692,7 @@ public static partial class Module public static void SendMessage(ReducerContext ctx, SendMessageSchedule scheduleArgs) { // Security check is important! - if (!ctx.Sender.Equals(ctx.Identity)) + if (!ctx.Sender().Equals(ctx.Identity)) { throw new Exception("Reducer SendMessage may not be invoked by clients, only via scheduling."); } @@ -1740,12 +1740,12 @@ public static partial class Module - **Best-Effort Scheduling:** Scheduled reducers are called on a best-effort basis and may be slightly delayed in their execution when a database is under heavy load. -- **Restricting Access (Security):** Scheduled reducers are normal reducers and _can_ still be called directly by clients. If a scheduled reducer should _only_ be called by the scheduler, it is crucial to begin the reducer with a check comparing the caller's identity (`ctx.Sender`) to the module's own identity (`ctx.Identity`). +- **Restricting Access (Security):** Scheduled reducers are normal reducers and _can_ still be called directly by clients. If a scheduled reducer should _only_ be called by the scheduler, it is crucial to begin the reducer with a check comparing the caller's identity (`ctx.Sender()`) to the module's own identity (`ctx.Identity`). ```csharp [Reducer] // Assuming linked via [Table(Scheduled=...)] public static void MyScheduledTask(ReducerContext ctx, MyScheduleArgs args) { - if (!ctx.Sender.Equals(ctx.Identity)) + if (!ctx.Sender().Equals(ctx.Identity)) { throw new Exception("Reducer MyScheduledTask may not be invoked by clients, only via scheduling."); } @@ -1757,7 +1757,7 @@ public static partial class Module ``` :::info Scheduled Reducers and Connections -Scheduled reducer calls originate from the SpacetimeDB scheduler itself, not from an external client connection. Therefore, within a scheduled reducer, `ctx.Sender` will be the module's own identity, and `ctx.ConnectionId` will be `null`. +Scheduled reducer calls originate from the SpacetimeDB scheduler itself, not from an external client connection. Therefore, within a scheduled reducer, `ctx.Sender()` will be the module's own identity, and `ctx.ConnectionId` will be `null`. ::: ##### Error Handling: Exceptions @@ -1827,7 +1827,7 @@ public static partial class Module [SpacetimeDB.View(Name = "MyPlayer", Public = true)] public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.Player.Identity.Find(ctx.Sender); + return ctx.Db.Player.Identity.Find(ctx.Sender()); } // View that returns all players at a specific level (same for all callers) @@ -1858,7 +1858,7 @@ public static partial class Module Views use one of two context types: -- **`ViewContext`**: Provides access to the caller's `Identity` through `ctx.Sender`. Use this when the view depends on who is querying it (e.g., "get my player"). +- **`ViewContext`**: Provides access to the caller's `Identity` through `ctx.Sender()`. Use this when the view depends on who is querying it (e.g., "get my player"). - **`AnonymousViewContext`**: Does not provide caller information. Use this when the view produces the same results regardless of who queries it (e.g., "get top 10 players"). Both contexts provide read-only access to tables and indexes through `ctx.Db`. @@ -1887,7 +1887,7 @@ The query builder provides a fluent API for constructing type-safe SQL queries: public static Query MyMessages(ViewContext ctx) { return ctx.Db.Message - .Filter(msg => msg.Sender == ctx.Sender) + .Filter(msg => msg.Sender == ctx.Sender()) .Build(); } diff --git a/modules/module-test-cs/Lib.cs b/modules/module-test-cs/Lib.cs index a8d7d7a05ba..72b69649960 100644 --- a/modules/module-test-cs/Lib.cs +++ b/modules/module-test-cs/Lib.cs @@ -219,7 +219,7 @@ static partial class Module [View(Name = "my_player", Public = true)] public static Player? my_player(ViewContext ctx) { - return (Player?)ctx.Db.player.identity.Find(ctx.Sender); + return (Player?)ctx.Db.player.identity.Find(ctx.Sender()); } // This reducer is run at module initialization. @@ -278,7 +278,7 @@ public static void log_module_identity(ReducerContext ctx) public static void test(ReducerContext ctx, TestAlias arg, TestB arg2, TestC arg3, TestF arg4) { Log.Info("BEGIN"); - Log.Info($"sender: {ctx.Sender}"); + Log.Info($"sender: {ctx.Sender()}"); Log.Info($"timestamp: {ctx.Timestamp}"); Log.Info($"bar: {arg2.foo}"); @@ -462,7 +462,7 @@ public static void test_btree_index_args(ReducerContext ctx) [Reducer] public static void assert_caller_identity_is_module_identity(ReducerContext ctx) { - var caller = ctx.Sender; + var caller = ctx.Sender(); var owner = ctx.Identity; if (!caller.Equals(owner)) { diff --git a/modules/sdk-test-connect-disconnect-cs/Lib.cs b/modules/sdk-test-connect-disconnect-cs/Lib.cs index 79af81d8cc4..200457fa616 100644 --- a/modules/sdk-test-connect-disconnect-cs/Lib.cs +++ b/modules/sdk-test-connect-disconnect-cs/Lib.cs @@ -19,12 +19,12 @@ static partial class Module [SpacetimeDB.Reducer(ReducerKind.ClientConnected)] public static void identity_connected(ReducerContext ctx) { - ctx.Db.connected.Insert(new Connected { identity = ctx.Sender}); + ctx.Db.connected.Insert(new Connected { identity = ctx.Sender()}); } [SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)] public static void identity_disconnected(ReducerContext ctx) { - ctx.Db.disconnected.Insert(new Disconnected { identity = ctx.Sender}); + ctx.Db.disconnected.Insert(new Disconnected { identity = ctx.Sender()}); } } diff --git a/modules/sdk-test-cs/Lib.cs b/modules/sdk-test-cs/Lib.cs index 06ddacbb631..0387d77abae 100644 --- a/modules/sdk-test-cs/Lib.cs +++ b/modules/sdk-test-cs/Lib.cs @@ -1887,25 +1887,25 @@ public static void update_pk_simple_enum(ReducerContext ctx, SimpleEnum a, int d [SpacetimeDB.Reducer] public static void insert_caller_one_identity(ReducerContext ctx) { - ctx.Db.one_identity.Insert(new OneIdentity { i = ctx.Sender }); + ctx.Db.one_identity.Insert(new OneIdentity { i = ctx.Sender() }); } [SpacetimeDB.Reducer] public static void insert_caller_vec_identity(ReducerContext ctx) { - ctx.Db.vec_identity.Insert(new VecIdentity { i = new List { ctx.Sender } }); + ctx.Db.vec_identity.Insert(new VecIdentity { i = new List { ctx.Sender() } }); } [SpacetimeDB.Reducer] public static void insert_caller_unique_identity(ReducerContext ctx, int data) { - ctx.Db.unique_identity.Insert(new UniqueIdentity { i = ctx.Sender, data = data }); + ctx.Db.unique_identity.Insert(new UniqueIdentity { i = ctx.Sender(), data = data }); } [SpacetimeDB.Reducer] public static void insert_caller_pk_identity(ReducerContext ctx, int data) { - ctx.Db.pk_identity.Insert(new PkIdentity { i = ctx.Sender, data = data }); + ctx.Db.pk_identity.Insert(new PkIdentity { i = ctx.Sender(), data = data }); } [SpacetimeDB.Reducer] diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index 5adc5d46a30..cbdab48eae6 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -150,13 +150,13 @@ public partial struct NullStringNullable [SpacetimeDB.View(Name = "my_player", Public = true)] public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.player.Identity.Find(ctx.Sender); + return ctx.Db.player.Identity.Find(ctx.Sender()); } [SpacetimeDB.View(Name = "my_account", Public = true)] public static Account? MyAccount(ViewContext ctx) { - return ctx.Db.account.Identity.Find(ctx.Sender) as Account; + return ctx.Db.account.Identity.Find(ctx.Sender()) as Account; } [SpacetimeDB.View(Name = "my_account_missing", Public = true)] @@ -280,23 +280,23 @@ public static void InsertNullStringIntoNullable(ReducerContext ctx) [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { - Log.Info($"Connect {ctx.Sender}"); + Log.Info($"Connect {ctx.Sender()}"); - if (ctx.Db.player.Identity.Find(ctx.Sender) is Player player) + if (ctx.Db.player.Identity.Find(ctx.Sender()) is Player player) { // We are not logging player login status, so do nothing } else { // Lets setup a new player with a level of 1 - ctx.Db.player.Insert(new Player { Identity = ctx.Sender, Name = "NewPlayer" }); - var playerId = (ctx.Db.player.Identity.Find(ctx.Sender)!).Value.Id; + ctx.Db.player.Insert(new Player { Identity = ctx.Sender(), Name = "NewPlayer" }); + var playerId = (ctx.Db.player.Identity.Find(ctx.Sender())!).Value.Id; ctx.Db.player_level.Insert(new PlayerLevel { PlayerId = playerId, Level = 1 }); } - if (ctx.Db.account.Identity.Find(ctx.Sender) is null) + if (ctx.Db.account.Identity.Find(ctx.Sender()) is null) { - ctx.Db.account.Insert(new Account { Identity = ctx.Sender, Name = "Account" }); + ctx.Db.account.Insert(new Account { Identity = ctx.Sender(), Name = "Account" }); } if (ctx.Db.nullable_vec.Id.Find(1) is null) @@ -614,10 +614,10 @@ public static ReturnStruct TxContextCapabilities(ProcedureContext ctx) } // Test 3: Verify transaction context properties are accessible - var txSender = tx.Sender; + var txSender = tx.Sender(); var txTimestamp = tx.Timestamp; - if (txSender.Equals(ctx.Sender) == false) + if (txSender.Equals(ctx.Sender()) == false) { throw new InvalidOperationException("Transaction sender should match procedure sender"); } @@ -654,14 +654,14 @@ public static ReturnStruct AuthenticationCapabilities(ProcedureContext ctx) { // Test 1: Verify authentication context is accessible from procedure context var procAuth = ctx.SenderAuth; - var procSender = ctx.Sender; + var procSender = ctx.Sender(); var procConnectionId = ctx.ConnectionId; var result = ctx.WithTx(tx => { // Test 2: Verify authentication context is accessible from transaction context var txAuth = tx.SenderAuth; - var txSender = tx.Sender; + var txSender = tx.Sender(); var txConnectionId = tx.ConnectionId; // Test 3: Authentication contexts should be consistent diff --git a/templates/chat-console-cs/spacetimedb/Lib.cs b/templates/chat-console-cs/spacetimedb/Lib.cs index 3d82175859a..020bf6c072b 100644 --- a/templates/chat-console-cs/spacetimedb/Lib.cs +++ b/templates/chat-console-cs/spacetimedb/Lib.cs @@ -24,7 +24,7 @@ public static void SetName(ReducerContext ctx, string name) { name = ValidateName(name); - if (ctx.Db.User.Identity.Find(ctx.Sender) is User user) + if (ctx.Db.User.Identity.Find(ctx.Sender()) is User user) { user.Name = name; ctx.Db.User.Identity.Update(user); @@ -49,7 +49,7 @@ public static void SendMessage(ReducerContext ctx, string text) ctx.Db.Message.Insert( new Message { - Sender = ctx.Sender, + Sender = ctx.Sender(), Text = text, Sent = ctx.Timestamp, } @@ -69,9 +69,9 @@ private static string ValidateMessage(string text) [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { - Log.Info($"Connect {ctx.Sender}"); + Log.Info($"Connect {ctx.Sender()}"); - if (ctx.Db.User.Identity.Find(ctx.Sender) is User user) + if (ctx.Db.User.Identity.Find(ctx.Sender()) is User user) { // 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. @@ -86,7 +86,7 @@ public static void ClientConnected(ReducerContext ctx) new User { Name = null, - Identity = ctx.Sender, + Identity = ctx.Sender(), Online = true, } ); @@ -96,7 +96,7 @@ public static void ClientConnected(ReducerContext ctx) [Reducer(ReducerKind.ClientDisconnected)] public static void ClientDisconnected(ReducerContext ctx) { - if (ctx.Db.User.Identity.Find(ctx.Sender) is User user) + if (ctx.Db.User.Identity.Find(ctx.Sender()) is User user) { // This user should exist, so set `Online: false`. user.Online = false;