Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
6e79a9f
feat(trees): GLB model variants with shader-based highlight
tcm390 Mar 2, 2026
7cf2ca1
perf: disable receiveShadow on non-terrain meshes
tcm390 Mar 2, 2026
2f95ef2
perf: shader-based highlight for all instancers, split GPUVegetation
tcm390 Mar 2, 2026
6d4724c
perf: uniform-based shader highlight for all entities, disable receiv…
tcm390 Mar 2, 2026
dc15266
fix: track resource variants for all types, not just trees
tcm390 Mar 2, 2026
1d99ac3
feat(trees): Fortnite-style tree shader with AO, sun shade, and share…
tcm390 Mar 3, 2026
eb76bff
perf(trees): refactor GLBTreeInstancer to BatchedMesh with texture-ba…
tcm390 Mar 3, 2026
537a0a2
perf(trees): dual-instancer strategy with BatchedMesh for variants an…
tcm390 Mar 4, 2026
b3eb0e4
fix(trees): fallback for models without vertex colors in tree shader
tcm390 Mar 4, 2026
791a561
feat(terrain): quadtree LOD system with uniform resolution chunks
tcm390 Mar 5, 2026
c6d68a8
fix(terrain): quadtree LOD improvements — blending, hysteresis, clean…
tcm390 Mar 5, 2026
b114004
feat(terrain): biome-blended terrain generation with centralized arch…
tcm390 Mar 6, 2026
d404afd
fix(terrain): road influence refresh for quadtree LOD chunks
tcm390 Mar 6, 2026
2bb5c6b
feat(terrain): enlarge island 1.5x, per-biome terrace steps, biome-aw…
tcm390 Mar 6, 2026
35c05a6
feat(terrain): stepped power plateau system for mountains and ponds
tcm390 Mar 6, 2026
e3d462e
feat(terrain): refactor terracing to floor-quantize with shelf/cliff …
tcm390 Mar 6, 2026
841cd2c
feat(terrain): scale up terrain height and noise for larger features
tcm390 Mar 6, 2026
a56c996
feat(terrain): add per-biome terraceHeightScale, terraceSlope and uni…
tcm390 Mar 7, 2026
9986191
refactor(terrain): unify constants across TerrainSystem, TerrainShade…
tcm390 Mar 7, 2026
a4431a6
tweak(terrain): reduce second mountain radius from 150 to 100
tcm390 Mar 7, 2026
6599be6
refactor(biome): rename Desert to Canyon and consolidate to 3-biome s…
tcm390 Mar 7, 2026
a9df3fe
fix: recover when cdp stream startup stalls
lalalune Mar 8, 2026
b61d5d9
fix(camera): eliminate Y-axis jitter in cinematic duel camera
dreaminglucid Mar 9, 2026
9e46f42
fix(streaming): correct announcement timer and tune camera director t…
dreaminglucid Mar 9, 2026
acf2ef0
feat(terrain): noise-driven landscape algorithm and per-biome cliff c…
tcm390 Mar 9, 2026
adcc0fb
fix(betting): add STREAMING_STATE_UPDATE type to streaming API and de…
dreaminglucid Mar 9, 2026
4ab7b5c
merge: integrate latest changes from main
dreaminglucid Mar 9, 2026
fcf7416
feat(terrain): replace flat colors with biome texture sampling
tcm390 Mar 9, 2026
25b32a6
refactor(trees): soft clamped lighting with day/night and wind support
tcm390 Mar 9, 2026
f1a31de
feat(shadows): replace CSM with single shadow map, enable terrain sha…
tcm390 Mar 9, 2026
bfbd9ee
fix(trees): add sky-color fog to tree shader
tcm390 Mar 9, 2026
54a0546
fix(terrain): relax slope walkability to match terrain generation
tcm390 Mar 10, 2026
7f7d436
merge: integrate origin/main into tcm/biome-terrain-generation
tcm390 Mar 10, 2026
fe02ab3
refactor(biomes): use BiomeType enum as single source of truth
tcm390 Mar 10, 2026
b4b1498
chore: revert prettier-only formatting changes
tcm390 Mar 10, 2026
897b932
Revert "chore: revert prettier-only formatting changes"
tcm390 Mar 10, 2026
d1590d3
refactor(biome): unify on procgen BiomeSystem as single source of truth
tcm390 Mar 10, 2026
e403f6e
refactor(biome): remove dead code and fix biome lookup consistency
tcm390 Mar 10, 2026
c222dff
fix: remove default PhysicsSystem in streaming mode to unblock world …
lalalune Mar 10, 2026
9711e62
fix(shared,procgen): resolve physics race condition and procgen type …
lalalune Mar 10, 2026
8932209
fix(streaming): resolve agent T-pose and re-enable autonomous behavior
dreaminglucid Mar 11, 2026
2a5e586
Merge branch 'main' into tcm/biome-terrain-generation
tcm390 Mar 11, 2026
44d07b4
fix(network): batch entity spawns per tile and fix entity leak on til…
tcm390 Mar 11, 2026
6df41f9
Merge pull request #1016 from HyperscapeAI/fetch-logs
lalalune Mar 12, 2026
f709b8a
Merge pull request #1015 from HyperscapeAI/develop
lalalune Mar 12, 2026
5c34de1
chore: trigger redeploy to restart agent spawner
lalalune Mar 12, 2026
82a5365
merge: tcm/biome-terrain-generation — biome terrain, quadtree LOD, tr…
lalalune Mar 12, 2026
4ece154
merge: feat/cinematic-camera-improvements — camera Y-jitter fix, agen…
lalalune Mar 12, 2026
09e8a64
chore: use alpha dist-tag for plugin-elizacloud, resolve merge conflict
lalalune Mar 12, 2026
4c92c3f
Merge branch 'dev' of https://github.com/HyperscapeAI/hyperscape into…
lalalune Mar 12, 2026
49c0f5c
chore: use alpha dist-tag for plugin-elizacloud
lalalune Mar 12, 2026
6c14c8e
perf: optimize TerrainQuadTree per-frame allocation and GLBTreeBatche…
lalalune Mar 12, 2026
5f34ccf
fix: remove unused eslint-disable directive in ClientCameraSystem
lalalune Mar 12, 2026
9104520
fix: update test expectations to match biome branch changes and fix g…
lalalune Mar 12, 2026
710f040
merge: dev branch — physics race condition fix, procgen type definiti…
lalalune Mar 12, 2026
2751b26
refactor(trees): biome-aware tree allocation with TreeId enum and per…
tcm390 Mar 12, 2026
d13626f
Merge branch 'main' into tcm/biome-terrain-generation
tcm390 Mar 12, 2026
dd8d6ad
feat(trees): reject tree placement on steep terrain slopes
tcm390 Mar 12, 2026
6295345
refactor(trees): merge distribution + placements into single trees ma…
tcm390 Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions packages/client/src/lib/api-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ type PublicRuntimeEnv = {
type WindowWithRuntimeEnv = Window & { env?: PublicRuntimeEnv };

function isLoopbackHost(hostname: string): boolean {
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
return (
hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1"
);
}

function normalizeBaseUrlString(url: URL): string {
const normalized = url.toString();
const isRootBase =
url.pathname === "/" &&
url.search.length === 0 &&
url.hash.length === 0;
url.pathname === "/" && url.search.length === 0 && url.hash.length === 0;
if (!isRootBase) {
return normalized;
}
Expand Down Expand Up @@ -69,9 +69,7 @@ export function normalizeBrowserLoopbackUrl(
}
}

function getRuntimeEnvValue(
key: keyof PublicRuntimeEnv,
): string | undefined {
function getRuntimeEnvValue(key: keyof PublicRuntimeEnv): string | undefined {
if (typeof window === "undefined") return undefined;
return normalizeBrowserLoopbackUrl(
(window as WindowWithRuntimeEnv).env?.[key],
Expand Down
5 changes: 2 additions & 3 deletions packages/client/src/screens/GameClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,13 @@ export function GameClient({
event.preventDefault?.();
console.error(
"[GameClient] GPU context lost - WebGPU device may have been lost. " +
"This indicates GPU resource exhaustion or driver issues.",
"This indicates GPU resource exhaustion or driver issues.",
);
};
// Listen for WebGPU context lost events (if supported in future/custom)
// canvas.addEventListener("webgpucontextlost", handleContextLost);

return () => {
};
return () => {};
}, [world]);

useEffect(() => {
Expand Down
12 changes: 6 additions & 6 deletions packages/client/src/screens/StreamingMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export function StreamingMode() {
c.agent1?.damageDealtThisFight === p.agent1?.damageDealtThisFight &&
c.agent2?.damageDealtThisFight === p.agent2?.damageDealtThisFight &&
Math.floor(c.timeRemaining / 1000) ===
Math.floor(p.timeRemaining / 1000) &&
Math.floor(p.timeRemaining / 1000) &&
state.leaderboard.length === prev.leaderboard.length
) {
return prev; // Same reference = no re-render
Expand Down Expand Up @@ -634,7 +634,7 @@ export function StreamingMode() {
if (!recorder || recorder.state !== "recording") return;
try {
recorder.requestData();
} catch { }
} catch {}
}, 250);
healthTimer = setInterval(() => {
if (!recorder || recorder.state !== "recording") return;
Expand All @@ -646,7 +646,7 @@ export function StreamingMode() {
);
try {
recorder.requestData();
} catch { }
} catch {}
}
}, 2000);
const videoTrack = stream?.getVideoTracks?.()[0] as // eslint-disable-next-line no-undef
Expand All @@ -657,7 +657,7 @@ export function StreamingMode() {
forceFrameTimer = setInterval(() => {
try {
videoTrack.requestFrame?.();
} catch { }
} catch {}
}, frameIntervalMs);
}
if (captureDebug) {
Expand All @@ -676,7 +676,7 @@ export function StreamingMode() {
if (recorder && recorder.state !== "inactive") {
try {
recorder.stop();
} catch { }
} catch {}
}
if (recorder) {
recorder.ondataavailable = null;
Expand All @@ -690,7 +690,7 @@ export function StreamingMode() {
if (oscillator) {
try {
oscillator.stop();
} catch { }
} catch {}
oscillator.disconnect();
oscillator = null;
}
Expand Down
13 changes: 10 additions & 3 deletions packages/client/src/stream.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ if (typeof window !== "undefined") {
if (envCdn && typeof envCdn === "string" && envCdn !== "undefined") {
let resolvedCdn = envCdn;
if (resolvedCdn.includes("127.0.0.1") || resolvedCdn.includes("0.0.0.0")) {
resolvedCdn = resolvedCdn.replace("127.0.0.1", "localhost").replace("0.0.0.0", "localhost");
resolvedCdn = resolvedCdn
.replace("127.0.0.1", "localhost")
.replace("0.0.0.0", "localhost");
}
windowWithEnv.__CDN_URL = resolvedCdn;
}
Expand Down Expand Up @@ -105,7 +107,9 @@ async function resetLocalStreamingCaches(): Promise<void> {
}

const searchParams = new URLSearchParams(window.location.search);
const isInternalCapture = isTruthyUrlFlag(searchParams.get("internalCapture"));
const isInternalCapture = isTruthyUrlFlag(
searchParams.get("internalCapture"),
);
const isLoopbackHost = ["localhost", "127.0.0.1", "0.0.0.0", "::1"].includes(
window.location.hostname,
);
Expand All @@ -125,7 +129,10 @@ async function resetLocalStreamingCaches(): Promise<void> {
);
}
} catch (error) {
console.warn("[StreamEntry] Failed to unregister service workers:", error);
console.warn(
"[StreamEntry] Failed to unregister service workers:",
error,
);
}
}

Expand Down
22 changes: 11 additions & 11 deletions packages/client/tests/e2e/utils/testWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function waitForGameLoad(
.catch(() => null);

if (loadedHandle) {
await loadedHandle.dispose().catch(() => { });
await loadedHandle.dispose().catch(() => {});
return;
}

Expand Down Expand Up @@ -96,7 +96,7 @@ export async function waitForLoadingScreenHidden(
.catch(() => null);

if (hiddenHandle) {
await hiddenHandle.dispose().catch(() => { });
await hiddenHandle.dispose().catch(() => {});
return;
}

Expand Down Expand Up @@ -201,7 +201,7 @@ export async function waitForPlayerSpawn(
.catch(() => null);

if (spawnedHandle) {
await spawnedHandle.dispose().catch(() => { });
await spawnedHandle.dispose().catch(() => {});
return;
}

Expand Down Expand Up @@ -340,7 +340,7 @@ export async function takeGameScreenshot(
const maxRetries = options.maxRetries ?? 5;
const retryDelay = options.retryDelay ?? 500;

await mkdir("screenshots", { recursive: true }).catch(() => { });
await mkdir("screenshots", { recursive: true }).catch(() => {});

for (let attempt = 0; attempt < maxRetries; attempt++) {
// Wait for canvas to be ready
Expand All @@ -356,7 +356,7 @@ export async function takeGameScreenshot(
if (!navigator.gpu) return false;

// Try to get a basic 2D context to verify canvas has dimensions and is ready
// (Note: WebGPU contexts can't readPixels directly without a staging buffer,
// (Note: WebGPU contexts can't readPixels directly without a staging buffer,
// so we just rely on dimensions and Context availability here)
return canvas.width > 0 && canvas.height > 0;
})
Expand Down Expand Up @@ -395,7 +395,7 @@ export async function takeGameScreenshot(

// Check if screenshot is large enough (actual rendered content)
if (pngBuffer.length >= minSize) {
await writeFile(screenshotPath, pngBuffer).catch(() => { });
await writeFile(screenshotPath, pngBuffer).catch(() => {});
console.log(
`[Screenshot] Captured "${name}": ${pngBuffer.length} bytes (attempt ${attempt + 1})`,
);
Expand All @@ -412,7 +412,7 @@ export async function takeGameScreenshot(
}

// Last attempt, accept whatever we got
await writeFile(screenshotPath, pngBuffer).catch(() => { });
await writeFile(screenshotPath, pngBuffer).catch(() => {});
console.log(
`[Screenshot] Captured "${name}": ${pngBuffer.length} bytes (fallback)`,
);
Expand Down Expand Up @@ -513,16 +513,16 @@ export function assertScreenshotsDifferent(
if (comparison.identical) {
throw new Error(
`Screenshots "${name1}" and "${name2}" are pixel-identical! ` +
`This indicates the game may not be rendering correctly or the scene isn't changing.`,
`This indicates the game may not be rendering correctly or the scene isn't changing.`,
);
}

if (comparison.diffPercentage < minDiffPercentage) {
throw new Error(
`Screenshots "${name1}" and "${name2}" are nearly identical ` +
`(only ${comparison.diffPercentage.toFixed(4)}% different, ${comparison.diffPixels} bytes). ` +
`Expected at least ${minDiffPercentage}% difference. ` +
`This may indicate the game isn't responding to input.`,
`(only ${comparison.diffPercentage.toFixed(4)}% different, ${comparison.diffPixels} bytes). ` +
`Expected at least ${minDiffPercentage}% difference. ` +
`This may indicate the game isn't responding to input.`,
);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dev": "mud dev-contracts",
"lint": "bunx solhint --disc 'src/**/*.sol'",
"tablegen": "mud tablegen",
"test": "if [ -n \"$(find test -name '*.t.sol' -print -quit 2>/dev/null)\" ]; then export PATH=\"$PWD/node_modules/.bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"; export PRIVATE_KEY=${PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80}; /opt/homebrew/Cellar/node/24.5.0/bin/node ./node_modules/.bin/mud test --port 4252; else echo 'No contract tests defined (test/**/*.t.sol not found); skipping mud test'; fi",
"test": "if [ -n \"$(find test -name '*.t.sol' -print -quit 2>/dev/null)\" ]; then export PATH=\"$PWD/node_modules/.bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"; export PRIVATE_KEY=${PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80}; mud test --port 4252; else echo 'No contract tests defined (test/**/*.t.sol not found); skipping mud test'; fi",
"worldgen": "mud worldgen"
},
"devDependencies": {
Expand Down
Loading
Loading