From 0559cc4e0b33b86f7812f88afc818ea4120bdd14 Mon Sep 17 00:00:00 2001 From: Kengxxiao <11478651+Kengxxiao@users.noreply.github.com> Date: Fri, 29 Apr 2022 00:46:19 +0800 Subject: [PATCH] official shop support --- src/main/java/emu/grasscutter/Config.java | 1 + .../java/emu/grasscutter/data/GameData.java | 15 ++++ .../data/common/ItemParamData.java | 6 ++ .../grasscutter/data/def/ShopGoodsData.java | 80 +++++++++++++++++++ .../emu/grasscutter/game/shop/ShopInfo.java | 15 ++++ .../grasscutter/game/shop/ShopManager.java | 34 ++++++++ .../packet/recv/HandlerBuyGoodsReq.java | 31 +++++-- .../server/packet/send/PacketGetShopRsp.java | 6 +- 8 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/def/ShopGoodsData.java diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 3e6e16d20..c50a21f58 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -72,6 +72,7 @@ public final class Config { public boolean WatchGacha = false; public int[] WelcomeEmotes = {2007, 1002, 4010}; public String WelcomeMotd = "Welcome to Grasscutter emu"; + public boolean EnableOfficialShop = true; public GameRates Game = new GameRates(); diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 4c7363054..2d15248ad 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -61,9 +61,12 @@ public class GameData { private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap rewardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap worldLevelDataMap = new Int2ObjectOpenHashMap<>(); + + private static final Int2ObjectMap shopGoodsDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); + private static Map> shopGoods = new HashMap<>(); public static Int2ObjectMap getMapByResourceDef(Class resourceDefinition) { Int2ObjectMap map = null; @@ -265,4 +268,16 @@ public class GameData { public static Int2ObjectMap getWorldLevelDataMap() { return worldLevelDataMap; } + + public static Map> getShopGoodsDataEntries() { + if (shopGoods.isEmpty()) { + shopGoodsDataMap.forEach((k, v) -> { + if (!shopGoods.containsKey(v.getShopType())) + shopGoods.put(v.getShopType(), new ArrayList<>()); + shopGoods.get(v.getShopType()).add(v); + }); + } + + return shopGoods; + } } diff --git a/src/main/java/emu/grasscutter/data/common/ItemParamData.java b/src/main/java/emu/grasscutter/data/common/ItemParamData.java index 9ec70f00b..a962c4618 100644 --- a/src/main/java/emu/grasscutter/data/common/ItemParamData.java +++ b/src/main/java/emu/grasscutter/data/common/ItemParamData.java @@ -3,6 +3,12 @@ package emu.grasscutter.data.common; public class ItemParamData { private int Id; private int Count; + + public ItemParamData() {} + public ItemParamData(int id, int count) { + this.Id = id; + this.Count = count; + } public int getId() { return Id; diff --git a/src/main/java/emu/grasscutter/data/def/ShopGoodsData.java b/src/main/java/emu/grasscutter/data/def/ShopGoodsData.java new file mode 100644 index 000000000..3ba2021f8 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/ShopGoodsData.java @@ -0,0 +1,80 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.common.ItemParamData; + +import java.util.List; + +@ResourceType(name = "ShopGoodsExcelConfigData.json") +public class ShopGoodsData extends GameResource { + private int GoodsId; + private int ShopType; + private int ItemId; + + private int ItemCount; + + private int CostScoin; + private int CostHcoin; + private int CostMcoin; + + private List CostItems; + private int MinPlayerLevel; + private int MaxPlayerLevel; + + private int BuyLimit; + private int SubTabId; + + @Override + public int getId() { + return getGoodsId(); + } + + public int getGoodsId() { + return GoodsId; + } + + public int getShopType() { + return ShopType; + } + + public int getItemId() { + return ItemId; + } + + public int getItemCount() { + return ItemCount; + } + + public int getCostScoin() { + return CostScoin; + } + + public int getCostHcoin() { + return CostHcoin; + } + + public int getCostMcoin() { + return CostMcoin; + } + + public List getCostItems() { + return CostItems; + } + + public int getMinPlayerLevel() { + return MinPlayerLevel; + } + + public int getMaxPlayerLevel() { + return MaxPlayerLevel; + } + + public int getBuyLimit() { + return BuyLimit; + } + + public int getSubTabId() { + return SubTabId; + } +} diff --git a/src/main/java/emu/grasscutter/game/shop/ShopInfo.java b/src/main/java/emu/grasscutter/game/shop/ShopInfo.java index b0e3b819b..2b10c2005 100644 --- a/src/main/java/emu/grasscutter/game/shop/ShopInfo.java +++ b/src/main/java/emu/grasscutter/game/shop/ShopInfo.java @@ -1,6 +1,7 @@ package emu.grasscutter.game.shop; import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.def.ShopGoodsData; import java.util.ArrayList; import java.util.List; @@ -23,6 +24,20 @@ public class ShopInfo { private int disableType = 0; private int secondarySheetId = 0; + public ShopInfo(ShopGoodsData sgd) { + this.goodsId = sgd.getGoodsId(); + this.goodsItem = new ItemParamData(sgd.getItemId(), sgd.getItemCount()); + this.scoin = sgd.getCostScoin(); + this.mcoin = sgd.getCostMcoin(); + this.hcoin = sgd.getCostHcoin(); + this.buyLimit = sgd.getBuyLimit(); + + this.minLevel = sgd.getMinPlayerLevel(); + this.maxLevel = sgd.getMaxPlayerLevel(); + this.costItemList = sgd.getCostItems().stream().filter(x -> x.getId() != 0).map(x -> new ItemParamData(x.getId(), x.getCount())).toList(); + this.secondarySheetId = sgd.getSubTabId(); + } + public int getHcoin() { return hcoin; } diff --git a/src/main/java/emu/grasscutter/game/shop/ShopManager.java b/src/main/java/emu/grasscutter/game/shop/ShopManager.java index 63436e517..4154b79bd 100644 --- a/src/main/java/emu/grasscutter/game/shop/ShopManager.java +++ b/src/main/java/emu/grasscutter/game/shop/ShopManager.java @@ -2,12 +2,19 @@ package emu.grasscutter.game.shop; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.def.ShopGoodsData; +import emu.grasscutter.net.proto.ItemParamOuterClass; +import emu.grasscutter.net.proto.ShopGoodsOuterClass; import emu.grasscutter.server.game.GameServer; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.io.FileReader; +import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; public class ShopManager { @@ -31,12 +38,39 @@ public class ShopManager { List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType()); if(banners.size() > 0) { for (ShopTable shopTable : banners) { + for (ShopInfo cost : shopTable.getItems()) { + if (cost.getCostItemList() != null) { + Iterator iterator = cost.getCostItemList().iterator(); + while (iterator.hasNext()) { + ItemParamData ipd = iterator.next(); + if (ipd.getId() == 201) { + cost.setHcoin(cost.getHcoin() + ipd.getCount()); + iterator.remove(); + } + if (ipd.getId() == 203) { + cost.setMcoin(cost.getMcoin() + ipd.getCount()); + iterator.remove(); + } + } + } + } getShopData().put(shopTable.getShopId(), shopTable.getItems()); } Grasscutter.getLogger().info("Shop data successfully loaded."); } else { Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0."); } + + if (Grasscutter.getConfig().getGameServerOptions().EnableOfficialShop) { + GameData.getShopGoodsDataEntries().forEach((k, v) -> { + if (!getShopData().containsKey(k.intValue())) + getShopData().put(k.intValue(), new ArrayList<>()); + for (ShopGoodsData sgd : v) { + var shopInfo = new ShopInfo(sgd); + getShopData().get(k.intValue()).add(shopInfo); + } + }); + } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java index 1df08de43..79e0f0fc3 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java @@ -1,9 +1,11 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.data.GameData; +import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.PlayerProperty; +import emu.grasscutter.game.shop.ShopInfo; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -16,6 +18,7 @@ import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Optional; @Opcodes(PacketOpcodes.BuyGoodsReq) @@ -24,8 +27,18 @@ public class HandlerBuyGoodsReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { BuyGoodsReqOuterClass.BuyGoodsReq buyGoodsReq = BuyGoodsReqOuterClass.BuyGoodsReq.parseFrom(payload); + List configShop = session.getServer().getShopManager().getShopData().get(buyGoodsReq.getShopType()); + if (configShop == null) + return; + + // Don't trust your users' input + List targetShopGoodsId = buyGoodsReq.getGoodsListList().stream().map(ShopGoodsOuterClass.ShopGoods::getGoodsId).toList(); + for (int goodsId : targetShopGoodsId) { + Optional sg2 = configShop.stream().filter(x -> x.getGoodsId() == goodsId).findFirst(); + if (sg2.isEmpty()) + continue; + ShopInfo sg = sg2.get(); - for (ShopGoodsOuterClass.ShopGoods sg : buyGoodsReq.getGoodsListList()) { if (sg.getScoin() > 0 && session.getPlayer().getMora() < buyGoodsReq.getBoughtNum() * sg.getScoin()) { return; } @@ -37,11 +50,13 @@ public class HandlerBuyGoodsReq extends PacketHandler { } HashMap itemsCache = new HashMap<>(); - for (ItemParamOuterClass.ItemParam p : sg.getCostItemListList()) { - Optional invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getItemId()).findFirst(); - if (invItem.isEmpty() || invItem.get().getCount() < p.getCount()) - return; - itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum()); + if (sg.getCostItemList() != null) { + for (ItemParamData p : sg.getCostItemList()) { + Optional invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getId()).findFirst(); + if (invItem.isEmpty() || invItem.get().getCount() < p.getCount()) + return; + itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum()); + } } session.getPlayer().setMora(session.getPlayer().getMora() - buyGoodsReq.getBoughtNum() * sg.getScoin()); @@ -56,10 +71,10 @@ public class HandlerBuyGoodsReq extends PacketHandler { } session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBoughtNum()); - GameItem item = new GameItem(GameData.getItemDataMap().get(sg.getGoodsItem().getItemId())); + GameItem item = new GameItem(GameData.getItemDataMap().get(sg.getGoodsItem().getId())); item.setCount(buyGoodsReq.getBoughtNum() * sg.getGoodsItem().getCount()); session.getPlayer().getInventory().addItem(item, ActionReason.Shop, true); // fix: not notify when got virtual item from shop - session.send(new PacketBuyGoodsRsp(buyGoodsReq.getShopType(), session.getPlayer().getGoodsLimitNum(sg.getGoodsId()), sg)); + session.send(new PacketBuyGoodsRsp(buyGoodsReq.getShopType(), session.getPlayer().getGoodsLimitNum(sg.getGoodsId()), buyGoodsReq.getGoodsListList().stream().filter(x -> x.getGoodsId() == goodsId).findFirst().get())); } session.getPlayer().save(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java index 4f57c72e6..10e12d666 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java @@ -1,6 +1,8 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.ShopGoodsData; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.shop.ShopInfo; import emu.grasscutter.game.shop.ShopManager; @@ -43,13 +45,15 @@ public class PacketGetShopRsp extends BasePacket { .setNextRefreshTime(info.getNextRefreshTime()) .setMinLevel(info.getMinLevel()) .setMaxLevel(info.getMaxLevel()) - .addAllPreGoodsIdList(info.getPreGoodsIdList()) .setMcoin(info.getMcoin()) .setDisableType(info.getDisableType()) .setSecondarySheetId(info.getSecondarySheetId()); if (info.getCostItemList() != null) { goods.addAllCostItemList(info.getCostItemList().stream().map(x -> ItemParamOuterClass.ItemParam.newBuilder().setItemId(x.getId()).setCount(x.getCount()).build()).collect(Collectors.toList())); } + if (info.getPreGoodsIdList() != null) { + goods.addAllPreGoodsIdList(info.getPreGoodsIdList()); + } goodsList.add(goods.build()); } shop.addAllGoodsList(goodsList);