Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 18 additions & 3 deletions apps/client/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,30 @@ import { updateFiles } from "./files";
import { MANIFEST } from "./server";

export const updateManifest = async (dir: string) => {
MANIFEST.version = await getVersion();
MANIFEST.files = [];
await updateFiles(dir);
MANIFEST.version = await getVersion(dir);
};

const getVersion = async () => {
const getVersion = async (dir: string) => {
try {
return await Bun.file("public/version").text();
} catch {
return "0.0.0";
return await fingerprintFiles(dir);
}
};

const fingerprintFiles = async (dir: string): Promise<string> => {
const stamps = await Promise.all(
MANIFEST.files.map(async ({ path }) => {
try {
const stat = await Bun.file(`${dir}${path}`).stat();
return `${path}:${stat.size}:${stat.mtimeMs}`;
} catch {
return `${path}:missing`;
}
}),
);
stamps.sort();
return Bun.hash(stamps.join("|")).toString(16);
};
31 changes: 27 additions & 4 deletions apps/website/src/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,41 @@ import { Logger } from "../utils/logger.utils";
import { setVersion } from "../version";
import { setLoadingStatus, setLoadingTotalFiles } from "../window";

const CACHE_LOCK_NAME = "nanoforge-game-cache";

async function withCacheLock<T>(fn: () => Promise<T>): Promise<T> {
if (typeof navigator === "undefined" || !navigator.locks) return fn();
return await navigator.locks.request(CACHE_LOCK_NAME, fn);
}

export class GameCache {
private readonly logger: Logger = new Logger("Cache");
private readonly fs: FileSystemManager = new FileSystemManager("game");

async updateCache(manifest: IManifest, force = false): Promise<IExtendedManifest> {
this.logger.info("Starting cache game files");
let extendedManifest: IExtendedManifest | undefined = await this._parseCache(manifest);
if (force || !isManifestUpToDate(manifest) || !extendedManifest)
extendedManifest = await this._updateCacheProcess(manifest);

let extendedManifest = await this._tryReuseCache(manifest, force);

if (!extendedManifest) {
extendedManifest = await withCacheLock(async () => {
return (
(await this._tryReuseCache(manifest, force)) ?? (await this._updateCacheProcess(manifest))
);
});
}

setVersion(manifest.version);
this.logger.info("Game files cached");
return extendedManifest as IExtendedManifest;
return extendedManifest;
}

private async _tryReuseCache(
manifest: IManifest,
force: boolean,
): Promise<IExtendedManifest | undefined> {
if (force || !isManifestUpToDate(manifest)) return undefined;
return this._parseCache(manifest);
}

private async _updateCacheProcess(manifest: IManifest): Promise<IExtendedManifest> {
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const runLoad = async () => {
const manifest = await getManifest();
runWatcher(manifest.watch);
const cache = new GameCache();
const extendedManifest = await cache.updateCache(manifest, true);
const extendedManifest = await cache.updateCache(manifest);
const [files, mainModule] = await loadGameFiles(extendedManifest);
const env = await getEnv();
setLoadingStatus("Starting game");
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ export const isManifestUpToDate = (manifest: IManifest): boolean => {
setLoadingStatus("Verifying manifest");
const currentVersion = getVersion();
if (!currentVersion || currentVersion !== manifest.version) return false;
return false;
return true;
};
Loading