mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
fix(level): split a scene object's config as the client does
LDF_FROM_STRING (1.10.64, 0x010fa220) splits an object's config at every comma and line break, leaves out empty entries and entries with no '=', and sets each key=value it finds. The reader split at line breaks only and parsed every piece, so an object with no config got an empty entry of unknown type, and a value with a comma would have stayed whole. Live scene files are unchanged; objects in older client files with an empty config no longer get the empty entry. Check in game: every world spawns its objects as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#include "LevelFile.h"
|
#include "LevelFile.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -198,8 +199,15 @@ void LevelFile::ReadSceneObjectDataChunk(std::istream& file, uint32_t version) {
|
|||||||
}
|
}
|
||||||
if (file.fail()) throw std::runtime_error("Failed to read from istream.");
|
if (file.fail()) throw std::runtime_error("Failed to read from istream.");
|
||||||
|
|
||||||
for (const auto& token : GeneralUtils::SplitString(GeneralUtils::UTF16ToWTF8(ldfString), '\n')) {
|
// LDF_FROM_STRING (0x010fa220): entries are split at every comma and line break, empty ones and ones without a
|
||||||
obj.settings.ParseInsert(token);
|
// '=' are left out
|
||||||
|
const auto config = GeneralUtils::UTF16ToWTF8(ldfString);
|
||||||
|
size_t start = 0;
|
||||||
|
while (start <= config.size()) {
|
||||||
|
const auto end = std::min(config.find_first_of(",\n", start), config.size());
|
||||||
|
const auto entry = config.substr(start, end - start);
|
||||||
|
if (entry.find('=') != std::string::npos) obj.settings.ParseInsert(entry);
|
||||||
|
start = end + 1;
|
||||||
}
|
}
|
||||||
ApplyClientConfigFixups(obj, version);
|
ApplyClientConfigFixups(obj, version);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
#include "GeneralUtils.h"
|
||||||
#include "LevelFile.h"
|
#include "LevelFile.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "ZoneFile.h"
|
#include "ZoneFile.h"
|
||||||
@@ -535,6 +537,25 @@ TEST(LevelFileTests, FixesUpConfigAsTheClient) {
|
|||||||
EXPECT_EQ(value(3, u"DisableModelBehaviors"), "DisableModelBehaviors=7:0");
|
EXPECT_EQ(value(3, u"DisableModelBehaviors"), "DisableModelBehaviors=7:0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An object's config splits at commas as well as line breaks; entries without a '=' are left out
|
||||||
|
TEST(LevelFileTests, SplitsConfigAsTheClient) {
|
||||||
|
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>(1);
|
||||||
|
w.Object(30, 2, 0, "a=0:x,b=1:4\n\nstray,c=0:y");
|
||||||
|
|
||||||
|
std::istringstream stream(w.Done());
|
||||||
|
LevelFile level;
|
||||||
|
level.Read(stream);
|
||||||
|
ASSERT_EQ(level.objects.size(), 1u);
|
||||||
|
const auto& settings = level.objects[0].settings;
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
for (const auto& [key, value] : settings) keys.push_back(GeneralUtils::UTF16ToWTF8(key));
|
||||||
|
std::ranges::sort(keys);
|
||||||
|
EXPECT_EQ(keys, (std::vector<std::string>{ "a", "b", "c" }));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(LevelFileTests, DamagedFilesKeepWhatWasRead) {
|
TEST(LevelFileTests, DamagedFilesKeepWhatWasRead) {
|
||||||
ZoneBytes w;
|
ZoneBytes w;
|
||||||
w.Put<uint16_t>(30).Put<uint16_t>(0);
|
w.Put<uint16_t>(30).Put<uint16_t>(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user