diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 19b3280f..7ade98c7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -37,7 +37,7 @@ jobs:
uses: actions/setup-node@v2.1.4
- uses: bahmutov/npm-install@v1
- name: nextjs cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}
diff --git a/package.json b/package.json
index d33a74eb..b0a40562 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "everdell",
"version": "0.1.0",
"engines": {
- "node": "16.x"
+ "node": "22.x"
},
"private": true,
"scripts": {
diff --git a/src/components/gameBoard.tsx b/src/components/gameBoard.tsx
index 2d3f49d7..3825b0b6 100644
--- a/src/components/gameBoard.tsx
+++ b/src/components/gameBoard.tsx
@@ -351,7 +351,7 @@ export const GameBoard: React.FC<{
-
+
{gameState.gameOptions.newleaf?.station ? (
<>
diff --git a/src/model/game.ts b/src/model/game.ts
index 94f8209e..a411534f 100644
--- a/src/model/game.ts
+++ b/src/model/game.ts
@@ -65,7 +65,7 @@ export class Game {
return cloneDeep({
gameId: this.gameId,
gameSecret: "",
- gameState: this.gameState.toJSON(includePrivate),
+ gameState: this.gameState.toJSON({ includePrivate, isRoot: true }),
// Deprecated, remove after 3/1/21
gameOptions: this.gameOptionsDeprecated,
...(includePrivate
diff --git a/src/model/gameState.test.ts b/src/model/gameState.test.ts
index 34779f46..d5886ff0 100644
--- a/src/model/gameState.test.ts
+++ b/src/model/gameState.test.ts
@@ -2192,14 +2192,14 @@ describe("GameState", () => {
player.nextSeason();
player.nextSeason();
});
- expect(gameState.gameLog).eql([
+ expect(gameState.getGameLog()).eql([
{ entry: [{ type: "text", text: "Game created with 2 players." }] },
{ entry: [{ type: "text", text: "Dealing cards to each player." }] },
{ entry: [{ type: "text", text: "Dealing cards to the Meadow." }] },
]);
gameState = gameState.next({ inputType: GameInputType.GAME_END });
gameState = gameState.next({ inputType: GameInputType.GAME_END });
- expect(gameState.gameLog).eql([
+ expect(gameState.getGameLog()).eql([
{ entry: [{ type: "text", text: "Game created with 2 players." }] },
{ entry: [{ type: "text", text: "Dealing cards to each player." }] },
{ entry: [{ type: "text", text: "Dealing cards to the Meadow." }] },
diff --git a/src/model/gameState.ts b/src/model/gameState.ts
index d9134a0f..69eaf1af 100644
--- a/src/model/gameState.ts
+++ b/src/model/gameState.ts
@@ -71,7 +71,7 @@ import { VisitorStack, intialVisitorStack } from "./visitor";
const MEADOW_SIZE = 8;
const STATION_SIZE = 3;
const STARTING_PLAYER_HAND_SIZE = 5;
-const MAX_GAME_LOG_BUFFER = 500;
+const MAX_GAME_LOG_BUFFER = 100;
const PRINT_GAME_LOGS = false;
@@ -142,7 +142,7 @@ export const gameTextToDebugStr = (gameText: GameText): string => {
export class GameState {
readonly gameStateId: number;
readonly gameOptions: GameOptions;
- readonly gameLog: GameLogEntry[];
+ private gameLog: GameLogEntry[];
// GameInputs that need to be shown to the user.
readonly pendingGameInputs: GameInputMultiStep[];
@@ -235,6 +235,14 @@ export class GameState {
return this._activePlayerId;
}
+ getGameLog(): GameLogEntry[] {
+ return this.gameLog;
+ }
+
+ setGameLog(gameLog: GameLogEntry[]): void {
+ this.gameLog = gameLog;
+ }
+
addGameLogFromTrainCarTile(
name: TrainCarTileName,
args: Parameters[0]
@@ -331,7 +339,13 @@ export class GameState {
});
}
- toJSON(includePrivate: boolean): GameStateJSON {
+ toJSON({
+ includePrivate,
+ isRoot,
+ }: {
+ includePrivate: boolean;
+ isRoot: boolean;
+ }): GameStateJSON {
return cloneDeep({
...{
gameStateId: this.gameStateId,
@@ -344,7 +358,6 @@ export class GameState {
playedGameInputs: [],
deck: this.deck.toJSON(includePrivate),
discardPile: this.discardPile.toJSON(includePrivate),
- gameLog: this.gameLog,
gameOptions: this.gameOptions,
riverDestinationMap: this.riverDestinationMap
? this.riverDestinationMap.toJSON(includePrivate)
@@ -360,13 +373,14 @@ export class GameState {
: null,
stationCards: this.stationCards,
gameStateJSONForUndo: null,
+ gameLog: isRoot ? this.gameLog.slice(-MAX_GAME_LOG_BUFFER) : [],
},
...(includePrivate
? {
pendingGameInputs: this.pendingGameInputs,
playedGameInputs: this.playedGameInputs,
gameStateJSONForUndo: this.gameStateForUndo
- ? this.gameStateForUndo.toJSON(true)
+ ? this.gameStateForUndo.toJSON({ includePrivate, isRoot: false })
: null,
}
: {}),
@@ -522,12 +536,12 @@ export class GameState {
}
clone(): GameState {
- const gameStateJSON = this.toJSON(true /* includePrivate */);
+ const gameStateJSON = this.toJSON({ includePrivate: true, isRoot: true });
return GameState.fromJSON(gameStateJSON);
}
private cloneAndIncrementGameStateId(): GameState {
- const gameStateJSON = this.toJSON(true /* includePrivate */);
+ const gameStateJSON = this.toJSON({ includePrivate: true, isRoot: true });
gameStateJSON.gameStateId += 1;
return GameState.fromJSON(gameStateJSON);
}
@@ -1337,7 +1351,13 @@ export class GameState {
if (!this.gameStateForUndo) {
throw new Error("Unable to undo");
}
- return this.gameStateForUndo;
+ const nextState = this.gameStateForUndo;
+ nextState.setGameLog(this.gameLog.slice(-MAX_GAME_LOG_BUFFER));
+ nextState.addGameLog([
+ this.getActivePlayer(),
+ " undid their last action.",
+ ]);
+ return nextState;
}
if (this.gameOptions.allowUndo && !skipUndo) {
this.gameStateForUndo = this.clone();