Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 13:40:23 -04:00
1 parent cc3b2fa4d5
commit daa417ec17
46 files changed
+1334 -172

No files matched your search

@@ -11,6 +11,7 @@ import { useOnlineSessionStore } from "../app/onlineSessionStore";
import { useOnlineGroupStore } from "../app/onlineGroupStore";
import { useDungeonRunStore } from "../game/dungeonRunStore";
import { subscribeDungeonEncounterSnapshots } from "../game/dungeonEncounterRuntime";
import { useLoginInputStore } from "../app/loginInputStore";
export function connectAuthoritativeDualScreen(
channel: BroadcastChannel,
@@ -27,6 +28,9 @@ export function connectAuthoritativeDualScreen(
const unsubscribeShell = useShellStore.subscribe(() => {
if (companionReady) publish();
});
const unsubscribeLogin = useLoginInputStore.subscribe(() => {
if (companionReady) publish();
});
const unsubscribeGame = useGameStore.subscribe(() => {
if (companionReady) gamePublisher.request();
});
@@ -66,6 +70,7 @@ export function connectAuthoritativeDualScreen(
if (companionReady) publish();
return () => {
unsubscribeShell();
unsubscribeLogin();
unsubscribeGame();
unsubscribeCombat();
unsubscribeParty();
+25
View File
@@ -4,11 +4,13 @@ import { useUiLayoutStore } from "../game/uiLayoutStore";
import { useDpsMeterStore } from "../game/dpsMeterStore";
import { emptyDpsMeter } from "../game/dpsMeter";
import type { DualScreenMessage } from "./dualScreenSync";
import { useLoginInputStore } from "../app/loginInputStore";
describe("dedicated companion DPS synchronization", () => {
afterEach(() => {
useUiLayoutStore.getState().finish(false);
useDpsMeterStore.setState(emptyDpsMeter(), true);
useLoginInputStore.getState().reset();
vi.unstubAllGlobals();
vi.useRealTimers();
});
@@ -45,4 +47,27 @@ describe("dedicated companion DPS synchronization", () => {
vi.advanceTimersByTime(100);
expect(postMessage).not.toHaveBeenCalled();
});
it("publishes login input immediately and stops after disconnect", () => {
vi.stubGlobal("window", { setTimeout, clearTimeout, location: { search: "?display=top" } });
const postMessage = vi.fn<(message: DualScreenMessage) => void>();
const channel = { postMessage, onmessage: null } as unknown as BroadcastChannel;
const disconnect = connectAuthoritativeDualScreen(
channel,
{ data: { type: "companion-ready" } } as MessageEvent<DualScreenMessage>,
);
postMessage.mockClear();
useLoginInputStore.getState().pressKey("char-1-0");
let latest = postMessage.mock.lastCall?.[0];
expect(latest?.type).toBe("app-state");
if (latest?.type !== "app-state") throw new Error("Missing login snapshot");
expect(latest.snapshot.login).toMatchObject({ username: "q", passwordLength: 0 });
disconnect();
postMessage.mockClear();
useLoginInputStore.getState().pressKey("char-1-1");
expect(postMessage).not.toHaveBeenCalled();
});
});
+17
View File
@@ -9,6 +9,7 @@ import { usePartyStore } from "../game/partyStore";
import { useGameStore } from "../game/store";
import { currentCompanionSnapshot, executeCompanionCommand } from "./dualScreenSync";
import { SHELVED_MODES_ENABLED } from "../app/releaseFeatures";
import { useLoginInputStore } from "../app/loginInputStore";
describe("dual-screen party commands", () => {
beforeEach(() => {
@@ -24,6 +25,22 @@ describe("dual-screen party commands", () => {
command: "defend",
objectiveBossId: "lady-anacondra-spawn",
});
useLoginInputStore.getState().reset();
});
it("publishes safe login input and accepts semantic keyboard commands", () => {
executeCompanionCommand({ name: "pressLoginKey", keyId: "char-1-0" });
executeCompanionCommand({ name: "selectLoginField", field: "password" });
executeCompanionCommand({ name: "pressLoginKey", keyId: "char-1-1" });
expect(useLoginInputStore.getState()).toMatchObject({ username: "q", password: "w" });
expect(currentCompanionSnapshot().login).toMatchObject({
username: "q",
passwordLength: 1,
activeField: "password",
selectedKeyId: "char-1-1",
});
expect(JSON.stringify(currentCompanionSnapshot())).not.toContain('"password":"w"');
});
it("publishes the current command and named boss objective", () => {
+12
View File
@@ -36,6 +36,12 @@ import { currentHudSnapshot, type HudSnapshot } from "./hudSnapshot";
import { HUD_ELEMENTS, type HudElementId } from "../game/uiLayout";
import { interactWithActiveDungeonObject } from "../game/dungeonWorldInteraction";
import { resurrectDeadPartyMembers } from "../game/partyResurrection";
import {
currentLoginInputSnapshot,
useLoginInputStore,
type LoginField,
type LoginInputSnapshot,
} from "../app/loginInputStore";
export { createDualScreenChannel } from "./dualScreenChannel";
@@ -142,6 +148,7 @@ export interface CompanionGameSnapshot {
export interface CompanionSnapshot {
keybinds?: InputPreferences;
phase: ShellPhase;
login: LoginInputSnapshot;
session: PlayerSession | null;
characters: CharacterProfile[];
selectedCharacterId: string | null;
@@ -152,6 +159,8 @@ export interface CompanionSnapshot {
}
export type CompanionCommand =
| { name: "pressLoginKey"; keyId: string }
| { name: "selectLoginField"; field: LoginField }
| { name: "selectCharacter"; characterId: string }
| { name: "openCreator" }
| { name: "cancelCreator" }
@@ -269,6 +278,7 @@ export function currentCompanionSnapshot(): CompanionSnapshot {
return {
keybinds: getInputPreferences(),
phase: shell.phase,
login: currentLoginInputSnapshot(),
session: shell.session,
characters: shell.characters,
selectedCharacterId: shell.selectedCharacterId,
@@ -354,6 +364,8 @@ export function executeCompanionCommand(command: CompanionCommand): void {
const game = useGameStore.getState();
const combat = useCombatStore.getState();
switch (command.name) {
case "pressLoginKey": useLoginInputStore.getState().pressKey(command.keyId); break;
case "selectLoginField": useLoginInputStore.getState().setActiveField(command.field); break;
case "editHudElement": {
if (game.overlay !== "ui-layout" || !useUiLayoutStore.getState().draft || !(command.id in HUD_ELEMENTS)) break;
const update: Partial<HudElementLayout> = {};