mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
feat: convert 3D scenery models on worker threads with deferred replies
The dashboard's web server answers one request at a time, so converting a big .nif (glom files up to tens of MB) held up every other request, flairs included. - dWeb: Web::Defer hands a request to another thread; the reply is sent from the web thread on its next poll (DeferredQueue). A client that leaves first cancels it and the late reply is dropped. The synchronous route API is unchanged. - Web::Shutdown closes connections while the state their close events touch is still alive; the destructor no longer runs handlers during static destruction (stopping the dashboard aborted in ~WSClient). - WorkerPool: priority lanes, with one thread only for urgent work (flairs, small models, textures), and limited background work. - Scenery: mesh and texture routes (and the showcase's) convert on the pool; thread-safe memory and disk caches, one conversion per model at a time with waiters sharing it; zones are converted ahead onto the disk cache while viewed. - Setting scenery_workers (0: half the cores, 2 to 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
46
dWeb/Web.h
46
dWeb/Web.h
@@ -10,6 +10,8 @@
|
||||
#include "json_fwd.hpp"
|
||||
#include "eHTTPStatusCode.h"
|
||||
#include "HTTPContext.h"
|
||||
#include "HTTPReply.h"
|
||||
#include "DeferredReply.h"
|
||||
#include "IHTTPMiddleware.h"
|
||||
|
||||
// Forward declarations for game namespace
|
||||
@@ -24,32 +26,6 @@ enum class eHTTPMethod;
|
||||
// Forward declaration for mongoose manager
|
||||
typedef struct mg_mgr mg_mgr;
|
||||
|
||||
// Content type enum for HTTP responses
|
||||
enum class eContentType {
|
||||
APPLICATION_JSON,
|
||||
TEXT_HTML,
|
||||
TEXT_CSS,
|
||||
TEXT_JAVASCRIPT,
|
||||
TEXT_PLAIN,
|
||||
TEXT_CSV,
|
||||
IMAGE_PNG,
|
||||
IMAGE_JPEG,
|
||||
APPLICATION_OCTET_STREAM,
|
||||
TEXT_PROMETHEUS // the Prometheus text exposition format
|
||||
};
|
||||
|
||||
// For passing HTTP messages between functions
|
||||
struct HTTPReply {
|
||||
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
|
||||
std::string message = "{\"error\":\"Not Found\"}";
|
||||
eContentType contentType = eContentType::APPLICATION_JSON;
|
||||
std::string location = ""; // For redirect responses (Location header)
|
||||
std::vector<std::string> headers{}; // Extra raw headers, e.g. "Set-Cookie: a=b"
|
||||
// When set on a 200 reply, this file is streamed from disk as the body (with contentType and headers) instead of
|
||||
// message, so large downloads never sit in memory
|
||||
std::string file{};
|
||||
};
|
||||
|
||||
// HTTP route structure
|
||||
// This structure is used to register HTTP routes
|
||||
// with the server. Each route has a path, method, optional middleware,
|
||||
@@ -109,6 +85,14 @@ public:
|
||||
void RegisterWSSubscription(const std::string& subscription, uint8_t minLevel = 0);
|
||||
// The level is looked up each time (for permissions that can change while running)
|
||||
void RegisterWSSubscription(const std::string& subscription, std::function<uint8_t()> minLevel);
|
||||
/**
|
||||
* Answer this request later (from any thread) instead of when the handler returns, for slow work that would hold
|
||||
* up every other request: the handler hands the returned DeferredReply to a worker and returns; the web thread
|
||||
* sends the worker's reply on its next poll. Headers middleware set on `reply` are sent with it.
|
||||
*/
|
||||
static DeferredReply Defer(HTTPReply& reply, const HTTPContext& context);
|
||||
// Deferred requests still waiting for their answers
|
||||
size_t PendingDeferred() const;
|
||||
// Add global middleware that applies to all routes
|
||||
void AddGlobalMiddleware(MiddlewarePtr middleware);
|
||||
// Set WebSocket authentication callback for token validation
|
||||
@@ -117,6 +101,12 @@ public:
|
||||
void SetWSApiAccessCallback(std::function<bool(uint8_t)> callback) { wsApiAccessCallback = std::move(callback); }
|
||||
// Returns if the web server is enabled
|
||||
bool IsEnabled() const { return enabled; };
|
||||
/**
|
||||
* Close every connection and stop listening, before the server's other state is torn down (the destructor runs
|
||||
* during static destruction, when what the connections' close events touch may be gone). Deferred requests still
|
||||
* waiting are cancelled; stop the workers that answer them first.
|
||||
*/
|
||||
void Shutdown();
|
||||
// Send a message to all connected WebSocket clients that are subscribed to the given topic
|
||||
void static SendWSMessage(std::string sub, nlohmann::json& message);
|
||||
// Send a message on a topic only to the subscribed connections of one account
|
||||
@@ -133,10 +123,14 @@ public:
|
||||
WSAuthCallback GetWSAuthCallback() const { return wsAuthCallback; }
|
||||
const std::function<bool(uint8_t)>& GetWSApiAccessCallback() const { return wsApiAccessCallback; }
|
||||
private:
|
||||
// Send the answers of deferred requests that have arrived
|
||||
void SendDeferredReplies();
|
||||
// mongoose manager
|
||||
mg_mgr mgr;
|
||||
// If the web server is enabled
|
||||
bool enabled = false;
|
||||
// mg_mgr_free has run (Shutdown)
|
||||
bool managerFreed = false;
|
||||
// WebSocket authentication callback
|
||||
WSAuthCallback wsAuthCallback = nullptr;
|
||||
std::function<bool(uint8_t)> wsApiAccessCallback = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user