diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index df6b14fcb..f407208aa 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -84,6 +84,8 @@ SystemAddress authServerMasterPeerSysAddr; SystemAddress chatServerMasterPeerSysAddr; SystemAddress dashboardServerMasterPeerSysAddr; SystemAddress ugcServerMasterPeerSysAddr; +// The UGC server's process id from its last start (0: not started), for the dashboard +uint32_t ugcServerPid = 0; namespace { // Dashboard player actions waiting for world servers to answer @@ -452,7 +454,7 @@ int main(int argc, char** argv) { // The UGC server makes and serves player models' meshes and icons (docs/UgcServer.md) if (Game::config->GetValue("enable_ugc_server") == "1") { - StartUgcServer(); + ugcServerPid = StartUgcServer(); } auto t = std::chrono::high_resolution_clock::now(); @@ -888,8 +890,8 @@ namespace { LOG("Reloading settings (changed on the dashboard)"); Game::config->ReloadConfig(); Game::im->LoadZoneLimits(); - // Everyone else: auth, chat and every world - for (const auto& peer : { authServerMasterPeerSysAddr, chatServerMasterPeerSysAddr }) { + // Everyone else: auth, chat, UGC and every world + for (const auto& peer : { authServerMasterPeerSysAddr, chatServerMasterPeerSysAddr, ugcServerMasterPeerSysAddr }) { if (peer != UNASSIGNED_SYSTEM_ADDRESS) MasterPackets::SendTo(peer, reload); } for (const auto& instance : Game::im->GetInstances()) { @@ -938,6 +940,9 @@ namespace { ServerListResponse response; response.authOnline = authServerMasterPeerSysAddr != UNASSIGNED_SYSTEM_ADDRESS ? 1 : 0; response.chatOnline = chatServerMasterPeerSysAddr != UNASSIGNED_SYSTEM_ADDRESS ? 1 : 0; + response.ugcEnabled = Game::config->GetValue("enable_ugc_server") == "1" ? 1 : 0; + response.ugcOnline = ugcServerMasterPeerSysAddr != UNASSIGNED_SYSTEM_ADDRESS ? 1 : 0; + response.ugcPid = ugcServerPid; for (const auto& inst : Game::im->GetInstances()) { if (!inst || !inst->GetIsReady() || inst->GetIsShuttingDown()) continue; @@ -1057,7 +1062,7 @@ void HandlePacket(Packet* packet) { MasterPackets::SendTo(dashboardServerMasterPeerSysAddr, offline); } - StartUgcServer(); + ugcServerPid = StartUgcServer(); } } @@ -1132,6 +1137,7 @@ int ShutdownSequence(int32_t signal) { if (allInstancesShutdown && \ authServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS && \ chatServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS && \ + ugcServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS && \ dashboardServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS) { LOG("Finished shutting down MasterServer!"); break; @@ -1154,6 +1160,9 @@ int ShutdownSequence(int32_t signal) { if (dashboardServerMasterPeerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) { LOG("Dashboard server did not shutdown in time"); } + if (ugcServerMasterPeerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) { + LOG("UGC server did not shutdown in time"); + } for (const auto& instance : Game::im->GetInstances()) { if (instance == nullptr) { continue; diff --git a/dNet/MasterPackets.cpp b/dNet/MasterPackets.cpp index d3935ae77..ebe95daba 100644 --- a/dNet/MasterPackets.cpp +++ b/dNet/MasterPackets.cpp @@ -241,6 +241,9 @@ namespace MasterPackets { bitStream.Write(instance.port); bitStream.Write(instance.isPrivate); } + bitStream.Write(ugcEnabled); + bitStream.Write(ugcOnline); + bitStream.Write(ugcPid); } bool ServerListResponse::Deserialize(RakNet::BitStream& bitStream) { @@ -259,6 +262,9 @@ namespace MasterPackets { VALIDATE_READ(bitStream.Read(instance.port)); VALIDATE_READ(bitStream.Read(instance.isPrivate)); } + VALIDATE_READ(bitStream.Read(ugcEnabled)); + VALIDATE_READ(bitStream.Read(ugcOnline)); + VALIDATE_READ(bitStream.Read(ugcPid)); return true; } diff --git a/dNet/MasterPackets.h b/dNet/MasterPackets.h index c11177c86..3b6a65bc2 100644 --- a/dNet/MasterPackets.h +++ b/dNet/MasterPackets.h @@ -262,6 +262,11 @@ namespace MasterPackets { uint8_t authOnline{}; uint8_t chatOnline{}; std::vector instances; // u32 count + // The UGC server (docs/UgcServer.md), after the instances: whether master starts it (enable_ugc_server), + // whether it is connected, and the process id master last started it as (0: unknown) + uint8_t ugcEnabled{}; + uint8_t ugcOnline{}; + uint32_t ugcPid{}; ServerListResponse() : LUBitStream(ServiceType::MASTER, MessageType::Master::SERVER_LIST_RESPONSE) {} void Serialize(RakNet::BitStream& bitStream) const override; diff --git a/dUgcServer/UgcProcessor.h b/dUgcServer/UgcProcessor.h index e3fa4ba15..ca0f34cc8 100644 --- a/dUgcServer/UgcProcessor.h +++ b/dUgcServer/UgcProcessor.h @@ -63,6 +63,13 @@ public: size_t Busy() const { std::lock_guard lock(m_Mutex); return m_Active; } size_t Threads() const { return m_Config.threads; } + // Main thread: totals since the start and the files' size (traffic reports, then the dashboard and /metrics) + uint64_t Made() const { return m_Made; } + uint64_t Failed() const { return m_Failed; } + uint64_t Evicted() const { return m_Evicted; } + uint64_t StoredBytes() const { return m_StoredBytes; } + uint64_t MaxStorageBytes() const { return m_Config.maxStorageBytes; } + private: struct Job { Kind kind{}; diff --git a/dUgcServer/UgcServer.cpp b/dUgcServer/UgcServer.cpp index b2bfcc8a4..3bafb1225 100644 --- a/dUgcServer/UgcServer.cpp +++ b/dUgcServer/UgcServer.cpp @@ -323,6 +323,15 @@ int main(int argc, char** argv) { TrafficStats::Local().SetGauge("workers_busy", [&processor] { return static_cast(processor.Busy()); }); TrafficStats::Local().SetGauge("workers_queued", [&processor] { return static_cast(processor.Queued()); }); TrafficStats::Local().SetGauge("workers_threads", [&processor] { return static_cast(processor.Threads()); }); + // Read on this thread, when a traffic report is taken (dServer::ReceiveFromMaster) + const std::vector>> ugcGauges{ + { "ugc_made_total", [&processor] { return static_cast(processor.Made()); } }, + { "ugc_failed_total", [&processor] { return static_cast(processor.Failed()); } }, + { "ugc_evicted_total", [&processor] { return static_cast(processor.Evicted()); } }, + { "ugc_stored_bytes", [&processor] { return static_cast(processor.StoredBytes()); } }, + { "ugc_max_storage_bytes", [&processor] { return static_cast(processor.MaxStorageBytes()); } }, + }; + for (const auto& [name, gauge] : ugcGauges) TrafficStats::Local().SetGauge(name, gauge); const auto listenIp = Game::config->GetValue("listen_ip").empty() ? std::string("0.0.0.0") : Game::config->GetValue("listen_ip"); const auto port = Setting("port", 2008); @@ -355,6 +364,7 @@ int main(int argc, char** argv) { TrafficStats::Local().SetGauge("workers_busy", nullptr); TrafficStats::Local().SetGauge("workers_queued", nullptr); TrafficStats::Local().SetGauge("workers_threads", nullptr); + for (const auto& [name, gauge] : ugcGauges) TrafficStats::Local().SetGauge(name, nullptr); Game::web.Shutdown(); g_Processor = nullptr; Database::Destroy("UgcServer"); diff --git a/tests/dGameTests/dNetTests/MasterPacketsTests.cpp b/tests/dGameTests/dNetTests/MasterPacketsTests.cpp index 847f0776c..a37479ea0 100644 --- a/tests/dGameTests/dNetTests/MasterPacketsTests.cpp +++ b/tests/dGameTests/dNetTests/MasterPacketsTests.cpp @@ -353,7 +353,16 @@ TEST(MasterPacketsTests, ServerListMatchesLegacy) { entry.port = instance.port; entry.isPrivate = instance.isPrivate ? 1 : 0; } - EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteServerList(b, true, count % 2, instances); }), StructPacket(response)); + response.ugcEnabled = 1; + response.ugcOnline = count % 2; + response.ugcPid = 4242 + count; + // The legacy list, then the UGC server's state + EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { + LegacyMaster::WriteServerList(b, true, count % 2, instances); + b.Write(1); + b.Write(count % 2); + b.Write(4242 + count); + }), StructPacket(response)); RakNet::BitStream stream; LoadPayload(stream, response); const auto legacy = LegacyMaster::ReadServerList(stream); @@ -365,6 +374,9 @@ TEST(MasterPacketsTests, ServerListMatchesLegacy) { EXPECT_EQ(legacy.instances[i].ip, copy.instances[i].ip.string); EXPECT_EQ(legacy.instances[i].isPrivate, copy.instances[i].isPrivate != 0); } + EXPECT_EQ(copy.ugcEnabled, 1); + EXPECT_EQ(copy.ugcOnline, count % 2); + EXPECT_EQ(copy.ugcPid, 4242 + count); ExpectTruncatedFails(response); } }