From 957293165b7e39e507f10ba76d37e037f73e0707 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Tue, 20 Oct 2026 10:33:45 -0400 Subject: [PATCH] Load PromotionClass from the promotion runes instead of static_rune_promotion LoadAllPromotions builds one PromotionClass per promotion CLASS rune (22, 2504-2526 without 2522) and caches it, then runs the DB loader, which skips cached ids, so only the CSR/admin rows 2901-2911 still come from static_rune_promotion (decision: DB fallback). The world server calls it in place of GET_ALL_PROMOTIONS; the login server now preloads promotions too, so character load there gets the same cached objects. The SQL loader stays until task 2.11. Value changes: Templar 2516 manaMod 14 -> 15 (decision: JSON, D2). description NULL -> rune_dsc blurb (unread). All other fields unchanged; final parity before the switch (scripts/p22.py): 22 promotion rows, 0 differences in name, token, health, stamina and the 163 allowed rune tokens. The static_rune_promotion comparator is removed; it would only compare the JSON with itself now. Co-Authored-By: Claude Opus 5.5 --- src/engine/objects/PromotionClass.java | 19 +++++++++ src/engine/parsers/cache/ParityCheck.java | 49 ----------------------- src/engine/server/login/LoginServer.java | 1 + src/engine/server/world/WorldServer.java | 2 +- 4 files changed, 21 insertions(+), 50 deletions(-) diff --git a/src/engine/objects/PromotionClass.java b/src/engine/objects/PromotionClass.java index 81e8b3d1..1cb4cf96 100644 --- a/src/engine/objects/PromotionClass.java +++ b/src/engine/objects/PromotionClass.java @@ -16,6 +16,7 @@ import engine.net.ByteBufferWriter; import engine.parsers.cache.CObjectIndex; import engine.parsers.objects.RuneTemplate; import engine.util.Hasher; +import org.pmw.tinylog.Logger; import java.sql.ResultSet; import java.sql.SQLException; @@ -162,6 +163,24 @@ public class PromotionClass extends AbstractGameObject { return tokens; } + public static void LoadAllPromotions() { + + int loadedCount = 0; + + for (RuneTemplate rune : RuneTemplate._runes.values()) { + if (!isPromotionRune(rune)) + continue; + + DbManager.addToCache(new PromotionClass(rune)); + loadedCount++; + } + + Logger.info("Loaded " + loadedCount + " promotion classes from runes"); + + // The CSR/admin rows 2901+ have no promotion rune; the DB loader skips ids already cached + DbManager.PromotionQueries.GET_ALL_PROMOTIONS(); + } + public static void serializeForClientMsg(PromotionClass promotionClass, ByteBufferWriter writer) { writer.putInt(3); // For BaseClass writer.putInt(0); // Pad diff --git a/src/engine/parsers/cache/ParityCheck.java b/src/engine/parsers/cache/ParityCheck.java index aaca0e3c..4d861267 100644 --- a/src/engine/parsers/cache/ParityCheck.java +++ b/src/engine/parsers/cache/ParityCheck.java @@ -9,17 +9,10 @@ package engine.parsers.cache; import engine.gameManager.ConfigManager; -import engine.gameManager.DbManager; -import engine.mbEnums; -import engine.objects.AbstractGameObject; -import engine.objects.PromotionClass; -import engine.parsers.objects.RuneTemplate; import org.pmw.tinylog.Logger; -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. @@ -35,10 +28,6 @@ public class ParityCheck { private int differenceCount = 0; private int missingCount = 0; - static { - register("static_rune_promotion", ParityCheck::comparePromotionClasses); - } - private ParityCheck(String table) { this.table = table; } @@ -92,42 +81,4 @@ public class ParityCheck { differenceCount++; Logger.warn("Parity: " + table + "/" + id + "/" + field + ": db=" + dbValue + " json=" + jsonValue); } - - private static void comparePromotionClasses(ParityCheck check) { - - ConcurrentHashMap cache; - PromotionClass promotionClass; - PromotionClass jsonClass; - RuneTemplate rune; - int id; - - cache = DbManager.getMap(mbEnums.GameObjectType.PromotionClass); - - if (cache == null) { - Logger.warn("Parity: static_rune_promotion: no PromotionClass objects loaded"); - return; - } - - for (AbstractGameObject cached : cache.values()) { - promotionClass = (PromotionClass) cached; - id = promotionClass.getObjectUUID(); - rune = RuneTemplate._runes.get(id); - - // The CSR/admin rows 2901+ are TALENT runes in the JSON, not promotions - if (rune != null && !PromotionClass.isPromotionRune(rune)) - rune = null; - - if (!check.compareRow(id, rune)) - continue; - - jsonClass = new PromotionClass(rune); - - check.compareField(id, "name", promotionClass.getName(), jsonClass.getName()); - check.compareField(id, "token", promotionClass.getToken(), jsonClass.getToken()); - check.compareField(id, "healthMod", promotionClass.getHealthMod(), jsonClass.getHealthMod()); - check.compareField(id, "manaMod", promotionClass.getManaMod(), jsonClass.getManaMod()); - check.compareField(id, "staminaMod", promotionClass.getStaminaMod(), jsonClass.getStaminaMod()); - check.compareField(id, "allowedRunes", new HashSet<>(promotionClass.getRuneList()), new HashSet<>(jsonClass.getRuneList())); - } - } } diff --git a/src/engine/server/login/LoginServer.java b/src/engine/server/login/LoginServer.java index 9b83202b..76d0a9e6 100644 --- a/src/engine/server/login/LoginServer.java +++ b/src/engine/server/login/LoginServer.java @@ -334,6 +334,7 @@ public class LoginServer { RuneBaseAttribute.LoadAllAttributes(); RuneBase.LoadAllRuneBases(); BaseClass.LoadAllBaseClasses(); + PromotionClass.LoadAllPromotions(); Race.loadAllRaces(); RuneBaseEffect.LoadRuneBaseEffects(); diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index 9faf7c02..b068e426 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -336,7 +336,7 @@ public class WorldServer { CObjectIndex.build(); Logger.info("Loading PromotionClasses"); - DbManager.PromotionQueries.GET_ALL_PROMOTIONS(); + PromotionClass.LoadAllPromotions(); Logger.info("Loading NPC and Mob Rune Sets"); NPCManager._runeSetMap = dbNPCHandler.LOAD_RUNES_FOR_NPC_AND_MOBS();