Release v0.1.20 2026-07-19
This commit is contained in:
+18
-77
@@ -51,12 +51,11 @@ import { DualDisplayFrame } from "./DualDisplayFrame";
|
||||
import { DEFAULT_CONTROLLER_GLYPHS } from "../input/controllerGlyphs";
|
||||
import {
|
||||
HOCKEY_PVP_GOAL_DAMAGE,
|
||||
HOCKEY_PVP_COUNTDOWN_MS,
|
||||
HOCKEY_PVP_QUEUE_TIMEOUT_MS,
|
||||
hockeyPvpBossAt,
|
||||
randomHockeyPvpCpuName,
|
||||
type HockeyPvpMatchConfig,
|
||||
} from "../game/hockeyHealingPvp";
|
||||
import { startHockeyPvpMatchmaking, type HockeyPvpMatchOperation } from "../frontend/hockeyPvpMatchmaking";
|
||||
import { BLOCKBREAKER_BREACH_DAMAGE } from "../game/blockbreaker";
|
||||
import {
|
||||
APPEARANCE_SLOT_DEFINITIONS,
|
||||
@@ -1545,10 +1544,7 @@ function ModeScreen({ onLaunch }: { onLaunch: (bossIds: readonly BossId[], diffi
|
||||
const [queueing, setQueueing] = useState(false);
|
||||
const [queueElapsed, setQueueElapsed] = useState(0);
|
||||
const queueActive = useRef(false);
|
||||
const queueTicket = useRef<string | null>(null);
|
||||
const queuePollTimer = useRef<number | null>(null);
|
||||
const queueCpuTimer = useRef<number | null>(null);
|
||||
const queueClockTimer = useRef<number | null>(null);
|
||||
const queueOperation = useRef<HockeyPvpMatchOperation | null>(null);
|
||||
const mode = MODE_COPY[modeId];
|
||||
const healer = hunter ? HEALER_CLASSES[hunter.activeClassId] : HEALER_CLASSES.priest;
|
||||
const progress = hunter?.healers[hunter.activeClassId];
|
||||
@@ -1568,42 +1564,19 @@ function ModeScreen({ onLaunch }: { onLaunch: (bossIds: readonly BossId[], diffi
|
||||
const selectBossGroup = (groupId: (typeof BOSS_GROUPS)[number]["id"]) => {
|
||||
selectBoss(BOSS_GROUP_BY_ID[groupId].bossIds[0]);
|
||||
};
|
||||
const clearQueueTimers = () => {
|
||||
if (queuePollTimer.current !== null) window.clearTimeout(queuePollTimer.current);
|
||||
if (queueCpuTimer.current !== null) window.clearTimeout(queueCpuTimer.current);
|
||||
if (queueClockTimer.current !== null) window.clearInterval(queueClockTimer.current);
|
||||
queuePollTimer.current = null;
|
||||
queueCpuTimer.current = null;
|
||||
queueClockTimer.current = null;
|
||||
};
|
||||
const completePvpQueue = (match: HockeyPvpMatchConfig) => {
|
||||
if (!queueActive.current) return;
|
||||
queueActive.current = false;
|
||||
clearQueueTimers();
|
||||
queueOperation.current = null;
|
||||
setQueueing(false);
|
||||
setMessage(match.role === "cpu" ? `CPU rival found: ${match.opponentName}.` : `Matched with ${match.opponentName}.`);
|
||||
onLaunch([hockeyPvpBossAt(match.seed, 0)], "initiate", match);
|
||||
};
|
||||
const fallbackToCpu = () => {
|
||||
if (!queueActive.current) return;
|
||||
const ticketId = queueTicket.current;
|
||||
queueTicket.current = null;
|
||||
if (ticketId) void onlineRepository.cancelHockeyPvpQueue(ticketId).catch(() => undefined);
|
||||
completePvpQueue({
|
||||
matchId: null,
|
||||
seed: Math.max(1, Math.floor(Math.random() * 0xffffffff)),
|
||||
opponentName: randomHockeyPvpCpuName(),
|
||||
role: "cpu",
|
||||
countdownEndsAtMs: Date.now() + HOCKEY_PVP_COUNTDOWN_MS,
|
||||
});
|
||||
};
|
||||
const cancelPvpQueue = () => {
|
||||
if (!queueActive.current) return;
|
||||
queueActive.current = false;
|
||||
clearQueueTimers();
|
||||
const ticketId = queueTicket.current;
|
||||
queueTicket.current = null;
|
||||
if (ticketId) void onlineRepository.cancelHockeyPvpQueue(ticketId).catch(() => undefined);
|
||||
queueOperation.current?.cancel();
|
||||
queueOperation.current = null;
|
||||
setQueueing(false);
|
||||
setQueueElapsed(0);
|
||||
setMessage("Matchmaking cancelled.");
|
||||
@@ -1614,54 +1587,22 @@ function ModeScreen({ onLaunch }: { onLaunch: (bossIds: readonly BossId[], diffi
|
||||
setQueueing(true);
|
||||
setQueueElapsed(0);
|
||||
setMessage(accountId ? "Searching online queue…" : "Offline queue: searching before CPU fallback…");
|
||||
const startedAt = Date.now();
|
||||
queueClockTimer.current = window.setInterval(() => setQueueElapsed(Date.now() - startedAt), 100);
|
||||
queueCpuTimer.current = window.setTimeout(fallbackToCpu, HOCKEY_PVP_QUEUE_TIMEOUT_MS);
|
||||
if (!accountId || !networkAppearsOnline()) return;
|
||||
try {
|
||||
const joined = await onlineRepository.joinHockeyPvpQueue(hunter.slotId, hunter.hunterName);
|
||||
if (!queueActive.current) return;
|
||||
queueTicket.current = joined.ticketId;
|
||||
if (joined.match) {
|
||||
completePvpQueue({
|
||||
matchId: joined.match.id,
|
||||
seed: joined.match.seed,
|
||||
countdownEndsAtMs: joined.match.countdownEndsAtMs,
|
||||
opponentName: joined.match.opponentName,
|
||||
role: joined.match.role,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const poll = async () => {
|
||||
if (!queueActive.current || !queueTicket.current) return;
|
||||
try {
|
||||
const result = await onlineRepository.pollHockeyPvpQueue(queueTicket.current);
|
||||
if (!queueActive.current) return;
|
||||
if (result.match) {
|
||||
completePvpQueue({
|
||||
matchId: result.match.id,
|
||||
seed: result.match.seed,
|
||||
countdownEndsAtMs: result.match.countdownEndsAtMs,
|
||||
opponentName: result.match.opponentName,
|
||||
role: result.match.role,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// Five-second CPU fallback remains authoritative during transient outages.
|
||||
}
|
||||
if (queueActive.current) queuePollTimer.current = window.setTimeout(poll, 350);
|
||||
};
|
||||
queuePollTimer.current = window.setTimeout(poll, 350);
|
||||
} catch {
|
||||
setMessage("Online queue unavailable. CPU fallback still searching…");
|
||||
}
|
||||
const operation = startHockeyPvpMatchmaking({
|
||||
slotId: hunter.slotId,
|
||||
hunterName: hunter.hunterName,
|
||||
online: Boolean(accountId && networkAppearsOnline()),
|
||||
onElapsed: setQueueElapsed,
|
||||
onOnlineUnavailable: () => setMessage("Online queue unavailable. CPU fallback still searching…"),
|
||||
});
|
||||
queueOperation.current = operation;
|
||||
const match = await operation.result;
|
||||
if (!match || queueOperation.current !== operation) return;
|
||||
completePvpQueue(match);
|
||||
};
|
||||
useEffect(() => () => {
|
||||
queueActive.current = false;
|
||||
clearQueueTimers();
|
||||
const ticketId = queueTicket.current;
|
||||
if (ticketId) void onlineRepository.cancelHockeyPvpQueue(ticketId).catch(() => undefined);
|
||||
queueOperation.current?.cancel();
|
||||
queueOperation.current = null;
|
||||
}, []);
|
||||
const leaveMode = () => {
|
||||
cancelPvpQueue();
|
||||
|
||||
Reference in New Issue
Block a user