# How DD Poker Handles Game State Persistence and Saved Games

> Learn how DD Poker manages game state persistence and saved games. The Game class serializes GameState objects, saving component details for seamless restarts.

- Repository: [Doug Donohoe/ddpoker](https://github.com/dougdonohoe/ddpoker)
- Tags: how-to-guide
- Published: 2026-02-28

---

**DD Poker persists game state by serializing a `GameState` object through a coordinated save/write cycle managed by the `Game` class, using `SaveDetails` to control precisely which components are written to disk.**

The open-source DD Poker project (available at `dougdonohoe/ddpoker`) implements a robust persistence layer that allows players to save progress mid-hand and resume later. The architecture separates concerns between orchestration, data representation, and granular persistence controls, enabling both full snapshots and partial updates depending on the context.

## Core Components of the Persistence Layer

The persistence system relies on three primary classes working in concert to manage **saved games** and **game state persistence**.

### Game.java – The Orchestration Controller

Located at [[`code/gameengine/src/main/java/com/donohoedigital/games/engine/Game.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/gameengine/src/main/java/com/donohoedigital/games/engine/Game.java)](https://github.com/dougdonohoe/ddpoker/blob/main/code/gameengine/src/main/java/com/donohoedigital/games/engine/Game.java), the `Game` class serves as the central coordinator. It determines when to trigger saves, manages the auto-save timer, and handles restoration of saved states.

Key methods include:

- **`saveWriteGame()`** – Performs a synchronized full persistence cycle
- **`autoSave()`** – Checks preferences and triggers automatic saves
- **`loadGame(GameState, boolean)`** – Restores the engine from a saved snapshot

### GameState.java – The Serialized Snapshot

The [`GameState`](https://github.com/dougdonohoe/ddpoker/blob/main/code/gamecommon/src/main/java/com/donohoedigital/games/config/GameState.java) class represents a single persisted snapshot containing player data, territories, phases, and custom module-specific payload. It handles low-level serialization logic and binary file I/O through `BaseDataFile` utilities.

### SaveDetails.java – Fine-Grained Persistence Control

[`SaveDetails`](https://github.com/dougdonohoe/ddpoker/blob/main/code/gamecommon/src/main/java/com/donohoedigital/games/config/SaveDetails.java) provides selective control over what gets persisted. Rather than always writing the entire game world, the engine uses this configuration object to skip expensive serialization steps when only specific data needs updating.

## The Save Workflow

DD Poker implements a deterministic, multi-step workflow for **game state persistence** that triggers on user actions or timer events.

### Triggering Saves

Saves initiate either manually through UI actions or automatically via the background timer. When a player finishes a hand, the UI invokes:

```java
public synchronized void saveWriteGame() {
    GameState state = getLastGameState();
    ApplicationError.assertNotNull(state, "No last game state");
    saveGame(state);      // Serialize into GameState
    writeGame(state);     // Write to filesystem
}

```

For **auto-save** functionality, the engine periodically checks `EngineConstants.PREF_AUTOSAVE`. If enabled and the game is not an online match, `Game.autoSave()` invokes the same `saveWriteGame()` method automatically.

### Serializing Game Data

The `Game._saveGame(state, details)` method executes a strict sequence to ensure consistent snapshots:

1. **Header initialization** – `state.initForSave(this, details)` stores hash values and game identifiers
2. **Subclass data** – Optional custom data via `saveSubclassData(state)`
3. **Players and observers** – `state.savePlayers(this)` and `state.saveObservers(this)`
4. **Current phase** – Added when `SAVE_ALL` is requested
5. **Territories** – All territories cached via `state.saveTerritories(territories)`
6. **Custom data** – Module-specific payloads through `state.saveCustomData()`

The `SaveDetails` parameter (typically `new SaveDetails(SaveDetails.SAVE_ALL)`) acts as a gate, determining which steps actually write data.

### Writing to Disk

`GameState.write()` persists the binary blob using the naming convention `<begin>.<nnnnnn>.<ext>` (for example, `save.000123.dat`). These files reside in the user-configurable directory returned by `GameConfigUtils.getSaveDir()`.

## Loading Saved Games

Restoring a session reverses the persistence process. The application constructs a `GameState` from the file system, then delegates restoration to the `Game` instance:

```java
File savedFile = new File(GameConfigUtils.getSaveDir(), "save.000045.dat");

// Read header and metadata
GameState savedState = new GameState(savedFile, true);

// Restore game state, processing the saved phase
Game currentGame = GameEngine.getGameEngine().getGame();
currentGame.loadGame(savedState, true);

```

The `_loadGame(state, bProcessPhase)` method restores players, territories, and phase data exactly as they existed when saved.

## Fine-Grained Control with SaveDetails

The `SaveDetails` class enables optimization by skipping unnecessary serialization. Available constants include:

- **`SAVE_ALL`** (1) – Persists everything including AI state, players, and territories
- **`SAVE_DIRTY`** (2) – Writes only modified components
- **`SAVE_NONE`** (3) – Skips persistence entirely

To exclude AI state from a save operation:

```java
SaveDetails details = new SaveDetails(SaveDetails.SAVE_ALL);
details.setSaveAI(SaveDetails.SAVE_NONE);
game.saveGame(currentState, details);

```

## Configuring Auto-Save Behavior

Auto-save functionality depends on the `EngineConstants.PREF_AUTOSAVE` preference stored in `EnginePrefs`. The engine checks this boolean before triggering automatic persistence cycles, ensuring **saved games** are created only when explicitly enabled by the user.

```java
if (prefs.getBoolean(EngineConstants.PREF_AUTOSAVE, false) && getLastGameState() != null) {
    saveWriteGame();
}

```

## Summary

- **DD Poker** uses a three-tier architecture: `Game` orchestrates, `GameState` represents data, and `SaveDetails` controls granularity.
- The `saveWriteGame()` method in [`Game.java`](https://github.com/dougdonohoe/ddpoker/blob/main/Game.java) coordinates the full persistence cycle from serialization to disk I/O.
- **Auto-save** triggers based on `EngineConstants.PREF_AUTOSAFE` preferences and excludes online matches.
- Files follow the pattern `save.NNNNNN.dat` in the configurable save directory.
- `SaveDetails` allows selective persistence, enabling optimizations by skipping AI state or unmodified territories when appropriate.

## Frequently Asked Questions

### How does DD Poker name its save files?

DD Poker generates save files using the pattern `<prefix>.<sequence_number>.<extension>`, such as `save.000123.dat`. The sequence number zero-pads to six digits, and the file location derives from `GameConfigUtils.getSaveDir()`.

### Can I disable auto-save in DD Poker?

Yes. Auto-save respects the `EngineConstants.PREF_AUTOSAVE` preference flag. When this boolean is false (the default), the `autoSave()` method in [`Game.java`](https://github.com/dougdonohoe/ddpoker/blob/main/Game.java) exits immediately without triggering `saveWriteGame()`.

### What data is included in a full game state save?

A complete save includes the game header with hash values, all player and observer states, the current phase (if `SAVE_ALL` is specified), territory data, and any custom module-specific data. The `SaveDetails` object passed to `_saveGame()` determines which of these components actually serialize.

### How does the game restore from a saved file?

The restoration process instantiates a `GameState` object with the target file and `bLoadHeader=true`, then calls `Game.loadGame()`. This method invokes `_loadGame()`, which reconstructs players, territories, and phase data in the engine to match the persisted snapshot exactly.