From 6acb07194663a1ba0a7aa8a8f7b4cd55f3283bab Mon Sep 17 00:00:00 2001 From: rekhoff Date: Fri, 21 Nov 2025 15:39:05 -0800 Subject: [PATCH 1/5] Initial addition of procedures in csharp module bindings --- crates/bindings-csharp/Codegen/Diag.cs | 21 ++ crates/bindings-csharp/Codegen/Module.cs | 196 +++++++++++++++++- crates/bindings-csharp/Runtime/Attrs.cs | 5 + .../Runtime/Internal/IProcedure.cs | 34 +++ .../Runtime/Internal/Module.cs | 61 +++++- crates/bindings-csharp/Runtime/bindings.c | 10 + 6 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 crates/bindings-csharp/Runtime/Internal/IProcedure.cs diff --git a/crates/bindings-csharp/Codegen/Diag.cs b/crates/bindings-csharp/Codegen/Diag.cs index 5b2a6fee9a0..8580bec1a64 100644 --- a/crates/bindings-csharp/Codegen/Diag.cs +++ b/crates/bindings-csharp/Codegen/Diag.cs @@ -79,6 +79,15 @@ IEnumerable fullNames $"Reducer method {method.Identifier} does not have a ReducerContext parameter.", method => method.ParameterList ); + + public static readonly ErrorDescriptor ProcedureContextParam = + new( + group, + "Procedures must have a first argument of type ProcedureContext", + method => + $"Procedure method {method.Identifier} does not have a ProcedureContext parameter.", + method => method.ParameterList + ); public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, @@ -91,6 +100,18 @@ string prefix $"Reducer method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.", ctx => ctx.method.Identifier ); + + public static readonly ErrorDescriptor<( + MethodDeclarationSyntax method, + string prefix + )> ProcedureReservedPrefix = + new( + group, + "Procedure method has a reserved name prefix", + ctx => + $"Procedure method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.", + ctx => ctx.method.Identifier + ); public static readonly UnusedErrorDescriptor IncompatibleTableSchedule = new(group); diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 23669c6023b..384712fb504 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1179,6 +1179,119 @@ public Scope.Extensions GenerateSchedule() } } +/// +/// Represents a procedure method declaration in a module. +/// +record ProcedureDeclaration +{ + public readonly string Name; + public readonly string FullName; + public readonly EquatableArray Args; + public readonly Scope Scope; + private readonly bool HasWrongSignature; + public readonly TypeUse ReturnType; + + public ProcedureDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag) + { + var methodSyntax = (MethodDeclarationSyntax)context.TargetNode; + var method = (IMethodSymbol)context.TargetSymbol; + var attr = context.Attributes.Single().ParseAs(); + + if ( + method.Parameters.FirstOrDefault()?.Type + is not INamedTypeSymbol { Name: "ProcedureContext" } + ) + { + diag.Report(ErrorDescriptor.ProcedureContextParam, methodSyntax); + HasWrongSignature = true; + } + + Name = method.Name; + if (Name.Length >= 2) + { + var prefix = Name[..2]; + if (prefix is "__" or "on" or "On") + { + diag.Report(ErrorDescriptor.ProcedureReservedPrefix, (methodSyntax, prefix)); + } + } + + ReturnType = TypeUse.Parse(method, method.ReturnType, diag); + + FullName = SymbolToName(method); + Args = new( + method + .Parameters.Skip(1) + .Select(p => new MemberDeclaration(p, p.Type, diag)) + .ToImmutableArray() + ); + Scope = new Scope(methodSyntax.Parent as MemberDeclarationSyntax); + } + + public string GenerateClass() + { + var invocationArgs = Args.Length == 0 + ? "" + : ", " + string.Join(", ", Args.Select(a => a.Name)); + + var invokeBody = HasWrongSignature + ? "throw new System.InvalidOperationException(\"Invalid procedure signature.\");" + : $$""" + var result = {{FullName}}((SpacetimeDB.ProcedureContext)ctx{{invocationArgs}}); + using var output = new MemoryStream(); + using var writer = new BinaryWriter(output); + new {{ReturnType.BSATNName}}().Write(writer, result); + return output.ToArray(); + """; + + return $$""" + class {{Name}} : SpacetimeDB.Internal.IProcedure { + {{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args)}} + + public SpacetimeDB.Internal.RawProcedureDefV9 MakeProcedureDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new( + nameof({{Name}}), + [{{MemberDeclaration.GenerateDefs(Args)}}], + new {{ReturnType.BSATNName}}().GetAlgebraicType(registrar) + ); + + public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) { + {{string.Join("\n", Args.Select(a => $"var {a.Name} = {a.Name}{TypeUse.BsatnFieldSuffix}.Read(reader);"))}} + {{invokeBody}} + } + } + """; + } + + public Scope.Extensions GenerateSchedule() + { + var extensions = new Scope.Extensions(Scope, FullName); + + // Mark the API as unstable. We use name `STDB_UNSTABLE` because: + // 1. It's a close equivalent of the `unstable` Cargo feature in Rust. + // 2. Our diagnostic IDs use either BSATN or STDB prefix depending on the package. + // 3. We don't expect to mark individual experimental features with numeric IDs, so we don't use the standard 1234 suffix. + extensions.Contents.Append( + $$""" + [System.Diagnostics.CodeAnalysis.Experimental("STDB_UNSTABLE")] + public static void VolatileNonatomicScheduleImmediate{{Name}}({{string.Join( + ", ", + Args.Select(a => $"{a.Type.Name} {a.Name}") + )}}) { + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream); + {{string.Join( + "\n", + Args.Select(a => $"new {a.Type.BSATNName}().Write(writer, {a.Name});") + )}} + SpacetimeDB.Internal.IProcedure.VolatileNonatomicScheduleImmediate(nameof({{Name}}), stream); + } + """ + ); + + return extensions; + } +} + record ClientVisibilityFilterDeclaration { public readonly string FullName; @@ -1358,6 +1471,31 @@ public void Initialize(IncrementalGeneratorInitializationContext context) r => r.FullName ); + var procedures = context + .SyntaxProvider.ForAttributeWithMetadataName( + fullyQualifiedMetadataName: typeof(ProcedureAttribute).FullName, + predicate: (node, ct) => true, // already covered by attribute restrictions + transform: (context, ct) => + context.ParseWithDiags(diag => new ProcedureDeclaration(context, diag)) + ) + .ReportDiagnostics(context) + .WithTrackingName("SpacetimeDB.Procedure.Parse"); + + procedures + .Select((p, ct) => p.GenerateSchedule()) + .WithTrackingName("SpacetimeDB.Procedure.GenerateSchedule") + .RegisterSourceOutputs(context); + + var addProcedures = CollectDistinct( + "Procedure", + context, + procedures + .Select((p, ct) => (p.Name, p.FullName, Class: p.GenerateClass())) + .WithTrackingName("SpacetimeDB.Procedure.GenerateClass"), + p => p.Name, + p => p.FullName + ); + var tableAccessors = CollectDistinct( "Table", context, @@ -1416,6 +1554,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput( tableAccessors .Combine(addReducers) + .Combine(addProcedures) .Combine(readOnlyAccessors) .Combine(views) .Combine(rlsFiltersArray) @@ -1423,11 +1562,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) (context, tuple) => { var ( - ((((tableAccessors, addReducers), readOnlyAccessors), views), rlsFilters), + (((((tableAccessors, addReducers), addProcedures), readOnlyAccessors), views), rlsFilters), columnDefaultValues ) = tuple; // Don't generate the FFI boilerplate if there are no tables or reducers. - if (tableAccessors.Array.IsEmpty && addReducers.Array.IsEmpty) + if (tableAccessors.Array.IsEmpty && addReducers.Array.IsEmpty && addProcedures.Array.IsEmpty) { return; } @@ -1465,6 +1604,25 @@ internal ReducerContext(Identity identity, ConnectionId? connectionId, Random ra } } + public sealed record ProcedureContext : Internal.IProcedureContext { + public readonly Identity Sender; + public readonly ConnectionId? ConnectionId; + public readonly Random Rng; + public readonly Timestamp Timestamp; + public readonly AuthCtx SenderAuth; + + // We need this property to be non-static for parity with client SDK. + public Identity Identity => Internal.IProcedureContext.GetIdentity(); + + internal ProcedureContext(Identity identity, ConnectionId? connectionId, Random random, Timestamp time) { + Sender = identity; + ConnectionId = connectionId; + Rng = random; + Timestamp = time; + SenderAuth = AuthCtx.BuildFromSystemTables(connectionId, identity); + } + } + public sealed record ViewContext : DbContext, Internal.IViewContext { public Identity Sender { get; } @@ -1512,6 +1670,8 @@ public sealed partial class LocalReadOnly { static class ModuleRegistration { {{string.Join("\n", addReducers.Select(r => r.Class))}} + + {{string.Join("\n", addProcedures.Select(r => r.Class))}} public static List ToListOrEmpty(T? value) where T : struct => value is null ? new List() : new List { value.Value }; @@ -1528,6 +1688,7 @@ public static void Main() { SpacetimeDB.Internal.Module.SetReducerContextConstructor((identity, connectionId, random, time) => new SpacetimeDB.ReducerContext(identity, connectionId, random, time)); SpacetimeDB.Internal.Module.SetViewContextConstructor(identity => new SpacetimeDB.ViewContext(identity, new SpacetimeDB.Internal.LocalReadOnly())); SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor(() => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly())); + SpacetimeDB.Internal.Module.SetProcedureContextConstructor((identity, connectionId, random, time) => new SpacetimeDB.ProcedureContext(identity, connectionId, random, time)); var __memoryStream = new MemoryStream(); var __writer = new BinaryWriter(__memoryStream); @@ -1537,6 +1698,12 @@ public static void Main() { $"SpacetimeDB.Internal.Module.RegisterReducer<{r.Name}>();" ) )}} + {{string.Join( + "\n", + addProcedures.Select(r => + $"SpacetimeDB.Internal.Module.RegisterProcedure<{r.Name}>();" + ) + )}} // IMPORTANT: The order in which we register views matters. // It must correspond to the order in which we call `GenerateDispatcherClass`. @@ -1601,6 +1768,31 @@ SpacetimeDB.Internal.BytesSink error args, error ); + + [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] + public static SpacetimeDB.Internal.Errno __call_procedure__( + uint id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink result_sink + ) => SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ); [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( diff --git a/crates/bindings-csharp/Runtime/Attrs.cs b/crates/bindings-csharp/Runtime/Attrs.cs index a60084150b8..8552739fe58 100644 --- a/crates/bindings-csharp/Runtime/Attrs.cs +++ b/crates/bindings-csharp/Runtime/Attrs.cs @@ -184,4 +184,9 @@ public sealed class ReducerAttribute(ReducerKind kind = ReducerKind.UserDefined) { public ReducerKind Kind => kind; } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class ProcedureAttribute() : Attribute + { + } } diff --git a/crates/bindings-csharp/Runtime/Internal/IProcedure.cs b/crates/bindings-csharp/Runtime/Internal/IProcedure.cs new file mode 100644 index 00000000000..ba7e6f13540 --- /dev/null +++ b/crates/bindings-csharp/Runtime/Internal/IProcedure.cs @@ -0,0 +1,34 @@ +namespace SpacetimeDB.Internal; + +using System.Text; +using SpacetimeDB.BSATN; + +public interface IProcedureContext +{ + public static Identity GetIdentity() + { + FFI.identity(out var identity); + return identity; + } +} + +public interface IProcedure +{ + RawProcedureDefV9 MakeProcedureDef(ITypeRegistrar registrar); + + // Return the serialized payload that should be sent to the host. + byte[] Invoke(BinaryReader reader, IProcedureContext ctx); + + public static void VolatileNonatomicScheduleImmediate(string name, MemoryStream args) + { + var name_bytes = Encoding.UTF8.GetBytes(name); + var args_bytes = args.ToArray(); + + FFI.volatile_nonatomic_schedule_immediate( + name_bytes, + (uint)name_bytes.Length, + args_bytes, + (uint)args_bytes.Length + ); + } +} diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index 849b2510fff..bcd5bdfacfa 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -35,12 +35,11 @@ internal AlgebraicType.Ref RegisterType(Func Reducers.Add(reducer); + internal void RegisterProcedure(RawProcedureDefV9 procedure) => MiscExports.Add(new RawMiscModuleExportV9.Procedure(procedure)); + internal void RegisterTable(RawTableDefV9 table) => Tables.Add(table); - internal void RegisterView(RawViewDefV9 view) - { - MiscExports.Add(new RawMiscModuleExportV9.View(view)); - } + internal void RegisterView(RawViewDefV9 view) => MiscExports.Add(new RawMiscModuleExportV9.View(view)); internal void RegisterRowLevelSecurity(RawRowLevelSecurityDefV9 rls) => RowLevelSecurity.Add(rls); @@ -60,6 +59,7 @@ public static class Module { private static readonly RawModuleDefV9 moduleDef = new(); private static readonly List reducers = []; + private static readonly List procedures = []; private static readonly List> viewDefs = []; private static readonly List viewDispatchers = []; private static readonly List anonymousViewDispatchers = []; @@ -73,11 +73,23 @@ private static Func< >? newReducerContext = null; private static Func? newViewContext = null; private static Func? newAnonymousViewContext = null; + + private static Func< + Identity, + ConnectionId?, + Random, + Timestamp, + IProcedureContext + >? newProcedureContext = null; public static void SetReducerContextConstructor( Func ctor ) => newReducerContext = ctor; + public static void SetProcedureContextConstructor( + Func ctor + ) => newProcedureContext = ctor; + public static void SetViewContextConstructor(Func ctor) => newViewContext = ctor; @@ -122,6 +134,14 @@ public static void RegisterReducer() reducers.Add(reducer); moduleDef.RegisterReducer(reducer.MakeReducerDef(typeRegistrar)); } + + public static void RegisterProcedure

