dotframe 0.1.7 → 0.1.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.8
4
+
5
+ From dogfood round 7 (Craft Ones on 0.1.7).
6
+
7
+ - `doctor` sim:math skips files marked `// dotframe-allow-math-file`, for render-only files the sim reaches because it builds the renderer.
8
+ - `deploy init` pins images: bun from `packageManager` (game or repo root, else the local bun) and the relay's dotframe from the version installed in the game.
9
+ - The vendor fix (doctor and native builds) names `DOTFRAME_VENDOR` for reusing an existing vendor dir.
10
+
3
11
  ## 0.1.7
4
12
 
5
13
  From dogfood rounds 5 and 6 (Craft Ones on iOS, online, and on the VPS).
@@ -14,14 +14,38 @@ async function gitRoot(ctx: Ctx, root: string): Promise<string> {
14
14
  return r.output.trim();
15
15
  }
16
16
 
17
+ interface Versions {
18
+ bun: string;
19
+ dotframe: string;
20
+ }
21
+
22
+ // Pinned versions, so an image built tomorrow is the image built today: bun from packageManager (the game's
23
+ // package.json, then the repo root's), else the bun running this CLI; dotframe as installed in the game.
24
+ function versions(gameRoot: string, repo: string): Versions {
25
+ let bun = Bun.version;
26
+ for (const dir of [gameRoot, repo]) {
27
+ const file = join(dir, "package.json");
28
+ if (!existsSync(file)) continue;
29
+ const manager = (JSON.parse(readFileSync(file, "utf8")) as { packageManager?: string }).packageManager ?? "";
30
+ const match = manager.match(/^bun@(\d+\.\d+\.\d+)/);
31
+ if (match) {
32
+ bun = match[1];
33
+ break;
34
+ }
35
+ }
36
+ const installed = join(gameRoot, "node_modules", "dotframe", "package.json");
37
+ const dotframe = existsSync(installed) ? (JSON.parse(readFileSync(installed, "utf8")) as { version: string }).version : pkg.version;
38
+ return { bun, dotframe };
39
+ }
40
+
17
41
  // game: the game's path inside the repo ("" at the root); context: the repo root, relative to deploy/.
