forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
3e30af4c98
commit
97ee0c13f9
25 files changed
+576
-179
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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));
|
||||
}
|
||||
Reference in new issue
Block a user