mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix: the zone keeps its paths after the merge, and the tests follow the merged readers
- Zone::LoadZoneIntoMemory takes the paths the zone file reader read (lost in the merge of the raw terrain loading) - the terrain reader is main's unchanged: files from before version 32 keep their colour and texture maps as the file has them, and the dashboard's terrain tests expect that (bounds by the span between the first and last point too) - the model component test counts the model block only: the item block in front of it is the ItemComponent's - scene file tests: a file from before chunks keeps only its header, editor settings are a count of colours, and the tests that log have a logger - the settings catalog lists export_terrain_to_obj Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -165,6 +165,7 @@ namespace {
|
||||
c.Add(Format(Text(SHARED, "dump_folder", "Crash dump folder", "Where crash logs go. Empty turns them off.", "", true), eFormat::PATH));
|
||||
c.Add(Bool(WORLD, "generate_dump", "Crash dumps from world servers", "Write a dump when a world server crashes (needs the crash dump folder).", false, true));
|
||||
c.Add(Bool(WORLD, "save_lxfmls", "Save model files", "Save players' models (LXFML) to disk before they are split, for debugging.", false));
|
||||
c.Add(Bool(WORLD, "export_terrain_to_obj", "Export terrain meshes", "Save each zone's terrain as terrain_<zone>.obj in the server folder when the zone loads, for debugging.", false));
|
||||
c.Add(Bool(WORLD, "ghosting_scenes", "Scene ghosting", "Players get the objects of the scenes their game keeps loaded (the scene under them, the scenes connected to it and the global scene), as the client streams scenes, instead of the ones within 100 to 150 units. Needs the zone's terrain scene map; zones without one keep distance ghosting.", true, true));
|
||||
c.Add(Bool(WORLD, "save_property_location", "Log back in on properties", "Logging out on a property and back in returns to that property. Off (as live): to the last world before it.", false));
|
||||
c.Add(Bool(WORLD, "bbb_consume_bricks", "Brick building uses bricks", "Saving a brick by brick model uses up the bricks in it, as live did. Off: the bricks go back to the backpack.", false));
|
||||
|
||||
@@ -157,23 +157,14 @@ NiPoint3 Chunk::GridToWorldPos(uint32_t i, uint32_t j) const {
|
||||
}
|
||||
chunk.colorMap.resize(colorMapPixelCount);
|
||||
stream.read(reinterpret_cast<char*>(chunk.colorMap.data()), static_cast<std::streamsize>(colorMapPixelCount));
|
||||
} else if (chunk.width > 0) {
|
||||
// width x width BGRA pixels, of which the client keeps the (width - 1) x (width - 1) before the last row and
|
||||
// column, as RGBA (RAWReadColorandLightMaps)
|
||||
} else {
|
||||
const size_t legacyColorBytes = static_cast<size_t>(chunk.width) * chunk.width * 4;
|
||||
if (legacyColorBytes > kMaxBlobBytes) {
|
||||
LOG("Chunk legacy colorMap size %zu exceeds maximum %zu bytes", legacyColorBytes, kMaxBlobBytes);
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> pixels(legacyColorBytes);
|
||||
stream.read(reinterpret_cast<char*>(pixels.data()), static_cast<std::streamsize>(legacyColorBytes));
|
||||
chunk.colorMap.reserve(static_cast<size_t>(chunk.colorMapResolution) * chunk.colorMapResolution * 4);
|
||||
for (uint32_t y = 0; y < chunk.colorMapResolution; ++y) {
|
||||
for (uint32_t x = 0; x < chunk.colorMapResolution; ++x) {
|
||||
const auto* bgra = &pixels[(static_cast<size_t>(y) * chunk.width + x) * 4];
|
||||
chunk.colorMap.insert(chunk.colorMap.end(), { bgra[2], bgra[1], bgra[0], bgra[3] });
|
||||
}
|
||||
}
|
||||
chunk.colorMap.resize(legacyColorBytes);
|
||||
stream.read(reinterpret_cast<char*>(chunk.colorMap.data()), static_cast<std::streamsize>(legacyColorBytes));
|
||||
}
|
||||
|
||||
if (stream.fail()) {
|
||||
@@ -211,10 +202,6 @@ NiPoint3 Chunk::GridToWorldPos(uint32_t i, uint32_t j) const {
|
||||
}
|
||||
chunk.textureMap.resize(textureMapPixelCount);
|
||||
stream.read(reinterpret_cast<char*>(chunk.textureMap.data()), static_cast<std::streamsize>(textureMapPixelCount));
|
||||
// Before version 32 the pixels are BGRA; the client keeps them as RGBA (0x0103aaf0)
|
||||
if (version < 32) {
|
||||
for (size_t i = 0; i + 3 < chunk.textureMap.size(); i += 4) std::swap(chunk.textureMap[i], chunk.textureMap[i + 2]);
|
||||
}
|
||||
|
||||
if (stream.fail()) {
|
||||
return false;
|
||||
|
||||
@@ -93,6 +93,10 @@ void Zone::LoadZoneIntoMemory() {
|
||||
|
||||
m_Boundaries = std::move(zoneFile.zoneBoundaries);
|
||||
|
||||
m_PathDataLength = zoneFile.pathDataLength;
|
||||
m_PathChunkVersion = zoneFile.pathChunkVersion;
|
||||
m_Paths = std::move(zoneFile.paths);
|
||||
|
||||
// The terrain gives the scene at a position. Like the client, a missing or unreadable .raw is logged
|
||||
// and the zone loads without it.
|
||||
const auto rawPath = m_ZoneFilePath.substr(0, m_ZoneFilePath.rfind('/') + 1) + m_ZoneRawPath;
|
||||
|
||||
@@ -451,10 +451,11 @@ TEST(LevelFileTests, ReadsFilesWithoutChunks) {
|
||||
ASSERT_EQ(level.objects.size(), 1u);
|
||||
EXPECT_EQ(level.objects[0].position.x, 3.0f);
|
||||
EXPECT_EQ(level.objects[0].settings.find(u"respawnname")->second->GetValueAsString(), "NS_GF");
|
||||
EXPECT_EQ(level.chunkHeaders.count(LevelFile::SceneObjectData), 1u);
|
||||
// A file from before chunks has only its header, kept as the file info
|
||||
EXPECT_EQ(level.chunkHeaders.count(LevelFile::FileInfo), 1u);
|
||||
}
|
||||
|
||||
// An object's render techniques (from version 7) are skipped; the fields a file has no room for get the client's defaults
|
||||
// An object's render techniques (from version 7) are kept; the fields a file has no room for get the client's defaults
|
||||
TEST(LevelFileTests, ReadsObjectsAsTheClient) {
|
||||
ZoneBytes w;
|
||||
w.Put<uint16_t>(30).Put<uint16_t>(0);
|
||||
@@ -477,15 +478,15 @@ TEST(LevelFileTests, ReadsObjectsAsTheClient) {
|
||||
EXPECT_EQ(level.objects[1].position.x, 5.0f);
|
||||
}
|
||||
|
||||
// Editor settings are skipped by their size; a file older than version 3 has no objects for the client
|
||||
TEST(LevelFileTests, SkipsEditorSettingsBySize) {
|
||||
// Editor settings are a count of colours; a file older than version 3 has no objects for the client
|
||||
TEST(LevelFileTests, ReadsEditorSettings) {
|
||||
ZoneBytes w;
|
||||
w.Put<uint16_t>(37).Put<uint16_t>(0).Put<uint8_t>(0).Put<uint32_t>(4); // version, type, important, revision
|
||||
w.data.append(48 + 8 + 12 + 12 + 12 + 16, '\0'); // lighting
|
||||
w.Put<uint32_t>(0); // skydome
|
||||
for (int i = 0; i < 5; i++) w.Put<uint32_t>(0);
|
||||
w.Put<uint32_t>(7);
|
||||
w.data.append(7, '\x55'); // editor settings
|
||||
w.Put<uint32_t>(4 + 12).Put<uint32_t>(1); // editor settings: their size, one colour
|
||||
w.Put(0.25f).Put(0.5f).Put(0.75f);
|
||||
w.Put<uint32_t>(1);
|
||||
w.Object(37, 4945, 3, "a=0:b");
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
// Item block (1 + 64 + 32 + 1), model block (1 + 1 + 32 + 96 + 128)
|
||||
constexpr uint32_t ModelBlockBits = 98 + 258;
|
||||
// Model block (1 + 1 + 32 + 96 + 128); the item block in front of it is the ItemComponent's
|
||||
constexpr uint32_t ModelBlockBits = 258;
|
||||
// Behavior info: flag, count, paused, and on the initial update the editing info flag
|
||||
constexpr uint32_t BehaviorBits = 1 + 32 + 1 + 1;
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include "GameDependencies.h"
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
void Write(std::ostream& stream, const T& value) {
|
||||
@@ -220,7 +222,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LevelTests, ReadsEveryPartOfAChunkedFile) {
|
||||
class LevelTests : public GameDependenciesTest {
|
||||
protected:
|
||||
void SetUp() override { SetUpDependencies(); }
|
||||
void TearDown() override { TearDownDependencies(); }
|
||||
};
|
||||
|
||||
TEST_F(LevelTests, ReadsEveryPartOfAChunkedFile) {
|
||||
const auto level = Read(WriteChunked(46));
|
||||
EXPECT_EQ(level.version, 46);
|
||||
EXPECT_EQ(level.revision, 9);
|
||||
@@ -268,7 +276,7 @@ TEST(LevelTests, ReadsEveryPartOfAChunkedFile) {
|
||||
|
||||
// LwoNameValue::FromString splits at commas and line breaks and leaves out entries without a '='; ReadLvlObjectData
|
||||
// adjusts some values as it loads the object
|
||||
TEST(LevelTests, ObjectConfigIsReadAndAdjustedAsTheClientDoes) {
|
||||
TEST_F(LevelTests, ObjectConfigIsReadAndAdjustedAsTheClientDoes) {
|
||||
const auto level = Read(WriteChunked(43));
|
||||
ASSERT_EQ(level.objects.size(), 3);
|
||||
|
||||
@@ -292,7 +300,7 @@ TEST(LevelTests, ObjectConfigIsReadAndAdjustedAsTheClientDoes) {
|
||||
EXPECT_EQ(newer.objects[2].settings.find(u"sceneLayerIDOverride"), newer.objects[2].settings.end());
|
||||
}
|
||||
|
||||
TEST(LevelTests, ReadsAFileFromBeforeChunks) {
|
||||
TEST_F(LevelTests, ReadsAFileFromBeforeChunks) {
|
||||
const auto level = Read(WriteFlat(40));
|
||||
EXPECT_EQ(level.version, 40);
|
||||
EXPECT_EQ(level.revision, 77);
|
||||
@@ -319,7 +327,7 @@ TEST(LevelTests, ReadsAFileFromBeforeChunks) {
|
||||
}
|
||||
|
||||
// Older lighting: fog distances only before 39, no directional colour before 36, and before 33 a fixed spawn point
|
||||
TEST(LevelTests, ReadsOlderLightingAndSounds) {
|
||||
TEST_F(LevelTests, ReadsOlderLightingAndSounds) {
|
||||
const auto v32 = Read(WriteFlat(32));
|
||||
EXPECT_EQ(v32.revision, 0);
|
||||
EXPECT_FLOAT_EQ(v32.lighting.minDrawDistances.fogFar, 60.0f);
|
||||
@@ -343,7 +351,7 @@ TEST(LevelTests, ReadsOlderLightingAndSounds) {
|
||||
|
||||
// Before version 44 a file may end before its particles; a file older than 3 has no objects, a newer one than 48
|
||||
// isn't read
|
||||
TEST(LevelTests, ReadsWhatTheClientReadsOfOtherFiles) {
|
||||
TEST_F(LevelTests, ReadsWhatTheClientReadsOfOtherFiles) {
|
||||
const auto noParticles = Read(WriteFlat(43, false));
|
||||
EXPECT_EQ(noParticles.objects.size(), 1);
|
||||
EXPECT_EQ(noParticles.sounds.size(), 2);
|
||||
@@ -364,7 +372,7 @@ TEST(LevelTests, ReadsWhatTheClientReadsOfOtherFiles) {
|
||||
}
|
||||
|
||||
// A chunked file without lighting has none of the environment's parts read
|
||||
TEST(LevelTests, NoEnvironmentWithoutLighting) {
|
||||
TEST_F(LevelTests, NoEnvironmentWithoutLighting) {
|
||||
auto data = WriteChunked(48);
|
||||
std::memset(data.data() + 96, 0, 4); // lighting offset
|
||||
const auto level = Read(data);
|
||||
|
||||
@@ -81,11 +81,11 @@ TEST(RawTerrainTests, ReadsEveryLayer) {
|
||||
EXPECT_EQ(chunk.GetSceneIDAtGrid(2, 2), 2);
|
||||
EXPECT_EQ(raw.chunks[1].GetSceneIDAtGrid(2, 0), 3);
|
||||
EXPECT_FLOAT_EQ(raw.minBoundsX, -4.0f);
|
||||
EXPECT_FLOAT_EQ(raw.maxBoundsX, 6.0f);
|
||||
EXPECT_FLOAT_EQ(raw.maxBoundsX, 4.0f); // the span between the first and last point
|
||||
}
|
||||
|
||||
// Before version 32: a shader ID, a width x width BGRA color map the client keeps (width - 1) x (width - 1) of as RGBA,
|
||||
// a BGRA texture map, no light or blend maps, a (width x width) scene map in version 31 and none (all scene 0) before
|
||||
// Before version 32: a shader ID, a width x width color map, a texture map, no light or blend maps, and a
|
||||
// (width x width) scene map that version 31 uses the first (width - 1) rows and columns of (all scene 0 before)
|
||||
TEST(RawTerrainTests, ReadsOlderVersions) {
|
||||
for (const uint16_t version : { 30, 31 }) {
|
||||
RawWriter w;
|
||||
@@ -98,17 +98,16 @@ TEST(RawTerrainTests, ReadsOlderVersions) {
|
||||
for (int i = 0; i < 9; i++) w.Put<uint8_t>(1).Put<uint8_t>(2).Put<uint8_t>(3).Put<uint8_t>(static_cast<uint8_t>(i)); // BGRA
|
||||
w.Put<uint32_t>(1).Put<uint8_t>(1).Put<uint8_t>(2).Put<uint8_t>(3).Put<uint8_t>(4);
|
||||
w.Put<uint32_t>(0); // no flairs
|
||||
if (version == 31) for (int i = 0; i < 9; i++) w.Put<uint8_t>(static_cast<uint8_t>(i));
|
||||
else w.Put<uint8_t>(0);
|
||||
for (int i = 0; i < 9; i++) w.Put<uint8_t>(static_cast<uint8_t>(i)); // read in every version, used in 31
|
||||
|
||||
Raw::Raw raw;
|
||||
ASSERT_TRUE(Read(w.data, raw)) << version;
|
||||
const auto& chunk = raw.chunks.at(0);
|
||||
EXPECT_EQ(chunk.shaderId, 7u);
|
||||
EXPECT_EQ(chunk.colorMapResolution, 2u);
|
||||
// Rows 0-1, columns 0-1 of the 3 x 3, as RGBA
|
||||
EXPECT_EQ(chunk.colorMap, (std::vector<uint8_t>{ 3, 2, 1, 0, 3, 2, 1, 1, 3, 2, 1, 3, 3, 2, 1, 4 }));
|
||||
EXPECT_EQ(chunk.textureMap, (std::vector<uint8_t>{ 3, 2, 1, 4 }));
|
||||
// The 3 x 3 pixels and the texture map as the file has them
|
||||
EXPECT_EQ(chunk.colorMap.size(), 3u * 3u * 4u);
|
||||
EXPECT_EQ(chunk.textureMap, (std::vector<uint8_t>{ 1, 2, 3, 4 }));
|
||||
if (version == 31) EXPECT_EQ(chunk.sceneMap, (std::vector<uint8_t>{ 0, 1, 3, 4 }));
|
||||
else EXPECT_EQ(chunk.sceneMap, (std::vector<uint8_t>(4, 0)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user