Skip to content

Commit 9452498

Browse files
authored
Merge branch 'master' into noa/unconflate-budget-and-energy
2 parents 54fc461 + 24f7624 commit 9452498

213 files changed

Lines changed: 11289 additions & 179 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/docker-compose.yml

Lines changed: 0 additions & 15 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,131 @@ jobs:
760760
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
761761
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
762762

763+
godot-testsuite:
764+
needs: [lints]
765+
permissions:
766+
contents: read
767+
runs-on: spacetimedb-new-runner-2
768+
env:
769+
CARGO_TARGET_DIR: ${{ github.workspace }}/target
770+
UseLocalBsatnRuntime: true
771+
steps:
772+
- name: Checkout repository
773+
id: checkout-stdb
774+
uses: actions/checkout@v4
775+
776+
- name: Setup dotnet
777+
uses: actions/setup-dotnet@v3
778+
with:
779+
global-json-file: global.json
780+
781+
- name: Override NuGet packages
782+
run: |
783+
dotnet pack -c Release crates/bindings-csharp/BSATN.Runtime
784+
dotnet pack -c Release crates/bindings-csharp/Runtime
785+
786+
# Write out the nuget config file to `nuget.config`. This causes the spacetimedb-csharp-sdk repository
787+
# to be aware of the local versions of the `bindings-csharp` packages in SpacetimeDB, and use them if
788+
# available. Otherwise, `spacetimedb-csharp-sdk` will use the NuGet versions of the packages.
789+
# This means that (if version numbers match) we will test the local versions of the C# packages, even
790+
# if they're not pushed to NuGet.
791+
# See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file for more info on the config file.
792+
cd sdks/csharp
793+
./tools~/write-nuget-config.sh ../..
794+
795+
- name: Restore .NET solution
796+
working-directory: sdks/csharp
797+
run: dotnet restore --configfile NuGet.Config SpacetimeDB.ClientSDK.sln
798+
799+
# Now, setup the Godot tests.
800+
- name: Patch spacetimedb dependency in Cargo.toml
801+
working-directory: demo/Blackholio/server-rust
802+
run: |
803+
sed -i "s|spacetimedb *=.*|spacetimedb = \{ path = \"../../../crates/bindings\" \}|" Cargo.toml
804+
cat Cargo.toml
805+
806+
- name: Install Rust toolchain
807+
uses: dsherret/rust-toolchain-file@v1
808+
- name: Set default rust toolchain
809+
run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)
810+
811+
- name: Cache Rust dependencies
812+
uses: Swatinem/rust-cache@v2
813+
with:
814+
workspaces: ${{ github.workspace }}
815+
shared-key: spacetimedb
816+
# Let the main CI job save the cache since it builds the most things
817+
save-if: false
818+
prefix-key: v1
819+
820+
# This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a.
821+
# ChatGPT suspects that this could be due to different build invocations using the same target dir,
822+
# and this makes sense to me because we only see it in this job where we mix `cargo build -p` with
823+
# `cargo build --manifest-path` (which apparently build different dependency trees).
824+
# However, we've been unable to fix it so... /shrug
825+
- name: Check v8 outputs
826+
run: |
827+
find "${CARGO_TARGET_DIR}"/ -type f | grep '[/_]v8' || true
828+
if ! [ -f "${CARGO_TARGET_DIR}"/release/gn_out/obj/librusty_v8.a ]; then
829+
echo "Could not find v8 output file librusty_v8.a; rebuilding manually."
830+
cargo clean --release -p v8 || true
831+
cargo build --release -p v8
832+
fi
833+
834+
- name: Install SpacetimeDB CLI from the local checkout
835+
run: |
836+
export CARGO_HOME="$HOME/.cargo"
837+
echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
838+
cargo install --force --path crates/cli --locked --message-format=short
839+
cargo install --force --path crates/standalone --locked --message-format=short
840+
# Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules).
841+
ln -sf $CARGO_HOME/bin/spacetimedb-cli $CARGO_HOME/bin/spacetime
842+
843+
- name: Generate client bindings
844+
working-directory: demo/Blackholio/server-rust
845+
run: bash ./generate.sh -y
846+
847+
- name: Check for changes
848+
run: |
849+
tools/check-diff.sh demo/Blackholio/client-godot/module_bindings || {
850+
echo 'Error: Godot bindings are dirty. Please run `demo/Blackholio/server-rust/generate.sh`.'
851+
exit 1
852+
}
853+
854+
- name: Patch SpacetimeDB Godot SDK dependency
855+
working-directory: demo/Blackholio/client-godot
856+
run: |
857+
dotnet remove package SpacetimeDB.ClientSDK.Godot
858+
dotnet add reference ../../../sdks/csharp/SpacetimeDB.ClientSDK.Godot.csproj
859+
cat blackholio.csproj
860+
861+
- name: Setup Godot
862+
uses: chickensoft-games/setup-godot@v2
863+
with:
864+
version: 4.6.2
865+
use-dotnet: true
866+
867+
- name: Restore Godot project
868+
working-directory: demo/Blackholio/client-godot
869+
run: dotnet restore --configfile ../../../NuGet.Config blackholio.csproj
870+
871+
- name: Build Godot project
872+
run: godot --headless --verbose --path demo/Blackholio/client-godot --build-solutions --quit
873+
874+
- name: Start SpacetimeDB
875+
run: |
876+
spacetime start &
877+
disown
878+
879+
- name: Publish godot-tests module to SpacetimeDB
880+
working-directory: demo/Blackholio/server-rust
881+
run: |
882+
spacetime login --server-issued-login local
883+
bash ./publish.sh
884+
885+
- name: Run Godot tests
886+
run: godot --headless --path demo/Blackholio/client-godot --scene res://tests/GodotPlayModeTests.tscn
887+
763888
csharp-testsuite:
764889
needs: [lints]
765890
runs-on: spacetimedb-new-runner-2
@@ -771,8 +896,6 @@ jobs:
771896
id: checkout-stdb
772897
uses: actions/checkout@v4
773898

