package javastory.channel.server; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import javastory.channel.ChannelClient; import javastory.db.Database; import javastory.db.DatabaseException; import javastory.game.Equip; import javastory.game.GameConstants; import javastory.game.InventoryType; import javastory.game.Item; import javastory.game.ItemType; import javastory.tools.packets.ChannelPackets; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class Storage implements Serializable { public static int DEFAULT_STORAGE_CAPACITY = 4; private static final long serialVersionUID = 9179541993413738569L; private final int id; private final List<Item> items; private int meso; private byte slots; private boolean hasChanged = false; private final Map<InventoryType, List<Item>> typeItems = Maps.newEnumMap(InventoryType.class); private Storage(final int id, final byte slots, final int meso) { this.id = id; this.slots = slots; this.items = Lists.newLinkedList(); this.meso = meso; } public static int create(final int accountId) throws SQLException { final Connection connection = Database.getConnection(); try ( final PreparedStatement ps = getInsertDefaultStorage(accountId, connection); final ResultSet rs = ps.getGeneratedKeys()) { if (!rs.next()) { return -1; } int storageid = rs.getInt(1); return storageid; } } private static PreparedStatement getInsertDefaultStorage(final int accountId, final Connection connection) throws SQLException { final PreparedStatement ps = connection.prepareStatement("INSERT INTO storages (accountid, slots, meso) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS); ps.setInt(1, accountId); ps.setInt(2, DEFAULT_STORAGE_CAPACITY); ps.setInt(3, 0); ps.executeUpdate(); return ps; } public static Storage loadStorage(final int accountId) { final Connection con = Database.getConnection(); Storage ret = null; int storageId; try ( PreparedStatement ps = getSelectStorage(accountId, con); ResultSet rs = ps.executeQuery()) { if (rs.next()) { storageId = rs.getInt("storageid"); ret = new Storage(storageId, rs.getByte("slots"), rs.getInt("meso")); ret.loadStoredItems(con); } else { storageId = create(accountId); ret = new Storage(storageId, (byte) DEFAULT_STORAGE_CAPACITY, 0); } } catch (final SQLException ex) { System.err.println("Error loading storage" + ex); } return ret; } private void loadStoredItems(final Connection con) throws SQLException { try ( PreparedStatement ps = getSelectStorageItems(con, this.id); ResultSet rs = ps.executeQuery()) { while (rs.next()) { final InventoryType type = InventoryType.fromNumber((byte) rs.getInt("inventorytype")); final Item loadedItem; if (type.equals(InventoryType.EQUIP) || type.equals(InventoryType.EQUIPPED)) { loadedItem = loadEquip(rs); } else { loadedItem = loadGenericItem(rs); } this.items.add(loadedItem); } } } private Item loadGenericItem(ResultSet rs) throws SQLException { final Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), rs.getByte("flag")); item.setOwner(rs.getString("owner")); item.setExpiration(rs.getLong("expiredate")); item.setFlag(rs.getByte("flag")); return item; } private Equip loadEquip(ResultSet rs) throws SQLException { final Equip equip = new Equip(rs.getInt("itemid"), rs.getByte("position"), rs.getInt("ringid"), rs.getByte("flag")); equip.setOwner(rs.getString("owner")); equip.setQuantity(rs.getShort("quantity")); equip.setAcc(rs.getShort("acc")); equip.setAvoid(rs.getShort("avoid")); equip.setDex(rs.getShort("dex")); equip.setHands(rs.getShort("hands")); equip.setHp(rs.getShort("hp")); equip.setInt(rs.getShort("int")); equip.setJump(rs.getShort("jump")); equip.setLuk(rs.getShort("luk")); equip.setMatk(rs.getShort("matk")); equip.setMdef(rs.getShort("mdef")); equip.setMp(rs.getShort("mp")); equip.setSpeed(rs.getShort("speed")); equip.setStr(rs.getShort("str")); equip.setWatk(rs.getShort("watk")); equip.setWdef(rs.getShort("wdef")); equip.setItemLevel(rs.getByte("itemLevel")); equip.setItemEXP(rs.getShort("itemEXP")); equip.setUpgradeSlots(rs.getByte("upgradeslots")); equip.setLevel(rs.getByte("level")); equip.setViciousHammer(rs.getByte("ViciousHammer")); equip.setExpiration(rs.getLong("expiredate")); equip.setFlag(rs.getByte("flag")); return equip; } private static PreparedStatement getSelectStorageItems(final Connection con, int storageId) throws SQLException { PreparedStatement pse = con.prepareStatement("SELECT * FROM inventoryitems LEFT JOIN inventoryequipment USING (inventoryitemid) WHERE storageid = ?"); pse.setInt(1, storageId); return pse; } private static PreparedStatement getSelectStorage(final int accountId, final Connection con) throws SQLException { PreparedStatement ps = con.prepareStatement("SELECT * FROM storages WHERE accountid = ?"); ps.setInt(1, accountId); return ps; } public void saveToDB() { if (!this.hasChanged) { return; } final Connection con = Database.getConnection(); try { updateStorage(con); deleteStoredItems(con); storeItems(con); } catch (final SQLException ex) { System.err.println("Error saving storage" + ex); } } // TODO: Create prepared statement in a method. Saves me nested try-with-resources blocks. private void storeItems(final Connection con) throws SQLException { PreparedStatement ps = con .prepareStatement( "INSERT INTO inventoryitems (storageid, itemid, inventorytype, position, quantity, owner, GM_Log, expiredate, flag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); final PreparedStatement pse = con .prepareStatement("INSERT INTO inventoryequipment VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); for (final Item item : this.items) { ps.setInt(1, this.id); ps.setInt(2, item.getItemId()); ps.setInt(3, item.getType().asNumber()); ps.setInt(4, item.getPosition()); ps.setInt(5, item.getQuantity()); ps.setString(6, item.getOwner()); ps.setString(7, item.getGMLog()); ps.setLong(8, item.getExpiration()); ps.setByte(9, item.getFlag()); ps.executeUpdate(); final ResultSet rs = ps.getGeneratedKeys(); int itemid; if (rs.next()) { itemid = rs.getInt(1); } else { throw new DatabaseException("Inserting char failed."); } if (item.getType() == ItemType.EQUIP) { pse.setInt(1, itemid); final Equip equip = (Equip) item; pse.setInt(2, equip.getUpgradeSlots()); pse.setInt(3, equip.getLevel()); pse.setInt(4, equip.getStr()); pse.setInt(5, equip.getDex()); pse.setInt(6, equip.getInt()); pse.setInt(7, equip.getLuk()); pse.setInt(8, equip.getHp()); pse.setInt(9, equip.getMp()); pse.setInt(10, equip.getWatk()); pse.setInt(11, equip.getMatk()); pse.setInt(12, equip.getWdef()); pse.setInt(13, equip.getMdef()); pse.setInt(14, equip.getAcc()); pse.setInt(15, equip.getAvoid()); pse.setInt(16, equip.getHands()); pse.setInt(17, equip.getSpeed()); pse.setInt(18, equip.getJump()); pse.setInt(19, equip.getRingId()); pse.setInt(20, equip.getViciousHammer()); pse.setInt(21, equip.getItemLevel()); pse.setInt(22, equip.getItemEXP()); pse.executeUpdate(); } } ps.close(); pse.close(); } private void deleteStoredItems(final Connection con) throws SQLException { try (PreparedStatement ps = con.prepareStatement("DELETE FROM inventoryitems WHERE storageid = ?")) { ps.setInt(1, this.id); ps.executeUpdate(); } } private void updateStorage(final Connection con) throws SQLException { try (PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?")) { ps.setInt(1, this.slots); ps.setInt(2, this.meso); ps.setInt(3, this.id); ps.executeUpdate(); } } public Item takeOut(final byte slot) { this.hasChanged = true; final Item ret = this.items.remove(slot); final InventoryType type = GameConstants.getInventoryType(ret.getItemId()); this.typeItems.put(type, Lists.newArrayList(this.filterItems(type))); return ret; } public void store(final Item item) { this.hasChanged = true; this.items.add(item); final InventoryType type = GameConstants.getInventoryType(item.getItemId()); this.typeItems.put(type, Lists.newArrayList(this.filterItems(type))); } public List<Item> getItems() { return Collections.unmodifiableList(this.items); } private List<Item> filterItems(final InventoryType type) { final List<Item> ret = Lists.newLinkedList(); for (final Item item : this.items) { if (GameConstants.getInventoryType(item.getItemId()) == type) { ret.add(item); } } return ret; } public byte getSlot(final InventoryType type, final byte slot) { // MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); byte ret = 0; for (final Item item : this.items) { if (item == this.typeItems.get(type).get(slot)) { return ret; } ret++; } return -1; } public void sendStorage(final ChannelClient c, final int npcId) { // sort by inventorytype to avoid confusion Collections.sort(this.items, new Comparator<Item>() { @Override public int compare(final Item item1, final Item item2) { final InventoryType item1Type = GameConstants.getInventoryType(item1.getItemId()); final InventoryType item2Type = GameConstants.getInventoryType(item2.getItemId()); if (item1Type.asNumber() < item2Type.asNumber()) { return -1; } else if (item1Type == item2Type) { return 0; } else { return 1; } } }); for (final InventoryType type : InventoryType.values()) { this.typeItems.put(type, Lists.newArrayList(this.items)); } c.write(ChannelPackets.getStorage(npcId, this.slots, this.items, this.meso)); } public void sendStored(final ChannelClient c, final InventoryType type) { c.write(ChannelPackets.storeStorage(this.slots, type, this.typeItems.get(type))); } public void sendTakenOut(final ChannelClient c, final InventoryType type) { c.write(ChannelPackets.takeOutStorage(this.slots, type, this.typeItems.get(type))); } public int getMeso() { return this.meso; } public void setMeso(final int meso) { if (meso < 0) { return; } this.hasChanged = true; this.meso = meso; } public void sendMeso(final ChannelClient c) { c.write(ChannelPackets.mesoStorage(this.slots, this.meso)); } public boolean isFull() { return this.items.size() >= this.slots; } public int getSlots() { return this.slots; } public void increaseSlots(final byte gain) { this.hasChanged = true; this.slots += gain; } public void setSlots(final byte set) { this.hasChanged = true; this.slots = set; } public void close() { this.typeItems.clear(); } }