18
- function files(config: Config, game: string, out: string, context: string): Record<string, string> {
42
+ function files(config: Config, game: string, out: string, context: string, v: Versions): Record<string, string> {
19
43
  const requires = (config.requires ?? []).filter((t: string): boolean => t !== "bun");
20
44
  const apt = requires.length > 0 ? `RUN apt-get update && apt-get install -y --no-install-recommends ${requires.join(" ")} && rm -rf /var/lib/apt/lists/*\n` : "";
21
45
  const at = game === "" ? "." : game;
22
46
  return {
23
47
  "Dockerfile.web": `# Static web build of ${config.name}, served by nginx. Generated by dotframe deploy init; edit freely.
24
- FROM oven/bun:1 AS build
48
+ FROM oven/bun:${v.bun} AS build
25
49
  ${apt}WORKDIR /app
26
50
  COPY . .
27
51
  RUN cd ${at} && bun install --frozen-lockfile && bun node_modules/dotframe/cli/main.ts build web
@@ -32,9 +56,10 @@ COPY --from=build ${join("/app", at, out)} /usr/share/nginx/html
32
56
  EXPOSE 80
33
57
  `,
34
58
  "Dockerfile.relay": `# Netplay relay for ${config.name}: dotframe relay serve. Generated by dotframe deploy init.
35
- FROM oven/bun:1-alpine
59
+ FROM oven/bun:${v.bun}-alpine
36
60
  WORKDIR /app
37
- RUN bun add dotframe@${pkg.version}
61
+ # The dotframe version the game has installed; bump it together with the game's.
62
+ RUN bun add dotframe@${v.dotframe}
38
63
  ENV PORT=8787
39
64
  EXPOSE 8787
40
65
  CMD ["bun", "node_modules/dotframe/cli/main.ts", "relay", "serve"]
@@ -86,7 +111,7 @@ export async function deployInit(ctx: Ctx, name: string | undefined, provider: s
86
111
  const repo = await gitRoot(ctx, config.root);
87
112
  const game = relative(repo, config.root);
88
113
  // compose.yaml sits in <game>/deploy; the web build context is the repo root, so workspace code is visible.
89
- const generated = files(config, game, t.out, relative(join(config.root, DIR), repo) || ".");
114
+ const generated = files(config, game, t.out, relative(join(config.root, DIR), repo) || ".", versions(config.root, repo));
90
115
  const dir = join(config.root, DIR);
91
116
  const written = Object.keys(generated).map((f: string): string => join(DIR, f));
92
117
  const existing = written.filter((f: string): boolean => existsSync(join(config.root, f)));
@@ -147,8 +147,9 @@ async function renderCheck(sim: Sim, root: string): Promise<Check> {
147
147
  }
148
148
 
149
149
  // Math.sin and friends differ in the last bits between engines and OSes, so simulation code that calls them drifts
150
- // between netplay peers and breaks replays on another machine. Render code may use them; mark a line with
151
- // `dotframe-allow-math` to silence it.
150
+ // between netplay peers and breaks replays on another machine. Render code may use them: mark a line with
151
+ // `dotframe-allow-math`, or a whole render-only file with `// dotframe-allow-math-file`. (The sim module builds the
152
+ // renderer for desync's purity check, so render files are reachable and a static scan cannot tell them apart.)
152
153
  const NONDETERMINISTIC = /\bMath\.(sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|exp|expm1|log|log1p|log2|log10|pow|cbrt|hypot)\b|[\w)\]]\s*\*\*\s*[\w(]/;
153
154
 
154
155
  // The engine's own files (draw2d's arcs, raster2d) are render code, however the game imports them: a vendored
@@ -173,7 +174,9 @@ function mathCheck(simFile: string): Check {
173
174
  const engine = new Map<string, boolean>();
174
175
  for (const file of importGraph(simFile).files) {
175
176
  if (!/\.tsx?$/.test(file) || file.includes(`${sep}node_modules${sep}`) || engineFile(file, engine)) continue;
176
- readFileSync(file, "utf8")
177
+ const text = readFileSync(file, "utf8");
178
+ if (text.includes("dotframe-allow-math-file")) continue;
179
+ text
177
180
  .split("\n")
178
181
  .forEach((line: string, i: number): void => {
179
182
  const code = line.replace(/\/\/.*$/, "");
@@ -187,6 +190,6 @@ function mathCheck(simFile: string): Check {
187
190
  ok: false,
188
191
  warn: true,
189
192
  detail: `${hits.length} platform-dependent math call(s) in code the sim reaches: ${shown}`,
190
- fix: "use dotframe/src/detmath (dsin, dcos, datan2, dexp, dpow, ...) in simulation code; mark render-only lines with // dotframe-allow-math",
193
+ fix: "use dotframe/src/detmath (dsin, dcos, datan2, dexp, dpow, ...) in simulation code; mark render-only lines with // dotframe-allow-math, or a render-only file with // dotframe-allow-math-file",
191
194
  };
192
195
  }
package/cli/native.ts CHANGED
@@ -46,7 +46,7 @@ export function sdlLibrary(platform: NativePlatform): string {
46
46
  }
47
47
 
48
48
  export function vendorFix(platform: NativePlatform): string {
49
- return `dotframe vendor ${platform}`;
49
+ return `dotframe vendor ${platform} (builds into ~/.dotframe/vendor), or point DOTFRAME_VENDOR at an existing vendor dir such as a dotframe checkout's vendor/`;
50
50
  }
51
51
 
52
52
  const SPECIFIER = /(?:from\s+|import\s*\(\s*|import\s+)(["'])([^"']+)\1/g;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotframe",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "TS-first game engine and agent-first CLI: one codebase for web, Discord, iOS and native binaries",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,6 +29,7 @@ dotframe config set targets.web.deploy.compose '"<compose id>"' # vps compose
29
29
  dotframe deploy web --dry-run && dotframe deploy web --yes
30
30
  ```
31
31
 
32
+ - Images are pinned: bun from `packageManager` (game or repo root, else the local bun) and the relay's dotframe from the version installed in the game. Rerun `deploy init --yes` after bumping either.
32
33
  - The web image installs the tools in `requires` (dotframe.json, for example `["ffmpeg"]`) and builds with dotframe; the relay image runs `dotframe relay serve`. The build context is the repo root, so monorepo ports see workspace code.
33
34
  - In Dokploy, route the domain's `/` to service `web` (port 80) and `/relay` to service `relay` (port 8787). For Discord, map `/` and `/relay` to that host.
34
35
  - Dokploy builds the pushed branch, not your working tree: deploy warns about unpushed or uncommitted work. On failure it returns the build log (through `vps compose logs`).
@@ -6,6 +6,7 @@ description: Build and install the iOS target of a dotframe game. Use when build
6
6
 
7
7
  ```sh
8
8
  dotframe vendor ios # once per machine: SDL3 (Xcode generator) and wgpu-native for iOS
9
+ # or reuse an existing vendor (a dotframe checkout's vendor/): export DOTFRAME_VENDOR=<path>
9
10
  dotframe doctor # vendor:ios, ios-runtime (scriptc pack matching the compiler), team placeholder
10
11
  dotframe build ios --json # dist/ios/<Scheme>.app
11
12
  dotframe device install ios --dry-run
@@ -43,7 +43,7 @@ Run several seeds and a high latency (474 ms is what a transatlantic relay measu
43
43
 
44
44
  ## Rules
45
45
 
46
- - Simulation code uses `dotframe/src/detmath` (dsin, dcos, datan2, dexp, dlog, dpow, ...), never `Math.sin` and friends or `**`: those differ in the last bits between engines and operating systems, so peers and replays drift (Craft Ones' replays diverged between macOS and Linux at frame 120). `dotframe doctor` warns about them (`sim:math`); mark render-only lines with `// dotframe-allow-math`. A replay passing on one machine proves nothing about another: run CI on a second OS.
46
+ - Simulation code uses `dotframe/src/detmath` (dsin, dcos, datan2, dexp, dlog, dpow, ...), never `Math.sin` and friends or `**`: those differ in the last bits between engines and operating systems, so peers and replays drift (Craft Ones' replays diverged between macOS and Linux at frame 120). `dotframe doctor` warns about them (`sim:math`); mark render-only lines with `// dotframe-allow-math`, or a whole render-only file with `// dotframe-allow-math-file` (the sim builds the renderer for desync, so render files count as reachable). A replay passing on one machine proves nothing about another: run CI on a second OS.
47
47
  - `desync` reports `restoreMs` early vs late; it warns when late restores cost more than twice the early ones (a heuristic for restore that replays from an earlier frame, which passes desync but stalls real rollbacks late in a match).
48
48
  - Build checksums from numbers in a fixed order, never from `JSON.stringify`: native builds can order keys differently from JavaScript, so a web peer and a native peer would report false desyncs.
49
49