/* EQ2Emu: Everquest II Server Emulator Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com) This file is part of EQ2Emu. EQ2Emu is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. EQ2Emu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with EQ2Emu. If not, see . */ #ifndef PEERMANAGER_H #define PEERMANAGER_H #include #include #include #include #include #include #include #include #include #include #include "../../common/types.h" class Client; enum HealthStatus { UNKNOWN = 0, WARN = 1, ERROR = 2, STARTUP = 3, OK = 4, SHUTDOWN = 5 }; enum class PeeringStatus { PRIMARY, SECONDARY }; struct HealthCheck { HealthStatus status; std::chrono::system_clock::time_point lastReceived; void updateStatus(HealthStatus newStatus); std::chrono::duration timeSinceLastCheck() const; }; struct GroupOptions; struct GroupMemberInfo; struct WhoAllPeerPlayer; struct GuildMember; struct ZoneChangeDetails { std::string peerId; std::string peerWorldAddress; std::string peerInternalWorldAddress; int16 peerWorldPort; std::string peerWebAddress; int16 peerWebPort; std::string zoneFileName; std::string zoneName; int32 zoneId; int32 instanceId; float safeX; float safeY; float safeZ; float safeHeading; bool lockState; sint16 minStatus; int16 minLevel; int16 maxLevel; int16 minVersion; int32 defaultLockoutTime; int32 defaultReenterTime; int8 instanceType; int32 numPlayers; void* zonePtr; bool peerAuthorized; int32 zoneKey; int32 authDispatchedTime; bool zoningPastAuth; ZoneChangeDetails() = default; ZoneChangeDetails(ZoneChangeDetails* copy_details); ZoneChangeDetails(std::string peer_id, std::string peer_world_address, std::string peer_internal_world_address, int16 peer_world_port, std::string peer_web_address, int16 peer_web_port, std::string zone_file_name, std::string zone_name, int32 zone_id, int32 instance_id, float safe_x, float safe_y, float safe_z, float safe_heading, bool lock_state, sint16 min_status, int16 min_level, int16 max_level, int16 min_version, int32 default_lockout_time, int32 default_reenter_time, int8 instance_type, int32 num_players); }; struct Peer { std::string id; PeeringStatus peeringStatus; HealthCheck healthCheck; std::string worldAddr; std::string internalWorldAddr; int16 worldPort; int16 peerPriority; std::string webAddr; int16 webPort; std::shared_ptr zone_tree; std::shared_ptr client_tree; std::atomic sentInitialPeerData; std::atomic wasOffline; mutable std::mutex dataMutex; // Mutex to protect access to ptree // Default constructor Peer() : zone_tree(std::make_shared()), client_tree(std::make_shared()) { healthCheck.status = HealthStatus::UNKNOWN; peerPriority = 65535; sentInitialPeerData = false; wasOffline = false; } Peer(std::string peerId, PeeringStatus status, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port) : id(std::move(peerId)), peeringStatus(status), worldAddr(std::move(client_address)), internalWorldAddr(std::move(client_internal_address)), worldPort(client_port), webAddr(std::move(web_address)), webPort(web_port), zone_tree(std::make_shared()), client_tree(std::make_shared()) { healthCheck.status = HealthStatus::STARTUP; peerPriority = 65535; sentInitialPeerData = false; wasOffline = false; } // Example function to output data as JSON string (for debug or logging) std::string getZoneDataAsJson() const { std::lock_guard lock(dataMutex); std::ostringstream oss; boost::property_tree::write_json(oss, *zone_tree); // Dereference data return oss.str(); } // Example function to output data as JSON string (for debug or logging) std::string getClientDataAsJson() const { std::lock_guard lock(dataMutex); std::ostringstream oss; boost::property_tree::write_json(oss, *client_tree); // Dereference data return oss.str(); } }; class PeerManager { private: std::map> peers; std::atomic uniqueGroupID{ 1 }; // Shared counter for unique IDs std::mutex idMutex; public: void addPeer(std::string id, PeeringStatus status, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port); void updateHealth(const std::string& id, HealthStatus newStatus); void updatePriority(const std::string& id, int16 priority); void updateZoneTree(const std::string& id, const boost::property_tree::ptree& newTree); void updateClientTree(const std::string& id, const boost::property_tree::ptree& newTree); void setZonePeerData(ZoneChangeDetails* opt_details, std::string peerId, std::string peerWorldAddress, std::string peerInternalWorldAddress, int16 peerWorldPort, std::string peerWebAddress, int16 peerWebPort, std::string zoneFileName, std::string zoneName, int32 zoneId, int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus, int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers); void setZonePeerDataSelf(ZoneChangeDetails* opt_details, std::string zoneFileName, std::string zoneName, int32 zoneId, int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus, int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers, void* zonePtr = nullptr); bool IsClientConnectedPeer(int32 account_id); std::string GetCharacterPeerId(std::string charName); void SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0); void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0); void sendZonePeerList(Client* client); std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false); void setZonePeerData(ZoneChangeDetails* opt_details); void setPrimary(const std::string& id); bool hasPrimary(); bool hasPriorityPeer(int16 priority); std::string getPriorityPeer(); void updatePeer(const std::string& web_address, int16 web_port, const std::string& client_address, const std::string& client_internal_address, int16 client_port, bool is_primary = false); std::string isPeer(const std::string& web_address, int16 web_port); HealthStatus getPeerStatus(const std::string& web_address, int16 web_port); bool hasPeers(); std::string assignUniqueNameForSecondary(const std::string& baseName, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port); std::optional getHealthyPeer() const; std::shared_ptr getHealthyPeerPtr() const; std::shared_ptr getHealthyPrimaryPeerPtr() const; std::shared_ptr getHealthyPeerWithLeastClients() const; std::shared_ptr getPeerById(const std::string& id) const; int32 getUniqueGroupId(); bool sendPrimaryNewGroupRequest(std::string leader, std::string member, int32 entity_id, GroupOptions* options); void sendPeersGroupMember(int32 group_id, GroupMemberInfo* info, bool is_update = false, std::string peerId = ""); void sendPeersRemoveGroupMember(int32 group_id, std::string name, int32 char_id, bool is_client); void populateGroupOptions(boost::property_tree::ptree& root, GroupOptions* options); void sendPeersNewGroupRequest(std::string peer_creation_address, int16 peer_creation_port, int32 group_id, std::string leader, std::string member, GroupOptions* options, std::string peerId = "", std::vector* raidGroups = nullptr, bool is_update = false); void sendPeersDisbandGroup(int32 group_id); bool sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name); void sendPeersAddGuildMember(int32 character_id, int32 guild_id, std::string invited_by, int32 join_timestamp, int8 rank); void sendPeersRemoveGuildMember(int32 character_id, int32 guild_id, std::string removed_by); void sendPeersCreateGuild(int32 guild_id); void sendPeersGuildPermission(int32 guild_id, int8 rank, int8 permission, int8 value_); void sendPeersGuildEventFilter(int32 guild_id, int8 event_id, int8 category, int8 value_); void SetPeerErrorState(std::string address, std::string port); void handlePrimaryConflict(const std::string& reconnectingPeerId); std::shared_ptr getCurrentPrimary(); void sendPeersMessage(const std::string& endpoint, int32 command, int32 sub_command = 0); void sendZonePlayerList(std::vector* queries, std::vector* peer_list, bool isGM); bool GetClientGuildDetails(int32 matchCharID, GuildMember* member_details); }; #endif // PEERMANAGER_H