Instantiate shops in ShopFactory

This commit is contained in:
P0nk
2023-03-29 21:59:00 +02:00
parent 2139147dcd
commit fe9dd75a23
5 changed files with 70 additions and 118 deletions

View File

@@ -70,14 +70,16 @@ public class ItemId {
// Throwing star // Throwing star
public static final int SUBI_THROWING_STARS = 2070000; public static final int SUBI_THROWING_STARS = 2070000;
public static final int HWABI_THROWING_STARS = 2070007; public static final int HWABI_THROWING_STARS = 2070007;
private static final int ORANGE_THROWING_STARS = 2070013;
public static final int BALANCED_FURY = 2070018; public static final int BALANCED_FURY = 2070018;
public static final int CRYSTAL_ILBI_THROWING_STARS = 2070016; public static final int CRYSTAL_ILBI_THROWING_STARS = 2070016;
private static final int THROWING_STAR_MIN = SUBI_THROWING_STARS;
private static final int THROWING_STAR_MAX = 2070016;
public static final int DEVIL_RAIN_THROWING_STAR = 2070014;
public static int[] allThrowingStarIds() { public static IntStream allThrowingStarIds() {
return IntStream.range(THROWING_STAR_MIN, THROWING_STAR_MAX + 1).toArray(); // excluding 2070015 - A Beginner Thief's Throwing Stars
return IntStream.concat(
IntStream.range(SUBI_THROWING_STARS, ORANGE_THROWING_STARS + 1),
IntStream.of(CRYSTAL_ILBI_THROWING_STARS, BALANCED_FURY)
);
} }
// Bullet // Bullet
@@ -87,8 +89,12 @@ public class ItemId {
public static final int BLAZE_CAPSULE = 2331000; public static final int BLAZE_CAPSULE = 2331000;
public static final int GLAZE_CAPSULE = 2332000; public static final int GLAZE_CAPSULE = 2332000;
public static int[] allBulletIds() { public static IntStream allBulletIds() {
return IntStream.range(BULLET_MIN, BULLET_MAX + 1).toArray(); return IntStream.range(BULLET_MIN, BULLET_MAX + 1);
}
public static IntStream allBulletCapsuleIds() {
return IntStream.of(BLAZE_CAPSULE, GLAZE_CAPSULE);
} }
// Starter // Starter
@@ -325,7 +331,7 @@ public class ItemId {
public static boolean isWeddingRing(int itemId) { public static boolean isWeddingRing(int itemId) {
return itemId == WEDDING_RING_MOONSTONE || itemId == WEDDING_RING_STAR || return itemId == WEDDING_RING_MOONSTONE || itemId == WEDDING_RING_STAR ||
itemId == WEDDING_RING_GOLDEN || itemId == WEDDING_RING_SILVER; itemId == WEDDING_RING_GOLDEN || itemId == WEDDING_RING_SILVER;
} }
// Priority buff // Priority buff

View File

@@ -49,6 +49,7 @@ import database.maker.MakerDao;
import database.maker.MakerInfoProvider; import database.maker.MakerInfoProvider;
import database.migration.FlywayRunner; import database.migration.FlywayRunner;
import database.note.NoteDao; import database.note.NoteDao;
import database.shop.ShopDao;
import net.ChannelDependencies; import net.ChannelDependencies;
import net.PacketProcessor; import net.PacketProcessor;
import net.netty.LoginServer; import net.netty.LoginServer;
@@ -978,7 +979,7 @@ public class Server {
MakerProcessor makerProcessor = new MakerProcessor(new MakerInfoProvider(new MakerDao(connection))); MakerProcessor makerProcessor = new MakerProcessor(new MakerInfoProvider(new MakerDao(connection)));
FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService); FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService);
DropProvider dropProvider = new DropProvider(new DropDao(connection)); DropProvider dropProvider = new DropProvider(new DropDao(connection));
ShopFactory shopFactory = new ShopFactory(); ShopFactory shopFactory = new ShopFactory(new ShopDao(connection));
CommandContext commandContext = new CommandContext(dropProvider, shopFactory); CommandContext commandContext = new CommandContext(dropProvider, shopFactory);
CommandsExecutor commandsExecutor = new CommandsExecutor(commandContext); CommandsExecutor commandsExecutor = new CommandsExecutor(commandContext);
ChannelDependencies channelDependencies = new ChannelDependencies(noteService, fredrickProcessor, ChannelDependencies channelDependencies = new ChannelDependencies(noteService, fredrickProcessor,

View File

@@ -106,11 +106,11 @@ public final class NPCTalkHandler extends AbstractPacketHandler {
} }
private boolean hasShop(NPC npc) { private boolean hasShop(NPC npc) {
return shopFactory.getShopForNPC(npc.getId()) != null; return shopFactory.getShop(npc.getId()) != null;
} }
private void sendShop(NPC npc, Client c) { private void sendShop(NPC npc, Client c) {
Shop shop = shopFactory.getShopForNPC(npc.getId()); Shop shop = shopFactory.getShop(npc.getId());
if (shop == null) { if (shop == null) {
return; return;
} }

View File

@@ -30,25 +30,16 @@ import constants.id.ItemId;
import constants.inventory.ItemConstants; import constants.inventory.ItemConstants;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import tools.DatabaseConnection;
import tools.PacketCreator; import tools.PacketCreator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* @author Matze * @author Matze
*/ */
public class Shop { public class Shop {
private static final Logger log = LoggerFactory.getLogger(Shop.class); private static final Logger log = LoggerFactory.getLogger(Shop.class);
private static final Set<Integer> rechargeableItems = new LinkedHashSet<>();
private static final short MAX_QUANTITY_PER_PURCHASE = 1000; // Should really use max stack size for the given item
private final int id; private final int id;
private final int npcId; private final int npcId;
@@ -56,27 +47,10 @@ public class Shop {
private final int tokenvalue = 1000000000; private final int tokenvalue = 1000000000;
private final int token = ItemId.GOLDEN_MAPLE_LEAF; private final int token = ItemId.GOLDEN_MAPLE_LEAF;
static { public Shop(int id, int npcId, List<ShopItem> items) {
for (int throwingStarId : ItemId.allThrowingStarIds()) {
rechargeableItems.add(throwingStarId);
}
rechargeableItems.add(ItemId.BLAZE_CAPSULE);
rechargeableItems.add(ItemId.GLAZE_CAPSULE);
rechargeableItems.add(ItemId.BALANCED_FURY);
rechargeableItems.remove(ItemId.DEVIL_RAIN_THROWING_STAR); // doesn't exist
for (int bulletId : ItemId.allBulletIds()) {
rechargeableItems.add(bulletId);
}
}
private Shop(int id, int npcId) {
this.id = id; this.id = id;
this.npcId = npcId; this.npcId = npcId;
items = new ArrayList<>(); this.items = new ArrayList<>(items);
}
private void addItem(ShopItem item) {
items.add(item);
} }
public void sendShop(Client c) { public void sendShop(Client c) {
@@ -242,57 +216,6 @@ public class Shop {
return items.get(slot); return items.get(slot);
} }
public static Shop createFromDB(int id, boolean isShopId) {
Shop ret = null;
int shopId;
try (Connection con = DatabaseConnection.getConnection()) {
final String query;
if (isShopId) {
query = "SELECT * FROM shops WHERE shopid = ?";
} else {
query = "SELECT * FROM shops WHERE npcid = ?";
}
try (PreparedStatement ps = con.prepareStatement(query)) {
ps.setInt(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
shopId = rs.getInt("shopid");
ret = new Shop(shopId, rs.getInt("npcid"));
} else {
return null;
}
}
}
try (PreparedStatement ps = con.prepareStatement("SELECT itemid, price, pitch FROM shopitems WHERE shopid = ? ORDER BY position DESC")) {
ps.setInt(1, shopId);
try (ResultSet rs = ps.executeQuery()) {
List<Integer> recharges = new ArrayList<>(rechargeableItems);
while (rs.next()) {
if (ItemConstants.isRechargeable(rs.getInt("itemid"))) {
ShopItem starItem = new ShopItem((short) 1, rs.getInt("itemid"), rs.getInt("price"), rs.getInt("pitch"));
ret.addItem(starItem);
if (rechargeableItems.contains(starItem.getItemId())) {
recharges.remove(Integer.valueOf(starItem.getItemId()));
}
} else {
ret.addItem(new ShopItem(MAX_QUANTITY_PER_PURCHASE, rs.getInt("itemid"), rs.getInt("price"), rs.getInt("pitch")));
}
}
for (Integer recharge : recharges) {
ret.addItem(new ShopItem((short) 0, recharge, 0, 0));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return ret;
}
public int getNpcId() { public int getNpcId() {
return npcId; return npcId;
} }

View File

@@ -1,8 +1,8 @@
/* /*
This file is part of the OdinMS Maple Story Server This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc> Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de> Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de> Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as it under the terms of the GNU Affero General Public License as
@@ -21,45 +21,67 @@
*/ */
package server; package server;
import java.util.HashMap; import com.github.benmanes.caffeine.cache.Cache;
import java.util.Map; import com.github.benmanes.caffeine.cache.Caffeine;
import constants.id.ItemId;
import constants.inventory.ItemConstants;
import database.shop.ShopDao;
import database.shop.ShopItem;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/** /**
* @author Matze * @author Matze
*/ */
public class ShopFactory { public class ShopFactory {
private final Map<Integer, Shop> shops = new HashMap<>(); private static final short MAX_QUANTITY_PER_PURCHASE = 1000; // Should really use max stack size for the given item
private final Map<Integer, Shop> npcShops = new HashMap<>(); private static final Set<Integer> rechargeableItemIds = rechargeableItemIds();
private final Cache<Integer, Shop> shops = Caffeine.newBuilder().build();
private final ShopDao shopDao;
private Shop loadShop(int id, boolean isShopId) { public ShopFactory(ShopDao shopDao) {
Shop ret = Shop.createFromDB(id, isShopId); this.shopDao = shopDao;
if (ret != null) { }
shops.put(ret.getId(), ret);
npcShops.put(ret.getNpcId(), ret); private static Set<Integer> rechargeableItemIds() {
} else if (isShopId) { IntStream stars = ItemId.allThrowingStarIds();
shops.put(id, null); IntStream ammo = IntStream.concat(ItemId.allBulletIds(), ItemId.allBulletCapsuleIds());
} else { return IntStream.concat(stars, ammo)
npcShops.put(id, null); .boxed()
} .collect(Collectors.toUnmodifiableSet());
return ret;
} }
public Shop getShop(int shopId) { public Shop getShop(int shopId) {
if (shops.containsKey(shopId)) { return shops.get(shopId, this::loadShop);
return shops.get(shopId);
}
return loadShop(shopId, true);
} }
public Shop getShopForNPC(int npcId) { private Shop loadShop(int shopId) {
if (npcShops.containsKey(npcId)) { Optional<database.shop.Shop> dbShop = shopDao.getShop(shopId);
return npcShops.get(npcId); if (dbShop.isEmpty()) {
throw new IllegalArgumentException("Shop with id %d does not exist".formatted(shopId));
} }
return loadShop(npcId, false); List<ShopItem> items = shopDao.getShopItems(shopId);
return new Shop(dbShop.get().id(), dbShop.get().npcId(), fromDbShopItems(items));
}
private List<server.ShopItem> fromDbShopItems(List<ShopItem> dbItems) {
Stream<server.ShopItem> purchaseableItems = dbItems.stream()
.map(dbItem -> {
short buyable = ItemConstants.isRechargeable(dbItem.itemId()) ? (short) 1 : MAX_QUANTITY_PER_PURCHASE;
int pitch = dbItem.pitch() == null ? 0 : dbItem.pitch();
return new server.ShopItem(buyable, dbItem.itemId(), dbItem.price(), pitch);
});
Stream<server.ShopItem> rechargeableItems = rechargeableItemIds.stream()
.map(rechItem -> new server.ShopItem((short) 0, rechItem, 0, 0));
return Stream.concat(purchaseableItems, rechargeableItems).toList();
} }
public void reloadShops() { public void reloadShops() {
shops.clear(); shops.invalidateAll();
npcShops.clear();
} }
} }