How the SavedPlayers Module in YimMenuV2 Persists and Restores Player Data
The SavedPlayers module in YimMenuV2 persists player data to a JSON file using nlohmann::json serialization, storing Rockstar IDs, names, and flags in an unordered map that loads on initialization and saves automatically on modifications.
The SavedPlayers subsystem in YimMenuV2 maintains a persistent record of player information across menu sessions. It tracks Rockstar IDs, usernames, modder flags, and tracking preferences in a local JSON file, ensuring user-defined player data survives between launches while keeping runtime-fetched social club data volatile.
Data Structure and Storage Model
The persistence layer relies on two primary C++ structures defined in the backend headers.
SavedPlayers maintains the container and filesystem logic. In src/game/backend/SavedPlayers.hpp (lines 12-13), the class declares an std::unordered_map<std::uint64_t, SavedPlayerData> m_SavedPlayers where the Rockstar ID serves as the unique key. This unordered map provides O(1) lookup performance for player entries.
SavedPlayerData defines the actual payload. Located in src/game/backend/SavedPlayerData.hpp (lines 45-55), this struct contains three persisted fields—m_Name, m_IsModder, and m_TrackPlayer—alongside the transient m_FetchedData member. The m_FetchedData field holds live social club information retrieved during gameplay and is explicitly excluded from disk storage.
Serialization is handled automatically via the nlohmann::json library. The NLOHMANN_DEFINE_TYPE_INTRUSIVE macro registers the three stored members for JSON conversion, enabling single-line serialization and deserialization without manual field mapping.
Initialization and Loading Process
When YimMenuV2 launches, the SavedPlayers constructor establishes the storage path and immediately hydrates the internal map from disk.
In src/game/backend/SavedPlayers.cpp (lines 50-54), the constructor sets the file path to saved_players.json using FileMgr::GetProjectFile("./saved_players.json") and invokes LoadImpl(). This ensures the player database is available before any gameplay logic executes.
The LoadImpl() function (lines 306-325) performs the actual disk read. It checks for file existence, parses the JSON content using nlohmann::json, and iterates over each key-value pair. Each JSON key is converted from string to uint64_t Rockstar ID, with the corresponding value deserialized into a SavedPlayerData object inserted into m_SavedPlayers. Parsing exceptions are caught and logged to prevent startup crashes from corrupt JSON.
Persistence Workflow
Modifications to player data trigger atomic writes to the JSON file through the SaveImpl() method.
Located in src/game/backend/SavedPlayers.cpp (lines 332-445), SaveImpl() serializes the entire m_SavedPlayers map back to JSON format. It opens saved_players.json with std::ofstream in truncation mode (std::ios::trunc), writes the formatted data, and ensures atomic replacement of the previous file contents. Like loading, exceptions during write operations are caught and logged rather than propagated.
Automatic save triggers include:
- Adding a new player via
AddPlayerDataImpl()(lines 77-88) - Updating a Rockstar ID or username
- Modifying player flags during
OnPlayerJoinImpl()(lines 52-63)
Runtime Operations and Script Integration
The module integrates with YimMenuV2's script system to maintain synchronization between stored data and live session states.
Adding Players: The AddPlayerDataImpl() method creates new SavedPlayerData entries and immediately calls SaveImpl() to persist the change. This ensures player records survive unexpected menu crashes.
Session Join Handling: When a player joins the current session, OnPlayerJoinImpl() queries the stored map using their Rockstar ID. If found, it copies the m_IsModder flag to the live player object and updates the stored name if it differs from the current session username, triggering a subsequent save.
Background Fetching: The RunScriptImpl loop (lines 28-39) periodically executes FetchPlayerInfoImpl() to retrieve fresh social club data for all saved players. This updates the runtime-only m_FetchedData fields without invoking SaveImpl(), preventing unnecessary disk writes for volatile information.
Code Examples
Adding a New Saved Player
std::uint64_t rockstarId = 123456789;
std::string_view username = "BadBoyBob";
// Inserts entry and persists to saved_players.json
YimMenu::SavedPlayers::AddPlayerData(rockstarId, username);
Updating Stored Player Information
if (auto* data = YimMenu::SavedPlayers::GetPlayerData(rockstarId))
{
data->m_Name = "NewName";
data->m_IsModder = true;
YimMenu::SavedPlayers::Save(); // Explicit persistence call
}
Removing a Player Entry
YimMenu::SavedPlayers::RemovePlayerData(rockstarId);
// Removal persists on next automatic save cycle
Summary
- Storage Format: Player data persists to
saved_players.jsonusing nlohmann::json serialization via theNLOHMANN_DEFINE_TYPE_INTRUSIVEmacro. - Core Container: An
std::unordered_map<std::uint64_t, SavedPlayerData>indexed by Rockstar ID provides fast lookups insrc/game/backend/SavedPlayers.hpp. - Lifecycle: The constructor in
SavedPlayers::SavedPlayers()triggersLoadImpl()to hydrate data at startup, whileSaveImpl()atomically writes changes to disk. - Data Segregation: Only
m_Name,m_IsModder, andm_TrackPlayerpersist;m_FetchedDataremains runtime-only. - Integration: Background scripts update live player info without triggering saves, while session join events automatically synchronize flags and names.
Frequently Asked Questions
What player data does YimMenuV2 actually save to disk?
YimMenuV2 persists the player's Rockstar ID (as the map key), display name, modder flag status, and tracking preference. According to the source in src/game/backend/SavedPlayerData.hpp, these correspond to m_Name, m_IsModder, and m_TrackPlayer. Live social club data stored in m_FetchedData is intentionally excluded from the JSON serialization.
How does the SavedPlayers module handle corrupt JSON files?
During initialization, LoadImpl() wraps the JSON parsing logic in exception handling. If saved_players.json is malformed or unreadable, the catch block logs the error and continues execution with an empty player map. The module does not crash on corrupt data; it simply starts fresh and overwrites the file on the next save operation.
When exactly does the module write to saved_players.json?
The module writes to disk immediately when SaveImpl() is invoked by AddPlayerDataImpl(), during Rockstar ID changes, or when player metadata updates in OnPlayerJoinImpl(). The write uses std::ios::trunc mode to replace the entire file atomically, ensuring data integrity even if the process terminates during the operation.
Why does the module use Rockstar ID as the primary key?
The Rockstar ID (uint64_t) provides a immutable, unique identifier that persists across username changes and session switches. Using this as the unordered_map key in m_SavedPlayers ensures that player records remain linked to the correct account even if the player changes their display name, which is stored only as secondary metadata.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →