feat: build a zone's 3D data on worker threads

Opening a zone the first time built its terrain, scene objects and manifests
and ran ImageMagick on the web thread, stalling the dashboard for seconds.

- Workers: the shared pool plus Workers::Reply (answer at once when built,
  else from a worker via Web::Defer).
- terrain_chunks/terrain_layers/scene/paths/scenery/flairs (world3d, property
  and showcase routes) and terrain textures go through it; results are built
  once in OnceCaches, the .raw is read once per zone for chunks, layers and
  flairs, deflated bodies are cached thread-safely.
- ImageMagick conversions are deduplicated and written under a temporary name.
- Workers don't query the CDClient, read settings or call mongoose: ZoneTable,
  render components, flairs, object names, LOT kinds and terrain texture names
  are read at startup; client_location is read once; base64 is plain C++.
- Logger writes one line at a time (mutex; localtime's buffer is shared).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-26 23:57:48 -05:00
parent 20f70ac0de
commit 17689cd663
20 changed files with 895 additions and 305 deletions

View File

@@ -3,8 +3,15 @@
#include <algorithm>
#include <ctime>
#include <filesystem>
#include <mutex>
#include <stdarg.h>
namespace {
// Servers with worker threads (the dashboard) log from several threads: one line is written at a time, and
// localtime's buffer is shared
std::mutex g_LogMutex;
}
Writer::~Writer() {
// Flush before we close
Flush();
@@ -49,6 +56,7 @@ Logger::Logger(const std::string& outpath, bool logToConsole, bool logDebugState
}
void Logger::vLog(const char* format, va_list args) {
std::lock_guard lock(g_LogMutex);
time_t t = time(NULL);
struct tm* time = localtime(&t);
char timeStr[70];
@@ -78,6 +86,7 @@ void Logger::LogDebug(const char* className, const char* format, ...) {
}
void Logger::Flush() {
std::lock_guard lock(g_LogMutex);
for (const auto& writer : m_Writers) {
writer->Flush();
}