How YimMenuV2 Persists Player Data Between Game Sessions Using SavedPlayers

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. 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) 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.

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, 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) 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:

// 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:

// 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

// 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

// 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

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

Automatic Loading at Startup

// 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →