Commit 0559cc4e authored by Kengxxiao's avatar Kengxxiao Committed by Melledy
Browse files

official shop support

parent 027bd28a
...@@ -72,6 +72,7 @@ public final class Config { ...@@ -72,6 +72,7 @@ public final class Config {
public boolean WatchGacha = false; public boolean WatchGacha = false;
public int[] WelcomeEmotes = {2007, 1002, 4010}; public int[] WelcomeEmotes = {2007, 1002, 4010};
public String WelcomeMotd = "Welcome to Grasscutter emu"; public String WelcomeMotd = "Welcome to Grasscutter emu";
public boolean EnableOfficialShop = true;
public GameRates Game = new GameRates(); public GameRates Game = new GameRates();
......
...@@ -61,9 +61,12 @@ public class GameData { ...@@ -61,9 +61,12 @@ public class GameData {
private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
// Cache // Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>(); private static Map<Integer, List<Integer>> fetters = new HashMap<>();
private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
public static Int2ObjectMap<?> getMapByResourceDef(Class<?> resourceDefinition) { public static Int2ObjectMap<?> getMapByResourceDef(Class<?> resourceDefinition) {
Int2ObjectMap<?> map = null; Int2ObjectMap<?> map = null;
...@@ -265,4 +268,16 @@ public class GameData { ...@@ -265,4 +268,16 @@ public class GameData {
public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() { public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() {
return worldLevelDataMap; return worldLevelDataMap;
} }
public static Map<Integer, List<ShopGoodsData>> 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;
}
} }
...@@ -3,6 +3,12 @@ package emu.grasscutter.data.common; ...@@ -3,6 +3,12 @@ package emu.grasscutter.data.common;
public class ItemParamData { public class ItemParamData {
private int Id; private int Id;
private int Count; private int Count;
public ItemParamData() {}
public ItemParamData(int id, int count) {
this.Id = id;
this.Count = count;
}
public int getId() { public int getId() {
return Id; return Id;
......
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<ItemParamData> 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<ItemParamData> getCostItems() {
return CostItems;
}
public int getMinPlayerLevel() {
return MinPlayerLevel;
}
public int getMaxPlayerLevel() {
return MaxPlayerLevel;
}
public int getBuyLimit() {
return BuyLimit;
}
public int getSubTabId() {
return SubTabId;
}
}
package emu.grasscutter.game.shop; package emu.grasscutter.game.shop;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.ShopGoodsData;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -23,6 +24,20 @@ public class ShopInfo { ...@@ -23,6 +24,20 @@ public class ShopInfo {
private int disableType = 0; private int disableType = 0;
private int secondarySheetId = 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() { public int getHcoin() {
return hcoin; return hcoin;
} }
......
...@@ -2,12 +2,19 @@ package emu.grasscutter.game.shop; ...@@ -2,12 +2,19 @@ package emu.grasscutter.game.shop;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; 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 emu.grasscutter.server.game.GameServer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileReader; import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.List; import java.util.List;
public class ShopManager { public class ShopManager {
...@@ -31,12 +38,39 @@ public class ShopManager { ...@@ -31,12 +38,39 @@ public class ShopManager {
List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType()); List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
if(banners.size() > 0) { if(banners.size() > 0) {
for (ShopTable shopTable : banners) { for (ShopTable shopTable : banners) {
for (ShopInfo cost : shopTable.getItems()) {
if (cost.getCostItemList() != null) {
Iterator<ItemParamData> 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()); getShopData().put(shopTable.getShopId(), shopTable.getItems());
} }
Grasscutter.getLogger().info("Shop data successfully loaded."); Grasscutter.getLogger().info("Shop data successfully loaded.");
} else { } else {
Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0."); 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) { } catch (Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
......
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.shop.ShopInfo;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
...@@ -16,6 +18,7 @@ import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify; ...@@ -16,6 +18,7 @@ import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@Opcodes(PacketOpcodes.BuyGoodsReq) @Opcodes(PacketOpcodes.BuyGoodsReq)
...@@ -24,8 +27,18 @@ public class HandlerBuyGoodsReq extends PacketHandler { ...@@ -24,8 +27,18 @@ public class HandlerBuyGoodsReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
BuyGoodsReqOuterClass.BuyGoodsReq buyGoodsReq = BuyGoodsReqOuterClass.BuyGoodsReq.parseFrom(payload); BuyGoodsReqOuterClass.BuyGoodsReq buyGoodsReq = BuyGoodsReqOuterClass.BuyGoodsReq.parseFrom(payload);
List<ShopInfo> configShop = session.getServer().getShopManager().getShopData().get(buyGoodsReq.getShopType());
if (configShop == null)
return;
// Don't trust your users' input
List<Integer> targetShopGoodsId = buyGoodsReq.getGoodsListList().stream().map(ShopGoodsOuterClass.ShopGoods::getGoodsId).toList();
for (int goodsId : targetShopGoodsId) {
Optional<ShopInfo> 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()) { if (sg.getScoin() > 0 && session.getPlayer().getMora() < buyGoodsReq.getBoughtNum() * sg.getScoin()) {
return; return;
} }
...@@ -37,11 +50,13 @@ public class HandlerBuyGoodsReq extends PacketHandler { ...@@ -37,11 +50,13 @@ public class HandlerBuyGoodsReq extends PacketHandler {
} }
HashMap<GameItem, Integer> itemsCache = new HashMap<>(); HashMap<GameItem, Integer> itemsCache = new HashMap<>();
for (ItemParamOuterClass.ItemParam p : sg.getCostItemListList()) { if (sg.getCostItemList() != null) {
Optional<GameItem> invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getItemId()).findFirst(); for (ItemParamData p : sg.getCostItemList()) {
if (invItem.isEmpty() || invItem.get().getCount() < p.getCount()) Optional<GameItem> invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getId()).findFirst();
return; if (invItem.isEmpty() || invItem.get().getCount() < p.getCount())
itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum()); return;
itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum());
}
} }
session.getPlayer().setMora(session.getPlayer().getMora() - buyGoodsReq.getBoughtNum() * sg.getScoin()); session.getPlayer().setMora(session.getPlayer().getMora() - buyGoodsReq.getBoughtNum() * sg.getScoin());
...@@ -56,10 +71,10 @@ public class HandlerBuyGoodsReq extends PacketHandler { ...@@ -56,10 +71,10 @@ public class HandlerBuyGoodsReq extends PacketHandler {
} }
session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBoughtNum()); 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()); 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.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(); session.getPlayer().save();
......
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.Grasscutter; 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.player.Player;
import emu.grasscutter.game.shop.ShopInfo; import emu.grasscutter.game.shop.ShopInfo;
import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.game.shop.ShopManager;
...@@ -43,13 +45,15 @@ public class PacketGetShopRsp extends BasePacket { ...@@ -43,13 +45,15 @@ public class PacketGetShopRsp extends BasePacket {
.setNextRefreshTime(info.getNextRefreshTime()) .setNextRefreshTime(info.getNextRefreshTime())
.setMinLevel(info.getMinLevel()) .setMinLevel(info.getMinLevel())
.setMaxLevel(info.getMaxLevel()) .setMaxLevel(info.getMaxLevel())
.addAllPreGoodsIdList(info.getPreGoodsIdList())
.setMcoin(info.getMcoin()) .setMcoin(info.getMcoin())
.setDisableType(info.getDisableType()) .setDisableType(info.getDisableType())
.setSecondarySheetId(info.getSecondarySheetId()); .setSecondarySheetId(info.getSecondarySheetId());
if (info.getCostItemList() != null) { if (info.getCostItemList() != null) {
goods.addAllCostItemList(info.getCostItemList().stream().map(x -> ItemParamOuterClass.ItemParam.newBuilder().setItemId(x.getId()).setCount(x.getCount()).build()).collect(Collectors.toList())); 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()); goodsList.add(goods.build());
} }
shop.addAllGoodsList(goodsList); shop.addAllGoodsList(goodsList);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment