From 52f4356cfad2c34c0b6702270285f1d9786d7f18 Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Sun, 12 Nov 2023 16:40:28 +0200 Subject: [PATCH] Add support for more custom params by updating the core dep --- package-lock.json | 6 +++- src/api.d.ts | 16 +++++----- src/client.ts | 10 +----- src/init-db.js | 12 +++++--- src/realtime-client.ts | 9 +----- src/server/index.ts | 2 +- src/server/session.ts | 70 +++++++++++++----------------------------- src/storage.js | 43 ++++++++++++++++++++++++-- 8 files changed, 85 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b47f98..47af61f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2644,8 +2644,12 @@ }, "node_modules/pujo-puyo-core": { "version": "0.2.0", - "resolved": "git+ssh://git@github.com/frostburn/pujo-puyo-core.git#11635a4b8dbf6c66ab34980099818d964b0d0454", + "resolved": "git+ssh://git@github.com/frostburn/pujo-puyo-core.git#5311f8589a15de489ccf9f04df39c5548f7dacc4", "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/frostburn" + }, "peerDependencies": { "typescript": "^5.0.0" } diff --git a/src/api.d.ts b/src/api.d.ts index fa5c862..4ca43c5 100644 --- a/src/api.d.ts +++ b/src/api.d.ts @@ -6,12 +6,17 @@ import { ReplayResult, SimpleGame, PlayedMove, + ReplayParams, } from 'pujo-puyo-core'; // Generic types type GameType = Replay['metadata']['type']; +interface PartialParams extends ReplayParams { + bagSeeds: null; +} + type Challenge = { uuid: string; gameType: GameType; @@ -182,12 +187,7 @@ interface ServerRealtimeMove extends PlayedMove { type GameParams = { type: 'game params'; - colorSelections: Replay['colorSelections']; - screenSeeds: Replay['screenSeeds']; - targetPoints: Replay['targetPoints']; - marginFrames: Replay['marginFrames']; - mercyFrames: Replay['mercyFrames']; - initialBags: number[][]; + params: PartialParams; identity: number; metadata: ReplayMetadata; }; @@ -207,8 +207,8 @@ type GameResult = { winner: ReplayResult['winner']; reason: ReplayResult['reason']; msSince1970: ReplayMetadata['endTime']; - gameSeeds: Replay['gameSeeds']; - initialBags: Replay['initialBags']; + bagSeeds: Replay['params']['bagSeeds']; + initialBags: Replay['params']['initialBags']; }; type SimpleState = { diff --git a/src/client.ts b/src/client.ts index 737c7e5..0b480d0 100644 --- a/src/client.ts +++ b/src/client.ts @@ -91,15 +91,7 @@ socket.addMessageListener((data: ServerMessage) => { console.log('Message received', data); } if (data.type === 'game params') { - mirrorGame = new MultiplayerGame( - null, - data.screenSeeds, - data.colorSelections, - data.initialBags, - data.targetPoints, - data.marginFrames, - data.mercyFrames - ); + mirrorGame = new MultiplayerGame(data.params); identity = data.identity; if (data.metadata.timeControl) { timer = FischerTimer.fromString(data.metadata.timeControl); diff --git a/src/init-db.js b/src/init-db.js index 3726fa4..1a659a3 100644 --- a/src/init-db.js +++ b/src/init-db.js @@ -34,14 +34,18 @@ import sql from './db.js'; await sql`CREATE TABLE replays ( id SERIAL PRIMARY KEY, private BOOLEAN, - -- Main fields - game_seeds BIGINT[] NOT NULL, - screen_seeds BIGINT[] NOT NULL, + -- Params fields + bag_seeds BIGINT[] NOT NULL, + garbage_seeds BIGINT[] NOT NULL, color_selections SMALLINT[][] NOT NULL, initial_bags SMALLINT[][] NOT NULL, - target_points SMALLINT[] NOT NULL, + -- Params.rules fields + clear_threshold SMALLINT NOT NULL, + jiggle_frames SMALLINT NOT NULL, + spark_frames SMALLINT NOT NULL, margin_frames REAL NOT NULL, mercy_frames REAL NOT NULL, + target_points SMALLINT[] NOT NULL, -- Moves are stored as horrible byte balls to save space moves INT[] NOT NULL, -- ReplayResult fields diff --git a/src/realtime-client.ts b/src/realtime-client.ts index 48cd353..e4f1f86 100644 --- a/src/realtime-client.ts +++ b/src/realtime-client.ts @@ -86,14 +86,7 @@ socket.addMessageListener((data: ServerMessage) => { console.log('Message received', data); } if (data.type === 'game params') { - const origin = new MultiplayerGame( - null, - data.screenSeeds, - data.colorSelections, - data.initialBags, - data.targetPoints, - data.marginFrames - ); + const origin = new MultiplayerGame(data.params); mirrorGame = new TimeWarpingMirror(origin); identity = data.identity; socket.sendMessage({type: 'ready'}); diff --git a/src/server/index.ts b/src/server/index.ts index 7f81094..132153f 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -104,7 +104,7 @@ function tick() { } for (const session of activeRealtimeSessions) { if (args.debug) { - console.log('Tick of', session.gameSeeds, ':', session.age); + console.log('Tick of', session.params.bagSeeds, ':', session.age); } session.tick(); } diff --git a/src/server/session.ts b/src/server/session.ts index 8e414d9..a1b90c1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -5,17 +5,17 @@ import { PlayedMove, Replay, ReplayMetadata, + ReplayParams, ReplayResultReason, TimeWarpingGame, - randomBag, - randomColorSelection, - randomSeed, + randomMultiplayer, } from 'pujo-puyo-core'; import {Player} from './player'; import { ClientMessage, GameResult, GameType, + PartialParams, PausingMove, PieceMessage, RealtimeMove, @@ -49,10 +49,7 @@ export type CompleteCallback = (session: WebSocketSession) => void; export class WebSocketSession { type: GameType | undefined = undefined; - gameSeeds: number[]; - screenSeeds: number[]; - colorSelections: number[][]; - initialBags: number[][]; + params: ReplayParams; metadata?: ReplayMetadata; winner?: number; reason: ReplayResultReason; @@ -66,12 +63,7 @@ export class WebSocketSession { onComplete?: CompleteCallback; constructor(players: Player[], private_: boolean, verbose?: boolean) { - this.gameSeeds = [randomSeed(), randomSeed()]; - this.screenSeeds = [randomSeed(), randomSeed()]; - const colorSelection = randomColorSelection(); - this.colorSelections = [colorSelection, colorSelection]; - const initialBag = randomBag(colorSelection); - this.initialBags = [initialBag, initialBag]; + this.params = randomMultiplayer(); this.players = players; this.ready = Array(players.length).fill(false); this.waitingForMove = Array(players.length).fill(false); @@ -86,16 +78,16 @@ export class WebSocketSession { if (!this.metadata) { throw new Error('Metadata must be set before calling start'); } + const params: PartialParams = { + ...this.params, + bagSeeds: null, + initialBags: this.params.initialBags.map(b => b.slice(0, 4)), + }; this.players.forEach((player, i) => { this.ready[i] = false; player.send({ type: 'game params', - colorSelections: this.colorSelections, - screenSeeds: this.screenSeeds, - targetPoints: origin.targetPoints, - marginFrames: origin.marginFrames, - mercyFrames: origin.mercyFrames, - initialBags: origin.initialBags, + params, identity: i, metadata: this.metadata!, }); @@ -103,7 +95,9 @@ export class WebSocketSession { }); if (this.verbose) { origin.log(); - console.log(`Starting game ${this.gameSeeds} (${this.screenSeeds})`); + console.log( + `Starting game ${this.params.garbageSeeds} (${this.params.garbageSeeds})` + ); } } @@ -139,8 +133,8 @@ export class WebSocketSession { winner: this.winner, reason: this.reason, msSince1970, - gameSeeds: this.gameSeeds, - initialBags: this.initialBags, + bagSeeds: this.params.bagSeeds, + initialBags: this.params.initialBags, }; if (this.verbose) { console.log('Sending result', result); @@ -181,8 +175,8 @@ export class WebSocketSession { winner: this.winner, reason: this.reason, msSince1970, - gameSeeds: this.gameSeeds, - initialBags: this.initialBags, + bagSeeds: this.params.bagSeeds, + initialBags: this.params.initialBags, }; if (this.verbose) { console.log('Sending result', result); @@ -274,13 +268,7 @@ export class WebSocketSession { throw new Error('Metadata must be set before converting to replay'); } return { - gameSeeds: this.gameSeeds, - screenSeeds: this.screenSeeds, - colorSelections: this.colorSelections, - initialBags: this.initialBags, - marginFrames: NaN, - mercyFrames: NaN, - targetPoints: [NaN, NaN], + params: this.params, moves: [], metadata: this.metadata, result: { @@ -302,12 +290,7 @@ export class PausingSession extends WebSocketSession { constructor(players: Player[], private_: boolean, verbose?: boolean) { super(players, private_, verbose); - this.game = new MultiplayerGame( - this.gameSeeds, - this.screenSeeds, - this.colorSelections, - this.initialBags - ); + this.game = new MultiplayerGame(this.params); this.passed = Array(players.length).fill(false); // TODO: True multiplayer this.hiddenMove = null; @@ -439,9 +422,6 @@ export class PausingSession extends WebSocketSession { toReplay(): Replay { const result = super.toReplay(); - result.marginFrames = this.game.marginFrames; - result.mercyFrames = this.game.mercyFrames; - result.targetPoints = this.game.targetPoints; result.moves = this.playedMoves; return result; } @@ -455,12 +435,7 @@ export class RealtimeSession extends WebSocketSession { constructor(players: Player[], private_: boolean, verbose?: boolean) { super(players, private_, verbose); - const origin = new MultiplayerGame( - this.gameSeeds, - this.screenSeeds, - this.colorSelections, - this.initialBags - ); + const origin = new MultiplayerGame(this.params); this.game = new TimeWarpingGame( origin, CHECKPOINT_INTERVAL, @@ -575,9 +550,6 @@ export class RealtimeSession extends WebSocketSession { toReplay(): Replay { const result = super.toReplay(); - result.marginFrames = this.game.origin.marginFrames; - result.mercyFrames = this.game.origin.mercyFrames; - result.targetPoints = this.game.origin.targetPoints; result.moves = this.game.moves; return result; } diff --git a/src/storage.js b/src/storage.js index 8627d63..70e769a 100644 --- a/src/storage.js +++ b/src/storage.js @@ -8,6 +8,22 @@ const PLAYER_BITS = 3; // Up to 8 players for future compatibility // Remaining bits implicitly allocated to 'time' // const TIME_BITS = 31 - ORIENTATION_BITS - Y1_BITS - X1_BITS - PLAYER_BITS; +const PARAMS_FIELDS = [ + 'bagSeeds', + 'garbageSeeds', + 'colorSelections', + 'initialBags', +]; + +const RULES_FIELDS = [ + 'clearThreshold', + 'jiggleFrames', + 'sparkFrames', + 'marginFrames', + 'mercyFrames', + 'targetPoints', +]; + const METADATA_FIELDS = [ 'names', 'elos', @@ -68,10 +84,17 @@ function decodeMove(byteBall) { } export async function saveReplay(replay, private_, userIds) { - const data = {...replay, ...replay.metadata}; + const data = { + ...replay, + ...replay.params, + ...replay.params.rules, + ...replay.metadata, + }; data.private = private_; delete data.result; + delete data.rules; + delete data.params; delete data.metadata; data.winner = replay.result.winner; @@ -109,8 +132,8 @@ export async function loadReplay(replayId) { } } - data.gameSeeds = data.gameSeeds.map(s => parseInt(s, 10)); - data.screenSeeds = data.screenSeeds.map(s => parseInt(s, 10)); + data.bagSeeds = data.bagSeeds.map(s => parseInt(s, 10)); + data.garbageSeeds = data.garbageSeeds.map(s => parseInt(s, 10)); data.moves = data.moves.map(decodeMove); data.result = { @@ -120,6 +143,20 @@ export async function loadReplay(replayId) { delete data.winner; delete data.reason; + data.params = {}; + + for (const field of PARAMS_FIELDS) { + data.params[field] = data[field]; + delete data[field]; + } + + data.params.rules = {}; + + for (const field of RULES_FIELDS) { + data.params.rules[field] = data[field]; + delete data[field]; + } + data.metadata = {}; for (const field of METADATA_FIELDS) {