Complete remaining client gap features

This commit is contained in:
shenlei
2026-09-04 13:25:05 +09:00
parent 13ccb8d02d
commit 9a4a044da5
47 changed files with 6667 additions and 140 deletions
+132
View File
@@ -0,0 +1,132 @@
#include "attribute_data.h"
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
namespace {
int failures = 0;
#define CHECK(condition, message) \
do { \
if (!(condition)) { \
std::fprintf(stderr, "FAIL: %s (%s:%d)\n", (message), __FILE__, \
__LINE__); \
++failures; \
} \
} while (0)
template <typename T>
void append(std::vector<std::uint8_t> &bytes, const T &value) {
const auto *p = reinterpret_cast<const std::uint8_t *>(&value);
bytes.insert(bytes.end(), p, p + sizeof(T));
}
void append_name(std::vector<std::uint8_t> &bytes, const char *name) {
char fixed[32] = {};
std::strncpy(fixed, name, sizeof(fixed));
bytes.insert(bytes.end(), reinterpret_cast<std::uint8_t *>(fixed),
reinterpret_cast<std::uint8_t *>(fixed) + sizeof(fixed));
}
void append_collision(std::vector<std::uint8_t> &bytes, std::uint32_t type,
const char *name, const float *position,
const float *dimensions, const float *quaternion,
std::size_t dimension_count) {
append(bytes, type);
append_name(bytes, name);
for (std::size_t i = 0; i < 3; ++i)
append(bytes, position[i]);
for (std::size_t i = 0; i < dimension_count; ++i)
append(bytes, dimensions[i]);
for (std::size_t i = 0; i < 4; ++i)
append(bytes, quaternion[i]);
}
void test_synthetic() {
std::vector<std::uint8_t> bytes;
const char header[] = "AttributeData";
bytes.insert(bytes.end(), header, header + sizeof(header));
const std::uint32_t collisions = 3;
const std::uint32_t heights = 1;
append(bytes, collisions);
append(bytes, heights);
const float p0[] = {10, 20, 30}, d0[] = {40, 50}, q0[] = {0, 0, 0, 1};
append_collision(bytes, 0, "plane", p0, d0, q0, 2);
const float p1[] = {-1, 2, -3}, d1[] = {7}, q1[] = {1, 2, 3, 4};
append_collision(bytes, 2, "sphere", p1, d1, q1, 1);
const float p2[] = {4, 5, 6}, d2[] = {8, 9, 10}, q2[] = {0, 0.5f, 0, 0.5f};
append_collision(bytes, 5, "obb", p2, d2, q2, 3);
append_name(bytes, "height0");
const std::uint32_t vertex_count = 3;
append(bytes, vertex_count);
const float vertices[] = {0, 0, 0, 100, 0, 0, 0, 100, 0};
for (float value : vertices)
append(bytes, value);
fmt::AttributeData parsed;
std::string error;
CHECK(fmt::parse_attribute_data(bytes.data(), bytes.size(), parsed, &error),
"synthetic .mdatr parses");
CHECK(parsed.collisions.size() == 3 && parsed.heights.size() == 1,
"synthetic counts");
CHECK(parsed.collisions[0].name == "plane" &&
parsed.collisions[0].dimensions[1] == 50.0f,
"plane fields");
CHECK(parsed.collisions[1].type == 2 &&
parsed.collisions[1].dimensions[0] == 7.0f &&
parsed.collisions[1].quaternion[0] == 1.0f,
"sphere fields");
CHECK(parsed.collisions[2].name == "obb" &&
parsed.collisions[2].dimensions[2] == 10.0f,
"obb fields");
CHECK(parsed.heights[0].name == "height0" &&
parsed.heights[0].vertices.size() == 3 &&
parsed.heights[0].vertices[1][0] == 100.0f,
"height triangle fields");
auto truncated = bytes;
truncated.pop_back();
CHECK(!fmt::parse_attribute_data(truncated.data(), truncated.size(), parsed, &error),
"truncated .mdatr rejected");
auto trailing = bytes;
trailing.push_back(0);
CHECK(!fmt::parse_attribute_data(trailing.data(), trailing.size(), parsed, &error),
"trailing .mdatr rejected");
}
void test_real_asset() {
const char *root = std::getenv("M2_ASSETS");
if (!root || !*root)
return;
const std::string path = std::string(root) +
"/metin2_patch_eu3/ymir work/zone/devilcave/devil_entrance1f.mdatr";
fmt::AttributeData parsed;
std::string error;
CHECK(fmt::parse_attribute_data_file(path, parsed, &error),
"real devil_entrance1f.mdatr parses");
CHECK(parsed.collisions.size() == 9 && parsed.heights.empty(),
"real .mdatr collision/height count");
CHECK(parsed.collisions[0].type == 0 && parsed.collisions[0].name == "collision04",
"real .mdatr first collision");
}
} // namespace
int main() {
test_synthetic();
test_real_asset();
if (failures == 0) {
std::puts("PASS: formats_attribute_data_test");
return 0;
}
std::fprintf(stderr, "%d check(s) failed\n", failures);
return 1;
}