From c88f4184eaa48efbdc69e76eb3c8a4a206434ad3 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 16 Aug 2025 14:52:24 -0400 Subject: Started writing a data validator Currently, it can check whether identifiers point to non-existent objects, or whether multiple objects share the same identifier. It can also determine whether an identifier is underspecified (e.g. a door doesn't specify a room, or a global connection doesn't specify a map). --- tools/validator/human_processor.cpp | 425 ++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) create mode 100644 tools/validator/human_processor.cpp (limited to 'tools/validator/human_processor.cpp') diff --git a/tools/validator/human_processor.cpp b/tools/validator/human_processor.cpp new file mode 100644 index 0000000..5382443 --- /dev/null +++ b/tools/validator/human_processor.cpp @@ -0,0 +1,425 @@ +#include "human_processor.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "structs.h" + +namespace com::fourisland::lingo2_archipelago { +namespace { + +template +T ReadMessageFromFile(const std::string& path) { + std::cout << "Processing " << path << std::endl; + + std::ifstream file(path); + std::stringstream buffer; + buffer << file.rdbuf(); + + T message; + google::protobuf::TextFormat::ParseFromString(buffer.str(), &message); + + return message; +} + +class HumanProcessor { + public: + HumanProcessor(const std::string& mapdir, CollectedInfo& info) + : mapdir_(mapdir), info_(info) {} + + void Run() { + std::filesystem::path datadir_path = mapdir_; + + ProcessConnectionsFile(datadir_path / "connections.txtpb", std::nullopt); + ProcessMaps(datadir_path); + ProcessIdsFile(datadir_path / "ids.txtpb"); + } + + private: + void ProcessMaps(std::filesystem::path path) { + std::filesystem::path maps_dir = path / "maps"; + for (auto const& dir_entry : + std::filesystem::directory_iterator(maps_dir)) { + ProcessMap(dir_entry.path()); + } + } + + void ProcessMap(std::filesystem::path path) { + std::string map_name = path.filename(); + + ProcessConnectionsFile(path / "connections.txtpb", map_name); + ProcessDoorsFile(path / "doors.txtpb", map_name); + ProcessRooms(path / "rooms", map_name); + } + + void ProcessRooms(std::filesystem::path path, + const std::string& current_map_name) { + for (auto const& dir_entry : std::filesystem::directory_iterator(path)) { + auto room = ReadMessageFromFile(dir_entry.path().string()); + ProcessRoom(room, current_map_name); + } + } + + void ProcessRoom(const HumanRoom& h_room, + const std::string& current_map_name) { + RoomIdentifier room_identifier; + room_identifier.set_map(current_map_name); + room_identifier.set_name(h_room.name()); + + RoomInfo& room_info = info_.rooms[room_identifier]; + room_info.definitions.push_back(h_room); + + for (const HumanPanel& h_panel : h_room.panels()) { + ProcessPanel(h_panel, current_map_name, h_room.name()); + } + + for (const HumanPainting& h_painting : h_room.paintings()) { + ProcessPainting(h_painting, current_map_name, h_room.name()); + } + + for (const HumanPort& h_port : h_room.ports()) { + ProcessPort(h_port, current_map_name, h_room.name()); + } + + for (const HumanLetter& h_letter : h_room.letters()) { + ProcessLetter(h_letter, current_map_name, h_room.name()); + } + + for (const HumanMastery& h_mastery : h_room.masteries()) { + ProcessMastery(h_mastery, current_map_name, h_room.name()); + } + + for (const HumanKeyholder& h_keyholder : h_room.keyholders()) { + ProcessKeyholder(h_keyholder, current_map_name, h_room.name()); + } + } + + void ProcessPanel(const HumanPanel& h_panel, + const std::string& current_map_name, + const std::string& current_room_name) { + PanelIdentifier panel_identifier; + panel_identifier.set_map(current_map_name); + panel_identifier.set_room(current_room_name); + panel_identifier.set_name(h_panel.name()); + + PanelInfo& panel_info = info_.panels[panel_identifier]; + panel_info.definitions.push_back(h_panel); + panel_info.proxies[h_panel.answer()].definitions.push_back(Proxy()); + + for (const Proxy& h_proxy : h_panel.proxies()) { + ProxyInfo& proxy_info = panel_info.proxies[h_proxy.answer()]; + proxy_info.definitions.push_back(h_proxy); + } + + if (h_panel.has_required_door()) { + DoorIdentifier required_door_identifier = + *GetCompleteDoorIdentifier(h_panel.required_door(), current_map_name); + DoorInfo& required_door_info = info_.doors[required_door_identifier]; + required_door_info.panels_referenced_by.push_back(panel_identifier); + } + + if (h_panel.has_required_room()) { + RoomIdentifier required_room_identifier = + *GetCompleteRoomIdentifier(h_panel.required_room(), current_map_name); + RoomInfo& required_room_info = info_.rooms[required_room_identifier]; + required_room_info.panels_referenced_by.push_back(panel_identifier); + } + } + + void ProcessPainting(const HumanPainting& h_painting, + const std::string& current_map_name, + const std::string& current_room_name) { + PaintingIdentifier painting_identifier; + painting_identifier.set_map(current_map_name); + painting_identifier.set_room(current_room_name); + painting_identifier.set_name(h_painting.name()); + + PaintingInfo& painting_info = info_.paintings[painting_identifier]; + painting_info.definitions.push_back(h_painting); + + if (h_painting.has_required_door()) { + DoorIdentifier required_door_identifier = *GetCompleteDoorIdentifier( + h_painting.required_door(), current_map_name); + DoorInfo& required_door_info = info_.doors[required_door_identifier]; + required_door_info.paintings_referenced_by.push_back(painting_identifier); + } + } + + void ProcessPort(const HumanPort& h_port, const std::string& current_map_name, + const std::string& current_room_name) { + PortIdentifier port_identifier; + port_identifier.set_map(current_map_name); + port_identifier.set_room(current_room_name); + port_identifier.set_name(h_port.name()); + + PortInfo& port_info = info_.ports[port_identifier]; + port_info.definitions.push_back(h_port); + + if (h_port.has_required_door()) { + DoorIdentifier required_door_identifier = + *GetCompleteDoorIdentifier(h_port.required_door(), current_map_name); + DoorInfo& required_door_info = info_.doors[required_door_identifier]; + required_door_info.ports_referenced_by.push_back(port_identifier); + } + } + + void ProcessLetter(const HumanLetter& h_letter, + const std::string& current_map_name, + const std::string& current_room_name) { + LetterIdentifier letter_identifier = + std::make_tuple(h_letter.key()[0], h_letter.level2()); + LetterInfo& letter_info = info_.letters[letter_identifier]; + + RoomIdentifier room_identifier; + room_identifier.set_map(current_map_name); + room_identifier.set_name(current_room_name); + letter_info.defined_in.push_back(room_identifier); + } + + void ProcessMastery(const HumanMastery& h_mastery, + const std::string& current_map_name, + const std::string& current_room_name) { + // Nothing really to validate about masteries yet. + } + + void ProcessKeyholder(const HumanKeyholder& h_keyholder, + const std::string& current_map_name, + const std::string& current_room_name) { + KeyholderIdentifier keyholder_identifier; + keyholder_identifier.set_map(current_map_name); + keyholder_identifier.set_room(current_room_name); + keyholder_identifier.set_name(h_keyholder.name()); + + KeyholderInfo& keyholder_info = info_.keyholders[keyholder_identifier]; + keyholder_info.definitions.push_back(h_keyholder); + } + + void ProcessDoorsFile(std::filesystem::path path, + const std::string& current_map_name) { + if (!std::filesystem::exists(path)) { + return; + } + + auto doors = ReadMessageFromFile(path.string()); + + for (const HumanDoor& door : doors.doors()) { + ProcessDoor(door, current_map_name); + } + } + + void ProcessDoor(const HumanDoor& h_door, + const std::string& current_map_name) { + DoorIdentifier door_identifier; + door_identifier.set_map(current_map_name); + door_identifier.set_name(h_door.name()); + + DoorInfo& door_info = info_.doors[door_identifier]; + door_info.definitions.push_back(h_door); + + if (h_door.has_location_room()) { + RoomIdentifier location_room_identifier; + location_room_identifier.set_map(current_map_name); + location_room_identifier.set_name(h_door.location_room()); + info_.rooms[location_room_identifier].doors_referenced_by.push_back( + door_identifier); + } + + for (const PaintingIdentifier& pi : h_door.move_paintings()) { + auto complete_painting_identifier = + GetCompletePaintingIdentifier(pi, current_map_name, std::nullopt); + if (complete_painting_identifier) { + PaintingInfo& move_painting_info = + info_.paintings[*complete_painting_identifier]; + move_painting_info.doors_referenced_by.push_back(door_identifier); + } else { + door_info.malformed_identifiers.paintings.push_back(pi); + } + } + + for (const PanelIdentifier& pi : h_door.panels()) { + auto complete_panel_identifier = GetCompletePanelIdentifierWithoutAnswer( + pi, current_map_name, std::nullopt); + if (complete_panel_identifier) { + PanelInfo& panel_info = info_.panels[*complete_panel_identifier]; + panel_info.doors_referenced_by.push_back(door_identifier); + + if (pi.has_answer()) { + panel_info.proxies[pi.answer()].doors_referenced_by.push_back( + door_identifier); + } + } else { + door_info.malformed_identifiers.panels.push_back(pi); + } + } + + for (const KeyholderIdentifier& ki : h_door.keyholders()) { + auto complete_keyholder_identifier = + GetCompleteKeyholderIdentifierWithoutKey(ki, current_map_name, + std::nullopt); + if (complete_keyholder_identifier) { + KeyholderInfo& keyholder_info = + info_.keyholders[*complete_keyholder_identifier]; + keyholder_info.doors_referenced_by.push_back(door_identifier); + } else { + door_info.malformed_identifiers.keyholders.push_back(ki); + } + } + + for (const RoomIdentifier& ri : h_door.rooms()) { + RoomIdentifier complete_room_identifier = + *GetCompleteRoomIdentifier(ri, current_map_name); + RoomInfo& room_info = info_.rooms[complete_room_identifier]; + room_info.doors_referenced_by.push_back(door_identifier); + } + + for (const DoorIdentifier& di : h_door.doors()) { + DoorIdentifier complete_door_identifier = + *GetCompleteDoorIdentifier(di, current_map_name); + DoorInfo& other_door_info = info_.doors[complete_door_identifier]; + other_door_info.doors_referenced_by.push_back(door_identifier); + } + } + + void ProcessConnectionsFile(std::filesystem::path path, + std::optional current_map_name) { + if (!std::filesystem::exists(path)) { + return; + } + + auto connections = ReadMessageFromFile(path.string()); + + for (const HumanConnection& connection : connections.connections()) { + ProcessConnection(connection, current_map_name); + } + } + + void ProcessConnection(const HumanConnection& human_connection, + const std::optional& current_map_name) { + if (human_connection.has_from_room()) { + if (current_map_name) { + RoomIdentifier room_identifier; + room_identifier.set_map(*current_map_name); + room_identifier.set_name(human_connection.from_room()); + + RoomInfo& room_info = info_.rooms[room_identifier]; + room_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout << "A global connection used from_room." << std::endl; + } + } else if (human_connection.has_from()) { + ProcessSingleConnection(human_connection, human_connection.from(), + current_map_name); + } + + if (human_connection.has_to_room()) { + if (current_map_name) { + RoomIdentifier room_identifier; + room_identifier.set_map(*current_map_name); + room_identifier.set_name(human_connection.to_room()); + + RoomInfo& room_info = info_.rooms[room_identifier]; + room_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout << "A global connection used to_room." << std::endl; + } + } else if (human_connection.has_to()) { + ProcessSingleConnection(human_connection, human_connection.to(), + current_map_name); + } + + if (human_connection.has_door()) { + auto door_identifier = + GetCompleteDoorIdentifier(human_connection.door(), current_map_name); + if (door_identifier) { + DoorInfo& door_info = info_.doors[*door_identifier]; + door_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed door identifier: " + << human_connection.door().ShortDebugString() << std::endl; + } + } + } + + void ProcessSingleConnection( + const HumanConnection& human_connection, + const HumanConnection::Endpoint& endpoint, + const std::optional& current_map_name) { + if (endpoint.has_room()) { + auto room_identifier = + GetCompleteRoomIdentifier(endpoint.room(), current_map_name); + if (room_identifier) { + RoomInfo& room_info = info_.rooms[*room_identifier]; + room_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed room identifier: " + << endpoint.room().ShortDebugString() << std::endl; + } + } else if (endpoint.has_painting()) { + auto painting_identifier = GetCompletePaintingIdentifier( + endpoint.painting(), current_map_name, std::nullopt); + if (painting_identifier) { + PaintingInfo& painting_info = info_.paintings[*painting_identifier]; + painting_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed painting identifier: " + << endpoint.painting().ShortDebugString() << std::endl; + } + } else if (endpoint.has_port()) { + auto port_identifier = GetCompletePortIdentifier( + endpoint.port(), current_map_name, std::nullopt); + if (port_identifier) { + PortInfo& port_info = info_.ports[*port_identifier]; + port_info.connections_referenced_by.push_back(human_connection); + } else { + // Not sure where else to store this right now. + std::cout + << "A connection used the following malformed port identifier: " + << endpoint.port().ShortDebugString() << std::endl; + } + } else if (endpoint.has_panel()) { + auto panel_identifier = GetCompletePanelIdentifierWithoutAnswer( + endpoint.panel(), current_map_name, std::nullopt); + if (panel_identifier) { + PanelInfo& panel_info = info_.panels[*panel_identifier]; + panel_info.connections_referenced_by.push_back(human_connection); + + if (endpoint.panel().has_answer()) { + panel_info.proxies[endpoint.panel().answer()] + .connections_referenced_by.push_back(human_connection); + } + } + } + } + + void ProcessIdsFile(std::filesystem::path path) { + // Ignore this for now. + } + + std::string mapdir_; + CollectedInfo& info_; +}; + +} // namespace + +void ProcessHumanData(const std::string& mapdir, CollectedInfo& info) { + HumanProcessor human_processor(mapdir, info); + human_processor.Run(); +} + +} // namespace com::fourisland::lingo2_archipelago -- cgit 1.4.1