From f0b37e67d160b64c87d8a0f3202cb9652242fe9d Mon Sep 17 00:00:00 2001 From: Emagi Date: Fri, 6 Dec 2024 09:11:30 -0500 Subject: [PATCH] Combined peering to a single request to reduce load --- source/WorldServer/Web/HTTPSClientPool.cpp | 70 +++--- source/WorldServer/Web/WorldWeb.cpp | 249 ++++++++++++--------- source/WorldServer/World.cpp | 2 + source/WorldServer/World.h | 7 +- 4 files changed, 189 insertions(+), 139 deletions(-) diff --git a/source/WorldServer/Web/HTTPSClientPool.cpp b/source/WorldServer/Web/HTTPSClientPool.cpp index 9ba784c..3189bcd 100644 --- a/source/WorldServer/Web/HTTPSClientPool.cpp +++ b/source/WorldServer/Web/HTTPSClientPool.cpp @@ -532,7 +532,7 @@ void HTTPSClientPool::pollPeerHealth(const std::string& server, const std::strin HealthStatus curStatus = peer_manager.getPeerStatus(server, web_worldport); id = peer_manager.isPeer(server, web_worldport); try { - auto response = client->sendRequest(server, port, "/status"); // Assumes HTTPSClient has a get method + auto response = client->sendRequest(server, port, "/peerstatus"); // Assumes HTTPSClient has a get method //std::cout << "Health check response from " << server << ":" << port << " - " << response << std::endl; boost::property_tree::ptree json_tree; @@ -544,29 +544,51 @@ void HTTPSClientPool::pollPeerHealth(const std::string& server, const std::strin std::string worldAddr(""), internalWorldAddr(""), clientIP(""); int16 worldPort = 0; - if (auto status = json_tree.get_optional("world_status")) { - online_status = status.get(); - } - if (auto priority = json_tree.get_optional("peer_priority")) { - peer_priority = priority.get(); - } - if (auto isprimary = json_tree.get_optional("peer_primary")) { - peer_primary = isprimary.get(); - } - if (auto peerclientaddr = json_tree.get_optional("peer_client_address")) { - worldAddr = peerclientaddr.get(); - } - if (auto peerclient_internaladdr = json_tree.get_optional("peer_client_internal_address")) { - internalWorldAddr = peerclient_internaladdr.get(); - } - if (auto peerclientport = json_tree.get_optional("peer_client_port")) { - worldPort = peerclientport.get(); - } - if(worldAddr.size() > 0 && worldPort > 0) { - peer_manager.updatePeer(server, web_worldport, worldAddr, internalWorldAddr, worldPort, peer_primary); - } - peer_manager.updatePriority(id, peer_priority); + try { + auto webStatusTree = json_tree.get_child("WebStatus"); + if (auto status = webStatusTree.get_optional("world_status")) { + online_status = status.get(); + } + if (auto priority = webStatusTree.get_optional("peer_priority")) { + peer_priority = priority.get(); + } + if (auto isprimary = webStatusTree.get_optional("peer_primary")) { + peer_primary = isprimary.get(); + } + if (auto peerclientaddr = webStatusTree.get_optional("peer_client_address")) { + worldAddr = peerclientaddr.get(); + } + if (auto peerclient_internaladdr = webStatusTree.get_optional("peer_client_internal_address")) { + internalWorldAddr = peerclient_internaladdr.get(); + } + if (auto peerclientport = webStatusTree.get_optional("peer_client_port")) { + worldPort = peerclientport.get(); + } + if(worldAddr.size() > 0 && worldPort > 0) { + peer_manager.updatePeer(server, web_worldport, worldAddr, internalWorldAddr, worldPort, peer_primary); + } + peer_manager.updatePriority(id, peer_priority); + } catch (const boost::property_tree::ptree_error& e) { + LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error accessing WebStatus tree: Peer %s at %s:%s - HAS PRIMARY status, demoting self.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str()); + } + // Process Clients + if (json_tree.find("Clients") != json_tree.not_found()) { + for (const auto& client : json_tree.get_child("Clients")) { + boost::property_tree::ptree clientTree = client.second; + peer_manager.updateClientTree(id, clientTree); + break; // should only be one tree + } + } + + // Process Zones + if (json_tree.find("Zones") != json_tree.not_found()) { + for (const auto& zone : json_tree.get_child("Zones")) { + boost::property_tree::ptree zoneTree = zone.second; + peer_manager.updateZoneTree(id, zoneTree); + break; // should only be one tree + } + } if (peer_primary && net.is_primary) { peer_manager.handlePrimaryConflict(id); std::shared_ptr hasPrimary = peer_manager.getHealthyPrimaryPeerPtr(); @@ -581,7 +603,6 @@ void HTTPSClientPool::pollPeerHealth(const std::string& server, const std::strin switch (curStatus) { case HealthStatus::STARTUP: { - pollPeerHealthData(client, id, server, port); if (online_status == "offline") { std::shared_ptr peer = peer_manager.getPeerById(id); if (peer) { @@ -620,7 +641,6 @@ void HTTPSClientPool::pollPeerHealth(const std::string& server, const std::strin break; } case HealthStatus::OK: { - pollPeerHealthData(client, id, server, port); if (!net.is_primary && !peer_manager.hasPrimary() && peer_priority < net.GetPeerPriority()) { LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str()); peer_manager.setPrimary(id); diff --git a/source/WorldServer/Web/WorldWeb.cpp b/source/WorldServer/Web/WorldWeb.cpp index fa6c0a6..ca9f00d 100644 --- a/source/WorldServer/Web/WorldWeb.cpp +++ b/source/WorldServer/Web/WorldWeb.cpp @@ -50,15 +50,27 @@ HTTPSClientPool peer_https_pool; void World::Web_worldhandle_status(const http::request& req, http::response& res) { res.set(http::field::content_type, "application/json; charset=utf-8"); + boost::property_tree::ptree pt; + world.Web_populate_status(pt); + + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} +void World::Web_populate_status(boost::property_tree::ptree& pt) { pt.put("web_status", "online"); bool world_online = world.world_loaded; - pt.put("world_status", world.world_loaded ? "online" : "offline"); + pt.put("world_status", world_online ? "online" : "offline"); pt.put("world_uptime", (getCurrentTimestamp() - world.world_uptime)); + auto [days, hours, minutes, seconds] = convertTimestampDuration((getCurrentTimestamp() - world.world_uptime)); - std::string uptime_str("Days: " + std::to_string(days) + ", " + "Hours: " + std::to_string(hours) + ", " + "Minutes: " + std::to_string(minutes) + ", " + "Seconds: " + std::to_string(seconds)); + std::string uptime_str = "Days: " + std::to_string(days) + ", Hours: " + std::to_string(hours) + ", Minutes: " + std::to_string(minutes) + ", Seconds: " + std::to_string(seconds); pt.put("world_uptime_string", uptime_str); + pt.put("login_connected", loginserver.Connected() ? "connected" : "disconnected"); pt.put("player_count", zone_list.GetZonesPlayersCount()); pt.put("client_count", numclients); @@ -69,7 +81,14 @@ void World::Web_worldhandle_status(const http::request& req, pt.put("peer_client_address", std::string(net.GetWorldAddress())); pt.put("peer_client_internal_address", std::string(net.GetInternalWorldAddress())); pt.put("peer_client_port", std::to_string(net.GetWorldPort())); +} +void World::Web_worldhandle_clients(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + + boost::property_tree::ptree pt; + zone_list.PopulateClientList(pt); + std::ostringstream oss; boost::property_tree::write_json(oss, pt); std::string json = oss.str(); @@ -77,76 +96,66 @@ void World::Web_worldhandle_status(const http::request& req, res.prepare_payload(); } -void World::Web_worldhandle_clients(const http::request& req, http::response& res) { - zone_list.PopulateClientList(res); +void ZoneList::PopulateClientList(boost::property_tree::ptree& pt) { + boost::property_tree::ptree maintree; + + MClientList.lock(); + for (auto& itr : client_map) { + if (itr.second) { + Client* cur = itr.second; + boost::property_tree::ptree client_pt; + + client_pt.put("character_id", cur->GetCharacterID()); + client_pt.put("character_name", cur->GetPlayer() ? cur->GetPlayer()->GetName() : ""); + client_pt.put("subtitle", cur->GetPlayer() ? cur->GetPlayer()->appearance.sub_title : ""); + client_pt.put("class1", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_class1() : 0); + client_pt.put("class2", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_class2() : 0); + client_pt.put("class3", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_class3() : 0); + client_pt.put("deity", cur->GetPlayer() ? cur->GetPlayer()->GetDeity() : 0); + client_pt.put("tradeskill_class1", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_class1() : 0); + client_pt.put("tradeskill_class2", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_class2() : 0); + client_pt.put("tradeskill_class3", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_class3() : 0); + client_pt.put("race", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_race() : 0); + client_pt.put("level", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_level() : 0); + client_pt.put("effective_level", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_effective_level() : 0); + client_pt.put("tradeskill_level", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_level() : 0); + client_pt.put("account_age", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_account_age_base() : 0); + client_pt.put("account_id", cur->GetAccountID()); + client_pt.put("version", cur->GetVersion()); + client_pt.put("status", cur->GetAdminStatus()); + client_pt.put("guild_id", cur->GetPlayer() && cur->GetPlayer()->GetGuild() ? cur->GetPlayer()->GetGuild()->GetID() : 0); + client_pt.put("flags", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_flags() : 0); + client_pt.put("flags2", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_flags2() : 0); + client_pt.put("adventure_class", cur->GetPlayer() ? cur->GetPlayer()->GetAdventureClass() : 0); + client_pt.put("tradeskill_class", cur->GetPlayer() ? cur->GetPlayer()->GetTradeskillClass() : 0); + client_pt.put("is_zoning", cur->IsZoning() || !cur->IsReadyForUpdates()); + + bool linkdead = cur->GetPlayer() ? ((cur->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0) : false; + client_pt.put("is_linkdead", linkdead); + client_pt.put("in_zone", cur->IsReadyForUpdates()); + client_pt.put("zone_id", cur->GetPlayer() && cur->GetPlayer()->GetZone() ? cur->GetPlayer()->GetZone()->GetZoneID() : 0); + client_pt.put("instance_id", cur->GetPlayer() && cur->GetPlayer()->GetZone() ? cur->GetPlayer()->GetZone()->GetInstanceID() : 0); + client_pt.put("zonename", cur->GetPlayer() && cur->GetPlayer()->GetZone() ? cur->GetPlayer()->GetZone()->GetZoneName() : "N/A"); + client_pt.put("zonedescription", cur->GetPlayer() && cur->GetPlayer()->GetZone() ? cur->GetPlayer()->GetZone()->GetZoneDescription() : ""); + + GroupMemberInfo* gmi = cur->GetPlayer() ? cur->GetPlayer()->GetGroupMemberInfo() : nullptr; + int32 group_id = 0; + bool group_leader = false; + if (gmi && gmi->group_id) { + group_id = gmi->group_id; + group_leader = gmi->leader; + } + client_pt.put("group_id", group_id); + client_pt.put("group_leader", group_leader); + + maintree.push_back(std::make_pair("", client_pt)); + } + } + MClientList.unlock(); + + pt.add_child("Clients", maintree); } -void ZoneList::PopulateClientList(http::response& res) { - res.set(http::field::content_type, "application/json; charset=utf-8"); - boost::property_tree::ptree maintree; - - std::ostringstream oss; - - MClientList.lock(); - map::iterator itr; - for (itr = client_map.begin(); itr != client_map.end(); itr++) { - if (itr->second) { - Client* cur = (Client*)itr->second; - boost::property_tree::ptree pt; - pt.put("character_id", cur->GetCharacterID()); - pt.put("character_name", cur->GetPlayer() ? cur->GetPlayer()->GetName() : ""); - pt.put("subtitle", cur->GetPlayer() ? cur->GetPlayer()->appearance.sub_title : ""); - pt.put("class1", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_class1() : 0); - pt.put("class2", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_class2() : 0); - pt.put("class3", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_class3() : 0); - pt.put("deity", cur->GetPlayer() ? cur->GetPlayer()->GetDeity() : 0); - pt.put("tradeskill_class1", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_class1() : 0); - pt.put("tradeskill_class2", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_class2() : 0); - pt.put("tradeskill_class3", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_class3() : 0); - pt.put("race", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_race() : 0); - pt.put("level", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_level() : 0); - pt.put("effective_level", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_effective_level() : 0); - pt.put("tradeskill_level", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_tradeskill_level() : 0); - pt.put("account_age", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_account_age_base() : 0); - pt.put("account_id", cur->GetAccountID()); - pt.put("version", cur->GetVersion()); - pt.put("status", cur->GetAdminStatus()); - pt.put("guild_id", cur->GetPlayer()->GetGuild() != nullptr ? cur->GetPlayer()->GetGuild()->GetID() : 0); - pt.put("flags", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_flags() : 0); - pt.put("flags2", cur->GetPlayer() ? cur->GetPlayer()->GetInfoStruct()->get_flags2() : 0); - pt.put("adventure_class", cur->GetPlayer() ? cur->GetPlayer()->GetAdventureClass() : 0); - pt.put("tradeskill_class", cur->GetPlayer() ? cur->GetPlayer()->GetTradeskillClass() : 0); - pt.put("is_zoning", (cur->IsZoning() || !cur->IsReadyForUpdates())); - - bool linkdead = cur->GetPlayer() ? (((cur->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0)) : false; - pt.put("is_linkdead", linkdead); - pt.put("in_zone", cur->IsReadyForUpdates()); - pt.put("zone_id", (cur->GetPlayer() && cur->GetPlayer()->GetZone()) ? cur->GetPlayer()->GetZone()->GetZoneID() : 0); - pt.put("instance_id", (cur->GetPlayer() && cur->GetPlayer()->GetZone()) ? cur->GetPlayer()->GetZone()->GetInstanceID() : 0); - pt.put("zonename", (cur->GetPlayer() && cur->GetPlayer()->GetZone()) ? cur->GetPlayer()->GetZone()->GetZoneName() : "N/A"); - pt.put("zonedescription", (cur->GetPlayer() && cur->GetPlayer()->GetZone()) ? cur->GetPlayer()->GetZone()->GetZoneDescription() : ""); - - GroupMemberInfo* gmi = cur->GetPlayer()->GetGroupMemberInfo(); - int32 group_id = 0; - bool group_leader = false; - if (gmi && gmi->group_id) { - group_id = gmi->group_id; - group_leader = gmi->leader; - } - pt.put("group_id", group_id); - pt.put("group_leader", group_leader); - maintree.push_back(std::make_pair("", pt)); - } - } - MClientList.unlock(); - - boost::property_tree::ptree result; - result.add_child("Clients", maintree); - boost::property_tree::write_json(oss, result); - std::string json = oss.str(); - res.body() = json; - res.prepare_payload(); -} void World::Web_worldhandle_setadminstatus(const http::request& req, http::response& res) { res.set(http::field::content_type, "application/json; charset=utf-8"); @@ -452,55 +461,56 @@ void World::Web_worldhandle_addpeer(const http::request& req, } void World::Web_worldhandle_zones(const http::request& req, http::response& res) { - zone_list.PopulateZoneList(res); -} - -void ZoneList::PopulateZoneList(http::response& res) { res.set(http::field::content_type, "application/json; charset=utf-8"); - boost::property_tree::ptree maintree; - + + boost::property_tree::ptree pt; + zone_list.PopulateZoneList(pt); + std::ostringstream oss; - list::iterator zone_iter; - ZoneServer* tmp = 0; - MZoneList.readlock(__FUNCTION__, __LINE__); - int zonesListed = 0; - for (zone_iter = zlist.begin(); zone_iter != zlist.end(); zone_iter++) { - tmp = *zone_iter; - boost::property_tree::ptree pt; - pt.put("zone_name", tmp->GetZoneName()); - pt.put("zone_file_name", tmp->GetZoneFile()); - pt.put("zone_id", tmp->GetZoneID()); - pt.put("instance_id", tmp->GetInstanceID()); - pt.put("shutting_down", tmp->isZoneShuttingDown()); - pt.put("instance_zone", tmp->IsInstanceZone()); - pt.put("num_players", tmp->NumPlayers()); - pt.put("city_zone", tmp->IsCityZone()); - pt.put("safe_x", tmp->GetSafeX()); - pt.put("safe_y", tmp->GetSafeY()); - pt.put("safe_z", tmp->GetSafeZ()); - pt.put("safe_heading", tmp->GetSafeHeading()); - pt.put("lock_state", tmp->GetZoneLockState()); - pt.put("min_status", tmp->GetMinimumStatus()); - pt.put("min_level", tmp->GetMinimumLevel()); - pt.put("max_level", tmp->GetMaximumLevel()); - pt.put("min_version", tmp->GetMinimumVersion()); - pt.put("default_lockout_time", tmp->GetDefaultLockoutTime()); - pt.put("default_reenter_time", tmp->GetDefaultReenterTime()); - pt.put("instance_type", (int8)tmp->GetInstanceType()); - pt.put("always_loaded", tmp->AlwaysLoaded()); - zonesListed++; - maintree.push_back(std::make_pair("", pt)); - } - MZoneList.releasereadlock(__FUNCTION__, __LINE__); - - boost::property_tree::ptree result; - result.add_child("Zones", maintree); - boost::property_tree::write_json(oss, result); + boost::property_tree::write_json(oss, pt); std::string json = oss.str(); res.body() = json; res.prepare_payload(); } +void ZoneList::PopulateZoneList(boost::property_tree::ptree& pt) { + boost::property_tree::ptree maintree; + list::iterator zone_iter; + ZoneServer* tmp = nullptr; + + MZoneList.readlock(__FUNCTION__, __LINE__); + for (zone_iter = zlist.begin(); zone_iter != zlist.end(); ++zone_iter) { + tmp = *zone_iter; + boost::property_tree::ptree zone_pt; + zone_pt.put("zone_name", tmp->GetZoneName()); + zone_pt.put("zone_file_name", tmp->GetZoneFile()); + zone_pt.put("zone_id", tmp->GetZoneID()); + zone_pt.put("instance_id", tmp->GetInstanceID()); + zone_pt.put("shutting_down", tmp->isZoneShuttingDown()); + zone_pt.put("instance_zone", tmp->IsInstanceZone()); + zone_pt.put("num_players", tmp->NumPlayers()); + zone_pt.put("city_zone", tmp->IsCityZone()); + zone_pt.put("safe_x", tmp->GetSafeX()); + zone_pt.put("safe_y", tmp->GetSafeY()); + zone_pt.put("safe_z", tmp->GetSafeZ()); + zone_pt.put("safe_heading", tmp->GetSafeHeading()); + zone_pt.put("lock_state", tmp->GetZoneLockState()); + zone_pt.put("min_status", tmp->GetMinimumStatus()); + zone_pt.put("min_level", tmp->GetMinimumLevel()); + zone_pt.put("max_level", tmp->GetMaximumLevel()); + zone_pt.put("min_version", tmp->GetMinimumVersion()); + zone_pt.put("default_lockout_time", tmp->GetDefaultLockoutTime()); + zone_pt.put("default_reenter_time", tmp->GetDefaultReenterTime()); + zone_pt.put("instance_type", static_cast(tmp->GetInstanceType())); + zone_pt.put("always_loaded", tmp->AlwaysLoaded()); + + maintree.push_back(std::make_pair("", zone_pt)); + } + MZoneList.releasereadlock(__FUNCTION__, __LINE__); + + pt.add_child("Zones", maintree); +} + void World::Web_worldhandle_addcharauth(const http::request& req, http::response& res) { res.set(http::field::content_type, "application/json; charset=utf-8"); boost::property_tree::ptree pt, json_tree; @@ -1296,3 +1306,18 @@ void World::Web_worldhandle_setguildeventfilter(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + + boost::property_tree::ptree pt; + world.Web_populate_status(pt); + zone_list.PopulateZoneList(pt); + zone_list.PopulateClientList(pt); + + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} \ No newline at end of file diff --git a/source/WorldServer/World.cpp b/source/WorldServer/World.cpp index 1930f58..c79b23d 100644 --- a/source/WorldServer/World.cpp +++ b/source/WorldServer/World.cpp @@ -277,6 +277,8 @@ void World::init(std::string web_ipaddr, int16 web_port, std::string cert_file, world_webserver->register_route("/removeguildmember", World::Web_worldhandle_removeguildmember); world_webserver->register_route("/setguildpermission", World::Web_worldhandle_setguildpermission); world_webserver->register_route("/setguildeventfilter", World::Web_worldhandle_setguildeventfilter); + + world_webserver->register_route("/peerstatus", World::Web_worldhandle_peerstatus); world_webserver->run(); LogWrite(INIT__INFO, 0, "Init", "World Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port); web_success = true; diff --git a/source/WorldServer/World.h b/source/WorldServer/World.h index 0c3ea63..94a8989 100644 --- a/source/WorldServer/World.h +++ b/source/WorldServer/World.h @@ -516,8 +516,8 @@ class ZoneList { void SendTimeUpdate(); - void PopulateClientList(http::response& res); - void PopulateZoneList(http::response& res); + void PopulateClientList(boost::property_tree::ptree& pt); + void PopulateZoneList(boost::property_tree::ptree& pt); private: Mutex MClientList; Mutex MZoneList; @@ -707,6 +707,9 @@ public: static void Web_worldhandle_removeguildmember(const http::request& req, http::response& res); static void Web_worldhandle_setguildpermission(const http::request& req, http::response& res); static void Web_worldhandle_setguildeventfilter(const http::request& req, http::response& res); + static void Web_worldhandle_peerstatus(const http::request& req, http::response& res); + + static void Web_populate_status(boost::property_tree::ptree& pt); Mutex MVoiceOvers; static sint64 newValue;