/* This file is part of the OdinMS Maple Story Server Copyright (C) 2008 ~ 2010 Patrick Huy <patrick.huy@frz.cc> Matthias Butz <matze@odinms.de> Jan Christian Meyer <vimes@odinms.de> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation. You may not use, modify or distribute this program under any other version of the GNU Affero General Public License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package server.stores; import constants.GameConstants; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.ResultSet; import java.sql.Connection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.lang.ref.WeakReference; import client.inventory.Item; import client.inventory.ItemLoader; import client.MapleCharacter; import client.MapleClient; import client.inventory.MapleInventoryType; import database.DatabaseConnection; import handling.channel.ChannelServer; import handling.world.World; import java.util.ArrayList; import server.maps.MapleMapObject; import server.maps.MapleMap; import server.maps.MapleMapObjectType; import tools.Pair; import tools.packet.PlayerShopPacket; public abstract class AbstractPlayerStore extends MapleMapObject implements IMaplePlayerShop { protected boolean open = false, available = false; protected String ownerName, des, pass; protected int ownerId, owneraccount, itemId, channel, map; protected AtomicInteger meso = new AtomicInteger(0); protected WeakReference<MapleCharacter> chrs[]; protected List<String> visitors = new LinkedList<>(); protected List<BoughtItem> bought = new LinkedList<>(); protected List<MaplePlayerShopItem> items = new LinkedList<>(); public AbstractPlayerStore(MapleCharacter owner, int itemId, String desc, String pass, int slots) { this.setPosition(owner.getTruePosition()); this.ownerName = owner.getName(); this.ownerId = owner.getId(); this.owneraccount = owner.getAccountID(); this.itemId = itemId; this.des = desc; this.pass = pass; this.map = owner.getMapId(); this.channel = owner.getClient().getChannel(); chrs = new WeakReference[slots]; for (int i = 0; i < chrs.length; i++) { chrs[i] = new WeakReference<>(null); } } @Override public int getMaxSize() { return chrs.length + 1; } @Override public int getSize() { return getFreeSlot() == -1 ? getMaxSize() : getFreeSlot(); } @Override public void broadcastToVisitors(byte[] packet) { broadcastToVisitors(packet, true); } public void broadcastToVisitors(byte[] packet, boolean owner) { for (WeakReference<MapleCharacter> chr : chrs) { if (chr != null && chr.get() != null) { chr.get().getClient().getSession().write(packet); } } if (getShopType() != IMaplePlayerShop.HIRED_MERCHANT && owner && getMCOwner() != null) { getMCOwner().getClient().getSession().write(packet); } } public void broadcastToVisitors(byte[] packet, int exception) { for (WeakReference<MapleCharacter> chr : chrs) { if (chr != null && chr.get() != null && getVisitorSlot(chr.get()) != exception) { chr.get().getClient().getSession().write(packet); } } if (getShopType() != IMaplePlayerShop.HIRED_MERCHANT && getMCOwner() != null && exception != ownerId) { getMCOwner().getClient().getSession().write(packet); } } @Override public int getMeso() { return meso.get(); } @Override public void setMeso(int meso) { this.meso.set(meso); } @Override public void setOpen(boolean open) { this.open = open; } @Override public boolean isOpen() { return open; } public boolean saveItems() { if (getShopType() != IMaplePlayerShop.HIRED_MERCHANT) { //hired merch only return false; } Connection con = DatabaseConnection.getConnection(); try { PreparedStatement ps = con.prepareStatement("DELETE FROM hiredmerch WHERE accountid = ? OR characterid = ?"); ps.setInt(1, owneraccount); ps.setInt(2, ownerId); ps.executeUpdate(); ps.close(); ps = con.prepareStatement("INSERT INTO hiredmerch (characterid, accountid, Mesos, time) VALUES (?, ?, ?, ?)", DatabaseConnection.RETURN_GENERATED_KEYS); ps.setInt(1, ownerId); ps.setInt(2, owneraccount); ps.setInt(3, meso.get()); ps.setLong(4, System.currentTimeMillis()); ps.executeUpdate(); ResultSet rs = ps.getGeneratedKeys(); if (!rs.next()) { rs.close(); ps.close(); throw new RuntimeException("Error, adding merchant to DB"); } final int packageid = rs.getInt(1); rs.close(); ps.close(); List<Pair<Item, MapleInventoryType>> iters = new ArrayList<>(); Item item; for (MaplePlayerShopItem pItems : items) { if (pItems.item == null || pItems.bundles <= 0) { continue; } if (pItems.item.getQuantity() <= 0 && !GameConstants.isRechargable(pItems.item.getItemId())) { continue; } item = pItems.item.copy(); item.setQuantity((short) (item.getQuantity() * pItems.bundles)); boolean add = iters.add(new Pair<>(item, GameConstants.getInventoryType(item.getItemId()))); } ItemLoader.HIRED_MERCHANT.saveItems(iters, packageid); return true; } catch (SQLException se) { } return false; } public MapleCharacter getVisitor(int num) { return chrs[num].get(); } @Override public void update() { if (isAvailable()) { if (getShopType() == IMaplePlayerShop.HIRED_MERCHANT) { getMap().broadcastMessage(PlayerShopPacket.updateHiredMerchant((HiredMerchant) this)); } else if (getMCOwner() != null) { getMap().broadcastMessage(PlayerShopPacket.sendPlayerShopBox(getMCOwner())); } } } @Override public void addVisitor(MapleCharacter visitor) { int i = getFreeSlot(); if (i > 0) { if (getShopType() >= 3) { broadcastToVisitors(PlayerShopPacket.getMiniGameNewVisitor(visitor, i, (MapleMiniGame) this)); } else { broadcastToVisitors(PlayerShopPacket.shopVisitorAdd(visitor, i)); } chrs[i - 1] = new WeakReference<>(visitor); if (!isOwner(visitor) && !visitors.contains(visitor.getName())) { visitors.add(visitor.getName()); } if (i == 3) { update(); } } } @Override public void removeVisitor(MapleCharacter visitor) { final byte slot = getVisitorSlot(visitor); boolean shouldUpdate = getFreeSlot() == -1; if (slot > 0) { broadcastToVisitors(PlayerShopPacket.shopVisitorLeave(slot), slot); chrs[slot - 1] = new WeakReference<>(null); if (shouldUpdate) { update(); } } } @Override public byte getVisitorSlot(MapleCharacter visitor) { for (byte i = 0; i < chrs.length; i++) { if (chrs[i] != null && chrs[i].get() != null && chrs[i].get().getId() == visitor.getId()) { return (byte) (i + 1); } } if (visitor.getId() == ownerId) { //can visit own store in merch, otherwise not. return 0; } return -1; } @Override public void removeAllVisitors(int error, int type) { for (int i = 0; i < chrs.length; i++) { MapleCharacter visitor = getVisitor(i); if (visitor != null) { if (type != -1) { visitor.getClient().getSession().write(PlayerShopPacket.shopErrorMessage(error, type)); } broadcastToVisitors(PlayerShopPacket.shopVisitorLeave(getVisitorSlot(visitor)), getVisitorSlot(visitor)); visitor.setPlayerShop(null); chrs[i] = new WeakReference<>(null); } } update(); } @Override public String getOwnerName() { return ownerName; } @Override public int getOwnerId() { return ownerId; } @Override public int getOwnerAccId() { return owneraccount; } @Override public String getDescription() { if (des == null) { return ""; } return des; } @Override public List<Pair<Byte, MapleCharacter>> getVisitors() { List<Pair<Byte, MapleCharacter>> chrz = new LinkedList<>(); for (byte i = 0; i < chrs.length; i++) { //include owner or no if (chrs[i] != null && chrs[i].get() != null) { boolean add = chrz.add(new Pair<>((byte) (i + 1), chrs[i].get())); } } return chrz; } @Override public List<MaplePlayerShopItem> getItems() { return items; } @Override public void addItem(MaplePlayerShopItem item) { //System.out.println("Adding item ... 2"); items.add(item); } @Override public boolean removeItem(int item) { return false; } @Override public void removeFromSlot(int slot) { items.remove(slot); } @Override public byte getFreeSlot() { for (byte i = 0; i < chrs.length; i++) { if (chrs[i] == null || chrs[i].get() == null) { return (byte) (i + 1); } } return -1; } @Override public int getItemId() { return itemId; } @Override public boolean isOwner(MapleCharacter chr) { return chr.getId() == ownerId && chr.getName().equals(ownerName); } @Override public String getPassword() { if (pass == null) { return ""; } return pass; } @Override public void sendDestroyData(MapleClient client) { } @Override public void sendSpawnData(MapleClient client) { } @Override public MapleMapObjectType getType() { return MapleMapObjectType.SHOP; } public MapleCharacter getMCOwnerWorld() { int ourChannel = World.Find.findChannel(ownerId); if (ourChannel <= 0) { return null; } return ChannelServer.getInstance(ourChannel).getPlayerStorage().getCharacterById(ownerId); } public MapleCharacter getMCOwnerChannel() { return ChannelServer.getInstance(channel).getPlayerStorage().getCharacterById(ownerId); } public MapleCharacter getMCOwner() { return getMap().getCharacterById(ownerId); } public MapleMap getMap() { return ChannelServer.getInstance(channel).getMapFactory().getMap(map); } @Override public int getGameType() { if (getShopType() == IMaplePlayerShop.HIRED_MERCHANT) { //hiredmerch return 5; } else if (getShopType() == IMaplePlayerShop.PLAYER_SHOP) { //shop lol return 4; } else if (getShopType() == IMaplePlayerShop.OMOK) { //omok return 1; } else if (getShopType() == IMaplePlayerShop.MATCH_CARD) { //matchcard return 2; } return 0; } @Override public boolean isAvailable() { return available; } @Override public void setAvailable(boolean b) { this.available = b; } @Override public List<BoughtItem> getBoughtItems() { return bought; } public static final class BoughtItem { public int id; public int quantity; public int totalPrice; public String buyer; public BoughtItem(final int id, final int quantity, final int totalPrice, final String buyer) { this.id = id; this.quantity = quantity; this.totalPrice = totalPrice; this.buyer = buyer; } } }