Update Healer Man server
This commit is contained in:
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();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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> = {};
|
||||
|
||||
Reference in new issue
Block a user