mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
fix(level): change scene object config as the client does while loading it
ReadLvlObjectData (1.10.64, 0x0103ba20) changes an object's config as it loads it: - before version 47 a respawn time is in milliseconds: a float over 100 or any u32 becomes that many seconds, as a float; - a model spawner (LOT 176 with spawntemplate 14) gets its subkey as its blueprintid unless it has one, DisableModelBehaviors true unless set, and preventRenderWrapping true; - before version 44 an object with sceneIDOverrideEnabled gets sceneLayerIDOverride 0. LevelFile now makes the same changes. On live worlds 1906 spawners in 114 scene files of versions 45-46 have a u32 respawn time; the world already divided those by 1000 but as an integer, so they now keep their fraction (a 2500 ms respawn is 2.5 s, not 2 s). No live scene has a model spawner; 62 objects of version 43 scenes get sceneLayerIDOverride. Check in game: enemies and smashables respawn after the same time as before on Forbidden Valley, Avant Gardens and Gnarled Forest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -127,6 +127,42 @@ void LevelFile::ReadFileInfoChunk(std::istream& file, ChunkHeader& header) {
|
||||
BinaryIO::BinaryRead(file, header.fileInfo.particleChunkStart);
|
||||
}
|
||||
|
||||
void LevelFile::ApplyClientConfigFixups(SceneObject& obj, uint32_t version) {
|
||||
// What ReadLvlObjectData (1.10.64, 0x0103ba20) changes in an object's config as it loads it
|
||||
auto& settings = obj.settings;
|
||||
const auto typeOf = [&settings](const std::u16string& key) {
|
||||
const auto it = settings.find(key);
|
||||
return it == settings.end() || !it->second ? LDF_TYPE_UNKNOWN : it->second->GetValueType();
|
||||
};
|
||||
const auto valueOf = [&settings]<typename T>(const std::u16string& key) {
|
||||
return static_cast<const LDFData<T>*>(settings.find(key)->second.get())->GetValue();
|
||||
};
|
||||
|
||||
// Before version 47 a respawn time was in milliseconds: a float over 100, or any u32, becomes seconds (a float)
|
||||
if (version < 47) {
|
||||
if (typeOf(u"respawn") == LDF_TYPE_FLOAT) {
|
||||
const auto respawn = valueOf.operator()<float>(u"respawn");
|
||||
if (respawn > 100.0f) settings.Insert<float>(u"respawn", respawn * 0.001f);
|
||||
} else if (typeOf(u"respawn") == LDF_TYPE_U32) {
|
||||
settings.Insert<float>(u"respawn", static_cast<float>(valueOf.operator()<uint32_t>(u"respawn") * 0.001));
|
||||
}
|
||||
}
|
||||
|
||||
// A model spawner (LOT 176 spawning template 14): its blueprint is its subkey unless it has one, its behaviors are
|
||||
// off unless it says otherwise, and it never wraps its render
|
||||
if (obj.lot == 176 && typeOf(u"spawntemplate") == LDF_TYPE_S32 && valueOf.operator()<int32_t>(u"spawntemplate") == 14) {
|
||||
if (typeOf(u"blueprintid") == LDF_TYPE_UNKNOWN) {
|
||||
const LWOOBJID subkey = typeOf(u"subkey") == LDF_TYPE_OBJID ? valueOf.operator()<LWOOBJID>(u"subkey") : LWOOBJID_EMPTY;
|
||||
settings.Insert<LWOOBJID>(u"blueprintid", subkey);
|
||||
}
|
||||
if (typeOf(u"DisableModelBehaviors") == LDF_TYPE_UNKNOWN) settings.Insert<bool>(u"DisableModelBehaviors", true);
|
||||
settings.Insert<bool>(u"preventRenderWrapping", true);
|
||||
}
|
||||
|
||||
// Before version 44 an object that overrides its scene goes to layer 0 of the scene it names
|
||||
if (version < 44 && typeOf(u"sceneIDOverrideEnabled") != LDF_TYPE_UNKNOWN) settings.Insert<uint32_t>(u"sceneLayerIDOverride", 0);
|
||||
}
|
||||
|
||||
void LevelFile::ReadSceneObjectDataChunk(std::istream& file, uint32_t version) {
|
||||
uint32_t objectsCount = 0;
|
||||
BinaryIO::BinaryRead(file, objectsCount);
|
||||
@@ -165,6 +201,7 @@ void LevelFile::ReadSceneObjectDataChunk(std::istream& file, uint32_t version) {
|
||||
for (const auto& token : GeneralUtils::SplitString(GeneralUtils::UTF16ToWTF8(ldfString), '\n')) {
|
||||
obj.settings.ParseInsert(token);
|
||||
}
|
||||
ApplyClientConfigFixups(obj, version);
|
||||
|
||||
objects.push_back(std::move(obj));
|
||||
}
|
||||
|
||||
@@ -65,4 +65,5 @@ private:
|
||||
static ChunkHeader ReadChunkHeader(std::istream& file, uint32_t start);
|
||||
void ReadFileInfoChunk(std::istream& file, ChunkHeader& header);
|
||||
void ReadSceneObjectDataChunk(std::istream& file, uint32_t version);
|
||||
static void ApplyClientConfigFixups(SceneObject& obj, uint32_t version);
|
||||
};
|
||||
|
||||
@@ -505,6 +505,36 @@ TEST(LevelFileTests, SkipsEditorSettingsBySize) {
|
||||
EXPECT_TRUE(oldLevel.objects.empty());
|
||||
}
|
||||
|
||||
// Config the client changes as it loads an object
|
||||
TEST(LevelFileTests, FixesUpConfigAsTheClient) {
|
||||
ZoneBytes w;
|
||||
w.Put<uint16_t>(30).Put<uint16_t>(0);
|
||||
w.data.append(48 + 12, '\0');
|
||||
w.Put<uint32_t>(0).Put<uint32_t>(4);
|
||||
w.Object(30, 176, 0, "respawn=3:5000\nsceneIDOverrideEnabled=7:1");
|
||||
w.Object(30, 176, 0, "respawn=5:20000");
|
||||
w.Object(30, 176, 0, "respawn=3:20\nspawntemplate=1:14\nsubkey=9:123");
|
||||
w.Object(30, 176, 0, "spawntemplate=1:14\nblueprintid=9:5\nDisableModelBehaviors=7:0");
|
||||
|
||||
std::istringstream stream(w.Done());
|
||||
LevelFile level;
|
||||
level.Read(stream);
|
||||
ASSERT_EQ(level.objects.size(), 4u);
|
||||
const auto value = [&level](size_t object, const std::u16string& key) {
|
||||
const auto it = level.objects[object].settings.find(key);
|
||||
return it == level.objects[object].settings.end() ? std::string("none") : it->second->GetString();
|
||||
};
|
||||
EXPECT_EQ(value(0, u"respawn"), "respawn=3:5.000000");
|
||||
EXPECT_EQ(value(0, u"sceneLayerIDOverride"), "sceneLayerIDOverride=5:0");
|
||||
EXPECT_EQ(value(1, u"respawn"), "respawn=3:20.000000");
|
||||
EXPECT_EQ(value(2, u"respawn"), "respawn=3:20.000000");
|
||||
EXPECT_EQ(value(2, u"blueprintid"), "blueprintid=9:123");
|
||||
EXPECT_EQ(value(2, u"DisableModelBehaviors"), "DisableModelBehaviors=7:1");
|
||||
EXPECT_EQ(value(2, u"preventRenderWrapping"), "preventRenderWrapping=7:1");
|
||||
EXPECT_EQ(value(3, u"blueprintid"), "blueprintid=9:5");
|
||||
EXPECT_EQ(value(3, u"DisableModelBehaviors"), "DisableModelBehaviors=7:0");
|
||||
}
|
||||
|
||||
TEST(LevelFileTests, DamagedFilesKeepWhatWasRead) {
|
||||
ZoneBytes w;
|
||||
w.Put<uint16_t>(30).Put<uint16_t>(0);
|
||||
|
||||
Reference in New Issue
Block a user