Files
DarkflameServer/dDashboardServer/routes/ClientAssets.h
Aaron Kimbrell bab092e5ee 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>
2026-09-27 09:33:11 -05:00

54 lines
2.2 KiB
C++

#pragma once
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include "dCommonVars.h"
#include "json.hpp"
/**
* Game client data for the dashboard: item details for tooltips, icons, and a read-only browser over the
* client's res folder (client_location). Textures are converted to PNG with ImageMagick and cached.
*/
namespace ClientAssets {
std::string ItemName(LOT lot);
// An object's raw Objects.name (not localized), or nullopt when the LOT isn't in the CDClient
std::optional<std::string> ObjectName(LOT lot);
// Name, description, rarity, stat bonuses, skills and item set (with set bonuses), cached per LOT
nlohmann::json ItemInfo(LOT lot);
std::string IconPathForLot(LOT lot);
std::string IconPathForId(int iconId);
// Relative path inside res/ using only safe characters and no traversal
bool IsSafeAssetPath(const std::string& path);
// A DDS or PNG texture from res/ as PNG, scaled down to fit maxSize. `opaque` drops the alpha channel first, for
// textures whose alpha isn't transparency (terrain textures have alpha 0 everywhere; kept, they'd come out black)
std::optional<std::string> TextureAsPng(const std::string& assetPath, uint32_t maxSize, bool opaque = false);
// Raw bytes of a file inside res/ (matched ignoring case)
std::optional<std::string> ReadResFile(const std::string& relativePath);
// Where a file inside res/ is on disk (matched ignoring case), if it exists
std::optional<std::filesystem::path> ResolveResFile(const std::string& relativePath);
// The client's res/ folder (empty when client_location isn't set)
std::filesystem::path ResFolder();
// ResolveResFile inside a res/ folder from ResFolder: reads no settings, so worker threads may call it
std::optional<std::filesystem::path> ResolveResFile(const std::string& relativePath, const std::filesystem::path& res);
// The res/-relative path of a file called `fileName` (ignoring case) anywhere under res/<folder>
std::optional<std::string> FindResFile(const std::string& folder, const std::string& fileName);
// Entries of a folder inside res/
std::optional<nlohmann::json> ListDirectory(const std::string& relativePath);
}
void RegisterClientAssetRoutes();