mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
feat(bbb): answer FetchModelMetadataRequest for brick built models
The client asks for a model's metadata (name, owner, behaviors and its blueprint's bricks and box) when it shows a brick built model item's tooltip, a model on a property or an exhibit, and shows BBB_LOADING_BLUEPRINT until it gets it. The world server never answered. It now does as live did: UG data for the model (found among the player's items by subkey, else among placed models) and, for a brick built model, the blueprint data from its ugc row and LXFML. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include "StringifiedEnum.h"
|
||||
#include "TinyXmlUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -30,6 +31,48 @@ namespace {
|
||||
</LXFML>)";
|
||||
}
|
||||
|
||||
Lxfml::Contents Lxfml::ReadContents(const std::string_view data) {
|
||||
Contents contents;
|
||||
if (data.empty()) return contents;
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.Parse(data.data(), data.size()) != tinyxml2::XML_SUCCESS) return contents;
|
||||
const auto* bricks = doc.FirstChildElement("LXFML") ? doc.FirstChildElement("LXFML")->FirstChildElement("Bricks") : nullptr;
|
||||
if (!bricks) return contents;
|
||||
|
||||
bool anyBone = false;
|
||||
for (const auto* brick = bricks->FirstChildElement("Brick"); brick; brick = brick->NextSiblingElement("Brick")) {
|
||||
// designID may carry a suffix ("3001;A")
|
||||
const std::string_view design = brick->Attribute("designID") ? brick->Attribute("designID") : "";
|
||||
const auto digits = design.substr(0, design.find_first_not_of("0123456789"));
|
||||
const auto designId = GeneralUtils::TryParse<uint32_t>(digits);
|
||||
if (designId) contents.designIds.push_back(*designId);
|
||||
|
||||
for (const auto* part = brick->FirstChildElement("Part"); part; part = part->NextSiblingElement("Part")) {
|
||||
for (const auto* bone = part->FirstChildElement("Bone"); bone; bone = bone->NextSiblingElement("Bone")) {
|
||||
const auto* transformation = bone->Attribute("transformation");
|
||||
if (!transformation) continue;
|
||||
const auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) continue;
|
||||
const auto x = GeneralUtils::TryParse<float>(split[9]);
|
||||
const auto y = GeneralUtils::TryParse<float>(split[10]);
|
||||
const auto z = GeneralUtils::TryParse<float>(split[11]);
|
||||
if (!x || !y || !z) continue;
|
||||
const NiPoint3 position{ *x, *y, *z };
|
||||
if (!anyBone) {
|
||||
contents.boxMin = position;
|
||||
contents.boxMax = position;
|
||||
anyBone = true;
|
||||
continue;
|
||||
}
|
||||
contents.boxMin = NiPoint3(std::min(contents.boxMin.x, position.x), std::min(contents.boxMin.y, position.y), std::min(contents.boxMin.z, position.z));
|
||||
contents.boxMax = NiPoint3(std::max(contents.boxMax.x, position.x), std::max(contents.boxMax.y, position.y), std::max(contents.boxMax.z, position.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
|
||||
Result toReturn;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user