#pragma once #include #include #include #include #include #include #include #include "../Player.hh" struct Lobby; namespace Episode3 { // The comment in Server.hh does not apply to this file (and BattleRecord.cc). class BattleRecord { public: struct PlayerEntry { PlayerLobbyDataDCGC lobby_data; PlayerInventory inventory; PlayerDispDataDCPCV3 disp; } __attribute__((packed)); struct Event { enum class Type : uint8_t { PLAYER_JOIN = 0, PLAYER_LEAVE = 1, SET_INITIAL_PLAYERS = 2, BATTLE_COMMAND = 3, GAME_COMMAND = 4, EP3_GAME_COMMAND = 5, CHAT_MESSAGE = 6, }; // Fields used for all events Type type; uint64_t timestamp; // Fields used for PLAYER_JOIN and SET_INITIAL_PLAYERS only std::vector players; // Fields used for PLAYER_LEAVE only uint8_t leaving_client_id; // Fields used for CHAT_MESSAGE only uint32_t guild_card_number; // Fields used for the COMMAND types and CHAT_MESSAGE std::string data; Event() = default; explicit Event(StringReader& r); void serialize(StringWriter& w) const; }; explicit BattleRecord(uint32_t behavior_flags); explicit BattleRecord(const std::string& data); std::string serialize() const; bool writable() const; bool battle_in_progress() const; const Event* get_first_event() const; void add_player( const PlayerLobbyDataDCGC& lobby_data, const PlayerInventory& inventory, const PlayerDispDataDCPCV3& disp); void delete_player(uint8_t client_id); void add_command(Event::Type type, const void* data, size_t size); void add_command(Event::Type type, std::string&& data); void add_chat_message(uint32_t guild_card_number, std::string&& data); // This function collapses all the existing player join/leave events into a // single SET_INITIAL_PLAYERS event, and deletes all events before the latest // BATTLE_COMMAND command that specifies the battle map. This should provide a // minimal set of commands to set up and start the battle during a replay. void set_battle_start_timestamp(); void set_battle_end_timestamp(); private: static constexpr uint64_t SIGNATURE = 0x14C946D56D1DAC5A; static bool is_map_definition_event(const Event& ev); bool is_writable; uint32_t behavior_flags; uint64_t battle_start_timestamp; uint64_t battle_end_timestamp; std::deque events; friend class BattleRecordPlayer; }; class BattleRecordPlayer { public: BattleRecordPlayer( std::shared_ptr rec, std::shared_ptr base); ~BattleRecordPlayer() = default; std::shared_ptr get_record() const; void set_lobby(std::shared_ptr l); void start(); private: static void dispatch_schedule_events(evutil_socket_t, short, void* ctx); void schedule_events(); std::shared_ptr record; std::deque::const_iterator event_it; uint64_t play_start_timestamp; std::shared_ptr base; std::weak_ptr lobby; std::shared_ptr next_command_ev; }; } // namespace Episode3