Order ranked emotes against delayed status responses

This commit is contained in:
2026-10-15 00:14:17 +09:00
parent 013807c873
commit 32d00bb60a
2 changed files with 6 additions and 2 deletions
+4 -2
View File
@@ -28,6 +28,7 @@ interface TerminalMatch {
interface QueueEntry { playerId: string; gameSessionId: string; expiresAt: number }
interface RankedMatch {
emotes: ReturnType<typeof createEmotes>;
emoteVersion: number;
id: string; playerIds: Pair<string>; appearances: Pair<FighterAppearance>; engine: MatchEngine;
actions: Pair<ActionId | null>; departed: Set<string>; timer?: ReturnType<Clock['setTimeout']>;
terminal?: TerminalMatch; saved: boolean; finishedExpiresAt?: Pair<number>;
@@ -222,7 +223,7 @@ export async function createRankedService(db: Db, clock: Clock = systemClock, en
state.selfIndex = selfIndex; state.ownAction = match.actions[selfIndex];
// Own submissions change this participant's version without revealing whether the
// opponent has locked a choice. Engine transitions always move past either value.
state.version = state.version * 2 + (state.ownAction === null ? 0 : 1);
state.version = (state.version + match.emoteVersion) * 2 + (state.ownAction === null ? 0 : 1);
state.ranked = true; state.appearances = structuredClone(match.appearances);
state.emotes = match.emotes.view(clock.now());
state.opponentLeft = match.departed.has(match.playerIds[1 - selfIndex]);
@@ -325,7 +326,7 @@ export async function createRankedService(db: Db, clock: Clock = systemClock, en
}
const first = await readProfile(candidate.playerId), second = await readProfile(playerId);
const id = randomUUID(), engine = createMatchEngine({ matchId: id, mode: 'friend', selfIndex: 0, clock, settings: await createGlobalSettingsStore(db).get() });
const match: RankedMatch = { id, engine, emotes: createEmotes(), playerIds: [candidate.playerId, playerId],
const match: RankedMatch = { id, engine, emotes: createEmotes(), emoteVersion: 0, playerIds: [candidate.playerId, playerId],
appearances: [first.appearance, second.appearance], actions: [null, null], departed: new Set(), saved: false };
engine.start(clock.now() + matchIntroductionDuration(engine.view().settings, true)); matches.add(match);
for (const id of match.playerIds) { queue.delete(id); byPlayer.set(id, match); }
@@ -348,6 +349,7 @@ export async function createRankedService(db: Db, clock: Clock = systemClock, en
if (match.engine.view().phase === 'finished' || match.departed.size) throw new ApiError(409, 'NO_ACTIVE_MATCH');
const slot = match.playerIds[0] === playerId ? 0 : 1;
if (!match.emotes.send(slot, id, clock.now())) throw new ApiError(400, 'INVALID_REQUEST');
match.emoteVersion++;
return statusView(playerId);
});
},