Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 18:08:43 -04:00
1 parent 3e30af4c98
commit 97ee0c13f9
25 files changed
+576 -179

No files matched your search

+15
View File
@@ -26,6 +26,7 @@ const MANASTORM_ACTIVITY: OnlineGroupActivity = {
function entryDependencies(enterDungeon: () => boolean) {
return {
prepareRandomizedDungeonContent: vi.fn(async () => undefined),
installDungeonRuntime: vi.fn(async () => undefined),
enterDungeon: vi.fn(enterDungeon),
installManastormRuntime: vi.fn(async () => undefined),
@@ -48,6 +49,20 @@ describe("online group activity entry", () => {
difficultyId: "heroic", bossDifficulty: 9, freshRun: true, runId: DUNGEON_ACTIVITY.id,
});
});
it("downloads the seeded randomized creature packs before shared entry", async () => {
const dependencies = entryDependencies(() => true);
const activity = {
...DUNGEON_ACTIVITY,
id: "shared-randomized-seed",
contentId: "randomized-bite-sized-stormwind-stockade",
};
await enterOnlineGroupActivity(activity, "healer", vi.fn(), dependencies);
expect(dependencies.prepareRandomizedDungeonContent).toHaveBeenCalledWith(activity.id);
expect(dependencies.prepareRandomizedDungeonContent.mock.invocationCallOrder[0])
.toBeLessThan(dependencies.enterDungeon.mock.invocationCallOrder[0]);
});
it("leaves a dungeon activity unconsumed when dungeon entry fails", async () => {
const consumeActivity = vi.fn();
const dependencies = entryDependencies(() => false);
+9
View File
@@ -12,11 +12,13 @@ import { useContentManagerSnapshot } from "../content/useContentManager";
import { SHELVED_MODES_ENABLED } from "./releaseFeatures";
import type { DungeonEntrySettings } from "./dungeonShellRuntimeBridge";
import { installShelvedOnlineGroupRuntime } from "@shelvedOnlineGroupRuntime";
import { RANDOMIZED_BITE_SIZED_STOCKADES_DUNGEON_ID } from "../game/dungeonContentSource";
const GROUP_POLL_MS = 2_500;
const ACTIVITY_JOIN_WINDOW_MS = 10 * 60 * 1000;
interface OnlineGroupActivityEntryDependencies {
readonly prepareRandomizedDungeonContent: (seed: string) => Promise<void>;
readonly installDungeonRuntime: () => Promise<void>;
readonly enterDungeon: (dungeonId: string, role?: PartyRole, settings?: DungeonEntrySettings) => boolean;
readonly installManastormRuntime: () => Promise<void>;
@@ -24,6 +26,10 @@ interface OnlineGroupActivityEntryDependencies {
}
const DEFAULT_ACTIVITY_ENTRY_DEPENDENCIES: OnlineGroupActivityEntryDependencies = {
prepareRandomizedDungeonContent: async (seed) => {
const { ensureRandomizedBiteSizedRunContent } = await import("./randomizedBiteSizedContent");
await ensureRandomizedBiteSizedRunContent(seed);
},
installDungeonRuntime: async () => {
const { installDungeonShellRuntime } = await import("./dungeonShellRuntime");
installDungeonShellRuntime();
@@ -47,6 +53,9 @@ export async function enterOnlineGroupActivity(
): Promise<boolean> {
let entered: boolean;
if (activity.type === "dungeon") {
if (activity.contentId === RANDOMIZED_BITE_SIZED_STOCKADES_DUNGEON_ID) {
await dependencies.prepareRandomizedDungeonContent(activity.id);
}
await dependencies.installDungeonRuntime();
entered = dependencies.enterDungeon(activity.contentId, role, {
difficultyId: activity.difficultyId,
@@ -0,0 +1,46 @@
import { describe, expect, it, vi } from "vitest";
import {
ensureRandomizedBiteSizedRunContent,
randomizedBiteSizedModelUrls,
} from "./randomizedBiteSizedContent";
describe("randomized bite-sized run content", () => {
it("derives only the distinct model URLs selected by the shared run seed", () => {
const first = randomizedBiteSizedModelUrls("asset-seed");
const second = randomizedBiteSizedModelUrls("asset-seed");
expect(first).toEqual(second);
expect(first.length).toBeGreaterThan(1);
expect(new Set(first).size).toBe(first.length);
expect(first.every((url) => url.startsWith("/assets/") && url.endsWith(".glb"))).toBe(true);
});
it("installs the base location and only the rolled creature model packs before entry", async () => {
const installPack = vi.fn(async () => undefined);
const installUrls = vi.fn(async () => undefined);
await ensureRandomizedBiteSizedRunContent("download-seed", {
development: false,
installPack,
installUrls,
packIsUsable: () => false,
packHasUpdate: () => false,
});
expect(installPack).toHaveBeenCalledWith("activity:randomized-bite-sized");
expect(installUrls).toHaveBeenCalledWith(randomizedBiteSizedModelUrls("download-seed"));
expect(installPack.mock.invocationCallOrder[0]).toBeLessThan(installUrls.mock.invocationCallOrder[0]);
});
it("skips network installation in the unrestricted development asset server", async () => {
const installPack = vi.fn(async () => undefined);
const installUrls = vi.fn(async () => undefined);
await ensureRandomizedBiteSizedRunContent("dev-seed", {
development: true,
installPack,
installUrls,
packIsUsable: () => false,
packHasUpdate: () => false,
});
expect(installPack).not.toHaveBeenCalled();
expect(installUrls).not.toHaveBeenCalled();
});
});
+47
View File
@@ -0,0 +1,47 @@
import {
contentPackHasUpdate,
contentPackIsUsable,
installContentForLogicalUrls,
installContentPack,
} from "../content/contentManager";
import {
RANDOMIZED_BITE_SIZED_STOCKADES_DUNGEON_ID,
dungeonContentPackId,
} from "../game/dungeonContentSource";
import { randomizedBiteSizedDefinitionForSeed } from "../game/dungeonRegistry";
export function randomizedBiteSizedModelUrls(seed: string): readonly string[] {
const definition = randomizedBiteSizedDefinitionForSeed(seed);
return [...new Set(Object.values(definition.entities).flatMap((entity) => (
entity.visual.model?.url ? [entity.visual.model.url] : []
)))].sort();
}
interface RandomizedContentDependencies {
readonly development: boolean;
readonly installPack: (packId: string) => Promise<void>;
readonly installUrls: (urls: readonly string[]) => Promise<void>;
readonly packIsUsable: (packId: string) => boolean;
readonly packHasUpdate: (packId: string) => boolean;
}
const DEFAULT_DEPENDENCIES: RandomizedContentDependencies = {
development: import.meta.env.DEV,
installPack: installContentPack,
installUrls: installContentForLogicalUrls,
packIsUsable: contentPackIsUsable,
packHasUpdate: contentPackHasUpdate,
};
/** Downloads the rolled location and creature packs before the seeded run enters gameplay. */
export async function ensureRandomizedBiteSizedRunContent(
seed: string,
dependencies: RandomizedContentDependencies = DEFAULT_DEPENDENCIES,
): Promise<void> {
if (dependencies.development) return;
const basePackId = dungeonContentPackId(RANDOMIZED_BITE_SIZED_STOCKADES_DUNGEON_ID);
if (!dependencies.packIsUsable(basePackId) || dependencies.packHasUpdate(basePackId)) {
await dependencies.installPack(basePackId);
}
await dependencies.installUrls(randomizedBiteSizedModelUrls(seed));
}