### Install GDReplayFormat via CPM Source: https://context7.com/maxnut/gdreplayformat/llms.txt Add GDReplayFormat as a dependency in your CMakeLists.txt using CPM. Ensure this is placed after `add_subdirectory` and before `setup_geode_mod`. ```cmake CPMAddPackage("gh:maxnut/GDReplayFormat#") target_link_libraries(${PROJECT_NAME} libGDR) ``` -------------------------------- ### Using Built-in PhysicsInput Extension in C++ Source: https://context7.com/maxnut/gdreplayformat/llms.txt Utilize the `PhysicsInput` extension for per-input physics state. This is recommended over custom extensions for frame-correction physics data. The example demonstrates setting up a replay with `PhysicsInput` and exporting/importing it. ```cpp #include #include struct PhysicsBot : gdr::Replay { PhysicsBot() : Replay("PhysicsBot", 1) {} }; PhysicsBot replay; replay.author = "player"; replay.framerate = 240.0; replay.platformer = false; // Store a jump input along with the player's physics state at that frame replay.inputs.push_back(PhysicsInput( 100, // frame 1, // button (jump) false, // player2 true, // down (pressed) 120.5f, // xPosition 240.0f, // yPosition 0.0f, // rotation 8.0, // xVelocity -5.2 // yVelocity )); auto res = replay.exportData("physics_replay.gdr2"); if (res.isOk()) { auto loaded = PhysicsBot::importData("physics_replay.gdr2"); auto& inp = loaded.unwrap().inputs[0]; std::cout << inp.xPosition << " " << inp.yPosition << "\n"; // 120.5 240 } ``` -------------------------------- ### Error Handling with gdr::Result in C++ Source: https://context7.com/maxnut/gdreplayformat/llms.txt Handle I/O operations using `gdr::Result`. Use `.isOk()`, `.isErr()`, `.unwrap()`, and `.unwrapErr()` for control flow and value extraction. `unwrapOr` provides a safe way to get a default value. ```cpp #include struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; // Import from a potentially bad file auto result = MyBot::importData("bad_file.gdr2"); if (result.isErr()) { std::string errMsg = result.unwrapErr(); // Possible errors: "Failed to open file for reading", // "Invalid magic: XYZ", // "Invalid extension size" std::cerr << "Error: " << errMsg << "\n"; } else { MyBot replay = std::move(result.unwrap()); std::cout << "Loaded " << replay.inputs.size() << " inputs\n"; } // unwrapOr for safe defaults gdr::Result r = gdr::Ok(42); int val = r.unwrapOr(0); // 42 gdr::Result err = gdr::Err("oops"); int def = err.unwrapOr(-1); // -1 ``` -------------------------------- ### Adding GDReplayFormat to a Geode Mod Project Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Demonstrates how to add the GDReplayFormat library as a dependency to a Geode mod project using CPM and how to initialize a custom bot class. ```cmake CPMAddPackage("gh:maxnut/GDReplayFormat#commithash") # REPLACE commithash with a specific commit hash target_link_libraries(${PROJECT_NAME} libGDR) ``` ```cpp #include struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; ``` -------------------------------- ### Create and Populate Replay Data Source: https://context7.com/maxnut/gdreplayformat/llms.txt Use the `gdr::Replay<>` class to create replay objects. Set metadata fields directly and add inputs and deaths to the respective vectors. The `Replay` constructor sets bot info. ```cpp #include // Minimal bot definition — no extensions struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; MyBot replay; replay.author = "player1"; replay.description = "first clear attempt"; replay.duration = 45.5f; replay.gameVersion = 22074; // GD 2.2074 replay.framerate = 240.0; replay.seed = 12345; replay.coins = 3; replay.ldm = false; replay.platformer = false; replay.levelInfo = {"Clubstep", 4}; // botInfo is set automatically via constructor: "MyBot", version 1 // Add inputs: frame, button (1=Jump,2=Left,3=Right), player2, down replay.inputs.push_back(gdr::Input<>(0, 1, false, true)); // p1 jump down @ frame 0 replay.inputs.push_back(gdr::Input<>(120, 1, false, false)); // p1 jump up @ frame 120 replay.inputs.push_back(gdr::Input<>(200, 1, true, true)); // p2 jump down @ frame 200 // Record a death at frame 350 replay.deaths.push_back(350); ``` -------------------------------- ### Configure Test Executable and Linking Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/CMakeLists.txt Defines a test executable 'libGDR_test' using 'test/main.cpp' and links it against the 'libGDR' interface library. The PRIVATE keyword ensures that 'libGDR' is only available to 'libGDR_test' and not to targets that link against 'libGDR_test'. ```cmake add_executable(libGDR_test test/main.cpp ) target_link_libraries(libGDR_test PRIVATE libGDR) ``` -------------------------------- ### Custom Replay and Input Extensions in C++ Source: https://context7.com/maxnut/gdreplayformat/llms.txt Define custom input and replay structures that inherit from gdr::Input and gdr::Replay respectively. Implement `parseExtension` and `saveExtension` to handle custom data. Use `shouldParseExtension` to conditionally parse replay extensions. ```cpp #include struct MyInput : gdr::Input<"MyInput"> { float xpos = 0.f; MyInput() = default; MyInput(uint64_t frame, uint8_t button, bool player2, bool down, float xpos) : gdr::Input<"MyInput">(frame, button, player2, down), xpos(xpos) {} void parseExtension(binary_reader& r) override { r >> xpos; } void saveExtension(binary_writer& w) const override { w << xpos; } }; struct MyReplay : gdr::Replay { int attempts = 0; MyReplay() : Replay("TestBot", 1) {} void parseExtension(binary_reader& reader) override { reader >> attempts; } void saveExtension(binary_writer& writer) const override { writer << attempts; } // Only parse extensions for replays recorded by "TestBot" v1 bool shouldParseExtension() const override { return botInfo.name == "TestBot" && botInfo.version == 1; } }; MyReplay replay; replay.attempts = 50; replay.levelInfo = {"Test Level", 5}; replay.inputs.push_back(MyInput(20, 1, false, true, 5.f)); auto exported = replay.exportData(); auto imported = MyReplay::importData(exported.unwrap()); std::cout << imported.unwrap().attempts << "\n"; // 50 ``` -------------------------------- ### Export Replay Data to Bytes or File Source: https://context7.com/maxnut/gdreplayformat/llms.txt Use `Replay::exportData()` to serialize replay data. It returns a `gdr::Result` which should be checked for errors before unwrapping. The exported data includes magic bytes and version information. ```cpp #include #include struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; MyBot replay; replay.author = "speedrunner"; replay.inputs.push_back(gdr::Input<>(0, 1, false, true)); // --- Export to byte vector --- auto res = replay.exportData(); if (res.isErr()) { std::cerr << "Export failed: " << res.unwrapErr() << "\n"; return 1; } std::vector bytes = std::move(res.unwrap()); // bytes[0..2] == "GDR" (magic), bytes[3] == version (varint) // --- Export directly to file --- auto fileRes = replay.exportData("my_replay.gdr2"); if (fileRes.isErr()) { std::cerr << "File write failed: " << fileRes.unwrapErr() << "\n"; } // File saved as "my_replay.gdr2" ``` -------------------------------- ### Add Post-Build Custom Command for Test Execution Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/CMakeLists.txt Configures a custom command to run the 'libGDR_test' executable after the target is built. This is useful for automatically executing tests as part of the build process. ```cmake add_custom_command( TARGET libGDR_test POST_BUILD COMMAND libGDR_test WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMENT "Running test..." ) ``` -------------------------------- ### Import GDR Replay Data from File or Bytes Source: https://context7.com/maxnut/gdreplayformat/llms.txt Use `Replay::importData()` to deserialize a GDR replay from a file path or a span of bytes. It validates the header and handles unknown tags. Ensure the `res` object is checked for errors before unwrapping. ```cpp #include #include #include struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; // --- Import from file --- auto res = MyBot::importData("my_replay.gdr2"); if (res.isErr()) { std::cerr << "Import failed: " << res.unwrapErr() << "\n"; return 1; } MyBot replay = std::move(res.unwrap()); std::cout << "Author: " << replay.author << "\n"; std::cout << "Bot: " << replay.botInfo.name << " v" << replay.botInfo.version << "\n"; std::cout << "Level: " << replay.levelInfo.name << " (" << replay.levelInfo.id << ")\n"; std::cout << "Inputs: " << replay.inputs.size() << "\n"; std::cout << "Deaths: " << replay.deaths.size() << "\n"; std::cout << "Framerate: " << replay.framerate << " fps\n"; // --- Import from raw bytes --- std::vector rawBytes = /* ... loaded from network or memory ... */ {}; auto res2 = MyBot::importData(std::span(rawBytes)); if (res2.isOk()) { MyBot r2 = std::move(res2.unwrap()); } ``` -------------------------------- ### Bot Information Structure Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Defines the structure for storing information about the bot that recorded the replay, including its name and version. ```cpp std::string name; /* Name of the bot that recorded the replay. */ int version = 1; /* Version of the bot that recorded the replay. */ ``` -------------------------------- ### gdr::Input Source: https://context7.com/maxnut/gdreplayformat/llms.txt Represents a single player action with fields for frame, button, player, and state. An optional `Tag` can be provided for custom input types. ```APIDOC ## `gdr::Input` — Input Record Represents a single player action. The optional `Tag` string literal uniquely identifies a custom input type for extension matching. Fields: `frame` (uint64), `button` (1=Jump, 2=Left, 3=Right), `player2` (bool), `down` (bool). ### Fields - `frame` (uint64): The frame number. - `button` (uint8): The button pressed (1=Jump, 2=Left, 3=Right). - `player2` (bool): Indicates if the input is for player 2. - `down` (bool): Indicates if the button is pressed down. ### Example ```cpp // Standard input Input<> i1(0, 1, false, true); // frame 0, jump, player1, pressed // Access fields std::cout << i1.frame << "\n"; // 0 ``` ``` -------------------------------- ### Set Minimum CMake Version and C++ Standard Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/CMakeLists.txt Specifies the minimum required CMake version and enforces C++23 standard compliance for the project. ```cmake cmake_minimum_required(VERSION 3.20) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) ``` -------------------------------- ### Input Data Structure Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Defines the structure for individual input records within a replay, including the frame number, button pressed, player, and press/release state. ```cpp uint64_t frame{}; /* Frame that the input was recorded on. */ uint8_t button{}; /* Pressed button. 1 = Jump, 2 = Left, 3 = Right. Can be converted directly to PlayerButton enum. */ bool player2{}; /* Whether the input was for player 2. */ bool down{}; /* Whether the button was pressed or released. */ ``` -------------------------------- ### Replay::importData() Source: https://context7.com/maxnut/gdreplayformat/llms.txt A static factory method to deserialize a GDR binary payload from either a byte span or a file path. It validates the magic header and automatically handles unknown tags for extension skipping. ```APIDOC ## `Replay::importData()` — Deserialize from Bytes or File Static factory method that parses a GDR binary payload from a `std::span` or a `std::filesystem::path`. Returns `gdr::Result`. ### Method Static Factory ### Parameters - `data` (`std::span` or `std::filesystem::path`): The binary data or file path to import. ### Returns - `gdr::Result`: A result object containing the deserialized replay or an error. ### Example ```cpp // Import from file auto res = MyBot::importData("my_replay.gdr2"); if (res.isErr()) { std::cerr << "Import failed: " << res.unwrapErr() << "\n"; } MyBot replay = std::move(res.unwrap()); // Import from raw bytes std::vector rawBytes = /* ... */ {}; auto res2 = MyBot::importData(std::span(rawBytes)); if (res2.isOk()) { MyBot r2 = std::move(res2.unwrap()); } ``` ``` -------------------------------- ### Replay Structure Definition Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Defines the main structure for storing replay data, including author, duration, game version, framerate, inputs, and deaths. ```cpp std::string author; /* Replay author. Usually the nickname of the person who recorded it. */ std::string description; /* Replay description. Not required. */ float duration{}; /* Duration of the replay in seconds. */ int gameVersion{}; /* Game version the replay was recorded on. (Example: 22074 for 2.2074, refer to GEODE_COMP_GD_VERSION) */ double framerate = 240.0; /* Framerate (ticks per second) of the replay. 240 by default, change if replay is recorded using physics bypass. */ int seed = 0; /* Random seed set when at the start of the attempt. */ int coins = 0; /* Number of coins collected in the level. */ bool ldm = false; /* Whether the replay was recorded in low detail mode. */ bool platformer = false; /* Whether the replay was recorded in platformer mode. */ Bot botInfo{}; /* Information about the bot that recorded the replay. */ Level levelInfo{}; /* Information about the level the replay was recorded on. */ std::vector inputs; /* Refer to inputs section. */ std::vector deaths; /* List of frames where a death should happen. */ ``` -------------------------------- ### Extend GDR Input with Custom Data Source: https://context7.com/maxnut/gdreplayformat/llms.txt Create a custom input struct inheriting from `gdr::Input<"Tag">` and override `parseExtension`/`saveExtension` to handle custom data. Unknown tags are safely skipped during import. ```cpp #include // Custom input carrying X position struct MyInput : gdr::Input<"MyInput"> { float xpos = 0.f; MyInput() = default; MyInput(uint64_t frame, uint8_t button, bool player2, bool down, float xpos) : Input(frame, button, player2, down), xpos(xpos) {} void parseExtension(binary_reader& reader) override { reader >> xpos; } void saveExtension(binary_writer& writer) const override { writer << xpos; } }; struct MyBot : gdr::Replay { MyBot() : Replay("MyBot", 1) {} }; MyBot replay; replay.inputs.push_back(MyInput(20, 1, false, true, 5.0f)); replay.inputs.push_back(MyInput(543, 1, false, false, 6.0f)); auto res = replay.exportData(); // Extension bytes are appended after each packed input varint. // When reimported with MyBot::importData(), xpos is restored. auto imported = MyBot::importData(res.unwrap()); std::cout << imported.unwrap().inputs[0].xpos << "\n"; // 5.0 ``` -------------------------------- ### Define Project and Interface Library Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/CMakeLists.txt Sets the project name and defines an interface library named libGDR. Interface libraries are useful for managing include directories and compiler options without producing an actual library file. ```cmake project(libGDR) add_library(libGDR INTERFACE) target_include_directories(libGDR INTERFACE include/) ``` -------------------------------- ### Replay::exportData() Source: https://context7.com/maxnut/gdreplayformat/llms.txt Exports the replay to a `std::vector` or directly to a `.gdr2` file. Returns `gdr::Result>` or `gdr::Result<>` respectively; check `.isErr()` before unwrapping. ```APIDOC ## `Replay::exportData()` — Serialize to Bytes or File Exports the replay to a `std::vector` or directly to a `.gdr2` file. Returns `gdr::Result>` or `gdr::Result<>` respectively; check `.isErr()` before unwrapping. ```cpp #include #include struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; MyBot replay; replay.author = "speedrunner"; replay.inputs.push_back(gdr::Input<>(0, 1, false, true)); // --- Export to byte vector --- auto res = replay.exportData(); if (res.isErr()) { std::cerr << "Export failed: " << res.unwrapErr() << "\n"; return 1; } std::vector bytes = std::move(res.unwrap()); // bytes[0..2] == "GDR" (magic), bytes[3] == version (varint) // --- Export directly to file --- auto fileRes = replay.exportData("my_replay.gdr2"); if (fileRes.isErr()) { std::cerr << "File write failed: " << fileRes.unwrapErr() << "\n"; } // File saved as "my_replay.gdr2" ``` ``` -------------------------------- ### Add Custom Input Field to GDR Replay Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Define a custom input struct by inheriting from Input and overriding parseExtension and saveExtension to handle custom data like xpos. ```cpp struct MyInput : Input<"MyInput"> { float xpos; MyInput() = default; MyInput(uint64_t frame, uint8_t button, bool player2, bool down, float xpos) : Input(frame, button, player2, down), xpos(xpos) {} void parseExtension(binary_reader& reader) override { reader >> xpos; } void saveExtension(binary_writer& writer) const override { writer << xpos; } }; ``` -------------------------------- ### Custom Input Extensions Source: https://context7.com/maxnut/gdreplayformat/llms.txt Allows extending `gdr::Input` with custom data by providing a unique string tag and implementing `parseExtension` and `saveExtension` methods. ```APIDOC ## Custom Input Extensions — `Input` with `parseExtension` / `saveExtension` Extend `gdr::Input` with a unique string tag and override `parseExtension`/`saveExtension` to add per-input custom data. Other bots that don't recognise the tag will safely skip the extension bytes. ### Usage 1. Define a struct inheriting from `gdr::Input<"YourTag">`. 2. Add custom fields to your struct. 3. Override `parseExtension(binary_reader& reader)` to read custom data. 4. Override `saveExtension(binary_writer& writer) const` to write custom data. ### Example ```cpp // Custom input carrying X position struct MyInput : gdr::Input<"MyInput"> { float xpos = 0.f; MyInput() = default; MyInput(uint64_t frame, uint8_t button, bool player2, bool down, float xpos) : Input(frame, button, player2, down), xpos(xpos) {} void parseExtension(binary_reader& reader) override { reader >> xpos; } void saveExtension(binary_writer& writer) const override { writer << xpos; } }; // ... later in code ... MyBot replay; replay.inputs.push_back(MyInput(20, 1, false, true, 5.0f)); auto imported = MyBot::importData(replay.exportData().unwrap()); std::cout << imported.unwrap().inputs[0].xpos << "\n"; // 5.0 ``` ``` -------------------------------- ### Level Information Structure Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Defines the structure for storing information about the level the replay was recorded on, including its ID and name. ```cpp uint32_t id{}; /* ID of the level the replay was recorded on. */ std::string name; /* Name of the level the replay was recorded on. */ ``` -------------------------------- ### gdr::Replay - Core Replay Class Source: https://context7.com/maxnut/gdreplayformat/llms.txt The primary class for creating, exporting, and importing replays. Metadata fields are set directly as public members. Template parameters are `S` for the derived class type and `T` for the input type. ```APIDOC ## `gdr::Replay` — Core Replay Class The primary class for creating, exporting, and importing replays. Template parameter `S` is the derived class type (for CRTP static polymorphism); `T` is the input type (defaults to `gdr::Input<>`). Metadata fields are set directly as public members. ```cpp #include // Minimal bot definition — no extensions struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; MyBot replay; replay.author = "player1"; replay.description = "first clear attempt"; replay.duration = 45.5f; replay.gameVersion = 22074; // GD 2.2074 replay.framerate = 240.0; replay.seed = 12345; replay.coins = 3; replay.ldm = false; replay.platformer = false; replay.levelInfo = {"Clubstep", 4}; // botInfo is set automatically via constructor: "MyBot", version 1 // Add inputs: frame, button (1=Jump,2=Left,3=Right), player2, down replay.inputs.push_back(gdr::Input<>(0, 1, false, true)); // p1 jump down @ frame 0 replay.inputs.push_back(gdr::Input<>(120, 1, false, false)); // p1 jump up @ frame 120 replay.inputs.push_back(gdr::Input<>(200, 1, true, true)); // p2 jump down @ frame 200 // Record a death at frame 350 replay.deaths.push_back(350); ``` ``` -------------------------------- ### Sort GDR Replay Inputs by Frame Number Source: https://context7.com/maxnut/gdreplayformat/llms.txt Call `Replay::sortInputs()` to perform a stable sort of the `inputs` vector by frame number. This is necessary after manual input insertion or when inputs are not guaranteed to be in order. ```cpp #include struct MyBot : gdr::Replay> { MyBot() : Replay("MyBot", 1) {} }; MyBot replay; // Add inputs out of order replay.inputs.push_back(gdr::Input<>(500, 1, false, true)); replay.inputs.push_back(gdr::Input<>(100, 1, false, true)); replay.inputs.push_back(gdr::Input<>(300, 1, true, true)); // p2 replay.sortInputs(); // Now: frame 100, 300, 500 in order std::cout << replay.inputs[0].frame << "\n"; // 100 std::cout << replay.inputs[1].frame << "\n"; // 300 std::cout << replay.inputs[2].frame << "\n"; // 500 ``` -------------------------------- ### Replay::sortInputs() Source: https://context7.com/maxnut/gdreplayformat/llms.txt Performs a stable sort of the `inputs` vector by frame number. This is automatically called after `importData()` to ensure correct input order. ```APIDOC ## `Replay::sortInputs()` — Sort by Frame Performs a stable sort of `inputs` by frame number. Required after manual input insertion if order is not guaranteed. Called automatically after `importData()` to interleave player 1 and player 2 inputs. ### Method `void sortInputs()` ### Description Sorts the `inputs` member of the `Replay` object by frame number in ascending order. ### Example ```cpp MyBot replay; // Add inputs out of order replay.inputs.push_back(gdr::Input<>(500, 1, false, true)); replay.inputs.push_back(gdr::Input<>(100, 1, false, true)); replay.sortInputs(); // Now inputs are ordered by frame: 100, 500 ``` ``` -------------------------------- ### Add Custom Replay Field to GDR Replay Source: https://github.com/maxnut/gdreplayformat/blob/gdr2/readme.md Extend the Replay struct to include custom replay-wide data such as attempts. Override parseExtension, saveExtension, and shouldParseExtension to manage this data. ```cpp struct MyReplay : Replay { int attempts; MyReplay() : Replay("TestBot", 1) {} void parseExtension(binary_reader& reader) override { reader >> attempts; } void saveExtension(binary_writer& writer) const override { writer << attempts; } bool shouldParseExtension() const override { return botInfo.name == "TestBot" && botInfo.version == 1; } }; ``` -------------------------------- ### Define and Access GDR Input Records Source: https://context7.com/maxnut/gdreplayformat/llms.txt The `gdr::Input<>` struct represents player actions with fields for frame, button, player2, and down state. An optional `Tag` string literal can be used for custom input types. ```cpp #include using namespace gdr; // Standard input (no extension) Input<> i1(0, 1, false, true); // frame 0, jump, player1, pressed Input<> i2(240, 1, false, false); // frame 240, jump, player1, released Input<> i3(300, 2, false, true); // frame 300, left (platformer), player1, pressed Input<> i4(300, 1, true, true); // frame 300, jump, player2, pressed // Access fields std::cout << i1.frame << "\n"; // 0 std::cout << (int)i1.button << "\n"; // 1 std::cout << i1.player2 << "\n"; // false std::cout << i1.down << "\n"; // true ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.