# How YimMenuV2 Persists Player Data Between Game Sessions Using SavedPlayers

> Discover how YimMenuV2 persists player data between game sessions using SavedPlayers. Learn how player info is serialized to a JSON file for seamless reloading.

- Repository: [YimMenu/YimMenuV2](https://github.com/YimMenu/YimMenuV2)
- Tags: how-to-guide
- Published: 2026-07-17

---

**YimMenuV2 persists player data between game sessions by serializing player information to a JSON file that is automatically loaded on startup and written whenever entries are modified.**

The **SavedPlayers** subsystem in YimMenuV2 provides a robust persistence layer that maintains player records—including Rockstar IDs, names, and modder flags—across menu launches. This implementation ensures that user-created lists and annotations survive between GTA V sessions without requiring manual configuration.

## The SavedPlayers Architecture

The persistence mechanism centers on the `SavedPlayers` class defined in [`src/game/backend/SavedPlayers.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/backend/SavedPlayers.hpp). This singleton manages an `std::unordered_map<std::uint64_t, SavedPlayerData>` named `m_SavedPlayers`, where the 64-bit integer key represents the player's unique Rockstar ID (lines 12–13).

The system separates **persistent data** (stored in JSON) from **runtime data** (live social club information). The `SavedPlayerData` structure (defined in [`src/game/backend/SavedPlayerData.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/backend/SavedPlayerData.hpp)) contains three serialized fields: `m_Name`, `m_IsModder`, and `m_TrackPlayer`. Runtime-only social club details are held in `m_FetchedData` and excluded from disk storage (lines 45–55).

## How Data Persistence Works

The persistence workflow follows a strict initialization-load-save cycle implemented in [`src/game/backend/SavedPlayers.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/backend/SavedPlayers.cpp).

### Initialization and File Location

When the menu initializes, the `SavedPlayers` constructor establishes the file path and immediately triggers a load operation. It uses `FileMgr::GetProjectFile("./saved_players.json")` to determine the storage location, then calls `LoadImpl()` to populate the internal map (lines 50–54).

### Loading from JSON

The `LoadImpl()` method handles deserialization. It checks for the existence of [`saved_players.json`](https://github.com/YimMenu/YimMenuV2/blob/main/saved_players.json), then uses the **nlohmann::json** library to parse the file. For each key-value pair in the root object, it converts the string key to a `uint64_t` Rockstar ID and constructs a `SavedPlayerData` object into the `m_SavedPlayers` map. Errors during parsing are caught and logged to prevent runtime crashes (lines 306–325).

### Saving to JSON

Persistence occurs through `SaveImpl()`, which serializes the entire map back to disk. The method iterates over `m_SavedPlayers`, converts each Rockstar ID to a string key, and leverages the `NLOHMANN_DEFINE_TYPE_INTRUSIVE` macro (defined in [`SavedPlayerData.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/SavedPlayerData.hpp)) to automatically serialize the three persistent fields. The output is written atomically using `std::ofstream` with `std::ios::trunc` to ensure file integrity (lines 332–445).

## Data Model and Serialization

### Internal Storage Structure

The system uses a hash map for O(1) lookups:

```cpp
// From src/game/backend/SavedPlayers.hpp (lines 12-13)
std::unordered_map<std::uint64_t, SavedPlayerData> m_SavedPlayers;

```

This structure allows immediate retrieval of player data using the Rockstar ID as the key.

### JSON Serialization with nlohmann

The `SavedPlayerData` struct uses the intrusive macro pattern for automatic JSON conversion:

```cpp
// Simplified representation from SavedPlayerData.hpp
NLOHMANN_DEFINE_TYPE_INTRUSIVE(SavedPlayerData, m_Name, m_IsModder, m_TrackPlayer)

```

This macro generates the necessary `to_json` and `from_json` functions, enabling single-line serialization such as `json[std::to_string(rid)] = data;` and deserialization like `SavedPlayerData data = val;`.

## Runtime Operations

### Adding and Updating Players

When a user adds a player, `SavedPlayers::AddPlayerDataImpl()` creates a new entry and immediately invokes `SaveImpl()` to persist the change (lines 77–88). Similarly, when `OnPlayerJoinImpl()` detects a player joining the session, it updates the stored name if it differs from the current display name and triggers a save (lines 52–63).

### Background Fetching vs Persistent Data

A background script (`SavedPlayers::RunScriptImpl`) periodically calls `FetchPlayerInfoImpl()` to update live social club data. This operation modifies only the transient `m_FetchedData` field and does not invoke the save routine, ensuring that temporary status information does not bloat the persistent JSON file (lines 28–39).

## Code Examples

### Adding a New Saved Player

```cpp
// Add a player to the persistent database
std::uint64_t rockstarId = 123456789;
std::string_view username = "ExamplePlayer";

YimMenu::SavedPlayers::AddPlayerData(rockstarId, username);
// Automatically persists to saved_players.json

```

### Accessing and Modifying Stored Data

```cpp
// Retrieve and update a saved player's name
if (auto* data = YimMenu::SavedPlayers::GetPlayerData(rockstarId))
{
    data->m_Name = "UpdatedName";
    YimMenu::SavedPlayers::Save(); // Explicitly persist changes
}

```

### Removing a Player Entry

```cpp
// Remove from memory; will be written to disk on next save operation
YimMenu::SavedPlayers::RemovePlayerData(rockstarId);

```

### Automatic Loading at Startup

```cpp
// The following happens automatically in src/main.cpp
// No manual intervention required
YimMenu::SavedPlayers::SavedPlayers(); // Constructor calls LoadImpl()

```

## Summary

- **File-based persistence**: YimMenuV2 stores player data in [`saved_players.json`](https://github.com/YimMenu/YimMenuV2/blob/main/saved_players.json) using the nlohmann JSON library.
- **Automatic initialization**: The `SavedPlayers` constructor loads existing data immediately upon menu startup.
- **Selective serialization**: Only `m_Name`, `m_IsModder`, and `m_TrackPlayer` are written to disk; runtime social club data remains in memory.
- **Atomic writes**: The `SaveImpl()` method uses truncate mode to ensure the JSON file is never corrupted during writes.
- **Map-based storage**: An `unordered_map` with Rockstar IDs as keys provides efficient lookups and updates.

## Frequently Asked Questions

### How does YimMenuV2 handle corrupted JSON files?

If `LoadImpl()` encounters malformed JSON during startup, it catches exceptions thrown by the nlohmann parser and logs the error. The menu continues running with an empty `m_SavedPlayers` map, effectively starting fresh without crashing the system.

### Can I manually edit the saved_players.json file?

Yes. The file is standard JSON with string keys representing Rockstar IDs and objects containing `m_Name`, `m_IsModder`, and `m_TrackPlayer` fields. Changes made while the menu is not running will be loaded on the next startup. Ensure valid JSON syntax to prevent parse errors.

### What is the difference between SaveImpl and AddPlayerDataImpl?

`AddPlayerDataImpl()` manages the logic for creating new entries and validating data before insertion, while `SaveImpl()` handles only the disk I/O operation of writing the entire `m_SavedPlayers` map to JSON. `AddPlayerDataImpl()` calls `SaveImpl()` after insertion to ensure immediate persistence.

### Why are some player fields not saved between sessions?

The `SavedPlayerData` struct explicitly separates persistent fields from runtime data using the `NLOHMANN_DEFINE_TYPE_INTRUSIVE` macro. Fields like `m_FetchedData` (which contains live social club stats) are excluded from this macro definition, making them volatile and session-specific.