mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
SceneLoader::ReadLvlFile (1.10.64):
- chunked files: the file info chunk starts the file and gives where
the environment, object and particle chunks start, 0 for none
(DoLvlChunk, ReadLvlChunk1000); the reader walked the chunks one after
another by their sizes instead;
- older files: the editor settings are a u32 size and that many bytes
(SceneLoader::ReadEditorSettings, skipped by size); the reader took
them for a u32 and then a count of 12 byte points;
- older files before version 3 have no objects for the client ("Level
file is unsupported").
LevelFile now does the same. Every scene file on disk reads the same
objects as before.
Check in game: every world spawns its objects as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
2.7 KiB
C++
64 lines
2.7 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <cstring>
|
|
|
|
#include "LevelObjects.h"
|
|
|
|
namespace {
|
|
struct LvlWriter {
|
|
std::string data;
|
|
template<typename T> LvlWriter& Put(T value) { data.append(reinterpret_cast<const char*>(&value), sizeof(T)); return *this; }
|
|
template<typename T> void At(size_t pos, T value) { std::memcpy(data.data() + pos, &value, sizeof(T)); }
|
|
// CHNK, id, version, type, size, data start; the size and start are filled in by End
|
|
size_t Chunk(uint32_t id) {
|
|
const auto start = data.size();
|
|
Put<uint32_t>(0x4B4E4843).Put<uint32_t>(id).Put<uint16_t>(1).Put<uint16_t>(0).Put<uint32_t>(0).Put<uint32_t>(0);
|
|
At<uint32_t>(start + 16, static_cast<uint32_t>(data.size()));
|
|
return start;
|
|
}
|
|
void End(size_t chunk) { At<uint32_t>(chunk + 12, static_cast<uint32_t>(data.size() - chunk)); }
|
|
void Object(uint32_t lot, float x, float y, float z, const std::string& settings) {
|
|
Put<uint64_t>(1).Put<uint32_t>(lot).Put<uint32_t>(0).Put<uint32_t>(0); // id, lot, node type, glom ID (version 41)
|
|
Put(x).Put(y).Put(z).Put(1.0f).Put(0.0f).Put(0.0f).Put(0.0f).Put(1.0f); // position, rotation, scale
|
|
Put<uint32_t>(static_cast<uint32_t>(settings.size()));
|
|
for (char c : settings) Put<uint16_t>(static_cast<uint16_t>(c));
|
|
Put<uint32_t>(0);
|
|
}
|
|
};
|
|
|
|
std::string SampleLevel() {
|
|
LvlWriter w;
|
|
auto info = w.Chunk(1000);
|
|
w.Put<uint32_t>(41).Put<uint32_t>(1).Put<uint32_t>(0).Put<uint32_t>(0).Put<uint32_t>(0);
|
|
w.End(info);
|
|
auto objects = w.Chunk(2001);
|
|
w.At<uint32_t>(info + 32, static_cast<uint32_t>(objects)); // the file info chunk says where the objects start, as in real files
|
|
w.Put<uint32_t>(4);
|
|
w.Object(4945, 10, 20, 30, "respawnname=0:NS_LW_Portal\ncustom_config_names=0:");
|
|
w.Object(6010, 1, 1, 1, "spawntemplate=1:6010"); // not a spawn point
|
|
w.Object(4945, 5, 5, 5, "respawnname=0:ClientOnly\nloadOnClientOnly=7:1"); // the world never loads it
|
|
w.Object(176, -4, 0, 8, "spawntemplate=1:4945\nrespawnname=0:FromSpawner\r"); // spawned objects carry it too
|
|
w.End(objects);
|
|
return w.data;
|
|
}
|
|
}
|
|
|
|
TEST(LevelObjectsTests, ReadsNamedSpawnPoints) {
|
|
const auto points = LevelObjects::ReadSpawnPoints(SampleLevel());
|
|
ASSERT_EQ(points.size(), 2u);
|
|
EXPECT_EQ(points[0].name, "NS_LW_Portal");
|
|
EXPECT_EQ(points[0].lot, 4945u);
|
|
EXPECT_FLOAT_EQ(points[0].x, 10);
|
|
EXPECT_FLOAT_EQ(points[0].z, 30);
|
|
EXPECT_EQ(points[1].name, "FromSpawner");
|
|
EXPECT_FLOAT_EQ(points[1].x, -4);
|
|
}
|
|
|
|
TEST(LevelObjectsTests, StopsOnDamagedFiles) {
|
|
auto level = SampleLevel();
|
|
level.resize(level.size() - 40); // cut into the last object
|
|
EXPECT_EQ(LevelObjects::ReadSpawnPoints(level).size(), 1u);
|
|
EXPECT_TRUE(LevelObjects::ReadSpawnPoints("").empty());
|
|
EXPECT_TRUE(LevelObjects::ReadSpawnPoints(std::string(64, 'x')).empty());
|
|
}
|