package server; import client.inventory.Item; import client.inventory.ItemLoader; import client.inventory.MapleInventoryType; import constants.GameConstants; import database.DatabaseConnection; import java.io.PrintStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import tools.Pair; import tools.packet.MTSCSPacket; public class MTSStorage { private static final long serialVersionUID = 231541893513228L; private long lastUpdate = System.currentTimeMillis(); private final Map<Integer, MTSCart> idToCart; private final AtomicInteger packageId; private final Map<Integer, MTSItemInfo> buyNow; private static MTSStorage instance; private boolean end = false; private ReentrantReadWriteLock mutex; private ReentrantReadWriteLock cart_mutex; public MTSStorage() { this.idToCart = new LinkedHashMap(); this.buyNow = new LinkedHashMap(); this.packageId = new AtomicInteger(1); this.mutex = new ReentrantReadWriteLock(); this.cart_mutex = new ReentrantReadWriteLock(); } public static final MTSStorage getInstance() { return instance; } public static final void load() { if (instance == null) { instance = new MTSStorage(); instance.loadBuyNow(); } } public final boolean check(int packageid) { return getSingleItem(packageid) != null; } public final boolean checkCart(int packageid, int charID) { MTSItemInfo item = getSingleItem(packageid); return (item != null) && (item.getCharacterId() != charID); } public final MTSItemInfo getSingleItem(int packageid) { this.mutex.readLock().lock(); try { return (MTSItemInfo) this.buyNow.get(Integer.valueOf(packageid)); } finally { this.mutex.readLock().unlock(); } } public final void addToBuyNow(MTSCart cart, Item item, int price, int cid, String seller, long expiration) { this.mutex.writeLock().lock(); int id; try { id = this.packageId.incrementAndGet(); this.buyNow.put(Integer.valueOf(id), new MTSItemInfo(price, item, seller, id, cid, expiration)); } finally { this.mutex.writeLock().unlock(); } cart.addToNotYetSold(id); } public final boolean removeFromBuyNow(int id, int cidBought, boolean check) { Item item = null; this.mutex.writeLock().lock(); try { if (this.buyNow.containsKey(Integer.valueOf(id))) { MTSItemInfo r = (MTSItemInfo) this.buyNow.get(Integer.valueOf(id)); if ((!check) || (r.getCharacterId() == cidBought)) { item = r.getItem(); this.buyNow.remove(Integer.valueOf(id)); } } } finally { this.mutex.writeLock().unlock(); } if (item != null) { this.cart_mutex.readLock().lock(); try { for (Map.Entry c : this.idToCart.entrySet()) { ((MTSCart) c.getValue()).removeFromCart(id); ((MTSCart) c.getValue()).removeFromNotYetSold(id); if (((Integer) c.getKey()).intValue() == cidBought) { ((MTSCart) c.getValue()).addToInventory(item); } } } finally { this.cart_mutex.readLock().unlock(); } } return item != null; } private final void loadBuyNow() { int lastPackage = 0; Connection con = DatabaseConnection.getConnection(); try { PreparedStatement ps = con.prepareStatement("SELECT * FROM mts_items WHERE tab = 1"); ResultSet rs = ps.executeQuery(); int cId; while (rs.next()) { lastPackage = rs.getInt("id"); cId = rs.getInt("characterid"); if (!this.idToCart.containsKey(Integer.valueOf(cId))) { this.idToCart.put(Integer.valueOf(cId), new MTSCart(cId)); } Map<Long, Pair<Item, MapleInventoryType>> items = ItemLoader.MTS.loadItems(false, lastPackage); if ((items != null) && (items.size() > 0)) { for (Pair i : items.values()) { this.buyNow.put(Integer.valueOf(lastPackage), new MTSItemInfo(rs.getInt("price"), (Item) i.getLeft(), rs.getString("seller"), lastPackage, cId, rs.getLong("expiration"))); } } } rs.close(); ps.close(); } catch (Exception e) { e.printStackTrace(); } this.packageId.set(lastPackage); } public final void saveBuyNow(boolean isShutDown) { if (this.end) { return; } this.end = isShutDown; if (isShutDown) { System.out.println("Saving MTS..."); } final Map<Integer, ArrayList<Item>> expire = new HashMap<Integer, ArrayList<Item>>(); final List<Integer> toRemove = new ArrayList<Integer>(); final long now = System.currentTimeMillis(); final Map<Integer, ArrayList<Pair<Item, MapleInventoryType>>> items = new HashMap<Integer, ArrayList<Pair<Item, MapleInventoryType>>>(); final Connection con = DatabaseConnection.getConnection(); mutex.writeLock().lock(); //lock wL so rL will also be locked try { PreparedStatement ps = con.prepareStatement("DELETE FROM mts_items WHERE tab = 1"); ps.execute(); ps.close(); ps = con.prepareStatement("INSERT INTO mts_items VALUES (?, ?, ?, ?, ?, ?)"); for (MTSItemInfo m : buyNow.values()) { if (now > m.getEndingDate()) { if (!expire.containsKey(m.getCharacterId())) { expire.put(m.getCharacterId(), new ArrayList<Item>()); } expire.get(m.getCharacterId()).add(m.getItem()); toRemove.add(m.getId()); items.put(m.getId(), null); //destroy from the mtsitems. } else { ps.setInt(1, m.getId()); ps.setByte(2, (byte) 1); ps.setInt(3, m.getPrice()); ps.setInt(4, m.getCharacterId()); ps.setString(5, m.getSeller()); ps.setLong(6, m.getEndingDate()); ps.executeUpdate(); if (!items.containsKey(m.getId())) { items.put(m.getId(), new ArrayList<Pair<Item, MapleInventoryType>>()); } items.get(m.getId()).add(new Pair<Item, MapleInventoryType>(m.getItem(), GameConstants.getInventoryType(m.getItem().getItemId()))); } } for (int i : toRemove) { buyNow.remove(i); } ps.close(); } catch (SQLException e) { e.printStackTrace(); } finally { mutex.writeLock().unlock(); } if (isShutDown) { System.out.println("Saving MTS items..."); } try { for (Entry<Integer, ArrayList<Pair<Item, MapleInventoryType>>> ite : items.entrySet()) { ItemLoader.MTS.saveItems(ite.getValue(), ite.getKey()); } } catch (SQLException e) { e.printStackTrace(); } if (isShutDown) { System.out.println("Saving MTS carts..."); } cart_mutex.writeLock().lock(); try { for (Entry<Integer, MTSCart> c : idToCart.entrySet()) { for (int i : toRemove) { c.getValue().removeFromCart(i); c.getValue().removeFromNotYetSold(i); } if (expire.containsKey(c.getKey())) { for (Item item : expire.get(c.getKey())) { c.getValue().addToInventory(item); } } c.getValue().save(); } } catch (SQLException e) { e.printStackTrace(); } finally { cart_mutex.writeLock().unlock(); } lastUpdate = System.currentTimeMillis(); } public final void checkExpirations() { if (System.currentTimeMillis() - this.lastUpdate > 3600000L) { saveBuyNow(false); } } public final MTSCart getCart(int characterId) { this.cart_mutex.readLock().lock(); MTSCart ret; try { ret = (MTSCart) this.idToCart.get(Integer.valueOf(characterId)); } finally { this.cart_mutex.readLock().unlock(); } if (ret == null) { this.cart_mutex.writeLock().lock(); try { ret = new MTSCart(characterId); this.idToCart.put(Integer.valueOf(characterId), ret); } catch (SQLException e) { e.printStackTrace(); } finally { this.cart_mutex.writeLock().unlock(); } } return ret; } public final byte[] getCurrentMTS(MTSCart cart) { this.mutex.readLock().lock(); try { return MTSCSPacket.sendMTS(getMultiItems(cart.getCurrentView(), cart.getPage()), cart.getTab(), cart.getType(), cart.getPage(), cart.getCurrentView().size()); } finally { this.mutex.readLock().unlock(); } } public final byte[] getCurrentNotYetSold(MTSCart cart) { this.mutex.readLock().lock(); try { List nys = new ArrayList(); List nyss = new ArrayList(cart.getNotYetSold()); for (Iterator i$ = nyss.iterator(); i$.hasNext();) { int i = ((Integer) i$.next()).intValue(); MTSItemInfo r = (MTSItemInfo) this.buyNow.get(Integer.valueOf(i)); if (r == null) { cart.removeFromNotYetSold(i); } else { nys.add(r); } } return MTSCSPacket.getNotYetSoldInv(nys); } finally { this.mutex.readLock().unlock(); } } public final byte[] getCurrentTransfer(MTSCart cart, boolean changed) { return MTSCSPacket.getTransferInventory(cart.getInventory(), changed); } public final List<MTSItemInfo> getMultiItems(List<Integer> items, int pageNumber) { List ret = new ArrayList(); List cartt = new ArrayList(items); if (pageNumber > cartt.size() / 16 + (cartt.size() % 16 > 0 ? 1 : 0)) { pageNumber = 0; } int maxSize = Math.min(cartt.size(), pageNumber * 16 + 16); int minSize = Math.min(cartt.size(), pageNumber * 16); for (int i = minSize; (i < maxSize) && (cartt.size() > i); i++) { MTSItemInfo r = (MTSItemInfo) this.buyNow.get(cartt.get(i)); if (r == null) { items.remove(i); cartt.remove(i); } else { ret.add(r); } } return ret; } public final List<Integer> getBuyNow(int type) { this.mutex.readLock().lock(); try { if (type == 0) { return new ArrayList(this.buyNow.keySet()); } Object ret = new ArrayList(this.buyNow.values()); List rett = new ArrayList(); for (int i = 0; i < ((List) ret).size(); i++) { MTSItemInfo r = (MTSItemInfo) ((List) ret).get(i); if ((r != null) && (GameConstants.getInventoryType(r.getItem().getItemId()).getType() == type)) { rett.add(Integer.valueOf(r.getId())); } } return rett; } finally { this.mutex.readLock().unlock(); } } public final List<Integer> getSearch(boolean item, String name, int type, int tab) { this.mutex.readLock().lock(); try { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); if ((tab != 1) || (name.length() <= 0)) { return new ArrayList(); } name = name.toLowerCase(); Object ret = new ArrayList(this.buyNow.values()); List rett = new ArrayList(); for (int i = 0; i < ((List) ret).size(); i++) { MTSItemInfo r = (MTSItemInfo) ((List) ret).get(i); if ((r != null) && ((type == 0) || (GameConstants.getInventoryType(r.getItem().getItemId()).getType() == type))) { String thename = item ? ii.getName(r.getItem().getItemId()) : r.getSeller(); if ((thename != null) && (thename.toLowerCase().contains(name))) { rett.add(Integer.valueOf(r.getId())); } } } return rett; } finally { this.mutex.readLock().unlock(); } } private final List<MTSItemInfo> getCartItems(MTSCart cart) { return getMultiItems(cart.getCart(), cart.getPage()); } public static class MTSItemInfo { private int price; private Item item; private String seller; private int id; private int cid; private long date; public MTSItemInfo(int price, Item item, String seller, int id, int cid, long date) { this.item = item; this.price = price; this.seller = seller; this.id = id; this.cid = cid; this.date = date; } public Item getItem() { return this.item; } public int getPrice() { return this.price; } public int getRealPrice() { return this.price + getTaxes(); } public int getTaxes() { return 0 + this.price * 5 / 100; } public int getId() { return this.id; } public int getCharacterId() { return this.cid; } public long getEndingDate() { return this.date; } public String getSeller() { return this.seller; } } }