() + where P : IProcedure, new() + { + var procedure = new P(); + procedures.Add(procedure); + moduleDef.RegisterProcedure(procedure.MakeProcedureDef(typeRegistrar)); + } public static void RegisterTable() where T : IStructuralReadWrite, new() @@ -296,6 +316,39 @@ BytesSink error return Errno.HOST_CALL_FAILURE; } } + + public static Errno __call_procedure__( + uint id, + ulong sender_0, ulong sender_1, ulong sender_2, ulong sender_3, + ulong conn_id_0, ulong conn_id_1, + Timestamp timestamp, + BytesSource args, + BytesSink resultSink + ) + { + try { + var sender = Identity.From(MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray()); + var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray()); + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); + + var ctx = newProcedureContext!(sender, connectionId, random, time); + + using var stream = new MemoryStream(args.Consume()); + using var reader = new BinaryReader(stream); + var bytes = procedures[(int)id].Invoke(reader, ctx); + if (stream.Position != stream.Length) + { + throw new Exception("Unrecognised extra bytes in the procedure arguments"); + } + resultSink.Write(bytes); + return Errno.OK; + } catch (Exception e) { + var errorBytes = System.Text.Encoding.UTF8.GetBytes(e.ToString()); + resultSink.Write(errorBytes); + return Errno.HOST_CALL_FAILURE; + } + } public static Errno __call_view__( uint id, diff --git a/crates/bindings-csharp/Runtime/bindings.c b/crates/bindings-csharp/Runtime/bindings.c index f256c03bd2e..be12f438959 100644 --- a/crates/bindings-csharp/Runtime/bindings.c +++ b/crates/bindings-csharp/Runtime/bindings.c @@ -160,6 +160,16 @@ EXPORT(int16_t, __call_reducer__, &conn_id_0, &conn_id_1, ×tamp, &args, &error); +EXPORT(int16_t, __call_procedure__, + (uint32_t id, + uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3, + uint64_t conn_id_0, uint64_t conn_id_1, + uint64_t timestamp, BytesSource args, BytesSink result_sink), + &id, + &sender_0, &sender_1, &sender_2, &sender_3, + &conn_id_0, &conn_id_1, + ×tamp, &args, &result_sink); + EXPORT(int16_t, __call_view__, (uint32_t id, uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3, From 158f7056c31fdc2131ef70995fba9d03cc414301 Mon Sep 17 00:00:00 2001 From: rekhoff Date: Mon, 24 Nov 2025 12:40:50 -0800 Subject: [PATCH 2/5] Adds a Procedure test to the base regression test --- .../examples~/regression-tests/server/Lib.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index 303f293d82c..bf24b92f5d4 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -3,6 +3,37 @@ using SpacetimeDB; +[SpacetimeDB.Type] +public partial class ReturnStruct +{ + public uint A; + public string B; + + public ReturnStruct(uint a, string b) + { + A = a; + B = b; + } + + public ReturnStruct() + { + A = 0; + B = string.Empty; + } +} + +[SpacetimeDB.Type] +public partial record ReturnEnum : SpacetimeDB.TaggedEnum<( + uint A, + string B + )>; + +[SpacetimeDB.Table(Name = "my_table", Public = true)] +public partial class MyTable +{ + public ReturnStruct Field { get; set; } = new(0, string.Empty); +} + public static partial class Module { [SpacetimeDB.Table(Name = "ExampleData", Public = true)] @@ -111,4 +142,34 @@ public static void ClientConnected(ReducerContext ctx) ctx.Db.PlayerLevel.Insert(new PlayerLevel { PlayerId = playerId, Level = 1 }); } } + + [SpacetimeDB.Procedure] + public static uint ReturnPrimitive(ProcedureContext ctx, uint lhs, uint rhs) + { + return lhs + rhs; + } + + [SpacetimeDB.Procedure] + public static ReturnStruct ReturnStructProcedure(ProcedureContext ctx, uint a, string b) + { + return new ReturnStruct(a, b); + } + + [SpacetimeDB.Procedure] + public static ReturnEnum ReturnEnumA(ProcedureContext ctx, uint a) + { + return new ReturnEnum.A(a); + } + + [SpacetimeDB.Procedure] + public static ReturnEnum ReturnEnumB(ProcedureContext ctx, string b) + { + return new ReturnEnum.B(b); + } + + [SpacetimeDB.Procedure] + public static SpacetimeDB.Unit WillPanic(ProcedureContext ctx) + { + throw new InvalidOperationException("This procedure is expected to panic"); + } } From 41cffe6df04f4ade35ec398cc5aef48a96d880e9 Mon Sep 17 00:00:00 2001 From: rekhoff Date: Mon, 24 Nov 2025 12:41:47 -0800 Subject: [PATCH 3/5] Updates formatting and snapshots --- .../diag/snapshots/Module#FFI.verified.cs | 56 ++++++++++++++++++ .../diag/snapshots/Module.verified.txt | 34 +++++------ .../server/snapshots/Module#FFI.verified.cs | 56 ++++++++++++++++++ crates/bindings-csharp/Codegen/Diag.cs | 6 +- crates/bindings-csharp/Codegen/Module.cs | 58 +++++++++++-------- crates/bindings-csharp/Runtime/Attrs.cs | 6 +- .../Runtime/Internal/Module.cs | 35 +++++++---- 7 files changed, 193 insertions(+), 58 deletions(-) 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 a2916cd3f00..57dbd88b5d1 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 @@ -38,6 +38,32 @@ Timestamp time } } + public sealed record ProcedureContext : Internal.IProcedureContext + { + public readonly Identity Sender; + public readonly ConnectionId? ConnectionId; + public readonly Random Rng; + public readonly Timestamp Timestamp; + public readonly AuthCtx SenderAuth; + + // We need this property to be non-static for parity with client SDK. + public Identity Identity => Internal.IProcedureContext.GetIdentity(); + + internal ProcedureContext( + Identity identity, + ConnectionId? connectionId, + Random random, + Timestamp time + ) + { + Sender = identity; + ConnectionId = connectionId; + Rng = random; + Timestamp = time; + SenderAuth = AuthCtx.BuildFromSystemTables(connectionId, identity); + } + } + public sealed record ViewContext : DbContext, Internal.IViewContext { public Identity Sender { get; } @@ -1852,6 +1878,10 @@ public static void Main() SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor( () => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly()) ); + SpacetimeDB.Internal.Module.SetProcedureContextConstructor( + (identity, connectionId, random, time) => + new SpacetimeDB.ProcedureContext(identity, connectionId, random, time) + ); var __memoryStream = new MemoryStream(); var __writer = new BinaryWriter(__memoryStream); @@ -2167,6 +2197,32 @@ SpacetimeDB.Internal.BytesSink error error ); + [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] + public static SpacetimeDB.Internal.Errno __call_procedure__( + uint id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink result_sink + ) => + SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ); + [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( uint id, diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt index 2f1f9e2b43a..05d99bddf5e 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt @@ -94,7 +94,7 @@ public partial record TestTableTaggedEnum : SpacetimeDB.TaggedEnum<(int X, int Y Message: Index attribute on a field applies directly to that field, so it doesn't accept the Columns parameter., Severity: Error, Descriptor: { - Id: STDB0013, + Id: STDB0015, Title: Index attribute on a field must not specify Columns, MessageFormat: Index attribute on a field applies directly to that field, so it doesn't accept the Columns parameter., Category: SpacetimeDB, @@ -145,7 +145,7 @@ public partial struct TestIndexIssues Message: Could not find the specified column UnknownColumn in TestIndexIssues., Severity: Error, Descriptor: { - Id: STDB0014, + Id: STDB0016, Title: Unknown column, MessageFormat: Could not find the specified column {0} in {1}., Category: SpacetimeDB, @@ -170,7 +170,7 @@ public partial struct TestIndexIssues Message: TestScheduleWithoutPrimaryKey is a scheduled table but doesn't have a primary key of type `ulong`., Severity: Error, Descriptor: { - Id: STDB0012, + Id: STDB0014, Title: Invalid scheduled table declaration, MessageFormat: {0}, Category: SpacetimeDB, @@ -195,7 +195,7 @@ public partial struct TestIndexIssues Message: TestScheduleWithWrongPrimaryKeyType is a scheduled table but doesn't have a primary key of type `ulong`., Severity: Error, Descriptor: { - Id: STDB0012, + Id: STDB0014, Title: Invalid scheduled table declaration, MessageFormat: {0}, Category: SpacetimeDB, @@ -212,7 +212,7 @@ public partial struct TestIndexIssues Message: Could not find the specified column ScheduledAt in TestScheduleIssues., Severity: Error, Descriptor: { - Id: STDB0014, + Id: STDB0016, Title: Unknown column, MessageFormat: Could not find the specified column {0} in {1}., Category: SpacetimeDB, @@ -237,7 +237,7 @@ public partial struct TestIndexIssues Message: TestScheduleWithWrongScheduleAtType is a scheduled table but doesn't have a primary key of type `ulong`., Severity: Error, Descriptor: { - Id: STDB0012, + Id: STDB0014, Title: Invalid scheduled table declaration, MessageFormat: {0}, Category: SpacetimeDB, @@ -262,7 +262,7 @@ public partial struct TestScheduleIssues Message: Could not find the specified column MissingField in TestScheduleIssues., Severity: Error, Descriptor: { - Id: STDB0014, + Id: STDB0016, Title: Unknown column, MessageFormat: Could not find the specified column {0} in {1}., Category: SpacetimeDB, @@ -287,7 +287,7 @@ public partial struct TestScheduleIssues Message: View 'ViewDefNoPublic' must have Public = true. Views are always public in SpacetimeDB., Severity: Error, Descriptor: { - Id: STDB0023, + Id: STDB0025, Title: Views must be public, MessageFormat: View '{0}' must have Public = true. Views are always public in SpacetimeDB., Category: SpacetimeDB, @@ -304,7 +304,7 @@ public partial struct TestScheduleIssues Message: View method ViewDefNoContext must have a first parameter of type ViewContext or AnonymousViewContext., Severity: Error, Descriptor: { - Id: STDB0020, + Id: STDB0022, Title: Views must start with ViewContext or AnonymousViewContext, MessageFormat: View method {0} must have a first parameter of type ViewContext or AnonymousViewContext., Category: SpacetimeDB, @@ -321,7 +321,7 @@ public partial struct TestScheduleIssues Message: View method ViewDefWrongContext must have a first parameter of type ViewContext or AnonymousViewContext., Severity: Error, Descriptor: { - Id: STDB0020, + Id: STDB0022, Title: Views must start with ViewContext or AnonymousViewContext, MessageFormat: View method {0} must have a first parameter of type ViewContext or AnonymousViewContext., Category: SpacetimeDB, @@ -346,7 +346,7 @@ public partial struct TestScheduleIssues Message: View 'ViewDefWrongReturn' must return Vec or Option., Severity: Error, Descriptor: { - Id: STDB0022, + Id: STDB0024, Title: Views must return Vec or Option, MessageFormat: View '{0}' must return Vec or Option., Category: SpacetimeDB, @@ -397,7 +397,7 @@ public partial struct TestScheduleIssues Message: Reducer method OnReducerWithReservedPrefix starts with 'On', which is a reserved prefix., Severity: Error, Descriptor: { - Id: STDB0009, + Id: STDB0010, Title: Reducer method has a reserved name prefix, MessageFormat: Reducer method {0} starts with '{1}', which is a reserved prefix., Category: SpacetimeDB, @@ -414,7 +414,7 @@ public partial struct TestScheduleIssues Message: Reducer method __ReducerWithReservedPrefix starts with '__', which is a reserved prefix., Severity: Error, Descriptor: { - Id: STDB0009, + Id: STDB0010, Title: Reducer method has a reserved name prefix, MessageFormat: Reducer method {0} starts with '{1}', which is a reserved prefix., Category: SpacetimeDB, @@ -427,7 +427,7 @@ public partial struct TestScheduleIssues Message: Several reducers are assigned to the same lifecycle kind Init: Reducers.TestDuplicateReducerKind1, Reducers.TestDuplicateReducerKind2, Severity: Error, Descriptor: { - Id: STDB0011, + Id: STDB0013, Title: Multiple reducers of the same kind, MessageFormat: Several reducers are assigned to the same lifecycle kind {0}: {1}, Category: SpacetimeDB, @@ -483,7 +483,7 @@ public partial struct TestScheduleIssues Message: Field MY_FILTER is marked as [ClientVisibilityFilter] but it is not public static readonly, Severity: Error, Descriptor: { - Id: STDB0016, + Id: STDB0018, Title: ClientVisibilityFilters must be public static readonly, MessageFormat: Field {0} is marked as [ClientVisibilityFilter] but it is not public static readonly, Category: SpacetimeDB, @@ -500,7 +500,7 @@ public partial struct TestScheduleIssues Message: Field MY_SECOND_FILTER is marked as [ClientVisibilityFilter] but it is not public static readonly, Severity: Error, Descriptor: { - Id: STDB0016, + Id: STDB0018, Title: ClientVisibilityFilters must be public static readonly, MessageFormat: Field {0} is marked as [ClientVisibilityFilter] but it is not public static readonly, Category: SpacetimeDB, @@ -517,7 +517,7 @@ public partial struct TestScheduleIssues Message: Field MY_THIRD_FILTER is marked as ClientVisibilityFilter but it has type string which is not SpacetimeDB.Filter, Severity: Error, Descriptor: { - Id: STDB0015, + Id: STDB0017, Title: ClientVisibilityFilters must be Filters, MessageFormat: Field {0} is marked as ClientVisibilityFilter but it has type {1} which is not SpacetimeDB.Filter, Category: SpacetimeDB, 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 f0d41da28d4..baac0b23f04 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 @@ -38,6 +38,32 @@ Timestamp time } } + public sealed record ProcedureContext : Internal.IProcedureContext + { + public readonly Identity Sender; + public readonly ConnectionId? ConnectionId; + public readonly Random Rng; + public readonly Timestamp Timestamp; + public readonly AuthCtx SenderAuth; + + // We need this property to be non-static for parity with client SDK. + public Identity Identity => Internal.IProcedureContext.GetIdentity(); + + internal ProcedureContext( + Identity identity, + ConnectionId? connectionId, + Random random, + Timestamp time + ) + { + Sender = identity; + ConnectionId = connectionId; + Rng = random; + Timestamp = time; + SenderAuth = AuthCtx.BuildFromSystemTables(connectionId, identity); + } + } + public sealed record ViewContext : DbContext, Internal.IViewContext { public Identity Sender { get; } @@ -1646,6 +1672,10 @@ public static void Main() SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor( () => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly()) ); + SpacetimeDB.Internal.Module.SetProcedureContextConstructor( + (identity, connectionId, random, time) => + new SpacetimeDB.ProcedureContext(identity, connectionId, random, time) + ); var __memoryStream = new MemoryStream(); var __writer = new BinaryWriter(__memoryStream); @@ -1731,6 +1761,32 @@ SpacetimeDB.Internal.BytesSink error error ); + [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] + public static SpacetimeDB.Internal.Errno __call_procedure__( + uint id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink result_sink + ) => + SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ); + [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( uint id, diff --git a/crates/bindings-csharp/Codegen/Diag.cs b/crates/bindings-csharp/Codegen/Diag.cs index 8580bec1a64..6b87a4028b0 100644 --- a/crates/bindings-csharp/Codegen/Diag.cs +++ b/crates/bindings-csharp/Codegen/Diag.cs @@ -79,7 +79,7 @@ IEnumerable fullNames $"Reducer method {method.Identifier} does not have a ReducerContext parameter.", method => method.ParameterList ); - + public static readonly ErrorDescriptor ProcedureContextParam = new( group, @@ -100,11 +100,11 @@ string prefix $"Reducer method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.", ctx => ctx.method.Identifier ); - + public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, string prefix - )> ProcedureReservedPrefix = + )> ProcedureReservedPrefix = new( group, "Procedure method has a reserved name prefix", diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 384712fb504..9988f7b027c 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1215,9 +1215,9 @@ public ProcedureDeclaration(GeneratorAttributeSyntaxContext context, DiagReporte diag.Report(ErrorDescriptor.ProcedureReservedPrefix, (methodSyntax, prefix)); } } - + ReturnType = TypeUse.Parse(method, method.ReturnType, diag); - + FullName = SymbolToName(method); Args = new( method @@ -1230,9 +1230,8 @@ public ProcedureDeclaration(GeneratorAttributeSyntaxContext context, DiagReporte public string GenerateClass() { - var invocationArgs = Args.Length == 0 - ? "" - : ", " + string.Join(", ", Args.Select(a => a.Name)); + var invocationArgs = + Args.Length == 0 ? "" : ", " + string.Join(", ", Args.Select(a => a.Name)); var invokeBody = HasWrongSignature ? "throw new System.InvalidOperationException(\"Invalid procedure signature.\");" @@ -1245,21 +1244,24 @@ public string GenerateClass() """; return $$""" - class {{Name}} : SpacetimeDB.Internal.IProcedure { - {{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args)}} - - public SpacetimeDB.Internal.RawProcedureDefV9 MakeProcedureDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new( - nameof({{Name}}), - [{{MemberDeclaration.GenerateDefs(Args)}}], - new {{ReturnType.BSATNName}}().GetAlgebraicType(registrar) - ); - - public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) { - {{string.Join("\n", Args.Select(a => $"var {a.Name} = {a.Name}{TypeUse.BsatnFieldSuffix}.Read(reader);"))}} - {{invokeBody}} - } - } - """; + class {{Name}} : SpacetimeDB.Internal.IProcedure { + {{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args)}} + + public SpacetimeDB.Internal.RawProcedureDefV9 MakeProcedureDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new( + nameof({{Name}}), + [{{MemberDeclaration.GenerateDefs(Args)}}], + new {{ReturnType.BSATNName}}().GetAlgebraicType(registrar) + ); + + public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) { + {{string.Join( + "\n", + Args.Select(a => $"var {a.Name} = {a.Name}{TypeUse.BsatnFieldSuffix}.Read(reader);") + )}} + {{invokeBody}} + } + } + """; } public Scope.Extensions GenerateSchedule() @@ -1485,7 +1487,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Select((p, ct) => p.GenerateSchedule()) .WithTrackingName("SpacetimeDB.Procedure.GenerateSchedule") .RegisterSourceOutputs(context); - + var addProcedures = CollectDistinct( "Procedure", context, @@ -1562,11 +1564,21 @@ public void Initialize(IncrementalGeneratorInitializationContext context) (context, tuple) => { var ( - (((((tableAccessors, addReducers), addProcedures), readOnlyAccessors), views), rlsFilters), + ( + ( + (((tableAccessors, addReducers), addProcedures), readOnlyAccessors), + views + ), + rlsFilters + ), columnDefaultValues ) = tuple; // Don't generate the FFI boilerplate if there are no tables or reducers. - if (tableAccessors.Array.IsEmpty && addReducers.Array.IsEmpty && addProcedures.Array.IsEmpty) + if ( + tableAccessors.Array.IsEmpty + && addReducers.Array.IsEmpty + && addProcedures.Array.IsEmpty + ) { return; } diff --git a/crates/bindings-csharp/Runtime/Attrs.cs b/crates/bindings-csharp/Runtime/Attrs.cs index 8552739fe58..8fb02057f7c 100644 --- a/crates/bindings-csharp/Runtime/Attrs.cs +++ b/crates/bindings-csharp/Runtime/Attrs.cs @@ -184,9 +184,7 @@ public sealed class ReducerAttribute(ReducerKind kind = ReducerKind.UserDefined) { public ReducerKind Kind => kind; } - + [AttributeUsage(AttributeTargets.Method, Inherited = false)] - public sealed class ProcedureAttribute() : Attribute - { - } + public sealed class ProcedureAttribute() : Attribute { } } diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index bcd5bdfacfa..46157d9308b 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -35,11 +35,13 @@ internal AlgebraicType.Ref RegisterType(Func Reducers.Add(reducer); - internal void RegisterProcedure(RawProcedureDefV9 procedure) => MiscExports.Add(new RawMiscModuleExportV9.Procedure(procedure)); + internal void RegisterProcedure(RawProcedureDefV9 procedure) => + MiscExports.Add(new RawMiscModuleExportV9.Procedure(procedure)); internal void RegisterTable(RawTableDefV9 table) => Tables.Add(table); - internal void RegisterView(RawViewDefV9 view) => MiscExports.Add(new RawMiscModuleExportV9.View(view)); + internal void RegisterView(RawViewDefV9 view) => + MiscExports.Add(new RawMiscModuleExportV9.View(view)); internal void RegisterRowLevelSecurity(RawRowLevelSecurityDefV9 rls) => RowLevelSecurity.Add(rls); @@ -73,7 +75,7 @@ private static Func< >? newReducerContext = null; private static Func? newViewContext = null; private static Func? newAnonymousViewContext = null; - + private static Func< Identity, ConnectionId?, @@ -134,7 +136,7 @@ public static void RegisterReducer() reducers.Add(reducer); moduleDef.RegisterReducer(reducer.MakeReducerDef(typeRegistrar)); } - + public static void RegisterProcedure

() where P : IProcedure, new() { @@ -316,19 +318,28 @@ BytesSink error return Errno.HOST_CALL_FAILURE; } } - + public static Errno __call_procedure__( uint id, - ulong sender_0, ulong sender_1, ulong sender_2, ulong sender_3, - ulong conn_id_0, ulong conn_id_1, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, Timestamp timestamp, BytesSource args, BytesSink resultSink ) { - try { - var sender = Identity.From(MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray()); - var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray()); + try + { + var sender = Identity.From( + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() + ); + var connectionId = ConnectionId.From( + MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray() + ); var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); var time = timestamp.ToStd(); @@ -343,7 +354,9 @@ BytesSink resultSink } resultSink.Write(bytes); return Errno.OK; - } catch (Exception e) { + } + catch (Exception e) + { var errorBytes = System.Text.Encoding.UTF8.GetBytes(e.ToString()); resultSink.Write(errorBytes); return Errno.HOST_CALL_FAILURE; From 592a6f25bc479d169fb292515ea49622e7025e76 Mon Sep 17 00:00:00 2001 From: rekhoff Date: Mon, 24 Nov 2025 12:51:58 -0800 Subject: [PATCH 4/5] Fix a C# formatting error --- sdks/csharp/examples~/regression-tests/server/Lib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index bf24b92f5d4..b6f0294a454 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -142,7 +142,7 @@ public static void ClientConnected(ReducerContext ctx) ctx.Db.PlayerLevel.Insert(new PlayerLevel { PlayerId = playerId, Level = 1 }); } } - + [SpacetimeDB.Procedure] public static uint ReturnPrimitive(ProcedureContext ctx, uint lhs, uint rhs) { From e17adcbff5587e2d254a7ede124425b5d33d06ee Mon Sep 17 00:00:00 2001 From: rekhoff Date: Mon, 24 Nov 2025 13:26:21 -0800 Subject: [PATCH 5/5] Regenerate regression-tests client module_bindings --- .../Procedures/ReturnEnumA.g.cs | 77 +++++++++++++++++ .../Procedures/ReturnEnumB.g.cs | 78 +++++++++++++++++ .../Procedures/ReturnPrimitive.g.cs | 82 ++++++++++++++++++ .../Procedures/ReturnStructProcedure.g.cs | 84 +++++++++++++++++++ .../module_bindings/Procedures/WillPanic.g.cs | 64 ++++++++++++++ .../module_bindings/SpacetimeDBClient.g.cs | 3 +- .../module_bindings/Tables/MyTable.g.cs | 27 ++++++ .../client/module_bindings/Types/MyTable.g.cs | 17 ++++ .../module_bindings/Types/ReturnEnum.g.cs | 15 ++++ .../module_bindings/Types/ReturnStruct.g.cs | 35 ++++++++ 10 files changed, 481 insertions(+), 1 deletion(-) create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumA.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumB.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnPrimitive.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnStructProcedure.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/WillPanic.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyTable.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyTable.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnEnum.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnStruct.g.cs diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumA.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumA.g.cs new file mode 100644 index 00000000000..542d05e76f6 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumA.g.cs @@ -0,0 +1,77 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteProcedures : RemoteBase + { + public void ReturnEnumA(uint a, ProcedureCallback callback) + { + // Convert the clean callback to the wrapper callback + InternalReturnEnumA(a, (ctx, result) => + { + if (result.IsSuccess && result.Value != null) + { + callback(ctx, ProcedureCallbackResult.Success(result.Value.Value)); + } + else + { + callback(ctx, ProcedureCallbackResult.Failure(result.Error!)); + } + }); + } + + private void InternalReturnEnumA(uint a, ProcedureCallback callback) + { + conn.InternalCallProcedure(new Procedure.ReturnEnumAArgs(a), callback); + } + + } + + public abstract partial class Procedure + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnEnumA + { + [DataMember(Name = "Value")] + public SpacetimeDB.Types.ReturnEnum Value; + + public ReturnEnumA(SpacetimeDB.Types.ReturnEnum Value) + { + this.Value = Value; + } + + public ReturnEnumA() + { + this.Value = null!; + } + } + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnEnumAArgs : Procedure, IProcedureArgs + { + [DataMember(Name = "a")] + public uint A; + + public ReturnEnumAArgs(uint A) + { + this.A = A; + } + + public ReturnEnumAArgs() + { + } + + string IProcedureArgs.ProcedureName => "ReturnEnumA"; + } + + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumB.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumB.g.cs new file mode 100644 index 00000000000..f8478dae31d --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnEnumB.g.cs @@ -0,0 +1,78 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteProcedures : RemoteBase + { + public void ReturnEnumB(string b, ProcedureCallback callback) + { + // Convert the clean callback to the wrapper callback + InternalReturnEnumB(b, (ctx, result) => + { + if (result.IsSuccess && result.Value != null) + { + callback(ctx, ProcedureCallbackResult.Success(result.Value.Value)); + } + else + { + callback(ctx, ProcedureCallbackResult.Failure(result.Error!)); + } + }); + } + + private void InternalReturnEnumB(string b, ProcedureCallback callback) + { + conn.InternalCallProcedure(new Procedure.ReturnEnumBArgs(b), callback); + } + + } + + public abstract partial class Procedure + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnEnumB + { + [DataMember(Name = "Value")] + public SpacetimeDB.Types.ReturnEnum Value; + + public ReturnEnumB(SpacetimeDB.Types.ReturnEnum Value) + { + this.Value = Value; + } + + public ReturnEnumB() + { + this.Value = null!; + } + } + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnEnumBArgs : Procedure, IProcedureArgs + { + [DataMember(Name = "b")] + public string B; + + public ReturnEnumBArgs(string B) + { + this.B = B; + } + + public ReturnEnumBArgs() + { + this.B = ""; + } + + string IProcedureArgs.ProcedureName => "ReturnEnumB"; + } + + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnPrimitive.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnPrimitive.g.cs new file mode 100644 index 00000000000..bf08909d233 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnPrimitive.g.cs @@ -0,0 +1,82 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteProcedures : RemoteBase + { + public void ReturnPrimitive(uint lhs, uint rhs, ProcedureCallback callback) + { + // Convert the clean callback to the wrapper callback + InternalReturnPrimitive(lhs, rhs, (ctx, result) => + { + if (result.IsSuccess && result.Value != null) + { + callback(ctx, ProcedureCallbackResult.Success(result.Value.Value)); + } + else + { + callback(ctx, ProcedureCallbackResult.Failure(result.Error!)); + } + }); + } + + private void InternalReturnPrimitive(uint lhs, uint rhs, ProcedureCallback callback) + { + conn.InternalCallProcedure(new Procedure.ReturnPrimitiveArgs(lhs, rhs), callback); + } + + } + + public abstract partial class Procedure + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnPrimitive + { + [DataMember(Name = "Value")] + public uint Value; + + public ReturnPrimitive(uint Value) + { + this.Value = Value; + } + + public ReturnPrimitive() + { + } + } + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnPrimitiveArgs : Procedure, IProcedureArgs + { + [DataMember(Name = "lhs")] + public uint Lhs; + [DataMember(Name = "rhs")] + public uint Rhs; + + public ReturnPrimitiveArgs( + uint Lhs, + uint Rhs + ) + { + this.Lhs = Lhs; + this.Rhs = Rhs; + } + + public ReturnPrimitiveArgs() + { + } + + string IProcedureArgs.ProcedureName => "ReturnPrimitive"; + } + + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnStructProcedure.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnStructProcedure.g.cs new file mode 100644 index 00000000000..96f3846fba3 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/ReturnStructProcedure.g.cs @@ -0,0 +1,84 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteProcedures : RemoteBase + { + public void ReturnStructProcedure(uint a, string b, ProcedureCallback callback) + { + // Convert the clean callback to the wrapper callback + InternalReturnStructProcedure(a, b, (ctx, result) => + { + if (result.IsSuccess && result.Value != null) + { + callback(ctx, ProcedureCallbackResult.Success(result.Value.Value)); + } + else + { + callback(ctx, ProcedureCallbackResult.Failure(result.Error!)); + } + }); + } + + private void InternalReturnStructProcedure(uint a, string b, ProcedureCallback callback) + { + conn.InternalCallProcedure(new Procedure.ReturnStructProcedureArgs(a, b), callback); + } + + } + + public abstract partial class Procedure + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnStructProcedure + { + [DataMember(Name = "Value")] + public SpacetimeDB.Types.ReturnStruct Value; + + public ReturnStructProcedure(SpacetimeDB.Types.ReturnStruct Value) + { + this.Value = Value; + } + + public ReturnStructProcedure() + { + this.Value = new(); + } + } + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnStructProcedureArgs : Procedure, IProcedureArgs + { + [DataMember(Name = "a")] + public uint A; + [DataMember(Name = "b")] + public string B; + + public ReturnStructProcedureArgs( + uint A, + string B + ) + { + this.A = A; + this.B = B; + } + + public ReturnStructProcedureArgs() + { + this.B = ""; + } + + string IProcedureArgs.ProcedureName => "ReturnStructProcedure"; + } + + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/WillPanic.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/WillPanic.g.cs new file mode 100644 index 00000000000..5a63ca7b547 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/WillPanic.g.cs @@ -0,0 +1,64 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteProcedures : RemoteBase + { + public void WillPanic(ProcedureCallback callback) + { + // Convert the clean callback to the wrapper callback + InternalWillPanic((ctx, result) => + { + if (result.IsSuccess && result.Value != null) + { + callback(ctx, ProcedureCallbackResult.Success(result.Value.Value)); + } + else + { + callback(ctx, ProcedureCallbackResult.Failure(result.Error!)); + } + }); + } + + private void InternalWillPanic(ProcedureCallback callback) + { + conn.InternalCallProcedure(new Procedure.WillPanicArgs(), callback); + } + + } + + public abstract partial class Procedure + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class WillPanic + { + [DataMember(Name = "Value")] + public SpacetimeDB.Unit Value; + + public WillPanic(SpacetimeDB.Unit Value) + { + this.Value = Value; + } + + public WillPanic() + { + } + } + [SpacetimeDB.Type] + [DataContract] + public sealed partial class WillPanicArgs : Procedure, IProcedureArgs + { + string IProcedureArgs.ProcedureName => "WillPanic"; + } + + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index f69d9fae6d1..8b3eecc9d4d 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 6cbedef7ca453ea48801d9192779fcee7a465a10). +// This was generated using spacetimedb cli version 1.9.0 (commit 6b66e0ec5ff6837618bf774e62b5844e8bcccd22). #nullable enable @@ -33,6 +33,7 @@ public RemoteTables(DbConnection conn) AddTable(Player = new(conn)); AddTable(PlayerLevel = new(conn)); AddTable(PlayersForLevel = new(conn)); + AddTable(MyTable = new(conn)); } } diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyTable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyTable.g.cs new file mode 100644 index 00000000000..e8703966257 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyTable.g.cs @@ -0,0 +1,27 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class MyTableHandle : RemoteTableHandle + { + protected override string RemoteTableName => "my_table"; + + internal MyTableHandle(DbConnection conn) : base(conn) + { + } + } + + public readonly MyTableHandle MyTable; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyTable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyTable.g.cs new file mode 100644 index 00000000000..5fe3407eadc --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyTable.g.cs @@ -0,0 +1,17 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class MyTable + { + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnEnum.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnEnum.g.cs new file mode 100644 index 00000000000..bb84b170ba7 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnEnum.g.cs @@ -0,0 +1,15 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + public partial record ReturnEnum : SpacetimeDB.TaggedEnum<( + uint A, + string B + )>; +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnStruct.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnStruct.g.cs new file mode 100644 index 00000000000..1deb22e335a --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/ReturnStruct.g.cs @@ -0,0 +1,35 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ReturnStruct + { + [DataMember(Name = "A")] + public uint A; + [DataMember(Name = "B")] + public string B; + + public ReturnStruct( + uint A, + string B + ) + { + this.A = A; + this.B = B; + } + + public ReturnStruct() + { + this.B = ""; + } + } +}