diff --git a/src/engine/objects/RuneBase.java b/src/engine/objects/RuneBase.java index 51e83afe..49d06241 100644 --- a/src/engine/objects/RuneBase.java +++ b/src/engine/objects/RuneBase.java @@ -12,6 +12,7 @@ package engine.objects; import engine.gameManager.DbManager; import engine.mbEnums; import engine.net.ByteBufferWriter; +import engine.parsers.objects.RuneTemplate; import engine.server.MBServerStatics; import java.sql.ResultSet; @@ -78,6 +79,27 @@ public class RuneBase extends AbstractGameObject { this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID()); } + /** + * Rune Template Constructor + * type and subtype are server enums that don't map cleanly from rune_type/rune_sub_type, so they + * come from the static_rune_runebase row, as do the requirement maps and effects until 2.5b/2.10. + */ + public RuneBase(RuneTemplate rune, RuneBase dbRuneBase) { + super(rune.template_id); + + this.name = rune.item_base_name; + this.description = rune.rune_dsc; + this.type = dbRuneBase.type; + this.subtype = dbRuneBase.subtype; + this.race.putAll(dbRuneBase.race); + this.baseClass.putAll(dbRuneBase.baseClass); + this.promotionClass.putAll(dbRuneBase.promotionClass); + this.discipline.putAll(dbRuneBase.discipline); + this.overwrite.addAll(dbRuneBase.overwrite); + this.levelRequired = dbRuneBase.levelRequired; + this.effectsList = dbRuneBase.effectsList; + } + public static RuneBase getRuneBase(int tableId) { if (tableId == 0) diff --git a/src/engine/objects/RuneBaseAttribute.java b/src/engine/objects/RuneBaseAttribute.java index 1fdf6719..6da30197 100644 --- a/src/engine/objects/RuneBaseAttribute.java +++ b/src/engine/objects/RuneBaseAttribute.java @@ -10,11 +10,15 @@ package engine.objects; import engine.gameManager.DbManager; +import engine.mbEnums; import engine.mbEnums.GameObjectType; +import engine.parsers.objects.RuneTemplate; +import engine.server.MBServerStatics; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; @@ -57,6 +61,33 @@ public class RuneBaseAttribute extends AbstractGameObject { this.runeBaseID = rs.getInt("RuneBaseID"); } + // Attribute ids: 0 = rune_creation_cost, 1-5 = rune_attr_adj, 6-10 = rune_max_attr_adj, + // 11-15 = item_attr_req; the AttributeType ordinals Strength..Spirit are 1-5. + + public static ArrayList getAttributesForRune(RuneTemplate rune) { + + ArrayList attributes = new ArrayList<>(); + + if (rune.rune_creation_cost != 0) + attributes.add(new RuneBaseAttribute((short) MBServerStatics.RUNE_COST_ATTRIBUTE_ID, (short) rune.rune_creation_cost)); + + addAttributes(attributes, rune.rune_attr_adj, 0); + addAttributes(attributes, rune.rune_max_attr_adj, 5); + addAttributes(attributes, rune.item_attr_req, 10); + + return attributes; + } + + private static void addAttributes(ArrayList attributes, EnumMap values, int idOffset) { + + for (mbEnums.AttributeType attributeType : values.keySet()) { + if (values.get(attributeType) == 0) + continue; + + attributes.add(new RuneBaseAttribute((short) (attributeType.ordinal() + idOffset), values.get(attributeType).shortValue())); + } + } + public static void LoadAllAttributes() { DbManager.RuneBaseAttributeQueries.GET_ATTRIBUTES_FOR_RUNEBASE(); diff --git a/src/engine/parsers/cache/ParityCheck.java b/src/engine/parsers/cache/ParityCheck.java index 4d861267..75c965bf 100644 --- a/src/engine/parsers/cache/ParityCheck.java +++ b/src/engine/parsers/cache/ParityCheck.java @@ -9,10 +9,19 @@ 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. @@ -28,6 +37,10 @@ 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; } @@ -81,4 +94,50 @@ 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; + } }