From c2e620bf7cc6386993e3364c6a9749fb9cd1344a Mon Sep 17 00:00:00 2001 From: Luke H-W Date: Wed, 28 Sep 2022 16:28:15 +0930 Subject: [PATCH] [BREAKING] Deobfuscate codex field, slight refactor (#1809) * Deobfuscate codex field, slight refactor * Fix relic sets in codex --- .../data/excels/CodexAnimalData.java | 30 +-- .../data/excels/CodexReliquaryData.java | 69 +++---- .../grasscutter/game/player/PlayerCodex.java | 177 +++++++----------- .../emu/grasscutter/game/world/Scene.java | 4 +- 4 files changed, 110 insertions(+), 170 deletions(-) diff --git a/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java b/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java index 0bc9c0521..bca9efa0d 100644 --- a/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java +++ b/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java @@ -1,38 +1,26 @@ package emu.grasscutter.data.excels; +import com.google.gson.annotations.SerializedName; + import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; +import lombok.Getter; @ResourceType(name = {"AnimalCodexExcelConfigData.json"}) public class CodexAnimalData extends GameResource { private int Id; - private String type; - private int describeId; - private int sortOrder; - private CodexAnimalUnlockCondition OCCLHPBCDGL; + @Getter private String type; + @Getter private int describeId; + @Getter private int sortOrder; + @SerializedName(value="countType", alternate={"OCCLHPBCDGL"}) + @Getter private CountType countType; @Override public int getId() { return Id; } - public String getType() { - return type; - } - - public int getDescribeId() { - return describeId; - } - - public int getSortOrder() { - return sortOrder; - } - - public CodexAnimalUnlockCondition getUnlockCondition() { - return OCCLHPBCDGL; - } - - public enum CodexAnimalUnlockCondition { + public enum CountType { CODEX_COUNT_TYPE_KILL, CODEX_COUNT_TYPE_CAPTURE } diff --git a/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java b/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java index 8ed747b53..d6fd8fef2 100644 --- a/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java +++ b/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java @@ -3,57 +3,44 @@ package emu.grasscutter.data.excels; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.ints.IntList; +import lombok.Getter; @ResourceType(name = {"ReliquaryCodexExcelConfigData.json"}) public class CodexReliquaryData extends GameResource { - private int Id; - private int suitId; - private int level; - private int cupId; - private int leatherId; - private int capId; - private int flowerId; - private int sandId; - private int sortOrder; + @Getter private int Id; + @Getter private int suitId; + @Getter private int level; + @Getter private int cupId; + @Getter private int leatherId; + @Getter private int capId; + @Getter private int flowerId; + @Getter private int sandId; + @Getter private int sortOrder; + private transient IntCollection ids; - public int getSortOrder() { - return sortOrder; + public boolean containsId(int id) { + return getIds().contains(id); } - public int getId() { - return Id; - } - - public int getSuitId() { - return suitId; - } - - public int getLevel() { - return level; - } - - public int getCupId() { - return cupId; - } - - public int getLeatherId() { - return leatherId; - } - - public int getCapId() { - return capId; - } - - public int getFlowerId() { - return flowerId; - } - - public int getSandId() { - return sandId; + public IntCollection getIds() { + if (this.ids == null) { + int[] idsArr = {cupId, leatherId, capId, flowerId, sandId}; + this.ids = IntList.of(idsArr); + } + return this.ids; } @Override public void onLoad() { + // Normalize all itemIds to the 0-substat form + cupId = (cupId/10) * 10; + leatherId = (leatherId/10) * 10; + capId = (capId/10) * 10; + flowerId = (flowerId/10) * 10; + sandId = (sandId/10) * 10; + GameData.getcodexReliquaryArrayList().add(this); GameData.getcodexReliquaryIdMap().put(getSuitId(), this); } diff --git a/src/main/java/emu/grasscutter/game/player/PlayerCodex.java b/src/main/java/emu/grasscutter/game/player/PlayerCodex.java index 191fc4112..09049544f 100644 --- a/src/main/java/emu/grasscutter/game/player/PlayerCodex.java +++ b/src/main/java/emu/grasscutter/game/player/PlayerCodex.java @@ -4,13 +4,12 @@ import dev.morphia.annotations.Entity; import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameData; import emu.grasscutter.data.excels.CodexAnimalData; -import emu.grasscutter.data.excels.CodexReliquaryData; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.inventory.GameItem; -import emu.grasscutter.game.inventory.ItemType; -import emu.grasscutter.game.inventory.MaterialType; import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify; +import lombok.Getter; +import lombok.val; import java.util.*; @@ -19,14 +18,14 @@ public class PlayerCodex { @Transient private Player player; //itemId is not codexId! - private Set unlockedWeapon; - private Map unlockedAnimal; - private Set unlockedMaterial; - private Set unlockedBook; - private Set unlockedTip; - private Set unlockedView; - private Set unlockedReliquary; - private Set unlockedReliquarySuitCodex; + @Getter private Set unlockedWeapon; + @Getter private Map unlockedAnimal; + @Getter private Set unlockedMaterial; + @Getter private Set unlockedBook; + @Getter private Set unlockedTip; + @Getter private Set unlockedView; + @Getter private Set unlockedReliquary; + @Getter private Set unlockedReliquarySuitCodex; public PlayerCodex(){ this.unlockedWeapon = new HashSet<>(); @@ -46,120 +45,86 @@ public class PlayerCodex { public void setPlayer(Player player) { this.player = player; + this.fixReliquaries(); } public void checkAddedItem(GameItem item){ - ItemType type = item.getItemData().getItemType(); - if (type == ItemType.ITEM_WEAPON){ - if(!getUnlockedWeapon().contains(item.getItemId())){ - getUnlockedWeapon().add(item.getItemId()); - var codexItem = GameData.getCodexWeaponDataIdMap().get(item.getItemId()); - if(codexItem != null){ - player.save(); - this.player.sendPacket(new PacketCodexDataUpdateNotify(2, codexItem.getId())); - } + val itemData = item.getItemData(); + val itemId = item.getItemId(); + switch (itemData.getItemType()) { + case ITEM_WEAPON -> { + Optional.ofNullable(GameData.getCodexWeaponDataIdMap().get(itemId)) + .ifPresent(codexData -> { + if (this.getUnlockedWeapon().add(itemId)) { + this.player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(2, codexData.getId())); + } + }); } - } - else if(type == ItemType.ITEM_MATERIAL){ - if( item.getItemData().getMaterialType() == MaterialType.MATERIAL_FOOD || - item.getItemData().getMaterialType() == MaterialType.MATERIAL_WIDGET|| - item.getItemData().getMaterialType() == MaterialType.MATERIAL_EXCHANGE|| - item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR_MATERIAL|| - item.getItemData().getMaterialType() == MaterialType.MATERIAL_NOTICE_ADD_HP){ - if (!getUnlockedMaterial().contains(item.getItemId())) { - var codexMaterial = GameData.getCodexMaterialDataIdMap().get(item.getItemId()); - if (codexMaterial != null) { - getUnlockedMaterial().add(item.getItemId()); - player.save(); - this.player.sendPacket(new PacketCodexDataUpdateNotify(4, codexMaterial.getId())); + case ITEM_MATERIAL -> { + switch (itemData.getMaterialType()) { + // Is this check even needed? + case MATERIAL_FOOD, MATERIAL_WIDGET, MATERIAL_EXCHANGE, MATERIAL_AVATAR_MATERIAL, MATERIAL_NOTICE_ADD_HP -> { + Optional.ofNullable(GameData.getCodexMaterialDataIdMap().get(itemId)) + .ifPresent(codexData -> { + if (this.getUnlockedMaterial().add(itemId)) { + this.player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(4, codexData.getId())); + } + }); } + default -> {} } } - } - else if(type == ItemType.ITEM_RELIQUARY) { - if(!getUnlockedReliquary().contains(item.getItemId())){ - getUnlockedReliquary().add(item.getItemId()); - checkUnlockedSuits(item); + case ITEM_RELIQUARY -> { + val reliquaryId = (itemId/10) * 10; // Normalize to 0-substat form + if (this.getUnlockedReliquary().add(reliquaryId)) + checkUnlockedSuits(reliquaryId); } + default -> {} } } - public void checkAnimal(GameEntity target, CodexAnimalData.CodexAnimalUnlockCondition condition){ - if(target instanceof EntityMonster){ - var monsterId = ((EntityMonster)target).getMonsterData().getId(); - var codexAnimal = GameData.getCodexAnimalDataMap().get(monsterId); + public void checkAnimal(GameEntity target, CodexAnimalData.CountType countType){ + if (target instanceof EntityMonster) { + val monsterId = ((EntityMonster) target).getMonsterData().getId(); + val codexAnimal = GameData.getCodexAnimalDataMap().get(monsterId); + if (codexAnimal == null) return; + + val animalCountType = codexAnimal.getCountType(); + if (animalCountType != countType && animalCountType != null) return; + + this.getUnlockedAnimal().merge(monsterId, 1, (i, j) -> i + 1); - if(!getUnlockedAnimal().containsKey(monsterId)) { - if (codexAnimal != null) { - if(codexAnimal.getUnlockCondition() == condition || codexAnimal.getUnlockCondition() == null){ - getUnlockedAnimal().put(monsterId, 1); - } - } - }else{ - getUnlockedAnimal().put(monsterId, getUnlockedAnimal().get(monsterId) + 1); - } player.save(); this.player.sendPacket(new PacketCodexDataUpdateNotify(3, monsterId)); } } - public void checkUnlockedSuits(GameItem item){ - int reliquaryId = item.getItemId(); - Optional excelReliquarySuitList = GameData.getcodexReliquaryArrayList().stream().filter( - x -> x.getCupId() == reliquaryId - || x.getLeatherId() == reliquaryId - || x.getCapId() == reliquaryId - || x.getFlowerId() == reliquaryId - || x.getSandId() == reliquaryId - ).findFirst(); - if(excelReliquarySuitList.isPresent()) { - var excelReliquarySuit = excelReliquarySuitList.get(); - if(!getUnlockedReliquarySuitCodex().contains(excelReliquarySuit.getId())){ - if( - getUnlockedReliquary().contains(excelReliquarySuit.getCupId()) && - getUnlockedReliquary().contains(excelReliquarySuit.getLeatherId()) && - getUnlockedReliquary().contains(excelReliquarySuit.getCapId()) && - getUnlockedReliquary().contains(excelReliquarySuit.getFlowerId()) && - getUnlockedReliquary().contains(excelReliquarySuit.getSandId()) - ){ - getUnlockedReliquarySuitCodex().add(excelReliquarySuit.getId()); - player.save(); - this.player.sendPacket(new PacketCodexDataUpdateNotify(8, excelReliquarySuit.getId())); - } - } - } + public void checkUnlockedSuits(int reliquaryId){ + GameData.getcodexReliquaryArrayList().stream() + .filter(x -> !this.getUnlockedReliquarySuitCodex().contains(x.getId())) + .filter(x -> x.containsId(reliquaryId)) + .filter(x -> this.getUnlockedReliquary().containsAll(x.getIds())) + .forEach(x -> { + int id = x.getId(); + this.getUnlockedReliquarySuitCodex().add(id); + this.player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(8, id)); + }); } - public Set getUnlockedWeapon() { - return unlockedWeapon; - } + @Deprecated // Maybe remove this if we ever stop caring about older dbs + private void fixReliquaries() { + // Migrate older database entries which were using non-canonical forms of itemIds + val newReliquaries = new HashSet(); + this.unlockedReliquary.forEach(i -> newReliquaries.add((i/10)*10)); + this.unlockedReliquary = newReliquaries; - public Map getUnlockedAnimal() { - return unlockedAnimal; + GameData.getcodexReliquaryArrayList().stream() + .filter(x -> !this.getUnlockedReliquarySuitCodex().contains(x.getId())) + .filter(x -> this.getUnlockedReliquary().containsAll(x.getIds())) + .forEach(x -> this.getUnlockedReliquarySuitCodex().add(x.getId())); + this.player.save(); } - - public Set getUnlockedMaterial() { - return unlockedMaterial; - } - - public Set getUnlockedBook() { - return unlockedBook; - } - - public Set getUnlockedTip() { - return unlockedTip; - } - - public Set getUnlockedView() { - return unlockedView; - } - - public Set getUnlockedReliquary() { - return unlockedReliquary; - } - - public Set getUnlockedReliquarySuitCodex() { - return unlockedReliquarySuitCodex; - } - } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 9702680ee..32fbec846 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -312,10 +312,10 @@ public class Scene { if (attacker instanceof EntityClientGadget gadgetAttacker) { var clientGadgetOwner = getEntityById(gadgetAttacker.getOwnerEntityId()); if (clientGadgetOwner instanceof EntityAvatar) { - ((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); + ((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CountType.CODEX_COUNT_TYPE_KILL); } } else if (attacker instanceof EntityAvatar avatarAttacker) { - avatarAttacker.getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); + avatarAttacker.getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CountType.CODEX_COUNT_TYPE_KILL); } }