From a95002fd405f6f7831ccc09a1a35ecebd6707240 Mon Sep 17 00:00:00 2001 From: zhaodice <63996691+zhaodice@users.noreply.github.com> Date: Sun, 29 May 2022 21:16:49 +0800 Subject: [PATCH] Hunting and deforestation support. (#1083) * add drops for animals wild pig,fishes,foxes,birds * append fox * Deforestation Support implement drop woods when attacking tree * Deforestation support (remove prints) implement drop woods when attacking tree * Deforestation support (remove prints) implement drop woods when attacking tree * add AutoRecycleHashMap Map's KEY is automatic expire if key long time no use (query or modify from HashMap) * use AutoRecycleHashMap in case of memory leak * fix bug * remove prints * static AutoRecycleHashMap * fix problems * Delete AutoRecycleHashMap.java * remove log * fix build * improve * remove unnecessary information Co-authored-by: Albedo <105265570+arub3do@users.noreply.github.com> Co-authored-by: Albedo <105265570+arub3do@users.noreply.github.com> --- .../DeforestationManager.java | 91 +++++++++++++++++++ .../DeforestationManager/HitTreeRecord.java | 57 ++++++++++++ .../emu/grasscutter/game/player/Player.java | 14 ++- .../packet/recv/HandlerHitTreeNotify.java | 21 +++++ src/main/resources/defaults/data/Drop.json | 40 ++++++++ 5 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java create mode 100644 src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java diff --git a/src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java new file mode 100644 index 000000000..ff7c0b1a9 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java @@ -0,0 +1,91 @@ +package emu.grasscutter.game.managers.DeforestationManager; + +import java.util.ArrayList; +import java.util.HashMap; + +import dev.morphia.annotations.Transient; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.proto.HitTreeNotifyOuterClass; +import emu.grasscutter.net.proto.VectorOuterClass; +import emu.grasscutter.utils.Position; + +public class DeforestationManager { + final static int RECORD_EXPIRED_SECONDS = 60*5; // 5 min + final static int RECORD_MAX_TIMES = 3; // max number of wood + final static int RECORD_MAX_TIMES_OTHER_HIT_TREE = 10; // if hit 10 times other trees, reset wood + + @Transient private final Player player; + @Transient private final ArrayList currentRecord; + @Transient private final static HashMap ColliderTypeToWoodItemID = new HashMap<>(); + static { + /* define wood types which reflected to item id*/ + ColliderTypeToWoodItemID.put(1,101301); + ColliderTypeToWoodItemID.put(2,101302); + ColliderTypeToWoodItemID.put(3,101303); + ColliderTypeToWoodItemID.put(4,101304); + ColliderTypeToWoodItemID.put(5,101305); + ColliderTypeToWoodItemID.put(6,101306); + ColliderTypeToWoodItemID.put(7,101307); + ColliderTypeToWoodItemID.put(8,101308); + ColliderTypeToWoodItemID.put(9,101309); + ColliderTypeToWoodItemID.put(10,101310); + ColliderTypeToWoodItemID.put(11,101311); + ColliderTypeToWoodItemID.put(12,101312); + } + public DeforestationManager(Player player){ + this.player = player; + this.currentRecord = new ArrayList<>(); + } + public void resetWood(){ + synchronized (currentRecord) { + currentRecord.clear(); + } + } + public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit){ + synchronized (currentRecord) { + //Grasscutter.getLogger().info("onDeforestationInvoke! Wood records {}", currentRecord); + VectorOuterClass.Vector hitPosition = hit.getHitPostion(); + int woodType = hit.getWoodType(); + if (ColliderTypeToWoodItemID.containsKey(woodType)) {// is a available wood type + Scene scene = player.getScene(); + int itemId = ColliderTypeToWoodItemID.get(woodType); + int positionHash = hitPosition.hashCode(); + HitTreeRecord record = searchRecord(positionHash); + if (record == null) { + record = new HitTreeRecord(positionHash); + }else{ + currentRecord.remove(record);// move it to last position + } + currentRecord.add(record); + if(currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE){ + currentRecord.remove(0); + } + if(record.record()) { + EntityItem entity = new EntityItem(scene, + null, + GameData.getItemDataMap().get(itemId), + new Position(hitPosition.getX(), hitPosition.getY(), hitPosition.getZ()), + 1, + false); + scene.addEntity(entity); + } + //record.record()=false : too many wood they have deforested, no more wood dropped! + } else { + Grasscutter.getLogger().warn("No wood type {} found.", woodType); + } + } + // unknown wood type + } + private HitTreeRecord searchRecord(int id){ + for (HitTreeRecord record : currentRecord) { + if (record.getUnique() == id) { + return record; + } + } + return null; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java new file mode 100644 index 000000000..ee78c7506 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java @@ -0,0 +1,57 @@ +package emu.grasscutter.game.managers.DeforestationManager; + + + +public class HitTreeRecord { + private final int unique; + private short count; // hit this tree times + private long time; // last available hitting time + HitTreeRecord(int unique){ + this.count = 0; + this.time = 0; + this.unique = unique; + } + + /** + * reset hit time + */ + private void resetTime(){ + this.time = System.currentTimeMillis(); + } + + + /** + * commit hit behavior + */ + public boolean record(){ + if (this.count < DeforestationManager.RECORD_MAX_TIMES) { + this.count++; + resetTime(); + return true; + } + // check expired + boolean isWaiting = System.currentTimeMillis() - this.time < DeforestationManager.RECORD_EXPIRED_SECONDS * 1000L; + if(isWaiting){ + return false; + }else{ + this.count = 1; + resetTime(); + return true; + } + } + /** + * get unique id + */ + public int getUnique(){ + return unique; + } + + @Override + public String toString() { + return "HitTreeRecord{" + + "unique=" + unique + + ", count=" + count + + ", time=" + time + + '}'; + } +} diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 3c741e269..c922b7d62 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -11,6 +11,7 @@ import emu.grasscutter.game.ability.AbilityManager; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.AvatarProfileData; import emu.grasscutter.game.avatar.AvatarStorage; +import emu.grasscutter.game.managers.DeforestationManager.DeforestationManager; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.GameEntity; @@ -145,11 +146,11 @@ public class Player { @Transient private MapMarksManager mapMarksManager; @Transient private StaminaManager staminaManager; @Transient private EnergyManager energyManager; + @Transient private DeforestationManager deforestationManager; private long springLastUsed; private HashMap mapMarks; - @Deprecated @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! public Player() { @@ -159,6 +160,8 @@ public class Player { this.mailHandler = new MailHandler(this); this.towerManager = new TowerManager(this); this.abilityManager = new AbilityManager(this); + this.deforestationManager = new DeforestationManager(this); + this.setQuestManager(new QuestManager(this)); this.pos = new Position(); this.rotation = new Position(); @@ -227,6 +230,7 @@ public class Player { this.staminaManager = new StaminaManager(this); this.sotsManager = new SotSManager(this); this.energyManager = new EnergyManager(this); + this.deforestationManager = new DeforestationManager(this); } public int getUid() { @@ -923,7 +927,6 @@ public class Player { // Add to inventory boolean success = getInventory().addItem(item, ActionReason.SubfieldDrop); if (success) { - if (!drop.isShare()) // not shared drop this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); else @@ -1109,6 +1112,10 @@ public class Player { return abilityManager; } + public DeforestationManager getDeforestationManager() { + return deforestationManager; + } + public HashMap getMapMarks() { return mapMarks; } public void setMapMarks(HashMap newMarks) { mapMarks = newMarks; } @@ -1294,6 +1301,9 @@ public class Player { // Call quit event. PlayerQuitEvent event = new PlayerQuitEvent(this); event.call(); + + //reset wood + getDeforestationManager().resetWood(); } public enum SceneLoadState { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java new file mode 100644 index 000000000..222116d56 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import java.math.BigInteger; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HitTreeNotifyOuterClass.HitTreeNotify; +import emu.grasscutter.server.game.GameSession; + +/** + * Implement Deforestation Function + */ +@Opcodes(PacketOpcodes.HitTreeNotify) +public class HandlerHitTreeNotify extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + HitTreeNotify hit = HitTreeNotify.parseFrom(payload); + session.getPlayer().getDeforestationManager().onDeforestationInvoke(hit); + } +} \ No newline at end of file diff --git a/src/main/resources/defaults/data/Drop.json b/src/main/resources/defaults/data/Drop.json index d1600aae2..c29a05de5 100644 --- a/src/main/resources/defaults/data/Drop.json +++ b/src/main/resources/defaults/data/Drop.json @@ -1,4 +1,44 @@ [ + {"monsterId":28040101,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040102,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040103,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040104,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040105,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040106,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040107,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040108,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020301,"dropDataList":[{"itemId":100061,"minCount":2,"maxCount":2,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020302,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020101,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020102,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020103,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020104,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020105,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020106,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020701,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020702,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020303,"dropDataList":[{"itemId":100094,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020304,"dropDataList":[{"itemId":100094,"minCount":2,"maxCount":3,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030401,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030402,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030403,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030404,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030405,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030406,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030407,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030408,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030409,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030301,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030302,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030303,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030304,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030305,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030306,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030307,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030308,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030309,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030310,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030311,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, { "monsterId": 21010101, "dropDataList": [