mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
fix(zone): keep the scenes of a zone file the client loads
LuzReader::ReadScenes (1.10.64) keeps a zone's scenes by scene ID and layer, so a later scene with the same ID and layer replaces an earlier one. ZoneLoader::ReadZoneFile then goes through them in scene ID order and - leaves out every layer of a scene ID that has no layer 0 (General) scene; - before version 41 loads only the layer 0 scene of each ID; from 41 on the other layers (Audio, FX) are loaded with it. ZoneFile now keeps the scenes the same way, so the world loads the same scenes as the client. No zone file on disk has a duplicate, a scene without a General layer, or a non-General layer before version 41, and their scenes are already in ID order: what every file reads is unchanged. Check in game: nothing to check (no live zone changes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ void ZoneFile::ReadHeader(std::istream& file) {
|
||||
for (uint32_t i = 0; i < sceneCount; ++i) {
|
||||
ReadScene(file, i);
|
||||
}
|
||||
KeepLoadedScenes();
|
||||
|
||||
ReadZoneBoundaries(file);
|
||||
|
||||
@@ -106,6 +107,21 @@ void ZoneFile::ReadScene(std::istream& file, uint32_t index) {
|
||||
scenes.push_back(std::move(scene));
|
||||
}
|
||||
|
||||
void ZoneFile::KeepLoadedScenes() {
|
||||
if (fileFormatVersion < FileFormatVersion::PrePreAlpha) return; // ResolveSceneTable does this for those
|
||||
// LuzReader::ReadScenes keeps the scenes by scene ID and layer, a later one replacing an earlier one.
|
||||
// ZoneLoader::ReadZoneFile then goes through them by scene ID: a scene ID with no General layer is left out, and
|
||||
// before Latest only the General layer is loaded
|
||||
std::map<std::pair<uint32_t, eSceneType>, ZoneScene> byID;
|
||||
for (auto& scene : scenes) byID.insert_or_assign({ scene.id, scene.sceneType }, std::move(scene));
|
||||
scenes.clear();
|
||||
for (auto& [key, scene] : byID) {
|
||||
if (!byID.contains({ key.first, eSceneType::General })) continue;
|
||||
if (key.second != eSceneType::General && fileFormatVersion < FileFormatVersion::Latest) continue;
|
||||
scenes.push_back(std::move(scene));
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneFile::ResolveSceneTable(const std::function<std::optional<std::string>(uint32_t)>& sceneName) {
|
||||
if (fileFormatVersion >= FileFormatVersion::PrePreAlpha) return;
|
||||
// ZoneLoader::ReadZoneFile: the scenes go by their SceneTable ID; each one with a row there gets the next index
|
||||
|
||||
@@ -256,6 +256,7 @@ struct ZoneFile {
|
||||
|
||||
private:
|
||||
void ReadScene(std::istream& file, uint32_t index);
|
||||
void KeepLoadedScenes();
|
||||
void ReadZoneBoundaries(std::istream& file);
|
||||
void ReadSceneTransition(std::istream& file);
|
||||
SceneTransitionInfo ReadSceneTransitionInfo(std::istream& file);
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace {
|
||||
w.Put<uint32_t>(41).Put<uint32_t>(3).Put<uint32_t>(1150); // version, revision, world
|
||||
w.Point(1, 2, 3).Put(1.0f).Put(0.0f).Put(0.0f).Put(0.0f); // spawn point and rotation
|
||||
w.Put<uint32_t>(1);
|
||||
w.Text("scene.lvl").Put<uint32_t>(7).Put<uint32_t>(2).Text("Global").Put<uint8_t>(1).Put<uint8_t>(2).Put<uint8_t>(3);
|
||||
w.Text("scene.lvl").Put<uint32_t>(7).Put<uint32_t>(0).Text("Global").Put<uint8_t>(1).Put<uint8_t>(2).Put<uint8_t>(3);
|
||||
w.Put<uint8_t>(0).Text("zone.raw").Text("Name").Text("Description"); // no zone boundaries
|
||||
w.Put<uint32_t>(1);
|
||||
for (int i = 0; i < 2; i++) w.Put<uint64_t>(1).Point(0, 0, 0);
|
||||
@@ -65,7 +65,7 @@ TEST(ZoneFileTests, ReadsScenesAndPaths) {
|
||||
ASSERT_EQ(zone.scenes.size(), 1u);
|
||||
EXPECT_EQ(zone.scenes[0].filename, "scene.lvl");
|
||||
EXPECT_EQ(zone.scenes[0].id, 7u);
|
||||
EXPECT_EQ(zone.scenes[0].sceneType, eSceneType::FX);
|
||||
EXPECT_EQ(zone.scenes[0].sceneType, eSceneType::General);
|
||||
EXPECT_EQ(zone.zoneRawPath, "zone.raw");
|
||||
ASSERT_EQ(zone.sceneTransitions.size(), 1u);
|
||||
EXPECT_EQ(zone.sceneTransitions[0].points.size(), 2u);
|
||||
@@ -183,6 +183,35 @@ TEST(ZoneFileTests, EarlyScenesAreNumberedInOrder) {
|
||||
EXPECT_EQ(zone.scenes[2].filename, "c.lvl");
|
||||
}
|
||||
|
||||
// The scenes the client loads: by scene ID and layer, a later one replacing an earlier one; none without a General
|
||||
// layer; before version 41 only the General layer
|
||||
TEST(ZoneFileTests, KeepsTheScenesTheClientLoads) {
|
||||
for (const uint32_t version : { 40u, 41u }) {
|
||||
ZoneBytes w;
|
||||
w.Put<uint32_t>(version).Put<uint32_t>(1).Put<uint32_t>(1100);
|
||||
w.Point(0, 0, 0).Put(1.0f).Put(0.0f).Put(0.0f).Put(0.0f);
|
||||
w.Put<uint32_t>(5);
|
||||
const auto scene = [&w](const std::string& file, uint32_t id, uint32_t layer) {
|
||||
w.Text(file).Put<uint32_t>(id).Put<uint32_t>(layer).Text("n").Put<uint8_t>(0).Put<uint8_t>(0).Put<uint8_t>(0);
|
||||
};
|
||||
scene("b.lvl", 2, 0);
|
||||
scene("b_audio.lvl", 2, 1);
|
||||
scene("orphan_audio.lvl", 3, 1);
|
||||
scene("a.lvl", 1, 0);
|
||||
scene("b2.lvl", 2, 0);
|
||||
w.Put<uint8_t>(0).Text("zone.raw").Text("Name").Text("Description");
|
||||
w.Put<uint32_t>(0).Put<uint32_t>(0).Put<uint32_t>(1).Put<uint32_t>(0);
|
||||
std::istringstream stream(w.data);
|
||||
ZoneFile zone;
|
||||
zone.Read(stream);
|
||||
EXPECT_FALSE(stream.fail());
|
||||
std::vector<std::string> files;
|
||||
for (const auto& read : zone.scenes) files.push_back(read.filename);
|
||||
if (version == 41) EXPECT_EQ(files, (std::vector<std::string>{ "a.lvl", "b2.lvl", "b_audio.lvl" }));
|
||||
else EXPECT_EQ(files, (std::vector<std::string>{ "a.lvl", "b2.lvl" }));
|
||||
}
|
||||
}
|
||||
|
||||
// A PrePreAlpha (30) file ends at its terrain file's name: no zone name, description, transitions or paths
|
||||
TEST(ZoneFileTests, PrePreAlphaHasNoZoneName) {
|
||||
ZoneBytes w;
|
||||
|
||||
Reference in New Issue
Block a user