9
3

PeerManager.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /*
  2. EQ2Emu: Everquest II Server Emulator
  3. Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
  4. This file is part of EQ2Emu.
  5. EQ2Emu is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. EQ2Emu is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "PeerManager.h"
  17. #include "../../common/Log.h"
  18. #include "../net.h"
  19. #include "../PlayerGroups.h"
  20. #include "HTTPSClientPool.h"
  21. extern NetConnection net;
  22. extern HTTPSClientPool peer_https_pool;
  23. // HealthCheck method definitions
  24. void HealthCheck::updateStatus(HealthStatus newStatus) {
  25. status = newStatus;
  26. lastReceived = std::chrono::system_clock::now();
  27. }
  28. std::chrono::duration<double> HealthCheck::timeSinceLastCheck() const {
  29. return std::chrono::system_clock::now() - lastReceived;
  30. }
  31. ZoneChangeDetails::ZoneChangeDetails(std::string peer_id, std::string peer_world_address, std::string peer_internal_world_address, int16 peer_world_port,
  32. std::string peer_web_address, int16 peer_web_port, std::string zone_file_name, std::string zone_name, int32 zone_id,
  33. int32 instance_id, float safe_x, float safe_y, float safe_z, float safe_heading, bool lock_state, sint16 min_status,
  34. int16 min_level, int16 max_level, int16 min_version, int32 default_lockout_time, int32 default_reenter_time, int8 instance_type, int32 num_players)
  35. : peerId(std::move(peer_id)), peerWorldAddress(peer_world_address), peerInternalWorldAddress(peer_internal_world_address), peerWorldPort(peer_world_port),
  36. peerWebAddress(peer_web_address), peerWebPort(peer_web_port), zoneFileName(zone_file_name), zoneName(zone_name), zoneId(zone_id), instanceId(instance_id),
  37. safeX(safe_x), safeY(safe_y), safeZ(safe_z), safeHeading(safe_heading), lockState(lock_state), minStatus(min_status), minLevel(min_level),
  38. maxLevel(max_level), minVersion(min_version), defaultLockoutTime(default_lockout_time), defaultReenterTime(default_reenter_time), instanceType(instance_type), numPlayers(num_players) {
  39. zonePtr = nullptr;
  40. }
  41. ZoneChangeDetails::ZoneChangeDetails(ZoneChangeDetails* copy_details) : peerId(copy_details->peerId), peerWorldAddress(copy_details->peerWorldAddress), peerWorldPort(copy_details->peerWorldPort),
  42. peerWebAddress(copy_details->peerWebAddress), peerWebPort(copy_details->peerWebPort), zoneFileName(copy_details->zoneFileName), zoneName(copy_details->zoneName),
  43. zoneId(copy_details->zoneId), instanceId(copy_details->instanceId), safeX(copy_details->safeX), safeY(copy_details->safeY), safeZ(copy_details->safeZ),
  44. safeHeading(copy_details->safeHeading), lockState(copy_details->lockState), minStatus(copy_details->minStatus), minLevel(copy_details->minLevel),
  45. maxLevel(copy_details->maxLevel), minVersion(copy_details->minVersion), defaultLockoutTime(copy_details->defaultLockoutTime),
  46. defaultReenterTime(copy_details->defaultReenterTime), instanceType(copy_details->instanceType), numPlayers(copy_details->numPlayers),
  47. peerAuthorized(copy_details->peerAuthorized), zoneKey(copy_details->zoneKey), authDispatchedTime(copy_details->authDispatchedTime),
  48. zoningPastAuth(copy_details->zoningPastAuth), zonePtr(copy_details->zonePtr) {
  49. }
  50. // PeerManager method definitions
  51. void PeerManager::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) {
  52. std::shared_ptr<Peer> peer = std::make_shared<Peer>(id, PeeringStatus::SECONDARY, client_address, client_internal_address, client_port, web_address, web_port);
  53. peers.emplace(id, peer);
  54. }
  55. void PeerManager::updateHealth(const std::string& id, HealthStatus newStatus) {
  56. if (peers.find(id) != peers.end()) {
  57. peers[id]->healthCheck.updateStatus(newStatus);
  58. }
  59. }
  60. void PeerManager::updatePriority(const std::string& id, int16 priority) {
  61. if (peers.find(id) != peers.end()) {
  62. peers[id]->peerPriority = priority;
  63. }
  64. }
  65. void PeerManager::updateZoneTree(const std::string& id, const boost::property_tree::ptree& newTree) {
  66. auto it = peers.find(id);
  67. if (it != peers.end()) {
  68. std::lock_guard<std::mutex> lock(it->second->dataMutex);
  69. *(it->second->zone_tree) = newTree;
  70. }
  71. }
  72. void PeerManager::updateClientTree(const std::string& id, const boost::property_tree::ptree& newTree) {
  73. auto it = peers.find(id);
  74. if (it != peers.end()) {
  75. std::lock_guard<std::mutex> lock(it->second->dataMutex);
  76. *(it->second->client_tree) = newTree;
  77. }
  78. }
  79. void PeerManager::setZonePeerData(ZoneChangeDetails* opt_details, std::string peerId, std::string peerWorldAddress, std::string peerInternalWorldAddress, int16 peerWorldPort,
  80. std::string peerWebAddress, int16 peerWebPort, std::string zoneFileName,
  81. std::string zoneName, int32 zoneId, int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
  82. int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers) {
  83. if (opt_details) {
  84. opt_details->peerId = peerId;
  85. opt_details->peerWorldAddress = peerWorldAddress;
  86. opt_details->peerInternalWorldAddress = peerInternalWorldAddress;
  87. opt_details->peerWorldPort = peerWorldPort;
  88. opt_details->peerWebAddress = peerWebAddress;
  89. opt_details->peerWebPort = peerWebPort;
  90. opt_details->zoneFileName = zoneFileName;
  91. opt_details->zoneName = zoneName;
  92. opt_details->zoneId = zoneId;
  93. opt_details->instanceId = instanceId;
  94. opt_details->safeX = safeX;
  95. opt_details->safeY = safeY;
  96. opt_details->safeZ = safeZ;
  97. opt_details->safeHeading = safeHeading;
  98. opt_details->lockState = lockState;
  99. opt_details->minStatus = minStatus;
  100. opt_details->minLevel = minLevel;
  101. opt_details->maxLevel = maxLevel;
  102. opt_details->minVersion = minVersion;
  103. opt_details->minVersion = minVersion;
  104. opt_details->defaultLockoutTime = defaultLockoutTime;
  105. opt_details->defaultReenterTime = defaultReenterTime;
  106. opt_details->instanceType = instanceType;
  107. opt_details->numPlayers = numPlayers;
  108. opt_details->zonePtr = nullptr;
  109. opt_details->peerAuthorized = false;
  110. opt_details->zoneKey = 0;
  111. opt_details->authDispatchedTime = 0;
  112. opt_details->zoningPastAuth = false;
  113. }
  114. }
  115. void PeerManager::setZonePeerDataSelf(ZoneChangeDetails* opt_details, std::string zoneFileName, std::string zoneName, int32 zoneId,
  116. int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
  117. int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType,
  118. int32 numPlayers, void* zonePtr) {
  119. if (opt_details) {
  120. opt_details->peerId = "self";
  121. opt_details->peerWorldAddress = net.GetWorldAddress();
  122. opt_details->peerInternalWorldAddress = net.GetInternalWorldAddress();
  123. opt_details->peerWorldPort = net.GetWorldPort();
  124. opt_details->peerWebAddress = net.GetWebWorldAddress();
  125. opt_details->peerWebPort = net.GetWebWorldPort();
  126. opt_details->zoneFileName = zoneFileName;
  127. opt_details->zoneName = zoneName;
  128. opt_details->zoneId = zoneId;
  129. opt_details->instanceId = instanceId;
  130. opt_details->safeX = safeX;
  131. opt_details->safeY = safeY;
  132. opt_details->safeZ = safeZ;
  133. opt_details->safeHeading = safeHeading;
  134. opt_details->lockState = lockState;
  135. opt_details->minStatus = minStatus;
  136. opt_details->minLevel = minLevel;
  137. opt_details->maxLevel = maxLevel;
  138. opt_details->minVersion = minVersion;
  139. opt_details->defaultLockoutTime = defaultLockoutTime;
  140. opt_details->defaultReenterTime = defaultReenterTime;
  141. opt_details->instanceType = instanceType;
  142. opt_details->numPlayers = numPlayers;
  143. opt_details->zonePtr = zonePtr;
  144. opt_details->peerAuthorized = true;
  145. opt_details->zoneKey = 0;
  146. opt_details->authDispatchedTime = 0;
  147. opt_details->zoningPastAuth = true;
  148. }
  149. }
  150. std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details, bool only_always_loaded) {
  151. for (auto& [peerId, peer] : peers) {
  152. if (peer->healthCheck.status != HealthStatus::OK)
  153. continue;
  154. try {
  155. std::lock_guard<std::mutex> lock(peer->dataMutex);
  156. for (const auto& zone : peer->zone_tree->get_child("Zones")) {
  157. // Access each field within the current zone
  158. std::string zone_name = zone.second.get<std::string>("zone_name");
  159. std::string zone_file_name = zone.second.get<std::string>("zone_file_name");
  160. int32 zone_id = zone.second.get<int32>("zone_id");
  161. int32 instance_id = zone.second.get<int32>("instance_id");
  162. bool shutting_down = zone.second.get<std::string>("shutting_down") == "true";
  163. bool instance_zone = zone.second.get<std::string>("instance_zone") == "true";
  164. int32 num_players = zone.second.get<int32>("num_players");
  165. bool city_zone = zone.second.get<std::string>("city_zone") == "true";
  166. float safe_x = zone.second.get<float>("safe_x");
  167. float safe_y = zone.second.get<float>("safe_y");
  168. float safe_z = zone.second.get<float>("safe_z");
  169. float safe_heading = zone.second.get<float>("safe_heading");
  170. bool lock_state = zone.second.get<bool>("lock_state");
  171. sint16 min_status = zone.second.get<sint16>("min_status");
  172. int16 min_level = zone.second.get<int16>("min_level");
  173. int16 max_level = zone.second.get<int16>("max_level");
  174. int16 min_version = zone.second.get<int16>("min_version");
  175. int32 default_lockout_time = zone.second.get<int32>("default_lockout_time");
  176. int32 default_reenter_time = zone.second.get<int32>("default_reenter_time");
  177. int8 instance_type = zone.second.get<int8>("instance_type");
  178. bool always_loaded = zone.second.get<bool>("always_loaded");
  179. if (only_always_loaded && !always_loaded)
  180. continue;
  181. if (!shutting_down) {
  182. bool match = false;
  183. if (instance_zone && inc_instance_id > 0 && instance_id == inc_instance_id) {
  184. match = true;
  185. }
  186. else if (!instance_zone && inc_instance_id == 0 && inc_zone_id > 0 && zone_id == inc_zone_id) {
  187. match = true;
  188. }
  189. else if (!instance_zone && inc_zone_name.length() > 0 && strncasecmp(zone_name.c_str(), inc_zone_name.c_str(), inc_zone_name.length()) == 0) {
  190. match = true;
  191. }
  192. if (match) {
  193. setZonePeerData(opt_details, peerId, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, zone_file_name, zone_name, zone_id, instance_id,
  194. safe_x, safe_y, safe_z, safe_heading, lock_state, min_status, min_level, max_level, min_version, default_lockout_time, default_reenter_time, instance_type, num_players);
  195. return peerId;
  196. }
  197. }
  198. }
  199. }
  200. catch (const std::exception& e) {
  201. LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error Parsing Zones for %s:%u", __FUNCTION__, peer->webAddr.c_str(), peer->webPort);
  202. }
  203. }
  204. return "";
  205. }
  206. void PeerManager::handlePrimaryConflict(const std::string& reconnectingPeerId) {
  207. // Compare IDs or priorities to decide on the primary role
  208. auto currentPrimary = getCurrentPrimary();
  209. auto reconnectingPeer = getPeerById(reconnectingPeerId);
  210. if (currentPrimary && (currentPrimary->peerPriority > reconnectingPeer->peerPriority || currentPrimary->healthCheck.status != HealthStatus::OK)) {
  211. // Demote the current primary
  212. if (reconnectingPeer && currentPrimary->healthCheck.status == HealthStatus::OK) {
  213. setPrimary(reconnectingPeerId);
  214. LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s forced to primary", __FUNCTION__, reconnectingPeer->id);
  215. if (currentPrimary) {
  216. LogWrite(PEERING__INFO, 0, "Peering", "%s: Demoted to secondary", __FUNCTION__);
  217. }
  218. }
  219. }
  220. else {
  221. // Demote the reconnecting peer
  222. if (currentPrimary && currentPrimary->healthCheck.status == HealthStatus::OK) {
  223. setPrimary(currentPrimary->id);
  224. LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s forced to primary", __FUNCTION__, currentPrimary->id);
  225. }
  226. }
  227. }
  228. void PeerManager::setPrimary(const std::string& id) {
  229. for (auto& [peerId, peer] : peers) {
  230. peer->peeringStatus = (peerId == id) ? PeeringStatus::PRIMARY : PeeringStatus::SECONDARY;
  231. }
  232. }
  233. bool PeerManager::hasPrimary() {
  234. for (auto& [peerId, peer] : peers) {
  235. if (peer->peeringStatus == PeeringStatus::PRIMARY)
  236. return true;
  237. }
  238. return false;
  239. }
  240. bool PeerManager::hasPriorityPeer(int16 priority) {
  241. for (auto& [peerId, peer] : peers) {
  242. if (peer->peerPriority == priority && peer->healthCheck.status == HealthStatus::OK)
  243. return true;
  244. }
  245. return false;
  246. }
  247. std::string PeerManager::getPriorityPeer() {
  248. int16 peerPriority = 65535;
  249. std::string id = "";
  250. for (auto& [peerId, peer] : peers) {
  251. if (peer->healthCheck.status > HealthStatus::ERROR && peer->healthCheck.status <= HealthStatus::OK &&
  252. peer->peerPriority > 0 && peer->peerPriority < peerPriority) {
  253. peerPriority = peer->peerPriority;
  254. id = peer->id;
  255. }
  256. }
  257. return id;
  258. }
  259. void PeerManager::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) {
  260. for (auto& [peerId, peer] : peers) {
  261. if (peer->webAddr == web_address && peer->webPort == web_port) {
  262. peer->worldAddr = client_address;
  263. peer->worldPort = client_port;
  264. peer->internalWorldAddr = client_internal_address;
  265. if (is_primary) {
  266. peer->peeringStatus = PeeringStatus::PRIMARY;
  267. }
  268. else {
  269. peer->peeringStatus = PeeringStatus::SECONDARY;
  270. }
  271. break;
  272. }
  273. }
  274. }
  275. std::string PeerManager::isPeer(const std::string& web_address, int16 web_port) {
  276. for (auto& [peerId, peer] : peers) {
  277. if (peer->webAddr == web_address && peer->webPort == web_port) {
  278. return peerId;
  279. }
  280. }
  281. return std::string("");
  282. }
  283. HealthStatus PeerManager::getPeerStatus(const std::string& web_address, int16 web_port) {
  284. for (auto& [peerId, peer] : peers) {
  285. if (peer->webAddr == web_address && peer->webPort == web_port) {
  286. return peer->healthCheck.status;
  287. }
  288. }
  289. return HealthStatus::UNKNOWN;
  290. }
  291. bool PeerManager::hasPeers() {
  292. return (peers.size() > 0);
  293. }
  294. std::string PeerManager::assignUniqueNameForSecondary(const std::string& baseName, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port) {
  295. int suffix = 1;
  296. std::string uniqueName = baseName + std::to_string(suffix);
  297. while (peers.find(uniqueName) != peers.end()) {
  298. uniqueName = baseName + std::to_string(suffix);
  299. ++suffix;
  300. }
  301. addPeer(uniqueName, PeeringStatus::SECONDARY, client_address, client_internal_address, client_port, web_address, web_port);
  302. updateHealth(uniqueName, HealthStatus::STARTUP);
  303. return uniqueName;
  304. }
  305. std::optional<std::string> PeerManager::getHealthyPeer() const {
  306. for (const auto& [id, peer] : peers) {
  307. if (peer->healthCheck.status == HealthStatus::OK) {
  308. return id;
  309. }
  310. }
  311. return std::nullopt;
  312. }
  313. std::shared_ptr<Peer> PeerManager::getHealthyPeerPtr() const {
  314. for (const auto& [id, peer] : peers) {
  315. if (peer->healthCheck.status == HealthStatus::OK) {
  316. return peer;
  317. }
  318. }
  319. return nullptr;
  320. }
  321. std::shared_ptr<Peer> PeerManager::getHealthyPrimaryPeerPtr() const {
  322. for (const auto& [id, peer] : peers) {
  323. if (peer->peeringStatus == PeeringStatus::PRIMARY && peer->healthCheck.status == HealthStatus::OK) {
  324. return peer;
  325. }
  326. }
  327. return nullptr;
  328. }
  329. std::shared_ptr<Peer> PeerManager::getHealthyPeerWithLeastClients() const {
  330. std::vector<std::shared_ptr<Peer>> healthyPeers;
  331. // Seed random generator
  332. std::srand(static_cast<unsigned>(std::time(nullptr)));
  333. // Step 1: Collect healthy peers
  334. for (auto& [peerId, peer] : peers) {
  335. // if setup to distribute to peers only, skip primary
  336. if (peer->peerPriority == 0 && peer->peeringStatus == PeeringStatus::PRIMARY) {
  337. continue;
  338. }
  339. if (peer->healthCheck.status == HealthStatus::OK) {
  340. healthyPeers.push_back(peer);
  341. }
  342. }
  343. if (healthyPeers.empty()) {
  344. return nullptr; // No healthy peers found
  345. }
  346. std::string treeList("Zones");
  347. // Step 2: Determine minimum number of "Clients" for healthy peers
  348. size_t minClientCount = std::numeric_limits<size_t>::max();
  349. for (const auto& peer : healthyPeers) {
  350. std::lock_guard<std::mutex> lock(peer->dataMutex); // Lock for thread-safe access
  351. std::shared_ptr<boost::property_tree::ptree> tree = peer->zone_tree;
  352. if (auto clientOpt = tree->get_child_optional(treeList.c_str())) {
  353. size_t clientCount = clientOpt.get().size();
  354. if (clientCount < minClientCount) {
  355. minClientCount = clientCount;
  356. }
  357. }
  358. else {
  359. // Consider peers without "Clients" node as having 0 clients
  360. minClientCount = 0;
  361. }
  362. }
  363. // Step 3: Collect all healthy peers with minClientCount
  364. std::vector<std::shared_ptr<Peer>> minClientPeers;
  365. for (const auto& peer : healthyPeers) {
  366. std::lock_guard<std::mutex> lock(peer->dataMutex);
  367. std::shared_ptr<boost::property_tree::ptree> tree = peer->zone_tree;
  368. size_t clientCount = 0;
  369. if (auto clientOpt = tree->get_child_optional(treeList.c_str())) {
  370. clientCount = clientOpt.get().size();
  371. }
  372. if (clientCount == minClientCount) {
  373. minClientPeers.push_back(peer);
  374. }
  375. }
  376. // Step 4: Select a random peer from the minClientPeers
  377. if (!minClientPeers.empty()) {
  378. size_t randomIndex = std::rand() % minClientPeers.size();
  379. return minClientPeers[randomIndex];
  380. }
  381. return nullptr; // Fallback if no peers match the criteria
  382. }
  383. std::shared_ptr<Peer> PeerManager::getPeerById(const std::string& id) const {
  384. auto it = peers.find(id);
  385. if (it != peers.end()) {
  386. return it->second; // Return the shared_ptr<Peer> if found
  387. }
  388. return nullptr; // Return nullptr if the peerId doesn't exist
  389. }
  390. // Function to get a unique integer for a peer
  391. int32 PeerManager::getUniqueGroupId() {
  392. std::lock_guard<std::mutex> lock(idMutex);
  393. uniqueGroupID++;
  394. if (uniqueGroupID == 0)
  395. uniqueGroupID++;
  396. return uniqueGroupID;
  397. }
  398. bool PeerManager::sendPrimaryNewGroupRequest(std::string leader, std::string member, int32 entity_id, GroupOptions* options) {
  399. std::shared_ptr<Peer> primary = getHealthyPrimaryPeerPtr();
  400. if (primary) {
  401. boost::property_tree::ptree root;
  402. root.put("peer_web_address", std::string(net.GetWebWorldAddress()));
  403. root.put("peer_web_port", std::to_string(net.GetWebWorldPort()));
  404. root.put("group_id", 0);
  405. root.put("leader_name", leader);
  406. root.put("member_name", member);
  407. root.put("member_entity_id", entity_id);
  408. populateGroupOptions(root, options);
  409. root.put("is_update", false);
  410. std::ostringstream jsonStream;
  411. boost::property_tree::write_json(jsonStream, root);
  412. std::string jsonPayload = jsonStream.str();
  413. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Leader %s, Member %s(%u)", __FUNCTION__, leader.c_str(), member.c_str(), entity_id);
  414. peer_https_pool.sendPostRequestToPeerAsync(primary->id, primary->webAddr, std::to_string(primary->webPort), "/newgroup", jsonPayload);
  415. return true;
  416. }
  417. return false;
  418. }
  419. // Helper function to populate the ptree with GroupOptions
  420. void PeerManager::populateGroupOptions(boost::property_tree::ptree& root, GroupOptions* options) {
  421. if (!options) return; // Handle null pointer gracefully
  422. root.put("loot_method", options->loot_method);
  423. root.put("loot_items_rarity", options->loot_items_rarity);
  424. root.put("auto_split", options->auto_split);
  425. root.put("default_yell", options->default_yell);
  426. root.put("group_lock_method", options->group_lock_method);
  427. root.put("group_autolock", options->group_autolock);
  428. root.put("solo_autolock", options->solo_autolock);
  429. root.put("auto_loot_method", options->auto_loot_method);
  430. root.put("last_looted_index", options->last_looted_index);
  431. }
  432. void PeerManager::sendPeersNewGroupRequest(std::string peer_creation_address, int16 peer_creation_port,
  433. int32 group_id, std::string leader, std::string member, GroupOptions* options,
  434. std::string peerId, std::vector<int32>* raidGroups, bool is_update) {
  435. boost::property_tree::ptree root;
  436. root.put("group_id", group_id);
  437. root.put("leader_name", leader);
  438. root.put("member_name", member);
  439. populateGroupOptions(root, options);
  440. root.put("peer_web_address", peer_creation_address);
  441. root.put("peer_web_port", peer_creation_port);
  442. if (raidGroups) {
  443. std::vector<int32>::iterator group_itr;
  444. int8 group_count = 0;
  445. for (group_itr = raidGroups->begin(); group_itr != raidGroups->end(); group_itr++) {
  446. std::string fieldName("group_id_");
  447. fieldName.append(std::to_string(group_count));
  448. root.put(fieldName, (*group_itr));
  449. group_count++;
  450. }
  451. }
  452. root.put("is_update", is_update);
  453. std::ostringstream jsonStream;
  454. boost::property_tree::write_json(jsonStream, root);
  455. std::string jsonPayload = jsonStream.str();
  456. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: ToPeer: %s (Optional), NewGroup %u, IsUpdate: %u", __FUNCTION__, peerId.c_str(), group_id, is_update);
  457. if (peerId.size() > 0) {
  458. std::shared_ptr<Peer> peer = getPeerById(peerId);
  459. if (peer->healthCheck.status == HealthStatus::OK) {
  460. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/newgroup", jsonPayload);
  461. }
  462. }
  463. else {
  464. for (const auto& [id, peer] : peers) {
  465. if (peer->healthCheck.status != HealthStatus::OK)
  466. continue;
  467. if (peer->webAddr == peer_creation_address && peer->webPort == peer_creation_port) // skip peer it was created on
  468. continue;
  469. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/newgroup", jsonPayload);
  470. }
  471. }
  472. }
  473. void PeerManager::sendPeersGroupMember(int32 group_id, GroupMemberInfo* info, bool is_update, std::string peerId) {
  474. if (!info) {
  475. return;
  476. }
  477. boost::property_tree::ptree root;
  478. root.put("group_id", group_id);
  479. root.put("member_name", info->name);
  480. root.put("is_client", info->is_client);
  481. root.put("zone", info->zone);
  482. root.put("current_hp", info->hp_current);
  483. root.put("max_hp", info->hp_max);
  484. root.put("current_power", info->power_current);
  485. root.put("max_power", info->power_max);
  486. root.put("level_current", info->level_current);
  487. root.put("level_max", info->level_max);
  488. root.put("race_id", info->race_id);
  489. root.put("class_id", info->class_id);
  490. root.put("is_leader", info->leader);
  491. root.put("mentor_target_char_id", info->mentor_target_char_id);
  492. root.put("client_peer_address", info->client_peer_address);
  493. root.put("client_peer_port", info->client_peer_port);
  494. root.put("is_raid_looter", info->is_raid_looter);
  495. root.put("is_update", is_update);
  496. std::ostringstream jsonStream;
  497. boost::property_tree::write_json(jsonStream, root);
  498. std::string jsonPayload = jsonStream.str();
  499. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Send Member %s ToPeer: %s (Optional), NewGroup %u, IsUpdate: %u", __FUNCTION__, info->name.c_str(), peerId.c_str(), group_id, is_update);
  500. if (peerId.size() > 0) {
  501. std::shared_ptr<Peer> peer = getPeerById(peerId);
  502. if (peer->healthCheck.status == HealthStatus::OK) {
  503. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addgroupmember", jsonPayload);
  504. }
  505. }
  506. else {
  507. for (const auto& [id, peer] : peers) {
  508. if (peer->healthCheck.status != HealthStatus::OK)
  509. continue;
  510. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addgroupmember", jsonPayload);
  511. }
  512. }
  513. }
  514. void PeerManager::sendPeersRemoveGroupMember(int32 group_id, std::string name, int32 char_id, bool is_client) {
  515. boost::property_tree::ptree root;
  516. root.put("group_id", group_id);
  517. root.put("member_name", name);
  518. root.put("is_client", is_client);
  519. root.put("character_id", char_id);
  520. std::ostringstream jsonStream;
  521. boost::property_tree::write_json(jsonStream, root);
  522. std::string jsonPayload = jsonStream.str();
  523. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Member %s ToPeer: %s (Optional), Group %u", __FUNCTION__, name.c_str(), group_id);
  524. for (const auto& [id, peer] : peers) {
  525. if (peer->healthCheck.status != HealthStatus::OK)
  526. continue;
  527. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removegroupmember", jsonPayload);
  528. }
  529. }
  530. void PeerManager::sendPeersDisbandGroup(int32 group_id) {
  531. boost::property_tree::ptree root;
  532. root.put("group_id", group_id);
  533. std::ostringstream jsonStream;
  534. boost::property_tree::write_json(jsonStream, root);
  535. std::string jsonPayload = jsonStream.str();
  536. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Disband Group %u", __FUNCTION__, group_id);
  537. for (const auto& [id, peer] : peers) {
  538. if (peer->healthCheck.status != HealthStatus::OK)
  539. continue;
  540. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/disbandgroup", jsonPayload);
  541. }
  542. }
  543. bool PeerManager::sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name) {
  544. std::shared_ptr<Peer> primary = getHealthyPrimaryPeerPtr();
  545. if (primary) {
  546. boost::property_tree::ptree root;
  547. root.put("peer_web_address", std::string(net.GetWebWorldAddress()));
  548. root.put("peer_web_port", std::to_string(net.GetWebWorldPort()));
  549. root.put("guild_name", guild_name);
  550. root.put("leader_name", leader_name);
  551. std::ostringstream jsonStream;
  552. boost::property_tree::write_json(jsonStream, root);
  553. std::string jsonPayload = jsonStream.str();
  554. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Create Guild Request %s, Leader %s", __FUNCTION__, guild_name.c_str(), leader_name.c_str());
  555. peer_https_pool.sendPostRequestToPeerAsync(primary->id, primary->webAddr, std::to_string(primary->webPort), "/createguild", jsonPayload);
  556. return true;
  557. }
  558. return false;
  559. }
  560. void PeerManager::sendPeersAddGuildMember(int32 character_id, int32 guild_id, std::string invited_by, int32 join_timestamp, int8 rank) {
  561. boost::property_tree::ptree root;
  562. root.put("guild_id", guild_id);
  563. root.put("character_id", character_id);
  564. root.put("invited_by", invited_by);
  565. root.put("join_timestamp", join_timestamp);
  566. root.put("rank", rank);
  567. std::ostringstream jsonStream;
  568. boost::property_tree::write_json(jsonStream, root);
  569. std::string jsonPayload = jsonStream.str();
  570. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Add Guild Member, Guild: %u, CharID: %u, InvitedBy: %s", __FUNCTION__, guild_id, character_id, invited_by.c_str());
  571. for (const auto& [id, peer] : peers) {
  572. if (peer->healthCheck.status != HealthStatus::OK)
  573. continue;
  574. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addguildmember", jsonPayload);
  575. }
  576. }
  577. void PeerManager::sendPeersRemoveGuildMember(int32 character_id, int32 guild_id, std::string removed_by) {
  578. boost::property_tree::ptree root;
  579. root.put("guild_id", guild_id);
  580. root.put("character_id", character_id);
  581. root.put("removed_by", removed_by);
  582. std::ostringstream jsonStream;
  583. boost::property_tree::write_json(jsonStream, root);
  584. std::string jsonPayload = jsonStream.str();
  585. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Guild Member, Guild: %u, CharID: %u, RemovedBy: %s", __FUNCTION__, guild_id, character_id, removed_by.c_str());
  586. for (const auto& [id, peer] : peers) {
  587. if (peer->healthCheck.status != HealthStatus::OK)
  588. continue;
  589. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removeguildmember", jsonPayload);
  590. }
  591. }
  592. void PeerManager::sendPeersCreateGuild(int32 guild_id) {
  593. boost::property_tree::ptree root;
  594. root.put("guild_id", guild_id);
  595. std::ostringstream jsonStream;
  596. boost::property_tree::write_json(jsonStream, root);
  597. std::string jsonPayload = jsonStream.str();
  598. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peers Guild Create, Guild: %u", __FUNCTION__, guild_id);
  599. for (const auto& [id, peer] : peers) {
  600. // primary creates the guild, skip it
  601. if (peer->healthCheck.status != HealthStatus::OK || peer->peeringStatus == PeeringStatus::PRIMARY)
  602. continue;
  603. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/createguild", jsonPayload);
  604. }
  605. }
  606. void PeerManager::sendPeersGuildPermission(int32 guild_id, int8 rank, int8 permission, int8 value_) {
  607. boost::property_tree::ptree root;
  608. root.put("guild_id", guild_id);
  609. root.put("rank", rank);
  610. root.put("permission", permission);
  611. root.put("value", value_);
  612. std::ostringstream jsonStream;
  613. boost::property_tree::write_json(jsonStream, root);
  614. std::string jsonPayload = jsonStream.str();
  615. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peers Guild Permission, Guild: %u, Rank: %u, Permission: %u, Value: %u", __FUNCTION__, guild_id, rank, permission, value_);
  616. for (const auto& [id, peer] : peers) {
  617. // primary creates the guild, skip it
  618. if (peer->healthCheck.status != HealthStatus::OK)
  619. continue;
  620. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/setguildpermission", jsonPayload);
  621. }
  622. }
  623. void PeerManager::sendPeersGuildEventFilter(int32 guild_id, int8 event_id, int8 category, int8 value_) {
  624. boost::property_tree::ptree root;
  625. root.put("guild_id", guild_id);
  626. root.put("event_id", event_id);
  627. root.put("category", category);
  628. root.put("value", value_);
  629. std::ostringstream jsonStream;
  630. boost::property_tree::write_json(jsonStream, root);
  631. std::string jsonPayload = jsonStream.str();
  632. LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peers Guild Event Filter, Guild: %u, EventID: %u, Category: %u, Value: %u", __FUNCTION__, guild_id, event_id, category, value_);
  633. for (const auto& [id, peer] : peers) {
  634. // primary creates the guild, skip it
  635. if (peer->healthCheck.status != HealthStatus::OK)
  636. continue;
  637. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/setguildeventfilter", jsonPayload);
  638. }
  639. }
  640. void PeerManager::SetPeerErrorState(std::string address, std::string port) {
  641. std::string id = isPeer(address, (int16)std::atol(port.c_str()));
  642. if (id.size() > 0)
  643. updateHealth(id, HealthStatus::ERROR);
  644. }
  645. std::shared_ptr<Peer> PeerManager::getCurrentPrimary() {
  646. for (const auto& [id, peer] : peers) {
  647. if (peer->peeringStatus == PeeringStatus::PRIMARY) {
  648. return peer;
  649. }
  650. }
  651. return nullptr; // Or throw an error if no primary found
  652. }
  653. void PeerManager::sendPeersMessage(const std::string& endpoint, int32 command, int32 sub_command) {
  654. boost::property_tree::ptree root;
  655. root.put("reload_command", command);
  656. root.put("sub_command", sub_command);
  657. std::ostringstream jsonStream;
  658. boost::property_tree::write_json(jsonStream, root);
  659. std::string jsonPayload = jsonStream.str();
  660. for (const auto& [id, peer] : peers) {
  661. if (peer->healthCheck.status != HealthStatus::OK)
  662. continue;
  663. peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), endpoint.c_str(), jsonPayload);
  664. }
  665. }