From 0ff7e7736cbc7d6aa3f2de3e188a632b2963d27a Mon Sep 17 00:00:00 2001 From: MagicBot Date: Wed, 21 Oct 2026 08:30:43 -0400 Subject: [PATCH] Load RuneBase and its attributes from the runes LoadAllRuneBases builds one RuneBase per static_rune_runebase row from the rune with the same id (all 315 have one) and fills runeBaseAttributeMap from the rune; the row now only supplies type, subtype, the runereq maps and effects. A row without a rune would stay the DB object, without attributes, with a warning. static_rune_runebaseattribute is no longer read at boot (the LoadAllAttributes calls are gone from both servers). The SQL loaders stay until task 2.11. Value changes (decisions: JSON, D2): - name: Confesser -> Confessor, Theif -> Thief, LagMaster -> Lagmaster, WP Dev -> Wolfpack Developer, Exception X -> Exceptional X (Strength, Dexterity, Constitution, Intelligence, Spirit). No code matches these names. - description: '' / CSR text -> rune_dsc (unread) - attributes added where the DB had none: - admin 2905 Lagmaster spi +200, spi max +200; 2907 Watcher con +200; 2909 Feature Character every max +200; 2911 QA Test Rune con +200 (applies to characters granted them; none hold them on dev) - creation cost 5/12/10/10/10 on 250116, 250117, 250123-250125 (duplicate talents not offered at creation) - base classes 2500-2503, Pet 2522, guards 252621-252623: start/max attributes (never player CharacterRunes; NPC rune attributes are unread) All 365 DB attribute rows match the JSON; final parity before the switch (scripts/p24.py): 0 other differences. The static_rune_runebase comparator is removed; it would only compare the JSON with itself now. Co-Authored-By: Claude Opus 5.5 --- src/engine/objects/RuneBase.java | 33 ++++++++++++- src/engine/parsers/cache/ParityCheck.java | 59 ----------------------- src/engine/server/login/LoginServer.java | 1 - src/engine/server/world/WorldServer.java | 1 - 4 files changed, 32 insertions(+), 62 deletions(-) diff --git a/src/engine/objects/RuneBase.java b/src/engine/objects/RuneBase.java index 49d06241..051af2c3 100644 --- a/src/engine/objects/RuneBase.java +++ b/src/engine/objects/RuneBase.java @@ -14,6 +14,7 @@ import engine.mbEnums; import engine.net.ByteBufferWriter; import engine.parsers.objects.RuneTemplate; import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; import java.sql.ResultSet; import java.sql.SQLException; @@ -122,9 +123,39 @@ public class RuneBase extends AbstractGameObject { } + // Iterates the static_rune_runebase rows: no rune field separates the player runes from the mob + // talents, and each row still holds the server type/subtype and requirement maps. + public static void LoadAllRuneBases() { - DbManager.RuneBaseQueries.LOAD_ALL_RUNEBASES(); + ArrayList dbRuneBases; + ArrayList attributes; + RuneTemplate rune; + int loadedCount = 0; + + dbRuneBases = DbManager.RuneBaseQueries.LOAD_ALL_RUNEBASES(); + RuneBaseAttribute.runeBaseAttributeMap.clear(); + + for (RuneBase dbRuneBase : dbRuneBases) { + rune = RuneTemplate._runes.get(dbRuneBase.getObjectUUID()); + + if (rune == null) { + Logger.warn("RuneBase " + dbRuneBase.getObjectUUID() + " has no rune; loaded from static_rune_runebase without attributes"); + continue; + } + + DbManager.addToCache(new RuneBase(rune, dbRuneBase)); + attributes = RuneBaseAttribute.getAttributesForRune(rune); + + // getAttrs() returns null for a rune without attributes, as the DB map did + if (!attributes.isEmpty()) + RuneBaseAttribute.runeBaseAttributeMap.put(rune.template_id, attributes); + + loadedCount++; + } + + Logger.info("Loaded " + loadedCount + " rune bases from runes"); + RuneBase.AllowedBaseClassRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_BASECLASS(); RuneBase.AllowedRaceRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_RACE(); } diff --git a/src/engine/parsers/cache/ParityCheck.java b/src/engine/parsers/cache/ParityCheck.java index 75c965bf..4d861267 100644 --- a/src/engine/parsers/cache/ParityCheck.java +++ b/src/engine/parsers/cache/ParityCheck.java @@ -9,19 +9,10 @@ package engine.parsers.cache; import engine.gameManager.ConfigManager; -import engine.gameManager.DbManager; -import engine.mbEnums; -import engine.objects.AbstractGameObject; -import engine.objects.RuneBase; -import engine.objects.RuneBaseAttribute; -import engine.parsers.objects.RuneTemplate; import org.pmw.tinylog.Logger; -import java.util.ArrayList; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; // Dev-only check that compares DB-built objects with the cache JSON before a loader is switched. @@ -37,10 +28,6 @@ public class ParityCheck { private int differenceCount = 0; private int missingCount = 0; - static { - register("static_rune_runebase", ParityCheck::compareRuneBases); - } - private ParityCheck(String table) { this.table = table; } @@ -94,50 +81,4 @@ public class ParityCheck { differenceCount++; Logger.warn("Parity: " + table + "/" + id + "/" + field + ": db=" + dbValue + " json=" + jsonValue); } - - // type, subtype and the requirement maps are copied from the DB row, so only name and attributes can differ. - // description is skipped: '' in the DB, the rune_dsc blurb in the JSON, and nothing reads it. - - private static void compareRuneBases(ParityCheck check) { - - ConcurrentHashMap cache; - RuneBase runeBase; - RuneBase jsonRuneBase; - RuneTemplate rune; - int id; - - cache = DbManager.getMap(mbEnums.GameObjectType.RuneBase); - - if (cache == null) { - Logger.warn("Parity: static_rune_runebase: no RuneBase objects loaded"); - return; - } - - for (AbstractGameObject cached : cache.values()) { - runeBase = (RuneBase) cached; - id = runeBase.getObjectUUID(); - rune = RuneTemplate._runes.get(id); - - if (!check.compareRow(id, rune)) - continue; - - jsonRuneBase = new RuneBase(rune, runeBase); - - check.compareField(id, "name", runeBase.getName(), jsonRuneBase.getName()); - check.compareField(id, "attributes", getAttributeSet(runeBase.getAttrs()), getAttributeSet(RuneBaseAttribute.getAttributesForRune(rune))); - } - } - - private static HashSet getAttributeSet(ArrayList attributes) { - - HashSet attributeSet = new HashSet<>(); - - if (attributes == null) - return attributeSet; - - for (RuneBaseAttribute attribute : attributes) - attributeSet.add(attribute.getAttributeID() + "=" + attribute.getModValue()); - - return attributeSet; - } } diff --git a/src/engine/server/login/LoginServer.java b/src/engine/server/login/LoginServer.java index 76d0a9e6..07743354 100644 --- a/src/engine/server/login/LoginServer.java +++ b/src/engine/server/login/LoginServer.java @@ -331,7 +331,6 @@ public class LoginServer { Logger.info("Initializing PowersManager."); PowersManager.initPowersManager(false); - RuneBaseAttribute.LoadAllAttributes(); RuneBase.LoadAllRuneBases(); BaseClass.LoadAllBaseClasses(); PromotionClass.LoadAllPromotions(); diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index b068e426..da22ecc9 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -348,7 +348,6 @@ public class WorldServer { Logger.info("Initializing Loot Manager"); LootManager.init(); - RuneBaseAttribute.LoadAllAttributes(); RuneBase.LoadAllRuneBases(); BaseClass.LoadAllBaseClasses(); Race.loadAllRaces();