Update Healer Man server
This commit is contained in:
1 parent
39458d5de6
commit
6cc1b9217f
203 files changed
+133929
-31271
No files matched your search
@@ -121,6 +121,12 @@ describe("browser content packs", () => {
|
||||
totalBytes: firstBytes.byteLength,
|
||||
error: "",
|
||||
});
|
||||
const entriesSpy = vi.spyOn(Object, "entries");
|
||||
for (let index = 0; index < 100; index += 1) {
|
||||
expect(contentPackIsUsable("characters:test")).toBe(true);
|
||||
}
|
||||
expect(entriesSpy).not.toHaveBeenCalled();
|
||||
entriesSpy.mockRestore();
|
||||
const active = storage.stores.get(BROWSER_CONTENT_CACHE_NAME);
|
||||
expect(await (await active?.match(browserContentObjectPath(manifest.files["/assets/characters/test.glb"])))?.text()).toBe("first-model");
|
||||
const originalObject = browserContentObjectPath(manifest.files["/assets/characters/test.glb"]);
|
||||
|
||||
@@ -78,6 +78,7 @@ let initialization: Promise<void> | null = null;
|
||||
let installedStorageValidation: Promise<void> | null = null;
|
||||
let installedStorageValidationComplete = Object.keys(installedState.packs).length === 0;
|
||||
const validatedInstalledFiles = new Set<string>();
|
||||
const usablePackCache = new Map<string, boolean>();
|
||||
|
||||
function safeLocalStorage(): Storage | null {
|
||||
try { return typeof window === "undefined" ? null : window.localStorage; } catch { return null; }
|
||||
@@ -148,6 +149,9 @@ function persistInstalledState(): void {
|
||||
}
|
||||
|
||||
function publish(patch: Partial<Omit<ContentManagerSnapshot, "revision">>): void {
|
||||
// Every receipt/storage/manifest change publishes before notifying consumers.
|
||||
// Cache both usable and unavailable packs until the next content revision.
|
||||
usablePackCache.clear();
|
||||
snapshot = { ...snapshot, ...patch, revision: snapshot.revision + 1 };
|
||||
listeners.forEach((listener) => listener());
|
||||
}
|
||||
@@ -364,7 +368,11 @@ function contentPackIsUsableInternal(packId: string, visiting: ReadonlySet<strin
|
||||
}
|
||||
|
||||
export function contentPackIsUsable(packId: string): boolean {
|
||||
return contentPackIsUsableInternal(packId, new Set());
|
||||
const cached = usablePackCache.get(packId);
|
||||
if (cached !== undefined) return cached;
|
||||
const usable = contentPackIsUsableInternal(packId, new Set());
|
||||
usablePackCache.set(packId, usable);
|
||||
return usable;
|
||||
}
|
||||
|
||||
/** Vite serves the complete source asset tree in development; shipped builds require an installed pack. */
|
||||
|
||||
@@ -48,6 +48,15 @@ describe("content manifest", () => {
|
||||
}).map(([logicalPath]) => logicalPath)).toEqual(["/assets/b.glb"]);
|
||||
});
|
||||
|
||||
it("reuses the pack index and replaces it when a new catalog file map arrives", () => {
|
||||
const first = filesForContentPack(manifest, "dungeon:test");
|
||||
expect(filesForContentPack(manifest, "dungeon:test")).toBe(first);
|
||||
const updated = { ...manifest, files: { "/assets/b.glb": manifest.files["/assets/b.glb"] } };
|
||||
expect(filesForContentPack(updated, "dungeon:test").map(([url]) => url)).toEqual(["/assets/b.glb"]);
|
||||
expect(filesForContentPack(manifest, "dungeon:test")).toBe(first);
|
||||
expect(filesForContentPack(updated, "missing")).toEqual([]);
|
||||
});
|
||||
|
||||
it("resolves a seeded run's logical models to distinct owning packs", () => {
|
||||
const withSecondPack: ContentManifest = {
|
||||
...manifest,
|
||||
|
||||
@@ -118,13 +118,31 @@ export function appVersionMeetsMinimum(current: string, minimum: string): boolea
|
||||
return true;
|
||||
}
|
||||
|
||||
type ContentPackFiles = readonly (readonly [string, ContentManifestFile])[];
|
||||
const packFileIndexes = new WeakMap<ContentManifest["files"], ReadonlyMap<string, ContentPackFiles>>();
|
||||
const EMPTY_PACK_FILES: ContentPackFiles = [];
|
||||
|
||||
/** Catalog file maps are immutable; index once instead of scanning during gameplay. */
|
||||
export function filesForContentPack(
|
||||
manifest: ContentManifest,
|
||||
packId: string,
|
||||
): readonly (readonly [string, ContentManifestFile])[] {
|
||||
return Object.entries(manifest.files)
|
||||
.filter(([, entry]) => entry.packId === packId)
|
||||
.sort(([left], [right]) => left.localeCompare(right));
|
||||
let index = packFileIndexes.get(manifest.files);
|
||||
if (!index) {
|
||||
const groups = new Map<string, [string, ContentManifestFile][]>();
|
||||
for (const [logicalPath, entry] of Object.entries(manifest.files)) {
|
||||
let files = groups.get(entry.packId);
|
||||
if (!files) {
|
||||
files = [];
|
||||
groups.set(entry.packId, files);
|
||||
}
|
||||
files.push([logicalPath, entry]);
|
||||
}
|
||||
for (const files of groups.values()) files.sort(([left], [right]) => left.localeCompare(right));
|
||||
index = groups;
|
||||
packFileIndexes.set(manifest.files, index);
|
||||
}
|
||||
return index.get(packId) ?? EMPTY_PACK_FILES;
|
||||
}
|
||||
|
||||
export interface ContentFileIdentity {
|
||||
|
||||
Reference in new issue
Block a user