From 49a2907bc5308297db311245a5fc168f2d5f02f1 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 09:56:03 -0700 Subject: [PATCH] fix(editor): cap quest count at 4096 and object count at 100k on load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A stale autosave or hand-edited JSON could carry an unbounded list: - 100k quests would emit 100k quest_template + queststarter/ender INSERTs (huge SQL, slow validate, slow chain walks). - 1M+ objects bloats the M2 instance SSBO and drags editor framerate to single digits. Caps mirror the 256-waypoint cap added in the previous batch — log a warning and drop the rest so the editor stays responsive. --- tools/editor/object_placer.cpp | 10 ++++++++++ tools/editor/quest_editor.cpp | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/tools/editor/object_placer.cpp b/tools/editor/object_placer.cpp index 9f59c938..f3dfb49d 100644 --- a/tools/editor/object_placer.cpp +++ b/tools/editor/object_placer.cpp @@ -337,7 +337,17 @@ bool ObjectPlacer::loadFromFile(const std::string& path) { selectedIndices_.clear(); uniqueIdCounter_ = 1; + // Cap object count — a stale autosave or biome-populate runaway + // could produce 100k+ entries that bloat the renderer instance + // SSBO and drag the editor framerate to single digits. + constexpr size_t kMaxObjects = 100'000; + for (const auto& jo : arr) { + if (objects_.size() >= kMaxObjects) { + LOG_WARNING("Object cap reached (", kMaxObjects, + ") — remaining entries dropped"); + break; + } PlacedObject obj; obj.type = static_cast(jo.value("type", 0)); obj.path = jo.value("path", ""); diff --git a/tools/editor/quest_editor.cpp b/tools/editor/quest_editor.cpp index 1558ed87..59d5ae8f 100644 --- a/tools/editor/quest_editor.cpp +++ b/tools/editor/quest_editor.cpp @@ -77,7 +77,18 @@ bool QuestEditor::loadFromFile(const std::string& path) { quests_.clear(); uint32_t maxId = 0; + // Cap total quest count — a stale autosave or hand-edited file + // could carry thousands of empty quests, each emitting a + // quest_template INSERT (and queststarter/questender + chain + // walks) on export. 4096 covers any realistic zone. + constexpr size_t kMaxQuests = 4096; + for (const auto& jq : arr) { + if (quests_.size() >= kMaxQuests) { + LOG_WARNING("Quest cap reached (", kMaxQuests, + ") — remaining entries dropped"); + break; + } Quest q; q.id = jq.value("id", 0u); q.title = jq.value("title", "Untitled");