mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-06 07:26:43 -04:00
Update NativeAOT-LLVM infrastructure to current ABI (#4515)
## Summary - Update the experimental NativeAOT-LLVM build path (`EXPERIMENTAL_WASM_AOT=1`) to include all host function imports from ABI versions 10.0 through 10.4 - Fix the compiler package reference to work on both Windows x64 and Linux x64 (was hardcoded to Windows only) - Add a CI smoketest to verify AOT builds work on Linux x64 ## Context See #4514 for the full writeup on the C# AOT situation. The `wasi-experimental` workload that all C# module builds depend on is deprecated and removed from .NET 9+. NativeAOT-LLVM is the recommended path forward for ahead-of-time compilation of C# to WebAssembly. The existing NativeAOT-LLVM support (added by RReverser in #713) was stale: missing imports added since then and a Windows-only package reference. ## Changes **`SpacetimeDB.Runtime.targets`:** - Add 10 missing `WasmImport` declarations across spacetime_10.0 through 10.4 - Replace `runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM` with `runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM` so it resolves correctly on Linux x64 as well - Use explicit version strings instead of the `$(SpacetimeNamespace)` variable **`ci.yml`:** - Add AOT build smoketest step in the `csharp-testsuite` job ## Test plan - [x] CI smoketest passes: `EXPERIMENTAL_WASM_AOT=1 dotnet publish -c Release` builds successfully on Linux x64 - [ ] Existing C# tests continue to pass (no changes to the default interpreter path) --------- Signed-off-by: Ryan <r.ekhoff@clockworklabs.io> Co-authored-by: Ryan <r.ekhoff@clockworklabs.io> Co-authored-by: Jason Larabie <jason@clockworklabs.io> Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
This commit is contained in:
@@ -98,6 +98,10 @@ jobs:
|
||||
cd $env:USERPROFILE\emsdk
|
||||
.\emsdk install 4.0.21
|
||||
.\emsdk activate 4.0.21
|
||||
# Add emscripten to PATH for subsequent steps and subprocesses
|
||||
$emsdkPath = "$env:USERPROFILE\emsdk\upstream\emscripten"
|
||||
Add-Content -Path $env:GITHUB_PATH -Value $emsdkPath
|
||||
Write-Host "Added $emsdkPath to PATH"
|
||||
|
||||
- name: Install psql (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
# Using NativeAOT-LLVM with SpacetimeDB C# Modules
|
||||
|
||||
This guide provides instructions for enabling NativeAOT-LLVM compilation for C# SpacetimeDB modules, which can provide performance improvements.
|
||||
|
||||
## Overview
|
||||
|
||||
NativeAOT-LLVM compiles C# modules to native WebAssembly (WASM) instead of using the Mono runtime.
|
||||
|
||||
> [!WARNING]
|
||||
> This is currently only supported for Windows server modules and is experimental.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **.NET SDK 8.x** (same version used by SpacetimeDB)
|
||||
- **Emscripten SDK (EMSDK)** installed (must contain `upstream/emscripten/emcc.bat`)
|
||||
- **(Optional) Binaryen (wasm-opt)** installed and on `PATH` (recommended: `version_116`)
|
||||
- **Windows** - NativeAOT-LLVM is currently only supported for Windows server modules
|
||||
|
||||
## Prerequisites Installation
|
||||
|
||||
### Install Emscripten SDK (EMSDK)
|
||||
|
||||
The Emscripten SDK is required for NativeAOT-LLVM compilation:
|
||||
|
||||
1. **Download and extract** the Emscripten SDK from `https://github.com/emscripten-core/emsdk`
|
||||
- Example path: `D:\Tools\emsdk`
|
||||
|
||||
2. **Set environment variable** (optional - the CLI will detect it automatically):
|
||||
```
|
||||
$env:EMSDK="D:\Tools\emsdk"
|
||||
```
|
||||
|
||||
### Install Binaryen (Optional)
|
||||
|
||||
Binaryen provides `wasm-opt` for WASM optimization (recommended for performance):
|
||||
|
||||
1. Download Binaryen https://github.com/WebAssembly/binaryen/releases/tag/version_116 for Windows
|
||||
2. Extract to e.g. `D:\Tools\binaryen`
|
||||
3. Add `D:\Tools\binaryen\bin` to `PATH`
|
||||
|
||||
To temporarily add to your current PowerShell session:
|
||||
```
|
||||
$env:PATH += ";D:\Tools\binaryen\bin"
|
||||
```
|
||||
4. Verify:
|
||||
```
|
||||
wasm-opt --version
|
||||
```
|
||||
|
||||
## Creating a New NativeAOT Project
|
||||
|
||||
When creating a new C# project, use the `--native-aot` flag:
|
||||
|
||||
```
|
||||
spacetime init --lang csharp --native-aot my-native-aot-project
|
||||
```
|
||||
|
||||
This automatically:
|
||||
- Creates a C# project with the required package references
|
||||
- Generates a `spacetime.json` with `"native-aot": true`
|
||||
- Configures the project for NativeAOT-LLVM compilation
|
||||
|
||||
## Converting an Existing Project
|
||||
|
||||
1. **Update spacetime.json**
|
||||
Add `"native-aot": true` to your `spacetime.json`:
|
||||
```json
|
||||
{
|
||||
"module": "your-module-name",
|
||||
"native-aot": true
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Once `spacetime.json` has `"native-aot": true`, you can simply run `spacetime publish` without the `--native-aot` flag. The CLI will automatically detect the configuration and use NativeAOT compilation.
|
||||
|
||||
2. **Ensure NuGet feed is configured**
|
||||
NativeAOT-LLVM packages come from **dotnet-experimental**. Add to `NuGet.Config`:
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
3. **Add NativeAOT package references**
|
||||
Add this `ItemGroup` to your `.csproj`:
|
||||
```xml
|
||||
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
Your complete `.csproj` should look like:
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SpacetimeDB.Runtime" Version="2.0.*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
## Publishing Your NativeAOT Module
|
||||
|
||||
After completing either the **Creating a New NativeAOT Project** or **Converting an Existing Project** steps above, you can publish your module normally:
|
||||
|
||||
```
|
||||
# From your project directory
|
||||
spacetime publish your-database-name
|
||||
```
|
||||
|
||||
If you have `"native-aot": true` in your `spacetime.json`, the CLI will automatically detect this and use NativeAOT compilation. Alternatively, you can use:
|
||||
|
||||
```
|
||||
spacetime publish --native-aot your-database-name
|
||||
```
|
||||
|
||||
The CLI will display "Using NativeAOT-LLVM compilation (experimental)" when NativeAOT is enabled.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Package source mapping enabled
|
||||
If you have **package source mapping** enabled in `NuGet.Config`, add mappings for the LLVM packages:
|
||||
|
||||
```xml
|
||||
<packageSourceMapping>
|
||||
<packageSource key="bsatn-runtime">
|
||||
<package pattern="SpacetimeDB.BSATN.Runtime" />
|
||||
</packageSource>
|
||||
<packageSource key="SpacetimeDB.Runtime">
|
||||
<package pattern="SpacetimeDB.Runtime" />
|
||||
</packageSource>
|
||||
<packageSource key="dotnet-experimental">
|
||||
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
|
||||
<package pattern="runtime.*" />
|
||||
</packageSource>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
```
|
||||
|
||||
### wasi-experimental workload install fails
|
||||
If the CLI cannot install the `wasi-experimental` workload automatically, install it manually:
|
||||
|
||||
```
|
||||
dotnet workload install wasi-experimental
|
||||
```
|
||||
|
||||
### Duplicate PackageReference warning
|
||||
You may see a `NU1504` warning about duplicate `PackageReference` items. This is expected and non-blocking.
|
||||
|
||||
### Code generation failed
|
||||
If you see errors like "Code generation failed for method", ensure:
|
||||
1. You're using `SpacetimeDB.Runtime` version 2.0.4 or newer
|
||||
2. All required package references are in your `.csproj`
|
||||
3. The `dotnet-experimental` feed is configured in `NuGet.Config`
|
||||
|
||||
@@ -206,6 +206,36 @@ partial class RawModuleDefV10
|
||||
|
||||
public static class Module
|
||||
{
|
||||
// Workaround for NativeAOT-LLVM IL scanner bug:
|
||||
// The scanner fails to compute vtables for TaggedEnum<T> base types when
|
||||
// concrete subtypes are only encountered indirectly (e.g., through Equals
|
||||
// calls on types containing TaggedEnum fields). This occurs when no user
|
||||
// table has indexes/primary keys, so RawIndexAlgorithm is never directly
|
||||
// constructed in user code.
|
||||
// By referencing concrete TaggedEnum subtypes here, we ensure the IL scanner
|
||||
// always processes their vtables. One variant per TaggedEnum is sufficient.
|
||||
[System.Runtime.CompilerServices.MethodImpl(
|
||||
System.Runtime.CompilerServices.MethodImplOptions.NoInlining
|
||||
)]
|
||||
private static void EnsureNativeAotTypeRoots()
|
||||
{
|
||||
// These constructions are never executed at runtime — they exist solely
|
||||
// to make the IL scanner compute vtables for TaggedEnum subtypes.
|
||||
// The condition is always false but the scanner must assume it could be true.
|
||||
if (Environment.TickCount < 0 && Environment.TickCount > 0)
|
||||
{
|
||||
_ = new RawIndexAlgorithm.BTree(null!);
|
||||
_ = new RawConstraintDataV9.Unique(null!);
|
||||
_ = new RawModuleDef.V10(null!);
|
||||
_ = new RawModuleDefV10Section.Typespace(null!);
|
||||
_ = new ExplicitNameEntry.Table(null!);
|
||||
_ = new MiscModuleExport.TypeAlias(null!);
|
||||
_ = new RawMiscModuleExportV9.ColumnDefaultValue(null!);
|
||||
_ = new SpacetimeDB.Filter.Sql(null!);
|
||||
_ = new ViewResultHeader.RowData(default);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly RawModuleDefV10 moduleDef = new();
|
||||
|
||||
private static readonly List<IReducer> reducers = [];
|
||||
@@ -419,6 +449,7 @@ public static class Module
|
||||
|
||||
public static void __describe_module__(BytesSink description)
|
||||
{
|
||||
EnsureNativeAotTypeRoots();
|
||||
try
|
||||
{
|
||||
var module = moduleDef.BuildModuleDefinition();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RootNamespace>SpacetimeDB</RootNamespace>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<RestoreAdditionalProjectSources Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json;$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -25,6 +26,13 @@
|
||||
<ProjectReference Include="../Codegen/Codegen.csproj" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- These must be explicit package dependencies so NuGet consumers can resolve the LLVM toolchain. -->
|
||||
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" IncludeAssets="All" />
|
||||
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" IncludeAssets="All" />
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" IncludeAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="" />
|
||||
<None Include="build/*" Pack="true" PackagePath="build" />
|
||||
|
||||
@@ -1,32 +1,50 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<Import
|
||||
Project="$(PkgMicrosoft_DotNet_ILCompiler_LLVM)\build\Microsoft.DotNet.ILCompiler.LLVM.targets"
|
||||
Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1' and '$(ILCompilerTargetsPath)' == '' and '$(PkgMicrosoft_DotNet_ILCompiler_LLVM)' != '' and Exists('$(PkgMicrosoft_DotNet_ILCompiler_LLVM)\build\Microsoft.DotNet.ILCompiler.LLVM.targets')" />
|
||||
|
||||
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
|
||||
<NativeLibrary Include="$(MSBuildThisFileDirectory)..\bindings.c" />
|
||||
<UnmanagedEntryPointsAssembly Include="SpacetimeDB.Runtime" />
|
||||
|
||||
<WasmImport Include="$(SpacetimeNamespace)!table_id_from_name" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!index_id_from_name" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_table_row_count" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_table_scan_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_index_scan_range_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_btree_scan_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!row_iter_bsatn_advance" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!row_iter_bsatn_close" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_insert_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_delete_by_index_scan_range_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_delete_by_btree_scan_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_delete_all_by_eq_bsatn" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!bytes_source_read" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!bytes_sink_write" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!console_log" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!console_timer_start" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!console_timer_end" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!volatile_nonatomic_schedule_immediate" />
|
||||
<WasmImport Include="$(SpacetimeNamespace)!datastore_clear" />
|
||||
<!-- spacetime_10.0 imports -->
|
||||
<WasmImport Include="spacetime_10.0!table_id_from_name" />
|
||||
<WasmImport Include="spacetime_10.0!index_id_from_name" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_table_row_count" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_table_scan_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_index_scan_range_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_btree_scan_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!row_iter_bsatn_advance" />
|
||||
<WasmImport Include="spacetime_10.0!row_iter_bsatn_close" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_insert_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_update_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_delete_by_index_scan_range_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_delete_by_btree_scan_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!datastore_delete_all_by_eq_bsatn" />
|
||||
<WasmImport Include="spacetime_10.0!bytes_source_read" />
|
||||
<WasmImport Include="spacetime_10.0!bytes_sink_write" />
|
||||
<WasmImport Include="spacetime_10.0!console_log" />
|
||||
<WasmImport Include="spacetime_10.0!console_timer_start" />
|
||||
<WasmImport Include="spacetime_10.0!console_timer_end" />
|
||||
<WasmImport Include="spacetime_10.0!volatile_nonatomic_schedule_immediate" />
|
||||
<WasmImport Include="spacetime_10.0!identity" />
|
||||
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
|
||||
<!-- spacetime_10.1 imports -->
|
||||
<WasmImport Include="spacetime_10.1!bytes_source_remaining_length" />
|
||||
|
||||
<!-- spacetime_10.2 imports -->
|
||||
<WasmImport Include="spacetime_10.2!get_jwt" />
|
||||
|
||||
<!-- spacetime_10.3 imports -->
|
||||
<WasmImport Include="spacetime_10.3!procedure_start_mut_tx" />
|
||||
<WasmImport Include="spacetime_10.3!procedure_commit_mut_tx" />
|
||||
<WasmImport Include="spacetime_10.3!procedure_abort_mut_tx" />
|
||||
<WasmImport Include="spacetime_10.3!procedure_http_request" />
|
||||
|
||||
<!-- spacetime_10.4 imports -->
|
||||
<WasmImport Include="spacetime_10.4!datastore_index_scan_point_bsatn" />
|
||||
<WasmImport Include="spacetime_10.4!datastore_delete_by_index_scan_point_bsatn" />
|
||||
|
||||
<CustomLinkerArg Include="-DEXPERIMENTAL_WASM_AOT" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -116,6 +116,7 @@ pub struct TemplateConfig {
|
||||
pub github_repo: Option<String>,
|
||||
pub template_def: Option<TemplateDefinition>,
|
||||
pub use_local: bool,
|
||||
pub native_aot: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -131,6 +132,8 @@ pub struct InitOptions {
|
||||
pub non_interactive: bool,
|
||||
/// When true, suppress the "Next steps" message after init (e.g. when called from `spacetime dev`).
|
||||
pub skip_next_steps: bool,
|
||||
/// When true, configure C# projects for NativeAOT-LLVM compilation.
|
||||
pub native_aot: bool,
|
||||
}
|
||||
|
||||
impl InitOptions {
|
||||
@@ -146,6 +149,7 @@ impl InitOptions {
|
||||
local: args.get_flag("local"),
|
||||
non_interactive: args.get_flag("non-interactive"),
|
||||
skip_next_steps: false,
|
||||
native_aot: args.get_flag("native-aot"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,6 +193,12 @@ pub fn cli() -> clap::Command {
|
||||
.action(clap::ArgAction::SetTrue)
|
||||
.help("Run in non-interactive mode"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("native-aot")
|
||||
.long("native-aot")
|
||||
.action(clap::ArgAction::SetTrue)
|
||||
.help("Configure C# project for NativeAOT-LLVM compilation (experimental, Windows only)"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fetch_templates_list() -> anyhow::Result<Vec<TemplateDefinition>> {
|
||||
@@ -346,6 +356,7 @@ fn create_template_config_from_template_str(
|
||||
github_repo: None,
|
||||
template_def: Some(template.clone()),
|
||||
use_local: true,
|
||||
native_aot: false,
|
||||
})
|
||||
} else {
|
||||
// GitHub template
|
||||
@@ -358,6 +369,7 @@ fn create_template_config_from_template_str(
|
||||
github_repo: Some(template_str.to_string()),
|
||||
template_def: None,
|
||||
use_local: true,
|
||||
native_aot: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -525,7 +537,13 @@ pub async fn exec_with_options(config: &mut Config, options: &InitOptions) -> an
|
||||
)?;
|
||||
init_from_template(&template_config, &template_config.project_path, is_server_only).await?;
|
||||
|
||||
if let Some(path) = create_default_spacetime_config_if_missing(&project_path)? {
|
||||
// Add NativeAOT-LLVM package references to C# projects if --native-aot was specified
|
||||
if options.native_aot && template_config.server_lang == Some(ServerLanguage::Csharp) {
|
||||
let server_dir = template_config.project_path.join("spacetimedb");
|
||||
add_native_aot_packages_to_csproj(&server_dir)?;
|
||||
}
|
||||
|
||||
if let Some(path) = create_default_spacetime_config_if_missing(&project_path, options.native_aot)? {
|
||||
println!("{} Created {}", "✓".green(), path.display());
|
||||
}
|
||||
|
||||
@@ -605,7 +623,10 @@ fn get_local_database_name(options: &InitOptions, project_name: &str, is_interac
|
||||
Ok(database_name)
|
||||
}
|
||||
|
||||
fn create_default_spacetime_config_if_missing(project_path: &Path) -> anyhow::Result<Option<PathBuf>> {
|
||||
fn create_default_spacetime_config_if_missing(
|
||||
project_path: &Path,
|
||||
native_aot: bool,
|
||||
) -> anyhow::Result<Option<PathBuf>> {
|
||||
let config_path = project_path.join(CONFIG_FILENAME);
|
||||
if config_path.exists() {
|
||||
return Ok(None);
|
||||
@@ -622,6 +643,10 @@ fn create_default_spacetime_config_if_missing(project_path: &Path) -> anyhow::Re
|
||||
.insert("module-path".to_string(), json!("./spacetimedb"));
|
||||
}
|
||||
|
||||
if native_aot {
|
||||
config.additional_fields.insert("native-aot".to_string(), json!(true));
|
||||
}
|
||||
|
||||
Ok(Some(config.save_to_dir(project_path)?))
|
||||
}
|
||||
|
||||
@@ -696,6 +721,7 @@ async fn get_template_config_non_interactive(
|
||||
github_repo: None,
|
||||
template_def: None,
|
||||
use_local: true,
|
||||
native_aot: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -761,6 +787,7 @@ async fn get_template_config_interactive(
|
||||
github_repo: None,
|
||||
template_def: None,
|
||||
use_local: true,
|
||||
native_aot: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -845,6 +872,7 @@ async fn get_template_config_interactive(
|
||||
github_repo: None,
|
||||
template_def: Some(template.clone()),
|
||||
use_local: true,
|
||||
native_aot: false,
|
||||
});
|
||||
} else if client_selection == github_clone_index {
|
||||
return loop {
|
||||
@@ -889,6 +917,7 @@ async fn get_template_config_interactive(
|
||||
github_repo: None,
|
||||
template_def: None,
|
||||
use_local: true,
|
||||
native_aot: false,
|
||||
});
|
||||
} else {
|
||||
unreachable!("Invalid selection index");
|
||||
@@ -1648,6 +1677,21 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> anyhow::Result<PathB
|
||||
anyhow::bail!("Cannot specify both --template and --lang. Language is determined by the template.");
|
||||
}
|
||||
|
||||
// Validate that --native-aot is only used with C# projects
|
||||
if options.native_aot {
|
||||
if let Some(lang) = server_lang
|
||||
&& lang.to_lowercase() != "csharp"
|
||||
&& lang.to_lowercase() != "c#"
|
||||
{
|
||||
anyhow::bail!("--native-aot is only supported for C# projects (--lang csharp)");
|
||||
}
|
||||
// Print warning about Windows-only support
|
||||
println!(
|
||||
"{}",
|
||||
"Note: NativeAOT-LLVM is experimental and building for this platform is currently only supported on Windows.".yellow()
|
||||
);
|
||||
}
|
||||
|
||||
if !is_interactive {
|
||||
// In non-interactive mode, validate all required args are present
|
||||
if project_name_arg.is_none() {
|
||||
@@ -1713,6 +1757,62 @@ pub fn init_csharp_project(project_path: &Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds NativeAOT-LLVM package references to an existing C# .csproj file and creates NuGet.Config.
|
||||
/// This is called when `--native-aot` is specified during `spacetime init`.
|
||||
fn add_native_aot_packages_to_csproj(project_path: &Path) -> anyhow::Result<()> {
|
||||
let csproj_path = project_path.join("StdbModule.csproj");
|
||||
if !csproj_path.exists() {
|
||||
anyhow::bail!("Could not find StdbModule.csproj at {}", csproj_path.display());
|
||||
}
|
||||
|
||||
let content = std::fs::read_to_string(&csproj_path)?;
|
||||
|
||||
// The NativeAOT-LLVM ItemGroup to add
|
||||
let native_aot_item_group = r#"
|
||||
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
</ItemGroup>
|
||||
"#;
|
||||
|
||||
// Insert the ItemGroup before the closing </Project> tag
|
||||
let new_content = if let Some(pos) = content.rfind("</Project>") {
|
||||
let (before, after) = content.split_at(pos);
|
||||
format!("{}{}{}", before.trim_end(), native_aot_item_group, after)
|
||||
} else {
|
||||
anyhow::bail!("Invalid .csproj file: missing </Project> tag");
|
||||
};
|
||||
|
||||
std::fs::write(&csproj_path, new_content)?;
|
||||
println!(
|
||||
"{} Added NativeAOT-LLVM package references to {}",
|
||||
"✓".green(),
|
||||
csproj_path.display()
|
||||
);
|
||||
|
||||
// Create NuGet.Config with the dotnet-experimental feed required for NativeAOT-LLVM packages
|
||||
let nuget_config_path = project_path.join("NuGet.Config");
|
||||
let nuget_config_content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
"#;
|
||||
|
||||
std::fs::write(&nuget_config_path, nuget_config_content)?;
|
||||
println!(
|
||||
"{} Created {} with dotnet-experimental feed",
|
||||
"✓".green(),
|
||||
nuget_config_path.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_typescript_project(project_path: &Path) -> anyhow::Result<()> {
|
||||
let export_files = vec![
|
||||
(
|
||||
@@ -2015,7 +2115,7 @@ mod tests {
|
||||
let project_path = temp.path();
|
||||
std::fs::create_dir_all(project_path.join("spacetimedb")).unwrap();
|
||||
|
||||
let created = create_default_spacetime_config_if_missing(project_path)
|
||||
let created = create_default_spacetime_config_if_missing(project_path, false)
|
||||
.unwrap()
|
||||
.expect("expected config to be created");
|
||||
assert_eq!(created, project_path.join("spacetime.json"));
|
||||
@@ -2028,6 +2128,23 @@ mod tests {
|
||||
parsed.get("module-path").and_then(|v| v.as_str()),
|
||||
Some("./spacetimedb")
|
||||
);
|
||||
assert!(parsed.get("native-aot").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_default_spacetime_config_with_native_aot() {
|
||||
let temp = tempfile::TempDir::new().unwrap();
|
||||
let project_path = temp.path();
|
||||
std::fs::create_dir_all(project_path.join("spacetimedb")).unwrap();
|
||||
|
||||
let created = create_default_spacetime_config_if_missing(project_path, true)
|
||||
.unwrap()
|
||||
.expect("expected config to be created");
|
||||
assert_eq!(created, project_path.join("spacetime.json"));
|
||||
|
||||
let content = std::fs::read_to_string(&created).unwrap();
|
||||
let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
|
||||
assert_eq!(parsed.get("native-aot").and_then(|v| v.as_bool()), Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -12,8 +12,8 @@ use std::{env, fs};
|
||||
use crate::common_args::ClearMode;
|
||||
use crate::config::Config;
|
||||
use crate::spacetime_config::{
|
||||
find_and_load_with_env, CommandConfig, CommandSchema, CommandSchemaBuilder, FlatTarget, Key, LoadedConfig,
|
||||
SpacetimeConfig,
|
||||
find_and_load_with_env, find_and_load_with_env_from, CommandConfig, CommandSchema, CommandSchemaBuilder,
|
||||
FlatTarget, Key, LoadedConfig, SpacetimeConfig,
|
||||
};
|
||||
use crate::util::{add_auth_header_opt, get_auth_header, strip_verbatim_prefix, AuthHeader, ResponseExt};
|
||||
use crate::util::{decode_identity, y_or_n};
|
||||
@@ -33,6 +33,7 @@ pub fn build_publish_schema(command: &clap::Command) -> Result<CommandSchema, an
|
||||
.key(Key::new("anon_identity"))
|
||||
.key(Key::new("parent"))
|
||||
.key(Key::new("organization"))
|
||||
.key(Key::new("native_aot").module_specific())
|
||||
.exclude("clear-database")
|
||||
.exclude("force")
|
||||
.exclude("no_config")
|
||||
@@ -76,19 +77,30 @@ pub fn get_filtered_publish_configs<'a>(
|
||||
.collect();
|
||||
|
||||
if matched.is_empty() {
|
||||
anyhow::bail!(
|
||||
"No database target matches '{}'. Available databases: {}",
|
||||
cli_database,
|
||||
spacetime_config
|
||||
.collect_all_targets_with_inheritance()
|
||||
.iter()
|
||||
.filter_map(|t| t.fields.get("database").and_then(|v| v.as_str()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
// When there is exactly one target in the config and the CLI-provided
|
||||
// database name doesn't match it, use that target's settings (e.g.
|
||||
// native-aot, module-path, build-options) and let CommandConfig merge
|
||||
// the CLI database name on top. This handles the common case where
|
||||
// `spacetime init` generated a random database suffix that differs
|
||||
// from the name the user passes on the CLI, while still picking up
|
||||
// module-specific config.
|
||||
let all_targets = spacetime_config.collect_all_targets_with_inheritance();
|
||||
if all_targets.len() == 1 {
|
||||
all_targets
|
||||
} else {
|
||||
anyhow::bail!(
|
||||
"No database target matches '{}'. Available databases: {}",
|
||||
cli_database,
|
||||
all_targets
|
||||
.iter()
|
||||
.filter_map(|t| t.fields.get("database").and_then(|v| v.as_str()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
} else {
|
||||
matched
|
||||
}
|
||||
|
||||
matched
|
||||
} else {
|
||||
all_targets
|
||||
};
|
||||
@@ -223,6 +235,12 @@ i.e. only lowercase ASCII letters and numbers, separated by dashes."),
|
||||
.action(Set)
|
||||
.help("Environment name for config file layering (e.g., dev, staging)")
|
||||
)
|
||||
.arg(
|
||||
Arg::new("native_aot")
|
||||
.long("native-aot")
|
||||
.action(SetTrue)
|
||||
.help("Use NativeAOT-LLVM compilation for C# modules (experimental, Windows only)")
|
||||
)
|
||||
.after_help("Run `spacetime help publish` for more detailed information.")
|
||||
}
|
||||
|
||||
@@ -293,13 +311,23 @@ pub async fn exec_with_options(
|
||||
let env = args.get_one::<String>("env").map(|s| s.as_str());
|
||||
|
||||
// Get publish configs (from spacetime.json or empty)
|
||||
let owned_loaded;
|
||||
let mut owned_loaded;
|
||||
let loaded_config_ref = if no_config {
|
||||
None
|
||||
} else if let Some(pre) = pre_loaded_config {
|
||||
Some(pre)
|
||||
} else {
|
||||
// First, try to load config from current directory
|
||||
owned_loaded = find_and_load_with_env(env)?;
|
||||
|
||||
// If no config found and --module-path is specified, try loading from module path.
|
||||
if owned_loaded.is_none()
|
||||
&& args.contains_id("module_path")
|
||||
&& let Some(module_path) = args.get_one::<PathBuf>("module_path")
|
||||
{
|
||||
owned_loaded = find_and_load_with_env_from(env, module_path.clone())?;
|
||||
}
|
||||
|
||||
owned_loaded.as_ref().inspect(|loaded| {
|
||||
if !quiet_config {
|
||||
for path in &loaded.loaded_files {
|
||||
@@ -420,6 +448,7 @@ async fn execute_publish_configs<'a>(
|
||||
let parent = parent_opt.as_deref();
|
||||
let org_opt = command_config.get_one::<String>("organization")?;
|
||||
let org = org_opt.as_deref();
|
||||
let native_aot = command_config.get_one::<bool>("native_aot")?.unwrap_or(false);
|
||||
|
||||
// If the user didn't specify an identity and we didn't specify an anonymous identity, then
|
||||
// we want to use the default identity
|
||||
@@ -447,6 +476,16 @@ async fn execute_publish_configs<'a>(
|
||||
println!("(JS) Skipping build. Instead we are publishing {}", path.display());
|
||||
(path.clone(), "Js")
|
||||
} else {
|
||||
// Set EXPERIMENTAL_WASM_AOT environment variable if native_aot is enabled
|
||||
// This is read by the C# build system (MSBuild) and by csharp.rs to determine output paths
|
||||
if native_aot {
|
||||
println!("Using NativeAOT-LLVM compilation (experimental)");
|
||||
// SAFETY: We are single-threaded at this point and no other code is reading
|
||||
// this environment variable concurrently.
|
||||
unsafe {
|
||||
env::set_var("EXPERIMENTAL_WASM_AOT", "1");
|
||||
}
|
||||
}
|
||||
build::exec_with_argstring(
|
||||
path_to_project
|
||||
.as_ref()
|
||||
|
||||
@@ -223,9 +223,7 @@ fn cli_publish_with_config_but_no_match_uses_cli_args() {
|
||||
|
||||
// Create a config with a different database name
|
||||
let config_content = r#"{
|
||||
"publish": {
|
||||
"database": "config-db-name"
|
||||
}
|
||||
"database": "config-db-name"
|
||||
}"#;
|
||||
std::fs::write(module_dir.join("spacetime.json"), config_content).expect("failed to write config");
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#![allow(clippy::disallowed_macros)]
|
||||
use spacetimedb_guard::ensure_binaries_built;
|
||||
use spacetimedb_smoketests::{have_emscripten, require_dotnet, workspace_root};
|
||||
use std::process::Command;
|
||||
|
||||
/// Test NativeAOT-LLVM build path for C# modules.
|
||||
/// Requires emscripten to be installed.
|
||||
/// Only runs on Windows since runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM
|
||||
/// is not available on the dotnet-experimental NuGet feed.
|
||||
#[test]
|
||||
fn test_build_csharp_module_aot() {
|
||||
require_dotnet!();
|
||||
|
||||
// NativeAOT-LLVM is only available on Windows
|
||||
if std::env::consts::OS != "windows" {
|
||||
eprintln!("Skipping AOT test - NativeAOT-LLVM for .NET 8 only available on Windows");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for emscripten - fail with helpful message if not available
|
||||
// Uses have_emscripten() which checks for both `emcc` and `emcc.bat` on Windows
|
||||
if !have_emscripten() {
|
||||
panic!(
|
||||
"NativeAOT-LLVM test requires emscripten but it was not found.\n\
|
||||
Install from: https://emscripten.org/docs/getting_started/downloads.html\n\
|
||||
Or ensure `emcc` is in your PATH."
|
||||
);
|
||||
}
|
||||
|
||||
let workspace = workspace_root();
|
||||
let _cli_path = ensure_binaries_built();
|
||||
|
||||
// Create isolated NuGet packages folder to avoid file lock conflicts
|
||||
// NativeAOT-LLVM packages contain DLLs that stay locked and interfere with other tests
|
||||
let nuget_packages_dir = tempfile::tempdir().expect("Failed to create temp directory for NuGet packages");
|
||||
|
||||
// Set EXPERIMENTAL_WASM_AOT=1 for this specific build
|
||||
// Build sdk-test-cs with NativeAOT-LLVM
|
||||
let mut cmd = Command::new("dotnet");
|
||||
cmd.arg("publish")
|
||||
.arg("-c")
|
||||
.arg("Release")
|
||||
.current_dir(workspace.join("modules/sdk-test-cs"))
|
||||
.env("EXPERIMENTAL_WASM_AOT", "1")
|
||||
.env("NUGET_PACKAGES", nuget_packages_dir.path());
|
||||
|
||||
let output = cmd.output().expect("Failed to run dotnet publish");
|
||||
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"NativeAOT-LLVM publish failed:\nstdout: {}\nstderr: {}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
// Clean up temp dir explicitly to verify no file locks remain
|
||||
// This ensures subsequent tests can clear NuGet locals without conflicts
|
||||
drop(nuget_packages_dir);
|
||||
|
||||
// Verify StdbModule.wasm was produced
|
||||
let wasm_path = workspace.join("modules/sdk-test-cs/bin/Release/net8.0/wasi-wasm/publish/StdbModule.wasm");
|
||||
assert!(wasm_path.exists(), "StdbModule.wasm not found at {:?}", wasm_path);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ mod client_connection_errors;
|
||||
mod confirmed_reads;
|
||||
mod connect_disconnect_from_cli;
|
||||
mod create_project;
|
||||
mod csharp_aot_module;
|
||||
mod csharp_module;
|
||||
mod database_lock;
|
||||
mod default_module_clippy;
|
||||
|
||||
@@ -115,6 +115,7 @@ Run `spacetime help publish` for more detailed information.
|
||||
* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com).
|
||||
* `--no-config` — Ignore spacetime.json configuration
|
||||
* `--env <ENV>` — Environment name for config file layering (e.g., dev, staging)
|
||||
* `--native-aot` — Use NativeAOT-LLVM compilation for C# modules (experimental, Windows only)
|
||||
|
||||
|
||||
|
||||
@@ -419,6 +420,7 @@ Initializes a new spacetime project.
|
||||
* `-t`, `--template <TEMPLATE>` — Template ID or GitHub repository (owner/repo or URL)
|
||||
* `--local` — Use local deployment instead of Maincloud
|
||||
* `--non-interactive` — Run in non-interactive mode
|
||||
* `--native-aot` — Configure C# project for NativeAOT-LLVM compilation (experimental, Windows only)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,5 +9,9 @@
|
||||
<ProjectReference Include="../../crates/bindings-csharp/Codegen/Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="../../crates/bindings-csharp/Runtime/Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" GeneratePathProperty="true" />
|
||||
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -82,6 +82,12 @@ void OnConnected(DbConnection conn, Identity identity, string authToken)
|
||||
.AddQuery(qb => qb.From.IenumerablePlayersFromIter())
|
||||
.AddQuery(qb => qb.From.IenumerableAdminsFromFilter())
|
||||
.AddQuery(qb => qb.From.IenumerablePlayersWithLevels())
|
||||
.AddQuery(qb => qb.From.EqualityPerson())
|
||||
.AddQuery(qb => qb.From.EqualityProduct())
|
||||
.AddQuery(qb => qb.From.EqualityOrder())
|
||||
.AddQuery(qb => qb.From.PlayerAction())
|
||||
.AddQuery(qb => qb.From.ActionBatch())
|
||||
.AddQuery(qb => qb.From.LogEntry())
|
||||
.Subscribe();
|
||||
|
||||
// If testing against Rust, the indexed parameter will need to be changed to: ulong indexed
|
||||
@@ -296,6 +302,96 @@ void OnConnected(DbConnection conn, Identity identity, string authToken)
|
||||
waiting--;
|
||||
ValidateCommittedReducer("InsertViewPkMembershipSecondary", ctx);
|
||||
};
|
||||
|
||||
// Equality test reducer callbacks
|
||||
conn.Reducers.OnRunAllEqualityTests += (ReducerEventContext ctx) =>
|
||||
{
|
||||
Log.Info("Got RunAllEqualityTests callback");
|
||||
// Note: waiting-- happens after validation in this callback
|
||||
Debug.Assert(
|
||||
ctx.Event.Status is Status.Committed,
|
||||
$"RunAllEqualityTests should commit, got {ctx.Event.Status}"
|
||||
);
|
||||
|
||||
// Validate results while subscription is still active
|
||||
// Must happen here before UnsubscribeThen removes the data
|
||||
if (ctx.Event.Status is Status.Committed)
|
||||
{
|
||||
Log.Info("Validating equality test results...");
|
||||
ValidateEqualityResults(ctx);
|
||||
Log.Info("Equality tests completed and validated");
|
||||
}
|
||||
// Decrement waiting here after validation is complete
|
||||
waiting--;
|
||||
};
|
||||
|
||||
conn.Reducers.OnAddEqualityPerson += (ReducerEventContext ctx, uint id, string name) =>
|
||||
{
|
||||
Log.Info($"Got AddEqualityPerson callback: id={id}, name={name}");
|
||||
// Note: waiting-- happens in OnRunAllEqualityTests after all tests complete
|
||||
};
|
||||
|
||||
conn.Reducers.OnAddEqualityProduct += (ReducerEventContext ctx, uint id, string name, int price, int quantity) =>
|
||||
{
|
||||
Log.Info($"Got AddEqualityProduct callback: id={id}, name={name}");
|
||||
// Note: waiting-- happens in OnRunAllEqualityTests after all tests complete
|
||||
};
|
||||
|
||||
conn.Reducers.OnRunEqualityTests += (ReducerEventContext ctx) =>
|
||||
{
|
||||
Log.Info("Got RunEqualityTests callback");
|
||||
// Note: waiting-- happens in OnRunAllEqualityTests after all tests complete
|
||||
};
|
||||
|
||||
conn.Reducers.OnRunComplexEqualityTests += (ReducerEventContext ctx) =>
|
||||
{
|
||||
Log.Info("Got RunComplexEqualityTests callback");
|
||||
// Note: waiting-- happens in OnRunAllEqualityTests after all tests complete
|
||||
};
|
||||
|
||||
conn.Reducers.OnRunEnumEqualityTests += (ReducerEventContext ctx) =>
|
||||
{
|
||||
Log.Info("Got RunEnumEqualityTests callback");
|
||||
// Note: waiting-- happens in OnRunAllEqualityTests after all tests complete
|
||||
};
|
||||
|
||||
// Equality test table insert callbacks
|
||||
conn.Db.EqualityPerson.OnInsert += (EventContext ctx, EqualityPerson row) =>
|
||||
{
|
||||
Log.Info($"EqualityPerson.OnInsert: Id={row.Id}, Name={row.Name}");
|
||||
};
|
||||
|
||||
conn.Db.EqualityProduct.OnInsert += (EventContext ctx, EqualityProduct row) =>
|
||||
{
|
||||
Log.Info($"EqualityProduct.OnInsert: Id={row.Id}, Name={row.Name}, Price={row.Price}");
|
||||
};
|
||||
|
||||
conn.Db.EqualityOrder.OnInsert += (EventContext ctx, EqualityOrder row) =>
|
||||
{
|
||||
Log.Info($"EqualityOrder.OnInsert: Id={row.Id}, Customer={row.CustomerName}");
|
||||
};
|
||||
|
||||
conn.Db.PlayerAction.OnInsert += (EventContext ctx, PlayerAction row) =>
|
||||
{
|
||||
var actionDesc = row.Action switch
|
||||
{
|
||||
GameAction.Move(var m) => $"Move({m})",
|
||||
GameAction.Attack(var a) => $"Attack({a})",
|
||||
GameAction.Defend(var d) => $"Defend({d})",
|
||||
_ => "Unknown"
|
||||
};
|
||||
Log.Info($"PlayerAction.OnInsert: Id={row.Id}, Action={actionDesc}");
|
||||
};
|
||||
|
||||
conn.Db.ActionBatch.OnInsert += (EventContext ctx, ActionBatch row) =>
|
||||
{
|
||||
Log.Info($"ActionBatch.OnInsert: Id={row.Id}, Actions.Count={row.Actions?.Count ?? 0}");
|
||||
};
|
||||
|
||||
conn.Db.LogEntry.OnInsert += (EventContext ctx, LogEntry row) =>
|
||||
{
|
||||
Log.Info($"LogEntry.OnInsert: {row.Message} at {row.Timestamp}");
|
||||
};
|
||||
}
|
||||
|
||||
const uint MAX_ID = 10;
|
||||
@@ -570,6 +666,78 @@ void ValidateIEnumerableViews(IRemoteDbContext conn)
|
||||
Log.Debug("IEnumerable views validation completed successfully");
|
||||
}
|
||||
|
||||
void ValidateEqualitySubscriptions(IRemoteDbContext conn)
|
||||
{
|
||||
Log.Debug("Checking equality test subscriptions...");
|
||||
|
||||
// Verify all equality test tables are accessible
|
||||
Debug.Assert(conn.Db.EqualityPerson != null, "EqualityPerson subscription should not be null");
|
||||
Debug.Assert(conn.Db.EqualityProduct != null, "EqualityProduct subscription should not be null");
|
||||
Debug.Assert(conn.Db.EqualityOrder != null, "EqualityOrder subscription should not be null");
|
||||
Debug.Assert(conn.Db.PlayerAction != null, "PlayerAction subscription should not be null");
|
||||
Debug.Assert(conn.Db.ActionBatch != null, "ActionBatch subscription should not be null");
|
||||
Debug.Assert(conn.Db.LogEntry != null, "LogEntry subscription should not be null");
|
||||
|
||||
Log.Debug("Equality test subscriptions validated successfully");
|
||||
}
|
||||
|
||||
void RunEqualityTests(SubscriptionEventContext ctx)
|
||||
{
|
||||
Log.Debug("Running equality tests...");
|
||||
|
||||
// Single waiting increment for the entire equality test phase
|
||||
// This ensures we wait for all callbacks AND validation before proceeding
|
||||
waiting++;
|
||||
|
||||
// Test 1: Add a person
|
||||
Log.Debug("Equality Test: Adding EqualityPerson...");
|
||||
ctx.Reducers.AddEqualityPerson(1, "Alice");
|
||||
|
||||
// Test 2: Add a product
|
||||
Log.Debug("Equality Test: Adding EqualityProduct...");
|
||||
ctx.Reducers.AddEqualityProduct(1, "Widget", 999, 100);
|
||||
|
||||
// Test 3: Run the comprehensive equality test suite
|
||||
Log.Debug("Equality Test: Running RunAllEqualityTests...");
|
||||
ctx.Reducers.RunAllEqualityTests();
|
||||
|
||||
Log.Debug("Equality tests initiated - waiting for completion");
|
||||
}
|
||||
|
||||
void ValidateEqualityResults(IRemoteDbContext conn)
|
||||
{
|
||||
Log.Debug("Validating equality test results...");
|
||||
|
||||
// Validate EqualityPerson data
|
||||
var alice = conn.Db.EqualityPerson.Id.Find(1);
|
||||
Debug.Assert(alice != null, "Alice should be in EqualityPerson table");
|
||||
Debug.Assert(alice.Name == "Alice", $"Expected Alice, got {alice.Name}");
|
||||
|
||||
// Validate EqualityProduct data
|
||||
var product = conn.Db.EqualityProduct.Id.Find(1);
|
||||
Debug.Assert(product != null, "Widget should be in EqualityProduct table");
|
||||
Debug.Assert(product.Name == "Widget", $"Expected Widget, got {product.Name}");
|
||||
Debug.Assert(product.Price == 999, $"Expected Price=999, got {product.Price}");
|
||||
|
||||
// Validate PlayerAction data (inserted by TestSumTypeEquality)
|
||||
var actions = conn.Db.PlayerAction.Iter().ToList();
|
||||
Debug.Assert(actions.Count >= 2, $"Expected at least 2 PlayerActions, got {actions.Count}");
|
||||
|
||||
// Validate ActionBatch data (inserted by TestListOfNullableSumTypes)
|
||||
var batches = conn.Db.ActionBatch.Iter().ToList();
|
||||
Debug.Assert(batches.Count >= 1, $"Expected at least 1 ActionBatch, got {batches.Count}");
|
||||
var batch = batches.FirstOrDefault(b => b.Id == 1);
|
||||
Debug.Assert(batch != null, "Expected ActionBatch with Id=1");
|
||||
Debug.Assert(batch.Actions != null, "ActionBatch should have non-null Actions list");
|
||||
Debug.Assert(batch.Actions.Count == 4, $"Expected 4 actions (2 null), got {batch.Actions.Count}");
|
||||
|
||||
// Validate LogEntry data (inserted by TestTableWithoutPrimaryKey)
|
||||
var logEntries = conn.Db.LogEntry.Iter().ToList();
|
||||
Debug.Assert(logEntries.Count >= 2, $"Expected at least 2 LogEntries, got {logEntries.Count}");
|
||||
|
||||
Log.Debug("Equality test results validated successfully");
|
||||
}
|
||||
|
||||
void ValidateSemijoinSubscriptions(IRemoteDbContext conn, Identity identity)
|
||||
{
|
||||
Log.Debug("Checking typed semijoin subscriptions...");
|
||||
@@ -882,6 +1050,10 @@ void OnSubscriptionApplied(SubscriptionEventContext context)
|
||||
ValidateIEnumerableViews(context);
|
||||
ValidateSemijoinSubscriptions(context, context.Identity!.Value);
|
||||
|
||||
// Run equality tests
|
||||
ValidateEqualitySubscriptions(context);
|
||||
RunEqualityTests(context);
|
||||
|
||||
// Do some operations that alter row state;
|
||||
// we will check that everything is in sync in the callbacks for these reducer calls.
|
||||
Log.Debug("Calling Add");
|
||||
|
||||
Generated
+78
@@ -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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void AddEqualityOrderHandler(ReducerEventContext ctx, uint id, string? customerName, System.Collections.Generic.List<SpacetimeDB.Types.ProductItem>? items);
|
||||
public event AddEqualityOrderHandler? OnAddEqualityOrder;
|
||||
|
||||
public void AddEqualityOrder(uint id, string? customerName, System.Collections.Generic.List<SpacetimeDB.Types.ProductItem>? items)
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.AddEqualityOrder(id, customerName, items));
|
||||
}
|
||||
|
||||
public bool InvokeAddEqualityOrder(ReducerEventContext ctx, Reducer.AddEqualityOrder args)
|
||||
{
|
||||
if (OnAddEqualityOrder == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnAddEqualityOrder(
|
||||
ctx,
|
||||
args.Id,
|
||||
args.CustomerName,
|
||||
args.Items
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class AddEqualityOrder : Reducer, IReducerArgs
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "customer_name")]
|
||||
public string? CustomerName;
|
||||
[DataMember(Name = "items")]
|
||||
public System.Collections.Generic.List<ProductItem>? Items;
|
||||
|
||||
public AddEqualityOrder(
|
||||
uint Id,
|
||||
string? CustomerName,
|
||||
System.Collections.Generic.List<ProductItem>? Items
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.CustomerName = CustomerName;
|
||||
this.Items = Items;
|
||||
}
|
||||
|
||||
public AddEqualityOrder()
|
||||
{
|
||||
}
|
||||
|
||||
string IReducerArgs.ReducerName => "add_equality_order";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+74
@@ -0,0 +1,74 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void AddEqualityPersonHandler(ReducerEventContext ctx, uint id, string name);
|
||||
public event AddEqualityPersonHandler? OnAddEqualityPerson;
|
||||
|
||||
public void AddEqualityPerson(uint id, string name)
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.AddEqualityPerson(id, name));
|
||||
}
|
||||
|
||||
public bool InvokeAddEqualityPerson(ReducerEventContext ctx, Reducer.AddEqualityPerson args)
|
||||
{
|
||||
if (OnAddEqualityPerson == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnAddEqualityPerson(
|
||||
ctx,
|
||||
args.Id,
|
||||
args.Name
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class AddEqualityPerson : Reducer, IReducerArgs
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "name")]
|
||||
public string Name;
|
||||
|
||||
public AddEqualityPerson(
|
||||
uint Id,
|
||||
string Name
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Name = Name;
|
||||
}
|
||||
|
||||
public AddEqualityPerson()
|
||||
{
|
||||
this.Name = "";
|
||||
}
|
||||
|
||||
string IReducerArgs.ReducerName => "add_equality_person";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+84
@@ -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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void AddEqualityProductHandler(ReducerEventContext ctx, uint id, string name, int price, int quantity);
|
||||
public event AddEqualityProductHandler? OnAddEqualityProduct;
|
||||
|
||||
public void AddEqualityProduct(uint id, string name, int price, int quantity)
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.AddEqualityProduct(id, name, price, quantity));
|
||||
}
|
||||
|
||||
public bool InvokeAddEqualityProduct(ReducerEventContext ctx, Reducer.AddEqualityProduct args)
|
||||
{
|
||||
if (OnAddEqualityProduct == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnAddEqualityProduct(
|
||||
ctx,
|
||||
args.Id,
|
||||
args.Name,
|
||||
args.Price,
|
||||
args.Quantity
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class AddEqualityProduct : Reducer, IReducerArgs
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "name")]
|
||||
public string Name;
|
||||
[DataMember(Name = "price")]
|
||||
public int Price;
|
||||
[DataMember(Name = "quantity")]
|
||||
public int Quantity;
|
||||
|
||||
public AddEqualityProduct(
|
||||
uint Id,
|
||||
string Name,
|
||||
int Price,
|
||||
int Quantity
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Name = Name;
|
||||
this.Price = Price;
|
||||
this.Quantity = Quantity;
|
||||
}
|
||||
|
||||
public AddEqualityProduct()
|
||||
{
|
||||
this.Name = "";
|
||||
}
|
||||
|
||||
string IReducerArgs.ReducerName => "add_equality_product";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void RunAllEqualityTestsHandler(ReducerEventContext ctx);
|
||||
public event RunAllEqualityTestsHandler? OnRunAllEqualityTests;
|
||||
|
||||
public void RunAllEqualityTests()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.RunAllEqualityTests());
|
||||
}
|
||||
|
||||
public bool InvokeRunAllEqualityTests(ReducerEventContext ctx, Reducer.RunAllEqualityTests args)
|
||||
{
|
||||
if (OnRunAllEqualityTests == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnRunAllEqualityTests(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class RunAllEqualityTests : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "run_all_equality_tests";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void RunComplexEqualityTestsHandler(ReducerEventContext ctx);
|
||||
public event RunComplexEqualityTestsHandler? OnRunComplexEqualityTests;
|
||||
|
||||
public void RunComplexEqualityTests()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.RunComplexEqualityTests());
|
||||
}
|
||||
|
||||
public bool InvokeRunComplexEqualityTests(ReducerEventContext ctx, Reducer.RunComplexEqualityTests args)
|
||||
{
|
||||
if (OnRunComplexEqualityTests == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnRunComplexEqualityTests(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class RunComplexEqualityTests : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "run_complex_equality_tests";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void RunEnumEqualityTestsHandler(ReducerEventContext ctx);
|
||||
public event RunEnumEqualityTestsHandler? OnRunEnumEqualityTests;
|
||||
|
||||
public void RunEnumEqualityTests()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.RunEnumEqualityTests());
|
||||
}
|
||||
|
||||
public bool InvokeRunEnumEqualityTests(ReducerEventContext ctx, Reducer.RunEnumEqualityTests args)
|
||||
{
|
||||
if (OnRunEnumEqualityTests == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnRunEnumEqualityTests(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class RunEnumEqualityTests : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "run_enum_equality_tests";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void RunEqualityTestsHandler(ReducerEventContext ctx);
|
||||
public event RunEqualityTestsHandler? OnRunEqualityTests;
|
||||
|
||||
public void RunEqualityTests()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.RunEqualityTests());
|
||||
}
|
||||
|
||||
public bool InvokeRunEqualityTests(ReducerEventContext ctx, Reducer.RunEqualityTests args)
|
||||
{
|
||||
if (OnRunEqualityTests == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnRunEqualityTests(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class RunEqualityTests : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "run_equality_tests";
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void ScheduleTaskHandler(ReducerEventContext ctx, ulong delayMicros);
|
||||
public event ScheduleTaskHandler? OnScheduleTask;
|
||||
|
||||
public void ScheduleTask(ulong delayMicros)
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.ScheduleTask(delayMicros));
|
||||
}
|
||||
|
||||
public bool InvokeScheduleTask(ReducerEventContext ctx, Reducer.ScheduleTask args)
|
||||
{
|
||||
if (OnScheduleTask == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnScheduleTask(
|
||||
ctx,
|
||||
args.DelayMicros
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class ScheduleTask : Reducer, IReducerArgs
|
||||
{
|
||||
[DataMember(Name = "delay_micros")]
|
||||
public ulong DelayMicros;
|
||||
|
||||
public ScheduleTask(ulong DelayMicros)
|
||||
{
|
||||
this.DelayMicros = DelayMicros;
|
||||
}
|
||||
|
||||
public ScheduleTask()
|
||||
{
|
||||
}
|
||||
|
||||
string IReducerArgs.ReducerName => "schedule_task";
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void TestListOfNullableSumTypesHandler(ReducerEventContext ctx);
|
||||
public event TestListOfNullableSumTypesHandler? OnTestListOfNullableSumTypes;
|
||||
|
||||
public void TestListOfNullableSumTypes()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.TestListOfNullableSumTypes());
|
||||
}
|
||||
|
||||
public bool InvokeTestListOfNullableSumTypes(ReducerEventContext ctx, Reducer.TestListOfNullableSumTypes args)
|
||||
{
|
||||
if (OnTestListOfNullableSumTypes == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnTestListOfNullableSumTypes(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class TestListOfNullableSumTypes : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "test_list_of_nullable_sum_types";
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void TestReducerWithSumTypeParamHandler(ReducerEventContext ctx, SpacetimeDB.Types.GameAction action);
|
||||
public event TestReducerWithSumTypeParamHandler? OnTestReducerWithSumTypeParam;
|
||||
|
||||
public void TestReducerWithSumTypeParam(SpacetimeDB.Types.GameAction action)
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.TestReducerWithSumTypeParam(action));
|
||||
}
|
||||
|
||||
public bool InvokeTestReducerWithSumTypeParam(ReducerEventContext ctx, Reducer.TestReducerWithSumTypeParam args)
|
||||
{
|
||||
if (OnTestReducerWithSumTypeParam == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnTestReducerWithSumTypeParam(
|
||||
ctx,
|
||||
args.Action
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class TestReducerWithSumTypeParam : Reducer, IReducerArgs
|
||||
{
|
||||
[DataMember(Name = "action")]
|
||||
public GameAction Action;
|
||||
|
||||
public TestReducerWithSumTypeParam(GameAction Action)
|
||||
{
|
||||
this.Action = Action;
|
||||
}
|
||||
|
||||
public TestReducerWithSumTypeParam()
|
||||
{
|
||||
this.Action = null!;
|
||||
}
|
||||
|
||||
string IReducerArgs.ReducerName => "test_reducer_with_sum_type_param";
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void TestSumTypeEqualityHandler(ReducerEventContext ctx);
|
||||
public event TestSumTypeEqualityHandler? OnTestSumTypeEquality;
|
||||
|
||||
public void TestSumTypeEquality()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.TestSumTypeEquality());
|
||||
}
|
||||
|
||||
public bool InvokeTestSumTypeEquality(ReducerEventContext ctx, Reducer.TestSumTypeEquality args)
|
||||
{
|
||||
if (OnTestSumTypeEquality == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnTestSumTypeEquality(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class TestSumTypeEquality : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "test_sum_type_equality";
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// 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 RemoteReducers : RemoteBase
|
||||
{
|
||||
public delegate void TestTableWithoutPrimaryKeyHandler(ReducerEventContext ctx);
|
||||
public event TestTableWithoutPrimaryKeyHandler? OnTestTableWithoutPrimaryKey;
|
||||
|
||||
public void TestTableWithoutPrimaryKey()
|
||||
{
|
||||
conn.InternalCallReducer(new Reducer.TestTableWithoutPrimaryKey());
|
||||
}
|
||||
|
||||
public bool InvokeTestTableWithoutPrimaryKey(ReducerEventContext ctx, Reducer.TestTableWithoutPrimaryKey args)
|
||||
{
|
||||
if (OnTestTableWithoutPrimaryKey == null)
|
||||
{
|
||||
if (InternalOnUnhandledReducerError != null)
|
||||
{
|
||||
switch (ctx.Event.Status)
|
||||
{
|
||||
case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;
|
||||
case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
OnTestTableWithoutPrimaryKey(
|
||||
ctx
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class Reducer
|
||||
{
|
||||
[SpacetimeDB.Type]
|
||||
[DataContract]
|
||||
public sealed partial class TestTableWithoutPrimaryKey : Reducer, IReducerArgs
|
||||
{
|
||||
string IReducerArgs.ReducerName => "test_table_without_primary_key";
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-1
@@ -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 2.0.5 (commit c095e61ada08c525487dce5279fdd0be472a2131).
|
||||
// This was generated using spacetimedb cli version 2.1.0 (commit 266eff98ab9e92e3c9308f101460151eea778d8e).
|
||||
|
||||
#nullable enable
|
||||
|
||||
@@ -29,12 +29,17 @@ namespace SpacetimeDB.Types
|
||||
{
|
||||
AddTable(Admins = new(conn));
|
||||
AddTable(Account = new(conn));
|
||||
AddTable(ActionBatch = new(conn));
|
||||
AddTable(AllViewPkPlayers = new(conn));
|
||||
AddTable(EqualityOrder = new(conn));
|
||||
AddTable(EqualityPerson = new(conn));
|
||||
AddTable(EqualityProduct = new(conn));
|
||||
AddTable(ExampleData = new(conn));
|
||||
AddTable(FindWhereTest = new(conn));
|
||||
AddTable(IenumerableAdminsFromFilter = new(conn));
|
||||
AddTable(IenumerablePlayersFromIter = new(conn));
|
||||
AddTable(IenumerablePlayersWithLevels = new(conn));
|
||||
AddTable(LogEntry = new(conn));
|
||||
AddTable(MyAccount = new(conn));
|
||||
AddTable(MyAccountMissing = new(conn));
|
||||
AddTable(MyLog = new(conn));
|
||||
@@ -45,6 +50,7 @@ namespace SpacetimeDB.Types
|
||||
AddTable(NullableVec = new(conn));
|
||||
AddTable(NullableVecView = new(conn));
|
||||
AddTable(Player = new(conn));
|
||||
AddTable(PlayerAction = new(conn));
|
||||
AddTable(PlayerLevel = new(conn));
|
||||
AddTable(PlayersAtLevelOne = new(conn));
|
||||
AddTable(RetryLog = new(conn));
|
||||
@@ -564,12 +570,17 @@ namespace SpacetimeDB.Types
|
||||
{
|
||||
new QueryBuilder().From.Admins().ToSql(),
|
||||
new QueryBuilder().From.Account().ToSql(),
|
||||
new QueryBuilder().From.ActionBatch().ToSql(),
|
||||
new QueryBuilder().From.AllViewPkPlayers().ToSql(),
|
||||
new QueryBuilder().From.EqualityOrder().ToSql(),
|
||||
new QueryBuilder().From.EqualityPerson().ToSql(),
|
||||
new QueryBuilder().From.EqualityProduct().ToSql(),
|
||||
new QueryBuilder().From.ExampleData().ToSql(),
|
||||
new QueryBuilder().From.FindWhereTest().ToSql(),
|
||||
new QueryBuilder().From.IenumerableAdminsFromFilter().ToSql(),
|
||||
new QueryBuilder().From.IenumerablePlayersFromIter().ToSql(),
|
||||
new QueryBuilder().From.IenumerablePlayersWithLevels().ToSql(),
|
||||
new QueryBuilder().From.LogEntry().ToSql(),
|
||||
new QueryBuilder().From.MyAccount().ToSql(),
|
||||
new QueryBuilder().From.MyAccountMissing().ToSql(),
|
||||
new QueryBuilder().From.MyLog().ToSql(),
|
||||
@@ -580,6 +591,7 @@ namespace SpacetimeDB.Types
|
||||
new QueryBuilder().From.NullableVec().ToSql(),
|
||||
new QueryBuilder().From.NullableVecView().ToSql(),
|
||||
new QueryBuilder().From.Player().ToSql(),
|
||||
new QueryBuilder().From.PlayerAction().ToSql(),
|
||||
new QueryBuilder().From.PlayerLevel().ToSql(),
|
||||
new QueryBuilder().From.PlayersAtLevelOne().ToSql(),
|
||||
new QueryBuilder().From.RetryLog().ToSql(),
|
||||
@@ -609,12 +621,17 @@ namespace SpacetimeDB.Types
|
||||
{
|
||||
public global::SpacetimeDB.Table<User, AdminsCols, AdminsIxCols> Admins() => new("admins", new AdminsCols("admins"), new AdminsIxCols("admins"));
|
||||
public global::SpacetimeDB.Table<Account, AccountCols, AccountIxCols> Account() => new("account", new AccountCols("account"), new AccountIxCols("account"));
|
||||
public global::SpacetimeDB.Table<ActionBatch, ActionBatchCols, ActionBatchIxCols> ActionBatch() => new("action_batch", new ActionBatchCols("action_batch"), new ActionBatchIxCols("action_batch"));
|
||||
public global::SpacetimeDB.Table<ViewPkPlayer, AllViewPkPlayersCols, AllViewPkPlayersIxCols> AllViewPkPlayers() => new("all_view_pk_players", new AllViewPkPlayersCols("all_view_pk_players"), new AllViewPkPlayersIxCols("all_view_pk_players"));
|
||||
public global::SpacetimeDB.Table<EqualityOrder, EqualityOrderCols, EqualityOrderIxCols> EqualityOrder() => new("equality_order", new EqualityOrderCols("equality_order"), new EqualityOrderIxCols("equality_order"));
|
||||
public global::SpacetimeDB.Table<EqualityPerson, EqualityPersonCols, EqualityPersonIxCols> EqualityPerson() => new("equality_person", new EqualityPersonCols("equality_person"), new EqualityPersonIxCols("equality_person"));
|
||||
public global::SpacetimeDB.Table<EqualityProduct, EqualityProductCols, EqualityProductIxCols> EqualityProduct() => new("equality_product", new EqualityProductCols("equality_product"), new EqualityProductIxCols("equality_product"));
|
||||
public global::SpacetimeDB.Table<ExampleData, ExampleDataCols, ExampleDataIxCols> ExampleData() => new("example_data", new ExampleDataCols("example_data"), new ExampleDataIxCols("example_data"));
|
||||
public global::SpacetimeDB.Table<WhereTest, FindWhereTestCols, FindWhereTestIxCols> FindWhereTest() => new("find_where_test", new FindWhereTestCols("find_where_test"), new FindWhereTestIxCols("find_where_test"));
|
||||
public global::SpacetimeDB.Table<User, IenumerableAdminsFromFilterCols, IenumerableAdminsFromFilterIxCols> IenumerableAdminsFromFilter() => new("ienumerable_admins_from_filter", new IenumerableAdminsFromFilterCols("ienumerable_admins_from_filter"), new IenumerableAdminsFromFilterIxCols("ienumerable_admins_from_filter"));
|
||||
public global::SpacetimeDB.Table<Player, IenumerablePlayersFromIterCols, IenumerablePlayersFromIterIxCols> IenumerablePlayersFromIter() => new("ienumerable_players_from_iter", new IenumerablePlayersFromIterCols("ienumerable_players_from_iter"), new IenumerablePlayersFromIterIxCols("ienumerable_players_from_iter"));
|
||||
public global::SpacetimeDB.Table<PlayerAndLevel, IenumerablePlayersWithLevelsCols, IenumerablePlayersWithLevelsIxCols> IenumerablePlayersWithLevels() => new("ienumerable_players_with_levels", new IenumerablePlayersWithLevelsCols("ienumerable_players_with_levels"), new IenumerablePlayersWithLevelsIxCols("ienumerable_players_with_levels"));
|
||||
public global::SpacetimeDB.Table<LogEntry, LogEntryCols, LogEntryIxCols> LogEntry() => new("log_entry", new LogEntryCols("log_entry"), new LogEntryIxCols("log_entry"));
|
||||
public global::SpacetimeDB.Table<Account, MyAccountCols, MyAccountIxCols> MyAccount() => new("my_account", new MyAccountCols("my_account"), new MyAccountIxCols("my_account"));
|
||||
public global::SpacetimeDB.Table<Account, MyAccountMissingCols, MyAccountMissingIxCols> MyAccountMissing() => new("my_account_missing", new MyAccountMissingCols("my_account_missing"), new MyAccountMissingIxCols("my_account_missing"));
|
||||
public global::SpacetimeDB.Table<MyLog, MyLogCols, MyLogIxCols> MyLog() => new("my_log", new MyLogCols("my_log"), new MyLogIxCols("my_log"));
|
||||
@@ -625,6 +642,7 @@ namespace SpacetimeDB.Types
|
||||
public global::SpacetimeDB.Table<NullableVec, NullableVecCols, NullableVecIxCols> NullableVec() => new("nullable_vec", new NullableVecCols("nullable_vec"), new NullableVecIxCols("nullable_vec"));
|
||||
public global::SpacetimeDB.Table<NullableVec, NullableVecViewCols, NullableVecViewIxCols> NullableVecView() => new("nullable_vec_view", new NullableVecViewCols("nullable_vec_view"), new NullableVecViewIxCols("nullable_vec_view"));
|
||||
public global::SpacetimeDB.Table<Player, PlayerCols, PlayerIxCols> Player() => new("player", new PlayerCols("player"), new PlayerIxCols("player"));
|
||||
public global::SpacetimeDB.Table<PlayerAction, PlayerActionCols, PlayerActionIxCols> PlayerAction() => new("player_action", new PlayerActionCols("player_action"), new PlayerActionIxCols("player_action"));
|
||||
public global::SpacetimeDB.Table<PlayerLevel, PlayerLevelCols, PlayerLevelIxCols> PlayerLevel() => new("player_level", new PlayerLevelCols("player_level"), new PlayerLevelIxCols("player_level"));
|
||||
public global::SpacetimeDB.Table<PlayerAndLevel, PlayersAtLevelOneCols, PlayersAtLevelOneIxCols> PlayersAtLevelOne() => new("players_at_level_one", new PlayersAtLevelOneCols("players_at_level_one"), new PlayersAtLevelOneIxCols("players_at_level_one"));
|
||||
public global::SpacetimeDB.Table<RetryLog, RetryLogCols, RetryLogIxCols> RetryLog() => new("retry_log", new RetryLogCols("retry_log"), new RetryLogIxCols("retry_log"));
|
||||
@@ -728,6 +746,9 @@ namespace SpacetimeDB.Types
|
||||
return reducer switch
|
||||
{
|
||||
Reducer.Add args => Reducers.InvokeAdd(eventContext, args),
|
||||
Reducer.AddEqualityOrder args => Reducers.InvokeAddEqualityOrder(eventContext, args),
|
||||
Reducer.AddEqualityPerson args => Reducers.InvokeAddEqualityPerson(eventContext, args),
|
||||
Reducer.AddEqualityProduct args => Reducers.InvokeAddEqualityProduct(eventContext, args),
|
||||
Reducer.Delete args => Reducers.InvokeDelete(eventContext, args),
|
||||
Reducer.EmitTestEvent args => Reducers.InvokeEmitTestEvent(eventContext, args),
|
||||
Reducer.InsertEmptyStringIntoNonNullable args => Reducers.InvokeInsertEmptyStringIntoNonNullable(eventContext, args),
|
||||
@@ -739,7 +760,16 @@ namespace SpacetimeDB.Types
|
||||
Reducer.InsertViewPkPlayer args => Reducers.InvokeInsertViewPkPlayer(eventContext, args),
|
||||
Reducer.InsertWhereTest args => Reducers.InvokeInsertWhereTest(eventContext, args),
|
||||
Reducer.Noop args => Reducers.InvokeNoop(eventContext, args),
|
||||
Reducer.RunAllEqualityTests args => Reducers.InvokeRunAllEqualityTests(eventContext, args),
|
||||
Reducer.RunComplexEqualityTests args => Reducers.InvokeRunComplexEqualityTests(eventContext, args),
|
||||
Reducer.RunEnumEqualityTests args => Reducers.InvokeRunEnumEqualityTests(eventContext, args),
|
||||
Reducer.RunEqualityTests args => Reducers.InvokeRunEqualityTests(eventContext, args),
|
||||
Reducer.ScheduleTask args => Reducers.InvokeScheduleTask(eventContext, args),
|
||||
Reducer.SetNullableVec args => Reducers.InvokeSetNullableVec(eventContext, args),
|
||||
Reducer.TestListOfNullableSumTypes args => Reducers.InvokeTestListOfNullableSumTypes(eventContext, args),
|
||||
Reducer.TestReducerWithSumTypeParam args => Reducers.InvokeTestReducerWithSumTypeParam(eventContext, args),
|
||||
Reducer.TestSumTypeEquality args => Reducers.InvokeTestSumTypeEquality(eventContext, args),
|
||||
Reducer.TestTableWithoutPrimaryKey args => Reducers.InvokeTestTableWithoutPrimaryKey(eventContext, args),
|
||||
Reducer.ThrowError args => Reducers.InvokeThrowError(eventContext, args),
|
||||
Reducer.UpdateViewPkPlayer args => Reducers.InvokeUpdateViewPkPlayer(eventContext, args),
|
||||
Reducer.UpdateWhereTest args => Reducers.InvokeUpdateWhereTest(eventContext, args),
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// 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 ActionBatchHandle : RemoteTableHandle<EventContext, ActionBatch>
|
||||
{
|
||||
protected override string RemoteTableName => "action_batch";
|
||||
|
||||
public sealed class IdUniqueIndex : UniqueIndexBase<uint>
|
||||
{
|
||||
protected override uint GetKey(ActionBatch row) => row.Id;
|
||||
|
||||
public IdUniqueIndex(ActionBatchHandle table) : base(table) { }
|
||||
}
|
||||
|
||||
public readonly IdUniqueIndex Id;
|
||||
|
||||
internal ActionBatchHandle(DbConnection conn) : base(conn)
|
||||
{
|
||||
Id = new(this);
|
||||
}
|
||||
|
||||
protected override object GetPrimaryKey(ActionBatch row) => row.Id;
|
||||
}
|
||||
|
||||
public readonly ActionBatchHandle ActionBatch;
|
||||
}
|
||||
|
||||
public sealed class ActionBatchCols
|
||||
{
|
||||
public global::SpacetimeDB.Col<ActionBatch, uint> Id { get; }
|
||||
public global::SpacetimeDB.Col<ActionBatch, System.Collections.Generic.List<GameAction?>> Actions { get; }
|
||||
|
||||
public ActionBatchCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.Col<ActionBatch, uint>(tableName, "id");
|
||||
Actions = new global::SpacetimeDB.Col<ActionBatch, System.Collections.Generic.List<GameAction?>>(tableName, "actions");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ActionBatchIxCols
|
||||
{
|
||||
public global::SpacetimeDB.IxCol<ActionBatch, uint> Id { get; }
|
||||
|
||||
public ActionBatchIxCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.IxCol<ActionBatch, uint>(tableName, "id");
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// 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 EqualityOrderHandle : RemoteTableHandle<EventContext, EqualityOrder>
|
||||
{
|
||||
protected override string RemoteTableName => "equality_order";
|
||||
|
||||
public sealed class IdUniqueIndex : UniqueIndexBase<uint>
|
||||
{
|
||||
protected override uint GetKey(EqualityOrder row) => row.Id;
|
||||
|
||||
public IdUniqueIndex(EqualityOrderHandle table) : base(table) { }
|
||||
}
|
||||
|
||||
public readonly IdUniqueIndex Id;
|
||||
|
||||
internal EqualityOrderHandle(DbConnection conn) : base(conn)
|
||||
{
|
||||
Id = new(this);
|
||||
}
|
||||
|
||||
protected override object GetPrimaryKey(EqualityOrder row) => row.Id;
|
||||
}
|
||||
|
||||
public readonly EqualityOrderHandle EqualityOrder;
|
||||
}
|
||||
|
||||
public sealed class EqualityOrderCols
|
||||
{
|
||||
public global::SpacetimeDB.Col<EqualityOrder, uint> Id { get; }
|
||||
public global::SpacetimeDB.Col<EqualityOrder, string> CustomerName { get; }
|
||||
public global::SpacetimeDB.Col<EqualityOrder, System.Collections.Generic.List<ProductItem>> Items { get; }
|
||||
|
||||
public EqualityOrderCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.Col<EqualityOrder, uint>(tableName, "id");
|
||||
CustomerName = new global::SpacetimeDB.Col<EqualityOrder, string>(tableName, "customer_name");
|
||||
Items = new global::SpacetimeDB.Col<EqualityOrder, System.Collections.Generic.List<ProductItem>>(tableName, "items");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EqualityOrderIxCols
|
||||
{
|
||||
public global::SpacetimeDB.IxCol<EqualityOrder, uint> Id { get; }
|
||||
|
||||
public EqualityOrderIxCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.IxCol<EqualityOrder, uint>(tableName, "id");
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// 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 EqualityPersonHandle : RemoteTableHandle<EventContext, EqualityPerson>
|
||||
{
|
||||
protected override string RemoteTableName => "equality_person";
|
||||
|
||||
public sealed class IdUniqueIndex : UniqueIndexBase<uint>
|
||||
{
|
||||
protected override uint GetKey(EqualityPerson row) => row.Id;
|
||||
|
||||
public IdUniqueIndex(EqualityPersonHandle table) : base(table) { }
|
||||
}
|
||||
|
||||
public readonly IdUniqueIndex Id;
|
||||
|
||||
internal EqualityPersonHandle(DbConnection conn) : base(conn)
|
||||
{
|
||||
Id = new(this);
|
||||
}
|
||||
|
||||
protected override object GetPrimaryKey(EqualityPerson row) => row.Id;
|
||||
}
|
||||
|
||||
public readonly EqualityPersonHandle EqualityPerson;
|
||||
}
|
||||
|
||||
public sealed class EqualityPersonCols
|
||||
{
|
||||
public global::SpacetimeDB.Col<EqualityPerson, uint> Id { get; }
|
||||
public global::SpacetimeDB.Col<EqualityPerson, string> Name { get; }
|
||||
|
||||
public EqualityPersonCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.Col<EqualityPerson, uint>(tableName, "id");
|
||||
Name = new global::SpacetimeDB.Col<EqualityPerson, string>(tableName, "name");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EqualityPersonIxCols
|
||||
{
|
||||
public global::SpacetimeDB.IxCol<EqualityPerson, uint> Id { get; }
|
||||
|
||||
public EqualityPersonIxCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.IxCol<EqualityPerson, uint>(tableName, "id");
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// 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 EqualityProductHandle : RemoteTableHandle<EventContext, EqualityProduct>
|
||||
{
|
||||
protected override string RemoteTableName => "equality_product";
|
||||
|
||||
public sealed class IdUniqueIndex : UniqueIndexBase<uint>
|
||||
{
|
||||
protected override uint GetKey(EqualityProduct row) => row.Id;
|
||||
|
||||
public IdUniqueIndex(EqualityProductHandle table) : base(table) { }
|
||||
}
|
||||
|
||||
public readonly IdUniqueIndex Id;
|
||||
|
||||
internal EqualityProductHandle(DbConnection conn) : base(conn)
|
||||
{
|
||||
Id = new(this);
|
||||
}
|
||||
|
||||
protected override object GetPrimaryKey(EqualityProduct row) => row.Id;
|
||||
}
|
||||
|
||||
public readonly EqualityProductHandle EqualityProduct;
|
||||
}
|
||||
|
||||
public sealed class EqualityProductCols
|
||||
{
|
||||
public global::SpacetimeDB.Col<EqualityProduct, uint> Id { get; }
|
||||
public global::SpacetimeDB.Col<EqualityProduct, string> Name { get; }
|
||||
public global::SpacetimeDB.Col<EqualityProduct, int> Price { get; }
|
||||
public global::SpacetimeDB.Col<EqualityProduct, int> Quantity { get; }
|
||||
|
||||
public EqualityProductCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.Col<EqualityProduct, uint>(tableName, "id");
|
||||
Name = new global::SpacetimeDB.Col<EqualityProduct, string>(tableName, "name");
|
||||
Price = new global::SpacetimeDB.Col<EqualityProduct, int>(tableName, "price");
|
||||
Quantity = new global::SpacetimeDB.Col<EqualityProduct, int>(tableName, "quantity");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EqualityProductIxCols
|
||||
{
|
||||
public global::SpacetimeDB.IxCol<EqualityProduct, uint> Id { get; }
|
||||
|
||||
public EqualityProductIxCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.IxCol<EqualityProduct, uint>(tableName, "id");
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// 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 LogEntryHandle : RemoteTableHandle<EventContext, LogEntry>
|
||||
{
|
||||
protected override string RemoteTableName => "log_entry";
|
||||
|
||||
internal LogEntryHandle(DbConnection conn) : base(conn)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public readonly LogEntryHandle LogEntry;
|
||||
}
|
||||
|
||||
public sealed class LogEntryCols
|
||||
{
|
||||
public global::SpacetimeDB.Col<LogEntry, string> Message { get; }
|
||||
public global::SpacetimeDB.Col<LogEntry, ulong> Timestamp { get; }
|
||||
|
||||
public LogEntryCols(string tableName)
|
||||
{
|
||||
Message = new global::SpacetimeDB.Col<LogEntry, string>(tableName, "message");
|
||||
Timestamp = new global::SpacetimeDB.Col<LogEntry, ulong>(tableName, "timestamp");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class LogEntryIxCols
|
||||
{
|
||||
|
||||
public LogEntryIxCols(string tableName)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// 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 PlayerActionHandle : RemoteTableHandle<EventContext, PlayerAction>
|
||||
{
|
||||
protected override string RemoteTableName => "player_action";
|
||||
|
||||
public sealed class IdUniqueIndex : UniqueIndexBase<uint>
|
||||
{
|
||||
protected override uint GetKey(PlayerAction row) => row.Id;
|
||||
|
||||
public IdUniqueIndex(PlayerActionHandle table) : base(table) { }
|
||||
}
|
||||
|
||||
public readonly IdUniqueIndex Id;
|
||||
|
||||
internal PlayerActionHandle(DbConnection conn) : base(conn)
|
||||
{
|
||||
Id = new(this);
|
||||
}
|
||||
|
||||
protected override object GetPrimaryKey(PlayerAction row) => row.Id;
|
||||
}
|
||||
|
||||
public readonly PlayerActionHandle PlayerAction;
|
||||
}
|
||||
|
||||
public sealed class PlayerActionCols
|
||||
{
|
||||
public global::SpacetimeDB.Col<PlayerAction, uint> Id { get; }
|
||||
public global::SpacetimeDB.Col<PlayerAction, GameAction> Action { get; }
|
||||
|
||||
public PlayerActionCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.Col<PlayerAction, uint>(tableName, "id");
|
||||
Action = new global::SpacetimeDB.Col<PlayerAction, GameAction>(tableName, "action");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PlayerActionIxCols
|
||||
{
|
||||
public global::SpacetimeDB.IxCol<PlayerAction, uint> Id { get; }
|
||||
|
||||
public PlayerActionIxCols(string tableName)
|
||||
{
|
||||
Id = new global::SpacetimeDB.IxCol<PlayerAction, uint>(tableName, "id");
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -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 ActionBatch
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "actions")]
|
||||
public System.Collections.Generic.List<GameAction?> Actions;
|
||||
|
||||
public ActionBatch(
|
||||
uint Id,
|
||||
System.Collections.Generic.List<GameAction?> Actions
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Actions = Actions;
|
||||
}
|
||||
|
||||
public ActionBatch()
|
||||
{
|
||||
this.Actions = new();
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// 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 EqualityOrder
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "customer_name")]
|
||||
public string? CustomerName;
|
||||
[DataMember(Name = "items")]
|
||||
public System.Collections.Generic.List<ProductItem>? Items;
|
||||
|
||||
public EqualityOrder(
|
||||
uint Id,
|
||||
string? CustomerName,
|
||||
System.Collections.Generic.List<ProductItem>? Items
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.CustomerName = CustomerName;
|
||||
this.Items = Items;
|
||||
}
|
||||
|
||||
public EqualityOrder()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -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 EqualityPerson
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "name")]
|
||||
public string Name;
|
||||
|
||||
public EqualityPerson(
|
||||
uint Id,
|
||||
string Name
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Name = Name;
|
||||
}
|
||||
|
||||
public EqualityPerson()
|
||||
{
|
||||
this.Name = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// 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 EqualityProduct
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "name")]
|
||||
public string Name;
|
||||
[DataMember(Name = "price")]
|
||||
public int Price;
|
||||
[DataMember(Name = "quantity")]
|
||||
public int Quantity;
|
||||
|
||||
public EqualityProduct(
|
||||
uint Id,
|
||||
string Name,
|
||||
int Price,
|
||||
int Quantity
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Name = Name;
|
||||
this.Price = Price;
|
||||
this.Quantity = Quantity;
|
||||
}
|
||||
|
||||
public EqualityProduct()
|
||||
{
|
||||
this.Name = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// 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 GameAction : SpacetimeDB.TaggedEnum<(
|
||||
string Move,
|
||||
string Attack,
|
||||
int Defend
|
||||
)>;
|
||||
}
|
||||
+35
@@ -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 LogEntry
|
||||
{
|
||||
[DataMember(Name = "message")]
|
||||
public string Message;
|
||||
[DataMember(Name = "timestamp")]
|
||||
public ulong Timestamp;
|
||||
|
||||
public LogEntry(
|
||||
string Message,
|
||||
ulong Timestamp
|
||||
)
|
||||
{
|
||||
this.Message = Message;
|
||||
this.Timestamp = Timestamp;
|
||||
}
|
||||
|
||||
public LogEntry()
|
||||
{
|
||||
this.Message = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -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 PlayerAction
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public uint Id;
|
||||
[DataMember(Name = "action")]
|
||||
public GameAction Action;
|
||||
|
||||
public PlayerAction(
|
||||
uint Id,
|
||||
GameAction Action
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Action = Action;
|
||||
}
|
||||
|
||||
public PlayerAction()
|
||||
{
|
||||
this.Action = null!;
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// 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 ProductItem
|
||||
{
|
||||
[DataMember(Name = "product_id")]
|
||||
public uint ProductId;
|
||||
[DataMember(Name = "quantity")]
|
||||
public int Quantity;
|
||||
|
||||
public ProductItem(
|
||||
uint ProductId,
|
||||
int Quantity
|
||||
)
|
||||
{
|
||||
this.ProductId = ProductId;
|
||||
this.Quantity = Quantity;
|
||||
}
|
||||
|
||||
public ProductItem()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// 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 ScheduledTask
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public ulong Id;
|
||||
[DataMember(Name = "task_name")]
|
||||
public string TaskName;
|
||||
[DataMember(Name = "scheduled_at")]
|
||||
public SpacetimeDB.ScheduleAt ScheduledAt;
|
||||
|
||||
public ScheduledTask(
|
||||
ulong Id,
|
||||
string TaskName,
|
||||
SpacetimeDB.ScheduleAt ScheduledAt
|
||||
)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.TaskName = TaskName;
|
||||
this.ScheduledAt = ScheduledAt;
|
||||
}
|
||||
|
||||
public ScheduledTask()
|
||||
{
|
||||
this.TaskName = "";
|
||||
this.ScheduledAt = null!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,6 +201,93 @@ public static partial class Module
|
||||
public ulong PlayerId;
|
||||
}
|
||||
|
||||
// === Equality Test Tables and Types ===
|
||||
|
||||
// Struct for testing struct equality (used in Product and Order)
|
||||
[SpacetimeDB.Type]
|
||||
public partial struct ProductItem
|
||||
{
|
||||
public uint ProductId;
|
||||
public int Quantity;
|
||||
}
|
||||
|
||||
// Simple struct - basic value type equality testing
|
||||
[SpacetimeDB.Table(Accessor = "equality_person", Public = true)]
|
||||
public partial struct EqualityPerson
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public uint Id;
|
||||
public string Name;
|
||||
}
|
||||
|
||||
// Complex struct with multiple fields for equality testing
|
||||
[SpacetimeDB.Table(Accessor = "equality_product", Public = true)]
|
||||
public partial struct EqualityProduct
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public uint Id;
|
||||
public string Name;
|
||||
public int Price; // Price in cents
|
||||
public int Quantity;
|
||||
}
|
||||
|
||||
// Record with reference type fields for equality testing
|
||||
[SpacetimeDB.Table(Accessor = "equality_order", Public = true)]
|
||||
public partial record EqualityOrder
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public uint Id;
|
||||
public string? CustomerName;
|
||||
public List<ProductItem>? Items;
|
||||
}
|
||||
|
||||
// Enum for equality testing
|
||||
public enum TestStatus
|
||||
{
|
||||
Pending,
|
||||
Active,
|
||||
Completed
|
||||
}
|
||||
|
||||
// Custom sum type for testing TaggedEnum equality
|
||||
[SpacetimeDB.Type]
|
||||
public partial record GameAction : TaggedEnum<(string Move, string Attack, int Defend)> { }
|
||||
|
||||
[SpacetimeDB.Table(Accessor = "player_action", Public = true)]
|
||||
public partial struct PlayerAction
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public uint Id;
|
||||
public GameAction Action;
|
||||
}
|
||||
|
||||
// List of nullable sum types - tests nullable suffix handling
|
||||
[SpacetimeDB.Table(Accessor = "action_batch", Public = true)]
|
||||
public partial struct ActionBatch
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public uint Id;
|
||||
public List<GameAction?> Actions;
|
||||
}
|
||||
|
||||
// Table WITHOUT primary key - triggers RawIndexDefV10.Equals/GetHashCode path
|
||||
[SpacetimeDB.Table(Accessor = "log_entry", Public = true)]
|
||||
public partial struct LogEntry
|
||||
{
|
||||
public string Message;
|
||||
public ulong Timestamp;
|
||||
}
|
||||
|
||||
// Scheduled table - tests ScheduleAt sum type (manually-written, uses ReferenceUse)
|
||||
[SpacetimeDB.Table(Accessor = "scheduled_task", Scheduled = "ExecuteScheduledTask", ScheduledAt = "ScheduledAt")]
|
||||
public partial struct ScheduledTask
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public ulong Id;
|
||||
public string TaskName;
|
||||
public ScheduleAt ScheduledAt;
|
||||
}
|
||||
|
||||
// At-most-one row: return T?
|
||||
[SpacetimeDB.View(Accessor = "my_player", Public = true)]
|
||||
public static Player? MyPlayer(ViewContext ctx)
|
||||
@@ -882,6 +969,255 @@ public static partial class Module
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void Noop(ReducerContext ctx) { }
|
||||
|
||||
// === Equality Test Reducers ===
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void AddEqualityPerson(ReducerContext ctx, uint id, string name)
|
||||
{
|
||||
ctx.Db.equality_person.Insert(new EqualityPerson { Id = id, Name = name });
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void AddEqualityProduct(ReducerContext ctx, uint id, string name, int price, int quantity)
|
||||
{
|
||||
ctx.Db.equality_product.Insert(new EqualityProduct { Id = id, Name = name, Price = price, Quantity = quantity });
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void AddEqualityOrder(ReducerContext ctx, uint id, string? customerName, List<ProductItem>? items)
|
||||
{
|
||||
ctx.Db.equality_order.Insert(new EqualityOrder { Id = id, CustomerName = customerName, Items = items });
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void RunEqualityTests(ReducerContext ctx)
|
||||
{
|
||||
Log.Info("=== Testing Equality ===");
|
||||
|
||||
// Test 1: Direct string comparison (no allocation)
|
||||
bool stringEqual = "Alice" == "Alice";
|
||||
Log.Info($"Test 1 - String equality: {stringEqual}");
|
||||
|
||||
// Test 2: Enum equality (no allocation)
|
||||
var s1 = TestStatus.Pending;
|
||||
var s2 = TestStatus.Pending;
|
||||
bool enumEqual = s1 == s2;
|
||||
Log.Info($"Test 2 - Enum equality: {enumEqual}");
|
||||
|
||||
// Test 3: Integer equality (no allocation)
|
||||
int i1 = 42;
|
||||
int i2 = 42;
|
||||
bool intEqual = i1 == i2;
|
||||
Log.Info($"Test 3 - Int equality: {intEqual}");
|
||||
|
||||
Log.Info("=== Equality Tests Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void RunComplexEqualityTests(ReducerContext ctx)
|
||||
{
|
||||
Log.Info("=== Testing Complex Equality ===");
|
||||
|
||||
// Test struct .Equals() - this used to cause boxing/allocation
|
||||
var item1 = new ProductItem { ProductId = 1, Quantity = 10 };
|
||||
var item2 = new ProductItem { ProductId = 1, Quantity = 10 };
|
||||
var item3 = new ProductItem { ProductId = 2, Quantity = 5 };
|
||||
|
||||
bool structEqual = item1.Equals(item2);
|
||||
bool structNotEqual = item1.Equals(item3);
|
||||
Log.Info($"Struct equality (same): {structEqual}");
|
||||
Log.Info($"Struct equality (different): {structNotEqual}");
|
||||
|
||||
// Test Person struct equality (table type)
|
||||
var person1 = new EqualityPerson { Id = 100, Name = "Test" };
|
||||
var person2 = new EqualityPerson { Id = 100, Name = "Test" };
|
||||
var person3 = new EqualityPerson { Id = 200, Name = "Other" };
|
||||
|
||||
bool personEqual = person1.Equals(person2);
|
||||
bool personNotEqual = person1.Equals(person3);
|
||||
Log.Info($"Person struct equality (same): {personEqual}");
|
||||
Log.Info($"Person struct equality (different): {personNotEqual}");
|
||||
|
||||
Log.Info("=== Complex Equality Tests Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void RunEnumEqualityTests(ReducerContext ctx)
|
||||
{
|
||||
var s1 = TestStatus.Pending;
|
||||
var s2 = TestStatus.Pending;
|
||||
var s3 = TestStatus.Active;
|
||||
|
||||
bool equal1 = s1 == s2; // Should be true
|
||||
bool equal2 = s1 == s3; // Should be false
|
||||
|
||||
Log.Info($"Enum equality (same): {equal1}");
|
||||
Log.Info($"Enum equality (different): {equal2}");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void TestTableWithoutPrimaryKey(ReducerContext ctx)
|
||||
{
|
||||
Log.Info("=== Testing Table WITHOUT Primary Key (RawIndexDefV10 path) ===");
|
||||
|
||||
// This triggers RawIndexDefV10.Equals/GetHashCode which previously failed
|
||||
ctx.Db.log_entry.Insert(new LogEntry { Message = "Test 1", Timestamp = 1000 });
|
||||
ctx.Db.log_entry.Insert(new LogEntry { Message = "Test 2", Timestamp = 2000 });
|
||||
|
||||
int count = 0;
|
||||
foreach (var entry in ctx.Db.log_entry.Iter())
|
||||
{
|
||||
count++;
|
||||
Log.Info($"LogEntry: {entry.Message} at {entry.Timestamp}");
|
||||
}
|
||||
|
||||
Log.Info($"Total log entries: {count}");
|
||||
Log.Info("=== Table Without Primary Key Test Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void TestSumTypeEquality(ReducerContext ctx)
|
||||
{
|
||||
Log.Info("=== Testing Sum Type (TaggedEnum) Equality ===");
|
||||
|
||||
// Test sum type equality via generated Equals
|
||||
var action1 = new GameAction.Move("North");
|
||||
var action2 = new GameAction.Move("North");
|
||||
var action3 = new GameAction.Attack("Sword");
|
||||
|
||||
bool equal = action1.Equals(action2);
|
||||
bool notEqual = action1.Equals(action3);
|
||||
|
||||
Log.Info($"Sum type equality (same variant): {equal}");
|
||||
Log.Info($"Sum type equality (different variant): {!notEqual}");
|
||||
|
||||
// Insert and retrieve sum type from table
|
||||
ctx.Db.player_action.Insert(new PlayerAction { Id = 1, Action = action1 });
|
||||
ctx.Db.player_action.Insert(new PlayerAction { Id = 2, Action = action3 });
|
||||
|
||||
foreach (var pa in ctx.Db.player_action.Iter())
|
||||
{
|
||||
var desc = pa.Action switch
|
||||
{
|
||||
GameAction.Move(var m) => $"Move: {m}",
|
||||
GameAction.Attack(var a) => $"Attack: {a}",
|
||||
GameAction.Defend(var d) => $"Defend: {d}",
|
||||
_ => "Unknown"
|
||||
};
|
||||
Log.Info($"PlayerAction {pa.Id}: {desc}");
|
||||
}
|
||||
|
||||
Log.Info("=== Sum Type Equality Test Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void TestListOfNullableSumTypes(ReducerContext ctx)
|
||||
{
|
||||
Log.Info("=== Testing List of Nullable Sum Types ===");
|
||||
|
||||
// This exercises SumTypeUse with nullable suffix (GameAction?)
|
||||
var actions = new List<GameAction?>
|
||||
{
|
||||
new GameAction.Move("North"),
|
||||
null,
|
||||
new GameAction.Attack("Bow"),
|
||||
null
|
||||
};
|
||||
|
||||
ctx.Db.action_batch.Insert(new ActionBatch { Id = 1, Actions = actions });
|
||||
|
||||
foreach (var batch in ctx.Db.action_batch.Iter())
|
||||
{
|
||||
Log.Info($"Batch {batch.Id} has {batch.Actions?.Count ?? 0} actions");
|
||||
if (batch.Actions != null)
|
||||
{
|
||||
for (int i = 0; i < batch.Actions.Count; i++)
|
||||
{
|
||||
var desc = batch.Actions[i] switch
|
||||
{
|
||||
null => "null",
|
||||
GameAction.Move(var m) => $"Move({m})",
|
||||
GameAction.Attack(var a) => $"Attack({a})",
|
||||
GameAction.Defend(var d) => $"Defend({d})",
|
||||
_ => "Unknown"
|
||||
};
|
||||
Log.Info($" Action {i}: {desc}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info("=== List of Nullable Sum Types Test Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void ExecuteScheduledTask(ReducerContext ctx, ScheduledTask task)
|
||||
{
|
||||
Log.Info($"Executing scheduled task: {task.TaskName}");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void ScheduleTask(ReducerContext ctx, ulong delayMicros)
|
||||
{
|
||||
Log.Info("=== Testing Scheduled Table (ScheduleAt sum type) ===");
|
||||
|
||||
// ScheduleAt is a manually-written sum type - tests ReferenceUse path
|
||||
var scheduledAt = ScheduleAt.TimeSpanFromMicroseconds((long)delayMicros);
|
||||
|
||||
ctx.Db.scheduled_task.Insert(new ScheduledTask
|
||||
{
|
||||
Id = 1,
|
||||
TaskName = "Test Task",
|
||||
ScheduledAt = scheduledAt
|
||||
});
|
||||
|
||||
Log.Info($"Scheduled task for {delayMicros} microseconds from now");
|
||||
Log.Info("=== Scheduled Table Test Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void TestReducerWithSumTypeParam(ReducerContext ctx, GameAction action)
|
||||
{
|
||||
Log.Info("=== Testing Reducer with Sum Type Parameter ===");
|
||||
|
||||
// This exercises SumTypeUse.EqualsStatement for parameter comparison
|
||||
var match = action switch
|
||||
{
|
||||
GameAction.Move(var m) => $"Moving: {m}",
|
||||
GameAction.Attack(var a) => $"Attacking with: {a}",
|
||||
GameAction.Defend(var d) => $"Defending with power: {d}",
|
||||
_ => "Unknown action"
|
||||
};
|
||||
|
||||
Log.Info(match);
|
||||
Log.Info("=== Reducer with Sum Type Parameter Test Complete ===");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void RunAllEqualityTests(ReducerContext ctx)
|
||||
{
|
||||
Log.Info("========== Starting Equality Tests ==========");
|
||||
|
||||
// Insert test data
|
||||
ctx.Db.equality_person.Insert(new EqualityPerson { Id = 1, Name = "Alice" });
|
||||
ctx.Db.equality_product.Insert(new EqualityProduct { Id = 1, Name = "Widget", Price = 999, Quantity = 100 });
|
||||
|
||||
// Run tests
|
||||
RunEqualityTests(ctx);
|
||||
RunComplexEqualityTests(ctx);
|
||||
RunEnumEqualityTests(ctx);
|
||||
|
||||
// New tests for sum type fixes
|
||||
TestTableWithoutPrimaryKey(ctx);
|
||||
TestSumTypeEquality(ctx);
|
||||
TestListOfNullableSumTypes(ctx);
|
||||
ScheduleTask(ctx, 1000000); // 1 second delay
|
||||
|
||||
// Test reducer with sum type parameter
|
||||
TestReducerWithSumTypeParam(ctx, new GameAction.Attack("Magic Sword"));
|
||||
|
||||
Log.Info("========== All Equality Tests Complete ==========");
|
||||
}
|
||||
|
||||
[SpacetimeDB.Procedure]
|
||||
public static void InsertWithTxPanic(ProcedureContext ctx)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,8 @@ cat >NuGet.Config <<EOF
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<!-- Experimental NuGet feed for Microsoft.DotNet.ILCompiler.LLVM packages -->
|
||||
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
|
||||
<!-- Local NuGet repositories -->
|
||||
<add key="Local SpacetimeDB.BSATN.Runtime" value="${SPACETIMEDB_REPO_PATH}/crates/bindings-csharp/BSATN.Runtime/bin/Release" />
|
||||
<!-- We need to override the module runtime as well because the examples use it -->
|
||||
@@ -30,6 +32,11 @@ cat >NuGet.Config <<EOF
|
||||
<packageSource key="Local SpacetimeDB.Runtime">
|
||||
<package pattern="SpacetimeDB.Runtime" />
|
||||
</packageSource>
|
||||
<!-- Experimental packages for NativeAOT-LLVM compilation -->
|
||||
<packageSource key="dotnet-experimental">
|
||||
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
|
||||
<package pattern="runtime.*" />
|
||||
</packageSource>
|
||||
<!-- Fallback for other packages (e.g. test deps). -->
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
@@ -43,6 +50,8 @@ cat >"${SPACETIMEDB_REPO_PATH}/NuGet.Config" <<EOF
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<!-- Experimental NuGet feed for Microsoft.DotNet.ILCompiler.LLVM packages -->
|
||||
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
|
||||
<!-- Local NuGet repositories -->
|
||||
<add key="Local SpacetimeDB.BSATN.Runtime" value="crates/bindings-csharp/BSATN.Runtime/bin/Release" />
|
||||
<!-- We need to override the module runtime as well because the examples use it -->
|
||||
@@ -58,6 +67,11 @@ cat >"${SPACETIMEDB_REPO_PATH}/NuGet.Config" <<EOF
|
||||
<packageSource key="Local SpacetimeDB.Runtime">
|
||||
<package pattern="SpacetimeDB.Runtime" />
|
||||
</packageSource>
|
||||
<!-- Experimental packages for NativeAOT-LLVM compilation -->
|
||||
<packageSource key="dotnet-experimental">
|
||||
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
|
||||
<package pattern="runtime.*" />
|
||||
</packageSource>
|
||||
<!-- Fallback for other packages (e.g. test deps). -->
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
|
||||
Reference in New Issue
Block a user