774-
# Run cheap .NET tests first. If those fail, no need to run expensive Unity tests.
775-
776899
- name: Setup dotnet
777900
uses: actions/setup-dotnet@v3
778901
with:

crates/bindings-macro/LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../licenses/BSL.txt
1+
../../licenses/apache2.txt

crates/bindings-sys/LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../licenses/BSL.txt
1+
../../licenses/apache2.txt

crates/bindings-typescript/src/sdk/decompress.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,24 @@ export async function decompress(
2828
const decompressedStream = readableStream.pipeThrough(decompressionStream);
2929

3030
// Collect the decompressed chunks efficiently
31-
const chunks = [];
32-
for await (const chunk of decompressedStream) {
33-
chunks.push(chunk);
31+
const reader = decompressedStream.getReader();
32+
const chunks: Uint8Array[] = [];
33+
let totalLength = 0;
34+
let result: any;
35+
36+
while (!(result = await reader.read()).done) {
37+
chunks.push(result.value);
38+
totalLength += result.value.length;
3439
}
35-
return new Blob(chunks).bytes();
40+
41+
// Allocate a single Uint8Array for the decompressed data
42+
const decompressedArray = new Uint8Array(totalLength);
43+
let chunkOffset = 0;
44+
45+
for (const chunk of chunks) {
46+
decompressedArray.set(chunk, chunkOffset);
47+
chunkOffset += chunk.length;
48+
}
49+
50+
return decompressedArray;
3651
}

crates/bindings/LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../licenses/BSL.txt
1+
../../licenses/apache2.txt

crates/core/src/db/relational_db.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use spacetimedb_datastore::locking_tx_datastore::datastore::TxMetrics;
1616
use spacetimedb_datastore::locking_tx_datastore::state_view::{
1717
IterByColEqMutTx, IterByColRangeMutTx, IterMutTx, StateView,
1818
};
19-
use spacetimedb_datastore::locking_tx_datastore::{ApplyHistoryCounters, IndexScanPointOrRange, MutTxId, TxId};
19+
use spacetimedb_datastore::locking_tx_datastore::{
20+
ApplyHistoryCounters, IndexScanPointOrRange, MutTxId, TxId, ViewCallInfo,
21+
};
2022
use spacetimedb_datastore::system_tables::{
2123
system_tables, StModuleRow, ST_CLIENT_ID, ST_CONNECTION_CREDENTIALS_ID, ST_VIEW_SUB_ID,
2224
};
@@ -1601,6 +1603,26 @@ impl RelationalDB {
16011603
Ok(())
16021604
}
16031605

1606+
/// Materialize a view call and replace its committed read set on transaction commit.
1607+
///
1608+
/// The read set is only marked for replacement after materialization succeeds. If the
1609+
/// transaction rolls back, the replacement marker rolls back with it.
1610+
pub fn materialize_view_call(
1611+
&self,
1612+
tx: &mut MutTxId,
1613+
table_id: TableId,
1614+
view_call: ViewCallInfo,
1615+
rows: Vec<ProductValue>,
1616+
) -> Result<(), DBError> {
1617+
match view_call.sender {
1618+
Some(sender) => self.materialize_view(tx, table_id, sender, rows)?,
1619+
None => self.materialize_anonymous_view(tx, table_id, rows)?,
1620+
}
1621+
tx.replace_view_read_set(view_call);
1622+
1623+
Ok(())
1624+
}
1625+
16041626
fn write_view_rows(
16051627
&self,
16061628
tx: &mut MutTxId,

crates/core/src/host/module_host.rs

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,66 @@ pub struct CallViewParams {
10881088
pub timestamp: Timestamp,
10891089
}
10901090

1091+
pub(crate) struct ResolvedViewForRefresh<'a> {
1092+
pub view_id: ViewId,
1093+
pub table_id: TableId,
1094+
pub view_def: &'a ViewDef,
1095+
}
1096+
1097+
/// Lookup a module's [`ViewDef`] and check for consistency among
1098+
/// its readset, `st_view`, and the [`ModuleDef`].
1099+
pub(crate) fn resolve_view_for_refresh<'a>(
1100+
tx: &MutTxId,
1101+
module_def: &'a ModuleDef,
1102+
view_call: &ViewCallInfo,
1103+
) -> anyhow::Result<ResolvedViewForRefresh<'a>> {
1104+
let st_view = tx
1105+
.lookup_st_view(view_call.view_id)
1106+
.with_context(|| format!("failed to look up view {}", view_call.view_id))?;
1107+
1108+
let view_id = st_view.view_id;
1109+
let table_id = st_view
1110+
.table_id
1111+
.ok_or_else(|| anyhow::anyhow!("view {:?} does not have a backing table", view_id))?;
1112+
1113+
let view_name: Identifier = st_view.view_name.into();
1114+
let view_def = module_def.view(&view_name).ok_or_else(|| {
1115+
anyhow::anyhow!(
1116+
"view `{}` for view id `{}` not found in current module",
1117+
view_name,
1118+
view_id
1119+
)
1120+
})?;
1121+
1122+
let is_anonymous = view_call.sender.is_none();
1123+
1124+
if st_view.is_anonymous != is_anonymous {
1125+
return Err(anyhow::anyhow!(
1126+
"found is_anonymous={} in st_view, but {} in readset when updating view `{}`",
1127+
st_view.is_anonymous,
1128+
is_anonymous,
1129+
view_name,
1130+
));
1131+
}
1132+
1133+
let is_anonymous = view_def.is_anonymous;
1134+
1135+
if st_view.is_anonymous != is_anonymous {
1136+
return Err(anyhow::anyhow!(
1137+
"found is_anonymous={} in st_view, but {} in module when updating view `{}`",
1138+
st_view.is_anonymous,
1139+
is_anonymous,
1140+
view_name,
1141+
));
1142+
}
1143+
1144+
Ok(ResolvedViewForRefresh {
1145+
view_id,
1146+
table_id,
1147+
view_def,
1148+
})
1149+
}
1150+
10911151
pub struct CallProcedureParams {
10921152
pub timestamp: Timestamp,
10931153
pub caller_identity: Identity,
@@ -2896,17 +2956,21 @@ impl ModuleHost {
28962956
let mut total_duration = Duration::ZERO;
28972957
let mut abi_duration = Duration::ZERO;
28982958
let mut trapped = false;
2899-
for ViewCallInfo {
2900-
view_id,
2901-
table_id,
2902-
fn_ptr,
2903-
sender,
2904-
} in tx.views_for_refresh().cloned().collect::<Vec<_>>()
2905-
{
2906-
let Some(view_def) = module_def.get_view_by_id(fn_ptr, sender.is_none()) else {
2907-
outcome = ViewOutcome::Failed(format!("view with fn_ptr `{fn_ptr}` not found"));
2908-
break;
2959+
for view_call in tx.views_for_refresh().cloned().collect::<Vec<_>>() {
2960+
let sender = view_call.sender;
2961+
let resolved = match resolve_view_for_refresh(&tx, module_def, &view_call) {
2962+
Ok(resolved) => resolved,
2963+
Err(err) => {
2964+
outcome = ViewOutcome::Failed(format!("failed to resolve view: {err}"));
2965+
break;
2966+
}
29092967
};
2968+
let ResolvedViewForRefresh {
2969+
view_id,
2970+
table_id,
2971+
view_def,
2972+
} = resolved;
2973+
let view_name = &view_def.name;
29102974
let args = match FunctionArgs::Nullary.into_tuple_for_def(module_def, view_def) {
29112975
Ok(args) => args,
29122976
Err(err) => {
@@ -2918,7 +2982,7 @@ impl ModuleHost {
29182982
let (result, trap) = Self::call_view_inner(
29192983
instance,
29202984
tx,
2921-
&view_def.name,
2985+
view_name,
29222986
view_id,
29232987
table_id,
29242988
view_def.fn_ptr,

0 commit comments

Comments
 (0)