Skip to content

Commit

Permalink
feat: add ability to quickly read game winners
Browse files Browse the repository at this point in the history
  • Loading branch information
vinceau committed Nov 3, 2022
1 parent 5a46672 commit b4c9013
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 3 deletions.
5 changes: 2 additions & 3 deletions src/SlippiGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
} from "./types";
import { SlpParser, SlpParserEvent } from "./utils/slpParser";
import type { SlpReadInput } from "./utils/slpReader";
import { getGameEnd } from "./utils/slpReader";
import { closeSlpFile, getMetadata, iterateEvents, openSlpFile, SlpInputSource } from "./utils/slpReader";

/**
Expand Down Expand Up @@ -205,9 +206,7 @@ export class SlippiGame {
}

public getWinners(): PlacementType[] {
this._process();

const gameEnd = this.getGameEnd();
const gameEnd = getGameEnd(this.input);
if (!gameEnd) {
return [];
}
Expand Down
37 changes: 37 additions & 0 deletions src/utils/slpReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { mapValues } from "lodash";
import type {
EventCallbackFunc,
EventPayloadTypes,
GameEndType,
GameInfoType,
GeckoCodeType,
MetadataType,
Expand All @@ -14,6 +15,7 @@ import type {
SelfInducedSpeedsType,
} from "../types";
import { Command } from "../types";
import { exists } from "./exists";
import { toHalfwidth } from "./fullwidth";

export enum SlpInputSource {
Expand Down Expand Up @@ -642,3 +644,38 @@ export function getMetadata(slpFile: SlpFileType): MetadataType | null {
// $FlowFixMe
return metadata;
}

export function getGameEnd(input: SlpReadInput): GameEndType | null {
let slpFile: SlpFileType | undefined;

try {
slpFile = openSlpFile(input);
const { rawDataPosition, rawDataLength, messageSizes } = slpFile;
const gameEndSize = messageSizes[Command.GAME_END];
if (!exists(gameEndSize)) {
return null;
}

// Subtract one to account for command byte
const gameEndPosition = rawDataPosition + rawDataLength - gameEndSize - 1;

// Add one to include command byte in payload
const buffer = new Uint8Array(gameEndSize + 1);
readRef(slpFile.ref, buffer, 0, buffer.length, gameEndPosition);
if (buffer[0] !== Command.GAME_END) {
// This isn't even a game end payload
return null;
}

const gameEndMessage = parseMessage(Command.GAME_END, buffer);
if (!gameEndMessage) {
return null;
}

return gameEndMessage as GameEndType;
} catch (err) {
return null;
} finally {
slpFile && closeSlpFile(slpFile);
}
}
63 changes: 63 additions & 0 deletions test/slpReader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import _ from "lodash";

import { SlippiGame } from "../src";
import { getGameEnd, SlpInputSource } from "../src/utils/slpReader";

describe("when reading game end directly", () => {
it("should return the same game end object", () => {
const game = new SlippiGame("slp/test.slp");
const gameEnd = game.getGameEnd()!;

const manualGameEnd = getManualGameEnd("slp/test.slp")!;
expect(gameEnd.gameEndMethod).toEqual(manualGameEnd.gameEndMethod);
expect(gameEnd.lrasInitiatorIndex).toEqual(manualGameEnd.lrasInitiatorIndex);
expect(gameEnd.placements.length).toEqual(manualGameEnd.placements.length);
});

it("should return the correct placings for 2 player games", () => {
const manualGameEnd = getManualGameEnd("slp/placementsTest/ffa_1p2p_winner_2p.slp")!;
const placements = manualGameEnd.placements!;
expect(placements).toHaveLength(4);
console.log(JSON.stringify(placements));
expect(placements[0].position).toBe(1); // player in port 1 is on second place
expect(placements[0].playerIndex).toBe(0);
expect(placements[1].position).toBe(0); // player in port 2 is on first place
expect(placements[1].playerIndex).toBe(1);
});

it("should return placings for 3 player games", () => {
let manualGameEnd = getManualGameEnd("slp/placementsTest/ffa_1p2p3p_winner_3p.slp")!;
let placements = manualGameEnd.placements!;
expect(placements).toBeDefined();
expect(placements).toHaveLength(4);

expect(placements[0].playerIndex).toBe(0);
expect(placements[1].playerIndex).toBe(1);
expect(placements[2].playerIndex).toBe(2);
expect(placements[3].playerIndex).toBe(3);

expect(placements[0].position).toBe(1); // Expect player 1 to be on second place
expect(placements[1].position).toBe(2); // Expect player 2 to be on third place
expect(placements[2].position).toBe(0); // Expect player 3 to be first place
expect(placements[3].position).toBe(-1); // Expect player 4 to not be present

manualGameEnd = getManualGameEnd("slp/placementsTest/ffa_1p2p4p_winner_4p.slp")!;
placements = manualGameEnd.placements!;
expect(placements).toBeDefined();
expect(placements).toHaveLength(4);

expect(placements[0].playerIndex).toBe(0);
expect(placements[1].playerIndex).toBe(1);
expect(placements[2].playerIndex).toBe(2);
expect(placements[3].playerIndex).toBe(3);

expect(placements[0].position).toBe(1); // Expect player 1 to be on second place
expect(placements[1].position).toBe(2); // Expect player 2 to be on third place
expect(placements[2].position).toBe(-1); // Expect player 3 to not be present
expect(placements[3].position).toBe(0); // Expect player 4 to be first place
});
});

function getManualGameEnd(filePath: string) {
return getGameEnd({ source: SlpInputSource.FILE, filePath });
}

0 comments on commit b4c9013

Please sign in to comment.