create-gametau 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gametau",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Scaffold a Tauri game that deploys to web + desktop",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -11,12 +11,12 @@
11
11
  "preview": "vite preview"
12
12
  },
13
13
  "dependencies": {
14
- "webtau": "^0.3.0"
14
+ "webtau": "^0.3.1"
15
15
  },
16
16
  "devDependencies": {
17
17
  "typescript": "^5.8.0",
18
18
  "vite": "^6.0.0",
19
- "webtau-vite": "^0.3.0",
19
+ "webtau-vite": "^0.3.1",
20
20
  "@tauri-apps/cli": "^2.0.0",
21
21
  "@tauri-apps/api": "^2.0.0"
22
22
  }
@@ -0,0 +1,62 @@
1
+ export interface SnapshotQueueOptions {
2
+ maxRetries?: number;
3
+ backoffMs?: number;
4
+ onError?: (error: unknown, retriesLeft: number) => void;
5
+ }
6
+
7
+ export function createSnapshotQueue<T>(
8
+ save: (snapshot: T) => Promise<void>,
9
+ options?: SnapshotQueueOptions,
10
+ ): { enqueue(snapshot: T): void } {
11
+ const maxRetries = options?.maxRetries ?? 3;
12
+ const baseBackoffMs = options?.backoffMs ?? 500;
13
+ const onError = options?.onError;
14
+
15
+ let snapshotInFlight = false;
16
+ let pendingSnapshot: T | null = null;
17
+
18
+ async function flush(): Promise<void> {
19
+ if (snapshotInFlight) return;
20
+ snapshotInFlight = true;
21
+ try {
22
+ while (pendingSnapshot !== null) {
23
+ const snapshot = pendingSnapshot;
24
+ pendingSnapshot = null;
25
+
26
+ let lastError: unknown;
27
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
28
+ try {
29
+ await save(snapshot);
30
+ lastError = null;
31
+ break;
32
+ } catch (error) {
33
+ lastError = error;
34
+ const retriesLeft = maxRetries - attempt;
35
+ onError?.(error, retriesLeft);
36
+ if (retriesLeft > 0) {
37
+ const delayMs = baseBackoffMs * 2 ** attempt;
38
+ await new Promise((r) => setTimeout(r, delayMs));
39
+ }
40
+ }
41
+ }
42
+
43
+ if (lastError !== null && lastError !== undefined) {
44
+ // All retries exhausted for this snapshot; continue draining
45
+ // pending queue so newer snapshots are not blocked.
46
+ }
47
+ }
48
+ } finally {
49
+ snapshotInFlight = false;
50
+ if (pendingSnapshot !== null) {
51
+ void flush();
52
+ }
53
+ }
54
+ }
55
+
56
+ return {
57
+ enqueue(snapshot: T): void {
58
+ pendingSnapshot = snapshot;
59
+ void flush();
60
+ },
61
+ };
62
+ }
@@ -1,5 +1,6 @@
1
1
  import { configure, isTauri } from "webtau";
2
2
  import { startGameLoop } from "./game/loop";
3
+ import { createSnapshotQueue } from "./game/snapshot-queue";
3
4
  import { initScene, updateScene } from "./game/scene";
4
5
  import {
5
6
  createServiceLayer,
@@ -69,6 +70,13 @@ async function main() {
69
70
  let tickInFlight = false;
70
71
  const tickRate = 1 / settings.tickRateHz;
71
72
 
73
+ const snapshotQueue = createSnapshotQueue<WorldView>(
74
+ async (snapshot) => {
75
+ await services.session.saveSnapshot(snapshot);
76
+ document.getElementById("session")!.textContent = `saved at tick ${snapshot.tick_count}`;
77
+ },
78
+ );
79
+
72
80
  startGameLoop(
73
81
  (dt) => {
74
82
  tickAccumulator += dt;
@@ -81,15 +89,16 @@ async function main() {
81
89
  updateHud(nextView, settings);
82
90
 
83
91
  if (nextView.tick_count % settings.autoSaveEveryTicks === 0) {
84
- await services.session.saveSnapshot(nextView);
85
- document.getElementById("session")!.textContent = `saved at tick ${nextView.tick_count}`;
92
+ snapshotQueue.enqueue(nextView);
86
93
  }
87
94
 
88
- await services.comms.publish({
95
+ // Intentional fire-and-forget: comms events are non-critical side effects
96
+ // and must not block the tick cadence. void suppresses the floating-promise lint.
97
+ void services.comms.publish({
89
98
  level: resolveAlertLevel(tickResult.score_delta),
90
99
  source: "engine",
91
100
  message: `score delta ${tickResult.score_delta}`,
92
- });
101
+ }).catch(console.error);
93
102
  })
94
103
  .catch(console.error)
95
104
  .finally(() => { tickInFlight = false; });
@@ -5,7 +5,7 @@ edition.workspace = true
5
5
 
6
6
  [dependencies]
7
7
  {{PROJECT_NAME}}-core = { path = "../core" }
8
- webtau = "0.3.0"
8
+ webtau = "0.3.1"
9
9
 
10
10
  [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
11
11
  tauri = { version = "2", features = [] }
@@ -11,7 +11,7 @@ wasm-bindgen = "0.2"
11
11
  serde = { version = "1", features = ["derive"] }
12
12
  serde-wasm-bindgen = "0.6"
13
13
  getrandom = { version = "0.2", features = ["js"] }
14
- webtau = "0.3.0"
14
+ webtau = "0.3.1"
15
15
  {{PROJECT_NAME}}-core = { path = "../core" }
16
16
  {{PROJECT_NAME}}-commands = { path = "../commands" }
17
17
 
@@ -12,12 +12,12 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "pixi.js": "^8.0.0",
15
- "webtau": "^0.3.0"
15
+ "webtau": "^0.3.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "typescript": "^5.8.0",
19
19
  "vite": "^6.0.0",
20
- "webtau-vite": "^0.3.0",
20
+ "webtau-vite": "^0.3.1",
21
21
  "@tauri-apps/cli": "^2.0.0",
22
22
  "@tauri-apps/api": "^2.0.0"
23
23
  }
@@ -12,13 +12,13 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "three": "^0.172.0",
15
- "webtau": "^0.3.0"
15
+ "webtau": "^0.3.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/three": "^0.172.0",
19
19
  "typescript": "^5.8.0",
20
20
  "vite": "^6.0.0",
21
- "webtau-vite": "^0.3.0",
21
+ "webtau-vite": "^0.3.1",
22
22
  "@tauri-apps/cli": "^2.0.0",
23
23
  "@tauri-apps/api": "^2.0.0"
24
24
  }