From 8b614d8df84dce58bbfe71f29b06b0644aa8ca09 Mon Sep 17 00:00:00 2001 From: Kengxxiao <11478651+Kengxxiao@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:42:02 +0800 Subject: [PATCH 1/3] implement simple drop system --- data/Drop.json | 14 +++ .../emu/grasscutter/game/drop/DropData.java | 57 +++++++++++ .../emu/grasscutter/game/drop/DropInfo.java | 16 +++ .../grasscutter/game/drop/DropManager.java | 99 +++++++++++++++++++ .../grasscutter/game/entity/EntityItem.java | 25 ++++- .../emu/grasscutter/game/player/Player.java | 31 +++--- .../emu/grasscutter/game/world/Scene.java | 33 +++---- .../grasscutter/server/game/GameServer.java | 17 +++- 8 files changed, 254 insertions(+), 38 deletions(-) create mode 100644 data/Drop.json create mode 100644 src/main/java/emu/grasscutter/game/drop/DropData.java create mode 100644 src/main/java/emu/grasscutter/game/drop/DropInfo.java create mode 100644 src/main/java/emu/grasscutter/game/drop/DropManager.java diff --git a/data/Drop.json b/data/Drop.json new file mode 100644 index 000000000..dfa37e8c8 --- /dev/null +++ b/data/Drop.json @@ -0,0 +1,14 @@ +[ + { + "monsterId": 20010201, + "dropDataList": [ + { + "itemId": 223, + "minCount": 1, + "maxCount": 100, + "minWeight": 0, + "maxWeight": 10000 + } + ] + } +] \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/drop/DropData.java b/src/main/java/emu/grasscutter/game/drop/DropData.java new file mode 100644 index 000000000..dd4d07b40 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/drop/DropData.java @@ -0,0 +1,57 @@ +package emu.grasscutter.game.drop; + +public class DropData { + private int minWeight; + private int maxWeight; + private int itemId; + private int minCount; + private int maxCount; + private boolean share = false; + + public int getItemId() { + return itemId; + } + + public void setItemId(int itemId) { + this.itemId = itemId; + } + + public int getMinCount() { + return minCount; + } + + + public int getMaxCount() { + return maxCount; + } + + + public int getMinWeight() { + return minWeight; + } + + public int getMaxWeight() { + return maxWeight; + } + + + public boolean isShare() { + return share; + } + + public void setIsShare(boolean share) { + this.share = share; + } + + public boolean isGive() { + return give; + } + + private boolean give = false; + + public boolean isExp() { + return exp; + } + + private boolean exp = false; +} diff --git a/src/main/java/emu/grasscutter/game/drop/DropInfo.java b/src/main/java/emu/grasscutter/game/drop/DropInfo.java new file mode 100644 index 000000000..d9ccfc4c5 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/drop/DropInfo.java @@ -0,0 +1,16 @@ +package emu.grasscutter.game.drop; + +import java.util.List; + +public class DropInfo { + public int getMonsterId() { + return monsterId; + } + + public List getDropDataList() { + return dropDataList; + } + + private int monsterId; + private List dropDataList; +} diff --git a/src/main/java/emu/grasscutter/game/drop/DropManager.java b/src/main/java/emu/grasscutter/game/drop/DropManager.java new file mode 100644 index 000000000..3bea18c71 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/drop/DropManager.java @@ -0,0 +1,99 @@ +package emu.grasscutter.game.drop; + +import com.google.gson.reflect.TypeToken; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.game.GameServer; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.Utils; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.io.FileReader; +import java.util.Collection; +import java.util.List; + +public class DropManager { + public GameServer getGameServer() { + return gameServer; + } + + private final GameServer gameServer; + + public Int2ObjectMap> getDropData() { + return dropData; + } + + private final Int2ObjectMap> dropData; + + public DropManager(GameServer gameServer) { + this.gameServer = gameServer; + this.dropData = new Int2ObjectOpenHashMap<>(); + this.load(); + } + + public synchronized void load() { + try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Drop.json")) { + getDropData().clear(); + List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType()); + if(banners.size() > 0) { + for (DropInfo di : banners) { + getDropData().put(di.getMonsterId(), di.getDropDataList()); + } + Grasscutter.getLogger().info("Drop data successfully loaded."); + } else { + Grasscutter.getLogger().error("Unable to load drop data. Drop data size is 0."); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void processDrop(DropData dd, EntityMonster em, Player gp) { + int target = Utils.randomRange(1, 10000); + if (target >= dd.getMinWeight() && target < dd.getMaxWeight()) { + ItemData itemData = GameData.getItemDataMap().get(dd.getItemId()); + int num = Utils.randomRange(dd.getMinCount(), dd.getMaxCount()); + if (!dd.isGive()) { + if (itemData.isEquip()) { + for (int i = 0; i < num; i++) { + float range = (5f + (.1f * num)); + Position pos = em.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityItem entity = new EntityItem(em.getScene(), gp, itemData, pos, num, dd.isShare()); + if (!dd.isShare()) + em.getScene().addEntityToSingleClient(gp, entity); + else + em.getScene().addEntity(entity); + } + } else { + Position pos = em.getPosition().clone().addY(3f); + EntityItem entity = new EntityItem(em.getScene(), gp, itemData, pos, num, dd.isShare()); + if (!dd.isShare()) + em.getScene().addEntityToSingleClient(gp, entity); + else + em.getScene().addEntity(entity); + } + } + } + } + + public void callDrop(EntityMonster em) { + int id = em.getMonsterData().getId(); + if (getDropData().containsKey(id)) { + for (DropData dd : getDropData().get(id)) { + if (dd.isShare()) + processDrop(dd, em, null); + else { + for (Player gp : em.getScene().getPlayers()) { + processDrop(dd, em, gp); + } + } + } + } + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index 5b1e05fc7..e23c1cb33 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -6,7 +6,6 @@ import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.world.Scene; -import emu.grasscutter.game.world.World; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; @@ -30,14 +29,30 @@ public class EntityItem extends EntityGadget { private final GameItem item; private final long guid; - + + private final boolean share; + public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) { super(scene); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.pos = new Position(pos); this.rot = new Position(); - this.guid = player.getNextGameGuid(); + this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid(); this.item = new GameItem(itemData, count); + this.share = true; + } + + // In official game, some drop items are shared to all players, and some other items are independent to all players + // For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different + // but if you broke regional mine, when someone picked up the drop then it disappeared + public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) { + super(scene); + this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); + this.pos = new Position(pos); + this.rot = new Position(); + this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid(); + this.item = new GameItem(itemData, count); + this.share = share; } @Override @@ -81,6 +96,10 @@ public class EntityItem extends EntityGadget { return null; } + public boolean isShare() { + return share; + } + @Override public SceneEntityInfo toProto() { EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 5970c3f13..2c18ce0ec 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -1,20 +1,16 @@ package emu.grasscutter.game.player; -import java.time.Instant; -import java.util.*; - import dev.morphia.annotations.*; import emu.grasscutter.GameConstants; import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; import emu.grasscutter.data.def.PlayerLevelData; import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.avatar.AvatarProfileData; -import emu.grasscutter.game.avatar.AvatarStorage; import emu.grasscutter.game.Account; import emu.grasscutter.game.CoopRequest; import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.avatar.AvatarProfileData; +import emu.grasscutter.game.avatar.AvatarStorage; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.friends.FriendsList; @@ -34,7 +30,6 @@ import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage; import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType; import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType; import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; -import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason; import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass; import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo; import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass; @@ -42,11 +37,12 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.*; -import emu.grasscutter.utils.Position; import emu.grasscutter.utils.DateHelper; +import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.time.Instant; import java.util.*; @Entity(value = "players", useDiscriminator = false) @@ -714,19 +710,30 @@ public class Player { return; } - // Delete - entity.getScene().removeEntity(entity); - // Handle if (entity instanceof EntityItem) { // Pick item EntityItem drop = (EntityItem) entity; + if (!drop.isShare()) // check drop owner to avoid someone picked up item in others' world + { + int dropOwner = (int)(drop.getGuid() >> 32); + if (dropOwner != getUid()) + return; + } + entity.getScene().removeEntity(entity); GameItem item = new GameItem(drop.getItemData(), drop.getCount()); // Add to inventory boolean success = getInventory().addItem(item, ActionReason.SubfieldDrop); if (success) { - this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); + + if (!drop.isShare()) // not shared drop + this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); + else + this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); } + } else { + // Delete directly + entity.getScene().removeEntity(entity); } return; diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index d9b01e0ef..543079153 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -1,28 +1,12 @@ package emu.grasscutter.game.world; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.danilopianini.util.SpatialIndex; - import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameDepot; -import emu.grasscutter.data.GameResource; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.WorldLevelData; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityClientGadget; -import emu.grasscutter.game.entity.EntityGadget; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.entity.*; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.TeamInfo; import emu.grasscutter.game.props.ClimateType; @@ -37,10 +21,12 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; -import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.danilopianini.util.SpatialIndex; + +import java.util.*; public class Scene { private final World world; @@ -228,6 +214,11 @@ public class Scene { this.addEntityDirectly(entity); this.broadcastPacket(new PacketSceneEntityAppearNotify(entity)); } + + public synchronized void addEntityToSingleClient(Player player, GameEntity entity) { + this.addEntityDirectly(entity); + player.sendPacket(new PacketSceneEntityAppearNotify(entity)); + } public synchronized void addEntities(Collection entities) { for (GameEntity entity : entities) { @@ -310,6 +301,12 @@ public class Scene { public void killEntity(GameEntity target, int attackerId) { // Packet this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); + + // Reward drop + if (target instanceof EntityMonster) { + Grasscutter.getGameServer().getDropManager().callDrop((EntityMonster) target); + } + this.removeEntity(target); // Death event diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 50889fd56..257ac9fef 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -1,15 +1,11 @@ package emu.grasscutter.server.game; -import java.net.InetSocketAddress; -import java.time.OffsetDateTime; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - import emu.grasscutter.GameConstants; import emu.grasscutter.Grasscutter; import emu.grasscutter.command.CommandMap; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; +import emu.grasscutter.game.drop.DropManager; import emu.grasscutter.game.dungeons.DungeonManager; import emu.grasscutter.game.gacha.GachaManager; import emu.grasscutter.game.managers.ChatManager; @@ -27,6 +23,11 @@ import emu.grasscutter.server.event.internal.ServerStartEvent; import emu.grasscutter.server.event.internal.ServerStopEvent; import emu.grasscutter.task.TaskMap; +import java.net.InetSocketAddress; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + public final class GameServer extends KcpServer { private final InetSocketAddress address; private final GameServerPacketHandler packetHandler; @@ -42,6 +43,7 @@ public final class GameServer extends KcpServer { private final DungeonManager dungeonManager; private final CommandMap commandMap; private final TaskMap taskMap; + private final DropManager dropManager; public GameServer(InetSocketAddress address) { super(address); @@ -60,6 +62,7 @@ public final class GameServer extends KcpServer { this.dungeonManager = new DungeonManager(this); this.commandMap = new CommandMap(true); this.taskMap = new TaskMap(true); + this.dropManager = new DropManager(this); // Schedule game loop. Timer gameLoop = new Timer(); @@ -109,6 +112,10 @@ public final class GameServer extends KcpServer { public MultiplayerManager getMultiplayerManager() { return multiplayerManager; } + + public DropManager getDropManager() { + return dropManager; + } public DungeonManager getDungeonManager() { return dungeonManager; From 57e408012b03875196124f9e42681bc233e4eed6 Mon Sep 17 00:00:00 2001 From: Kengxxiao <11478651+Kengxxiao@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:44:12 +0800 Subject: [PATCH 2/3] reset drop table in reload command --- .../java/emu/grasscutter/command/commands/ReloadCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java index 26dbb903b..6a3697acb 100644 --- a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java @@ -16,6 +16,7 @@ public final class ReloadCommand implements CommandHandler { CommandHandler.sendMessage(sender, "Reloading config."); Grasscutter.loadConfig(); Grasscutter.getGameServer().getGachaManager().load(); + Grasscutter.getGameServer().getDropManager().load(); Grasscutter.getDispatchServer().loadQueries(); CommandHandler.sendMessage(sender, "Reload complete."); } From 3c543e5c671dd8827d793e25426a64210634b828 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Wed, 27 Apr 2022 14:44:36 +0200 Subject: [PATCH 3/3] Update Drop.json data/ will appear in everyones files, so better change it to something more sane --- data/Drop.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/Drop.json b/data/Drop.json index dfa37e8c8..1fd5dd69e 100644 --- a/data/Drop.json +++ b/data/Drop.json @@ -1,14 +1,14 @@ [ { - "monsterId": 20010201, + "monsterId": 21010101, "dropDataList": [ { - "itemId": 223, + "itemId": 112005, "minCount": 1, - "maxCount": 100, + "maxCount": 3, "minWeight": 0, "maxWeight": 10000 } ] } -] \ No newline at end of file +]