Files
DarkflameServer/dUgcServer/UgcStorage.h
Aaron Kimbrell abf1cc1d80 feat: UGC server that makes and serves player models' meshes and icons
A new server (dUgcServer, started by master with enable_ugc_server=1) that
takes unprocessed ugc and ugc_modular_build rows from the database and makes
what the client downloads with UGCUSE3DSERVICES: an optimized NIF (hidden
faces removed, ambient occlusion baked into vertex colors) and a 128px DDS
icon for player models, rendered by a software rasterizer from the client's
LDD brick primitives, and icons for cars and rockets assembled from their
modules per ModularBuildComponent/ModuleComponent. It serves them, with the
models' LXFML, over HTTP in the client's UGCC<dc>/3DOPTIMIZED and
IMAGE128DDS layout with .gz and .checksum files, and keeps its folder under
a size cap.

Processing state lives in ugc.is_optimized plus new processed_at,
process_attempts and process_error columns (and the same on
ugc_modular_build). ServiceType::UGC is appended. NifFile moves to dCommon
and records named node transforms for the modules' attach points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:57 -05:00

61 lines
1.9 KiB
C++

#pragma once
#include <cstdint>
#include <filesystem>
#include <map>
#include <optional>
#include <string>
#include <vector>
#include "dCommonVars.h"
/**
* Where the UGC server keeps what it makes: <root>/models/<id % 1000>/<id>/ for player models and
* <root>/modular/<id % 1000>/<id>/ for modular builds. An item's files are written to a temporary folder and renamed
* into place, so a request never sees half of them. Plain file operations, safe from any thread for different items.
*/
class UgcStorage {
public:
enum class Kind { MODEL, MODULAR };
// The files an item has, by name (see FileName)
using Files = std::map<std::string, std::string>;
explicit UgcStorage(std::filesystem::path root);
const std::filesystem::path& GetRoot() const { return m_Root; }
std::filesystem::path Folder(Kind kind, LWOOBJID id) const;
// A file of an item, if it is there; `name` must be one of the names the server writes
std::optional<std::filesystem::path> File(Kind kind, LWOOBJID id, const std::string& name) const;
// Replaces an item's files with `files`; the bytes written, nullopt (and `error`) on failure
std::optional<uint64_t> Write(Kind kind, LWOOBJID id, const Files& files, std::string& error) const;
// Deletes an item's files
void Remove(Kind kind, LWOOBJID id) const;
// Marks an item used now, for eviction
void Touch(Kind kind, LWOOBJID id) const;
struct Entry {
Kind kind{};
LWOOBJID id{};
uint64_t bytes{};
std::filesystem::file_time_type used{};
};
// Everything stored, with sizes and when it was last used
std::vector<Entry> List() const;
// Removes the least recently used items until the total is at most `maxBytes`; what was removed
std::vector<Entry> Evict(uint64_t maxBytes) const;
// Names the server writes (and serves); anything else is refused
static bool IsKnownFile(const std::string& name);
private:
std::filesystem::path m_Root;
};