diff --git a/build/built-jar.properties b/build/built-jar.properties index 1dddac2c40..61c2adeeb4 100644 --- a/build/built-jar.properties +++ b/build/built-jar.properties @@ -1,4 +1,4 @@ -#Mon, 25 Sep 2017 01:46:09 -0300 +#Tue, 26 Sep 2017 00:15:28 -0300 C\:\\Nexon\\MapleSolaxia\\MapleSolaxiaV2= diff --git a/build/classes/client/MapleCharacter$12.class b/build/classes/client/MapleCharacter$12.class index e736acf9ee..6a62838746 100644 Binary files a/build/classes/client/MapleCharacter$12.class and b/build/classes/client/MapleCharacter$12.class differ diff --git a/build/classes/client/MapleCharacter$13.class b/build/classes/client/MapleCharacter$13.class index 34bde9a6aa..d3f52fd50a 100644 Binary files a/build/classes/client/MapleCharacter$13.class and b/build/classes/client/MapleCharacter$13.class differ diff --git a/build/classes/client/MapleCharacter$14.class b/build/classes/client/MapleCharacter$14.class index 6037fe54ff..255c64f4c7 100644 Binary files a/build/classes/client/MapleCharacter$14.class and b/build/classes/client/MapleCharacter$14.class differ diff --git a/build/classes/client/MapleCharacter$15.class b/build/classes/client/MapleCharacter$15.class index db05f3875a..fa7d606867 100644 Binary files a/build/classes/client/MapleCharacter$15.class and b/build/classes/client/MapleCharacter$15.class differ diff --git a/build/classes/client/MapleCharacter$16.class b/build/classes/client/MapleCharacter$16.class index 62129b955a..1ab3d761bd 100644 Binary files a/build/classes/client/MapleCharacter$16.class and b/build/classes/client/MapleCharacter$16.class differ diff --git a/build/classes/client/MapleCharacter$17.class b/build/classes/client/MapleCharacter$17.class index 0430f5e907..a4e3b6e033 100644 Binary files a/build/classes/client/MapleCharacter$17.class and b/build/classes/client/MapleCharacter$17.class differ diff --git a/build/classes/client/MapleCharacter$18.class b/build/classes/client/MapleCharacter$18.class index 28b558a69e..b6e98f3a01 100644 Binary files a/build/classes/client/MapleCharacter$18.class and b/build/classes/client/MapleCharacter$18.class differ diff --git a/build/classes/client/MapleCharacter$19.class b/build/classes/client/MapleCharacter$19.class index b66acd2e28..167759f2c9 100644 Binary files a/build/classes/client/MapleCharacter$19.class and b/build/classes/client/MapleCharacter$19.class differ diff --git a/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class b/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class index 8b4b8c77f7..9afef49517 100644 Binary files a/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class and b/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class differ diff --git a/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class b/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class index 65e9a34750..5ad57c5686 100644 Binary files a/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class and b/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class differ diff --git a/build/classes/client/MapleCharacter$SkillEntry.class b/build/classes/client/MapleCharacter$SkillEntry.class index 40d7d03d26..bcc52c1fc7 100644 Binary files a/build/classes/client/MapleCharacter$SkillEntry.class and b/build/classes/client/MapleCharacter$SkillEntry.class differ diff --git a/build/classes/client/MapleCharacter.class b/build/classes/client/MapleCharacter.class index d5e8d6a729..bc92c84f44 100644 Binary files a/build/classes/client/MapleCharacter.class and b/build/classes/client/MapleCharacter.class differ diff --git a/build/classes/client/inventory/ItemFactory.class b/build/classes/client/inventory/ItemFactory.class index f947434d16..de06d0915b 100644 Binary files a/build/classes/client/inventory/ItemFactory.class and b/build/classes/client/inventory/ItemFactory.class differ diff --git a/build/classes/constants/ServerConstants.class b/build/classes/constants/ServerConstants.class index 458e585da8..0f084b32ab 100644 Binary files a/build/classes/constants/ServerConstants.class and b/build/classes/constants/ServerConstants.class differ diff --git a/build/classes/net/server/Server$1.class b/build/classes/net/server/Server$1.class index 06eb077f6a..03815bf333 100644 Binary files a/build/classes/net/server/Server$1.class and b/build/classes/net/server/Server$1.class differ diff --git a/build/classes/net/server/Server.class b/build/classes/net/server/Server.class index 4bef61ba52..22d554a6ab 100644 Binary files a/build/classes/net/server/Server.class and b/build/classes/net/server/Server.class differ diff --git a/build/classes/net/server/channel/handlers/HiredMerchantRequest.class b/build/classes/net/server/channel/handlers/HiredMerchantRequest.class index f1273bf4fb..d2fb39fd73 100644 Binary files a/build/classes/net/server/channel/handlers/HiredMerchantRequest.class and b/build/classes/net/server/channel/handlers/HiredMerchantRequest.class differ diff --git a/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class b/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class index 651f198286..463158ce7a 100644 Binary files a/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class and b/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class differ diff --git a/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class b/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class index a043aa9590..92dd331d9e 100644 Binary files a/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class and b/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class differ diff --git a/build/classes/net/server/channel/handlers/TakeDamageHandler.class b/build/classes/net/server/channel/handlers/TakeDamageHandler.class index 204bf8b9cd..35a4bde059 100644 Binary files a/build/classes/net/server/channel/handlers/TakeDamageHandler.class and b/build/classes/net/server/channel/handlers/TakeDamageHandler.class differ diff --git a/build/classes/net/server/world/World$1.class b/build/classes/net/server/world/World$1.class index af99445edf..d522d36d64 100644 Binary files a/build/classes/net/server/world/World$1.class and b/build/classes/net/server/world/World$1.class differ diff --git a/build/classes/net/server/world/World.class b/build/classes/net/server/world/World.class index e2e7aa6861..22bd854aed 100644 Binary files a/build/classes/net/server/world/World.class and b/build/classes/net/server/world/World.class differ diff --git a/build/classes/server/maps/HiredMerchant$SoldItem.class b/build/classes/server/maps/HiredMerchant$SoldItem.class index ec0540adc6..0f41d3e8e1 100644 Binary files a/build/classes/server/maps/HiredMerchant$SoldItem.class and b/build/classes/server/maps/HiredMerchant$SoldItem.class differ diff --git a/build/classes/server/maps/HiredMerchant.class b/build/classes/server/maps/HiredMerchant.class index d515db0951..177d1481fb 100644 Binary files a/build/classes/server/maps/HiredMerchant.class and b/build/classes/server/maps/HiredMerchant.class differ diff --git a/build/classes/tools/MaplePacketCreator.class b/build/classes/tools/MaplePacketCreator.class index 793fdd2e25..f9657fa238 100644 Binary files a/build/classes/tools/MaplePacketCreator.class and b/build/classes/tools/MaplePacketCreator.class differ diff --git a/dist/MapleSolaxia.jar b/dist/MapleSolaxia.jar index a0c8db00ee..b0954fde12 100644 Binary files a/dist/MapleSolaxia.jar and b/dist/MapleSolaxia.jar differ diff --git a/docs/feature_list.txt b/docs/feature_list.txt index 145f6b6f7c..b4d5d2856e 100644 --- a/docs/feature_list.txt +++ b/docs/feature_list.txt @@ -67,6 +67,7 @@ Server potentials: * Custom jail system (needs provided custom wz). * Delete Character 100% (requires ENABLE_PIC activated). * Boats, elevator and other travelling mechanics fully working. +* Eanbled Hired Merchant can be used anywhere but FM Entrance. * Vega's spell. * Pet item ignore. * Autosaver (periodically saves on DB current state of every player in-game). diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 52b8e10045..453e227721 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -549,4 +549,8 @@ Consertado GPQ n 23 Setembro 2017, Adicionado Water of Life. -Consertado bug com sistema novo de buffs ao entrar no cash shop e em outros cenários onde não se detectava o melhor buff corretamente. \ No newline at end of file +Consertado bug com sistema novo de buffs ao entrar no cash shop e em outros cenários onde não se detectava o melhor buff corretamente. + +25 Setembro 2017, +Adicionado proteção de acesso concorrente a ações de Hired Merchant. +Corrigido alguns problemas com Hired Merchant não retornando a quantidade correta de itens. \ No newline at end of file diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml index 5af0ed1acc..d11e7eb3df 100644 --- a/nbproject/private/private.xml +++ b/nbproject/private/private.xml @@ -2,6 +2,30 @@ - + + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PlayerLoggedinHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/TakeDamageHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/command/Commands.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/maps/HiredMerchant.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleStatEffect.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/inventory/Item.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/scripts/npc/9030000.js + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/maps/MapleMapObject.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/ExpTable.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/ServerConstants.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/world/World.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/worker/HiredMerchantWorker.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/FredrickHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PlayerInteractionHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/tools/MaplePacketCreator.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/HiredMerchantRequest.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/AutoAssignHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/inventory/ItemFactory.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/Server.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/scripting/npc/NPCConversationManager.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MapleCharacter.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/maps/MapleMap.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/Channel.java + diff --git a/sql/db_database.sql b/sql/db_database.sql index 855063d158..36024833ee 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -12984,6 +12984,15 @@ CREATE TABLE IF NOT EXISTS `inventoryitems` ( KEY `CHARID` (`characterid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +CREATE TABLE IF NOT EXISTS `inventorymerchant` ( + `inventorymerchantid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `inventoryitemid` int(10) unsigned NOT NULL DEFAULT '0', + `characterid` int(11) DEFAULT NULL, + `bundles` int(10) NOT NULL DEFAULT '0', + PRIMARY KEY (`inventorymerchantid`), + KEY `INVENTORYITEMID` (`inventoryitemid`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + CREATE TABLE IF NOT EXISTS `ipbans` ( `ipbanid` int(10) unsigned NOT NULL AUTO_INCREMENT, `ip` varchar(40) NOT NULL DEFAULT '', diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 909e01a0d1..8578d06424 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -2574,7 +2574,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { buffExpires.put(sourceid, expirationtime); } - private void removeEffectFromItemEffectHolder(Integer sourceid, MapleBuffStat buffStat) { + private boolean removeEffectFromItemEffectHolder(Integer sourceid, MapleBuffStat buffStat) { Map lbe = buffEffects.get(sourceid); if(lbe.remove(buffStat) != null) { @@ -2584,7 +2584,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { buffEffects.remove(sourceid); buffExpires.remove(sourceid); } + + return true; } + + return false; } private void removeItemEffectHolder(Integer sourceid) { @@ -2634,9 +2638,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private void extractBuffValue(int sourceid, MapleBuffStat stat) { chrLock.lock(); try { - if(buffEffects.get(sourceid).remove(stat) != null) { - buffEffectsCount.put(stat, (byte)(buffEffectsCount.get(stat) - 1)); - } + removeEffectFromItemEffectHolder(sourceid, stat); } finally { chrLock.unlock(); } @@ -2755,7 +2757,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { for (Entry stat : stats.entrySet()) { int sourceid = stat.getValue().effect.getBuffSourceId(); - if(buffEffects.get(sourceid) == null) { + if(!buffEffects.containsKey(sourceid)) { buffExpires.remove(sourceid); } @@ -6537,10 +6539,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void addMerchantMesos(int add) { + int newAmount; + try { + newAmount = (int)Math.min((long)merchantmeso + add, Integer.MAX_VALUE); + System.out.println("adding" + add + " now" + newAmount); + Connection con = DatabaseConnection.getConnection(); try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) { - ps.setInt(1, merchantmeso + add); + ps.setInt(1, newAmount); ps.setInt(2, id); ps.executeUpdate(); } @@ -6550,7 +6557,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { e.printStackTrace(); return; } - merchantmeso += add; + merchantmeso = newAmount; } public void setMerchantMeso(int set) { diff --git a/src/client/inventory/ItemFactory.java b/src/client/inventory/ItemFactory.java index 5bd1d98401..6812e63554 100644 --- a/src/client/inventory/ItemFactory.java +++ b/src/client/inventory/ItemFactory.java @@ -57,6 +57,20 @@ public enum ItemFactory { } public List> loadItems(int id, boolean login) throws SQLException { + if(value != 6) return loadItemsCommon(id, login); + else return loadItemsMerchant(id, login); + } + + public void saveItems(List> items, int id, Connection con) throws SQLException { + saveItems(items, null, id, con); + } + + public synchronized void saveItems(List> items, List bundlesList, int id, Connection con) throws SQLException { + if(value != 6) saveItemsCommon(items, id, con); + else saveItemsMerchant(items, bundlesList, id, con); + } + + private List> loadItemsCommon(int id, boolean login) throws SQLException { List> items = new ArrayList<>(); PreparedStatement ps = null; @@ -135,7 +149,7 @@ public enum ItemFactory { return items; } - public synchronized void saveItems(List> items, int id, Connection con) throws SQLException { + private void saveItemsCommon(List> items, int id, Connection con) throws SQLException { PreparedStatement ps = null; PreparedStatement pse = null; ResultSet rs = null; @@ -227,4 +241,212 @@ public enum ItemFactory { lock.unlock(); } } + + private List> loadItemsMerchant(int id, boolean login) throws SQLException { + List> items = new ArrayList<>(); + + PreparedStatement ps = null, ps2 = null; + ResultSet rs = null, rs2 = null; + Connection con = DatabaseConnection.getConnection(); + try { + StringBuilder query = new StringBuilder(); + query.append("SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `"); + query.append(account ? "accountid" : "characterid").append("` = ?"); + + if (login) { + query.append(" AND `inventorytype` = ").append(MapleInventoryType.EQUIPPED.getType()); + } + + ps = con.prepareStatement(query.toString()); + ps.setInt(1, value); + ps.setInt(2, id); + rs = ps.executeQuery(); + + while (rs.next()) { + ps2 = con.prepareStatement("SELECT `bundles` FROM `inventorymerchant` WHERE `inventoryitemid` = ?"); + ps2.setInt(1, rs.getInt("inventoryitemid")); + rs2 = ps2.executeQuery(); + + short bundles = 0; + if(rs2.next()) { + bundles = rs2.getShort("bundles"); + } + + MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype")); + + if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) { + Equip equip = new Equip(rs.getInt("itemid"), (short) rs.getInt("position")); + equip.setOwner(rs.getString("owner")); + equip.setQuantity((short) rs.getInt("quantity")); + equip.setAcc((short) rs.getInt("acc")); + equip.setAvoid((short) rs.getInt("avoid")); + equip.setDex((short) rs.getInt("dex")); + equip.setHands((short) rs.getInt("hands")); + equip.setHp((short) rs.getInt("hp")); + equip.setInt((short) rs.getInt("int")); + equip.setJump((short) rs.getInt("jump")); + equip.setVicious((short) rs.getInt("vicious")); + equip.setFlag((byte) rs.getInt("flag")); + equip.setLuk((short) rs.getInt("luk")); + equip.setMatk((short) rs.getInt("matk")); + equip.setMdef((short) rs.getInt("mdef")); + equip.setMp((short) rs.getInt("mp")); + equip.setSpeed((short) rs.getInt("speed")); + equip.setStr((short) rs.getInt("str")); + equip.setWatk((short) rs.getInt("watk")); + equip.setWdef((short) rs.getInt("wdef")); + equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); + equip.setLevel((byte) rs.getByte("level")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); + equip.setRingId(rs.getInt("ringid")); + items.add(new Pair(equip, mit)); + } else { + if(bundles > 0) { + Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), rs.getInt("petid")); + item.setOwner(rs.getString("owner")); + item.setExpiration(rs.getLong("expiration")); + item.setGiftFrom(rs.getString("giftFrom")); + item.setFlag((byte) rs.getInt("flag")); + items.add(new Pair<>(item, mit)); + } + } + + rs2.close(); + ps2.close(); + } + + rs.close(); + ps.close(); + con.close(); + } finally { + if (rs2 != null && !rs2.isClosed()) { + rs2.close(); + } + if (ps2 != null && !ps2.isClosed()) { + ps2.close(); + } + if (rs != null && !rs.isClosed()) { + rs.close(); + } + if (ps != null && !ps.isClosed()) { + ps.close(); + } + if (con != null && !con.isClosed()) { + con.close(); + } + } + return items; + } + + private void saveItemsMerchant(List> items, List bundlesList, int id, Connection con) throws SQLException { + PreparedStatement ps = null; + PreparedStatement pse = null; + ResultSet rs = null; + + lock.lock(); + try { + ps = con.prepareStatement("DELETE FROM `inventorymerchant` WHERE `characterid` = ?"); + ps.setInt(1, id); + ps.executeUpdate(); + ps.close(); + + StringBuilder query = new StringBuilder(); + query.append("DELETE `inventoryitems`, `inventoryequipment` FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `"); + query.append(account ? "accountid" : "characterid").append("` = ?"); + ps = con.prepareStatement(query.toString()); + ps.setInt(1, value); + ps.setInt(2, id); + ps.executeUpdate(); + ps.close(); + ps = con.prepareStatement("INSERT INTO `inventoryitems` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + + if (!items.isEmpty()) { + int i = 0; + for (Pair pair : items) { + Item item = pair.getLeft(); + Short bundles = bundlesList.get(i); + MapleInventoryType mit = pair.getRight(); + i++; + + ps.setInt(1, value); + ps.setString(2, account ? null : String.valueOf(id)); + ps.setString(3, account ? String.valueOf(id) : null); + ps.setInt(4, item.getItemId()); + ps.setInt(5, mit.getType()); + ps.setInt(6, item.getPosition()); + ps.setInt(7, item.getQuantity()); + ps.setString(8, item.getOwner()); + ps.setInt(9, item.getPetId()); + ps.setInt(10, item.getFlag()); + ps.setLong(11, item.getExpiration()); + ps.setString(12, item.getGiftFrom()); + ps.executeUpdate(); + + rs = ps.getGeneratedKeys(); + if (!rs.next()) { + throw new RuntimeException("Inserting item failed."); + } + + int genKey = rs.getInt(1); + rs.close(); + + pse = con.prepareStatement("INSERT INTO `inventorymerchant` VALUES (DEFAULT, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + pse.setInt(1, genKey); + pse.setInt(2, id); + pse.setInt(3, bundles); + pse.executeUpdate(); + pse.close(); + + if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) { + pse = con.prepareStatement("INSERT INTO `inventoryequipment` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + pse.setInt(1, genKey); + + 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, 0); + pse.setInt(20, equip.getVicious()); + pse.setInt(21, equip.getItemLevel()); + pse.setInt(22, equip.getItemExp()); + pse.setInt(23, equip.getRingId()); + pse.executeUpdate(); + + pse.close(); + } + } + } + + ps.close(); + } finally { + if (ps != null && !ps.isClosed()) { + ps.close(); + } + if (pse != null && !pse.isClosed()) { + pse.close(); + } + if(rs != null && !rs.isClosed()) { + rs.close(); + } + + lock.unlock(); + } + } } \ No newline at end of file diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index 2798aaaa0d..3c64e28177 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -43,10 +43,12 @@ public class ServerConstants { public static final boolean USE_ITEM_SORT = true; public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id. public static final boolean USE_PARTY_SEARCH = false; + public static final boolean USE_MERCHANT_ANYWHERE = true; //Enables player shops and hired merchants outside FM rooms (except FM entrance). public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically. public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour). public static final boolean USE_SERVER_AUTOASSIGNER = true; //Server-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments. public static final boolean USE_REFRESH_RANK_MOVE = true; + public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) public static final boolean USE_ENFORCE_MDOOR_POSITION = true; //Forces mystic door to be spawned near spawnpoints. (since things bugs out other way, and this helps players to locate the door faster) public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped. public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll. diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 2653260ef0..3005d29030 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -39,6 +39,8 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Lock; import net.MapleServerHandler; import net.mina.MapleCodecFactory; @@ -79,6 +81,7 @@ public class Server implements Runnable { private static Server instance = null; private List> worldRecommendedList = new LinkedList<>(); private final Map guilds = new LinkedHashMap<>(); + private final Lock shutdownLock = new ReentrantLock(); private final PlayerBuffStorage buffStorage = new PlayerBuffStorage(); private final Map alliances = new LinkedHashMap<>(); private boolean online = false; @@ -99,7 +102,8 @@ public class Server implements Runnable { return worldRecommendedList; } - public void removeChannel(int worldid, int channel) { + /* + public void removeChannel(int worldid, int channel) { //lol don't! channels.remove(channel); World world = worlds.get(worldid); @@ -107,6 +111,7 @@ public class Server implements Runnable { world.removeChannel(channel); } } + */ public Channel getChannel(int world, int channel) { return worlds.get(world).getChannel(channel); @@ -702,69 +707,75 @@ public class Server implements Runnable { return worlds; } - public final Runnable shutdown(final boolean restart) {//only once :D + public final Runnable shutdown(final boolean restart) {//no player should be online when trying to shutdown! return new Runnable() { @Override public void run() { - System.out.println((restart ? "Restarting" : "Shutting down") + " the server!\r\n"); - if (getWorlds() == null) return;//already shutdown - for (World w : getWorlds()) { - w.shutdown(); - } - /*for (World w : getWorlds()) { - while (w.getPlayerStorage().getAllCharacters().size() > 0) { - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - System.err.println("FUCK MY LIFE"); + shutdownLock.lock(); + + try { + System.out.println((restart ? "Restarting" : "Shutting down") + " the server!\r\n"); + if (getWorlds() == null) return;//already shutdown + for (World w : getWorlds()) { + w.shutdown(); + } + /*for (World w : getWorlds()) { + while (w.getPlayerStorage().getAllCharacters().size() > 0) { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + System.err.println("FUCK MY LIFE"); + } } } - } - for (Channel ch : getAllChannels()) { - while (ch.getConnectedClients() > 0) { - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - System.err.println("FUCK MY LIFE"); + for (Channel ch : getAllChannels()) { + while (ch.getConnectedClients() > 0) { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + System.err.println("FUCK MY LIFE"); + } + } + }*/ + + TimerManager.getInstance().purge(); + TimerManager.getInstance().stop(); + + for (Channel ch : getAllChannels()) { + while (!ch.finishedShutdown()) { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + ie.printStackTrace(); + System.err.println("FUCK MY LIFE"); + } } } - }*/ + worlds.clear(); + worlds = null; + channels.clear(); + channels = null; + worldRecommendedList.clear(); + worldRecommendedList = null; - TimerManager.getInstance().purge(); - TimerManager.getInstance().stop(); - - for (Channel ch : getAllChannels()) { - while (!ch.finishedShutdown()) { + System.out.println("Worlds + Channels are offline."); + acceptor.unbind(); + acceptor = null; + if (!restart) { + System.exit(0); + } else { + System.out.println("\r\nRestarting the server....\r\n"); try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - ie.printStackTrace(); - System.err.println("FUCK MY LIFE"); + instance.finalize();//FUU I CAN AND IT'S FREE + } catch (Throwable ex) { + ex.printStackTrace(); } + instance = null; + System.gc(); + getInstance().run();//DID I DO EVERYTHING?! D: } - } - worlds.clear(); - worlds = null; - channels.clear(); - channels = null; - worldRecommendedList.clear(); - worldRecommendedList = null; - - System.out.println("Worlds + Channels are offline."); - acceptor.unbind(); - acceptor = null; - if (!restart) { - System.exit(0); - } else { - System.out.println("\r\nRestarting the server....\r\n"); - try { - instance.finalize();//FUU I CAN AND IT'S FREE - } catch (Throwable ex) { - ex.printStackTrace(); - } - instance = null; - System.gc(); - getInstance().run();//DID I DO EVERYTHING?! D: + } finally { + shutdownLock.unlock(); } } }; diff --git a/src/net/server/channel/handlers/FredrickHandler.java b/src/net/server/channel/handlers/FredrickHandler.java index b1a931f1b3..0af9a78353 100644 --- a/src/net/server/channel/handlers/FredrickHandler.java +++ b/src/net/server/channel/handlers/FredrickHandler.java @@ -72,10 +72,10 @@ public class FredrickHandler extends AbstractMaplePacketHandler { chr.getHiredMerchant().clearItems(); for (int i = 0; i < items.size(); i++) { - Item item = items.get(i).getLeft(); + Item item = items.get(i).getLeft(); MapleInventoryManipulator.addFromDrop(c, item, false); String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId()); - FilePrinter.printError(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n"); + FilePrinter.printError(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n"); } c.announce(MaplePacketCreator.fredrickMessage((byte) 0x1E)); diff --git a/src/net/server/channel/handlers/HiredMerchantRequest.java b/src/net/server/channel/handlers/HiredMerchantRequest.java index 73c76cf97f..58b21a7c56 100644 --- a/src/net/server/channel/handlers/HiredMerchantRequest.java +++ b/src/net/server/channel/handlers/HiredMerchantRequest.java @@ -26,6 +26,7 @@ import client.MapleCharacter; import java.sql.SQLException; import java.util.Arrays; import client.MapleClient; +import constants.ServerConstants; import net.AbstractMaplePacketHandler; import server.maps.MapleMapObjectType; import tools.MaplePacketCreator; @@ -38,7 +39,7 @@ import tools.data.input.SeekableLittleEndianAccessor; public final class HiredMerchantRequest extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && chr.getMapId() > 910000000 && chr.getMapId() < 910000023) { + if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && ((ServerConstants.USE_MERCHANT_ANYWHERE && chr.getMapId() != 910000000) || (chr.getMapId() > 910000000 && chr.getMapId() < 910000023))) { if (!chr.hasMerchant()) { try { if (ItemFactory.MERCHANT.loadItems(chr.getId(), false).isEmpty() && chr.getMerchantMeso() == 0) { diff --git a/src/net/server/channel/handlers/PlayerInteractionHandler.java b/src/net/server/channel/handlers/PlayerInteractionHandler.java index 8d60569f9b..20a91415a6 100644 --- a/src/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/net/server/channel/handlers/PlayerInteractionHandler.java @@ -28,6 +28,7 @@ import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import constants.ItemConstants; +import constants.ServerConstants; import java.util.Arrays; @@ -234,7 +235,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { } else if (!merchant.isOpen()) { chr.dropMessage(1, "This shop is in maintenance, please come by later."); return; - } else if (merchant.getFreeSlot() == -1) { + } else if (merchant.getFreeSlotThreadsafe() == -1) { chr.dropMessage(1, "This shop has reached it's maximum capacity, please come by later."); return; } else { @@ -259,10 +260,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { game.chat(c, slea.readMapleAsciiString()); } } else if (merchant != null) { - String message = chr.getName() + " : " + slea.readMapleAsciiString(); - byte slot = (byte) (merchant.getVisitorSlot(c.getPlayer()) + 1); - merchant.getMessages().add(new Pair<>(message, slot)); - merchant.broadcastToVisitors(MaplePacketCreator.hiredMerchantChat(message, slot)); + merchant.sendMessage(c.getPlayer(), slea.readMapleAsciiString()); } } else if (mode == Action.EXIT.getCode()) { if (chr.getTrade() != null) { @@ -408,7 +406,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { } else if (mode == Action.CONFIRM.getCode()) { MapleTrade.completeTrade(c.getPlayer()); } else if (mode == Action.ADD_ITEM.getCode() || mode == Action.PUT_ITEM.getCode()) { - MapleInventoryType type = MapleInventoryType.getByType(slea.readByte()); + MapleInventoryType type = MapleInventoryType.getByType(slea.readByte()); short slot = slea.readShort(); short bundles = slea.readShort(); if (chr.getInventory(type).getItem(slot) == null || chr.getItemQuantity(chr.getInventory(type).getItem(slot).getItemId(), false) < bundles || chr.getInventory(type).getItem(slot).getFlag() == ItemConstants.UNTRADEABLE) { @@ -425,6 +423,9 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { Item sellItem = ivItem.copy(); if (chr.getItemQuantity(ivItem.getItemId(), false) < perBundle * bundles) { return; + } else if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(ivItem.getItemId())) { + c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be sold on the Player Shop.")); + return; } sellItem.setQuantity(perBundle); MaplePlayerShopItem item = new MaplePlayerShopItem(sellItem, bundles, price); @@ -521,7 +522,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop)); } else if (merchant != null) { merchant.buy(c, item, quantity); - merchant.broadcastToVisitors(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer())); + merchant.broadcastToVisitorsThreadsafe(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer())); } } else if (mode == Action.TAKE_ITEM_BACK.getCode()) { HiredMerchant merchant = chr.getHiredMerchant(); @@ -555,7 +556,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { chr.setHasMerchant(false); } if (merchant != null && merchant.isOwner(c.getPlayer())) { - merchant.getMessages().clear(); + merchant.clearMessages(); merchant.setOpen(true); } chr.setHiredMerchant(null); diff --git a/src/net/server/channel/handlers/TakeDamageHandler.java b/src/net/server/channel/handlers/TakeDamageHandler.java index a9c082233c..9f1e5e9759 100644 --- a/src/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/net/server/channel/handlers/TakeDamageHandler.java @@ -117,8 +117,14 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { return; } } catch(ClassCastException e) { - e.printStackTrace(); - FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity."); + //this happens due to mob on last map damaging player just before changing maps + + if(ServerConstants.USE_DEBUG) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity."); + } + + return; } direction = slea.readByte(); diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 565bd9e69b..17683c65a1 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -86,7 +86,6 @@ public class World { private long mountUpdate; private Map activeMerchants = new LinkedHashMap<>(); - private ScheduledFuture MerchantsSchedule; private long merchantUpdate; private ScheduledFuture charactersSchedule; diff --git a/src/server/maps/HiredMerchant.java b/src/server/maps/HiredMerchant.java index c5a1fc527e..4761df7a3c 100644 --- a/src/server/maps/HiredMerchant.java +++ b/src/server/maps/HiredMerchant.java @@ -73,7 +73,13 @@ public class HiredMerchant extends AbstractMapleMapObject { this.map = owner.getMap(); } - public void broadcastToVisitors(final byte[] packet) { + public void broadcastToVisitorsThreadsafe(final byte[] packet) { + synchronized(visitors) { + broadcastToVisitors(packet); + } + } + + private void broadcastToVisitors(final byte[] packet) { for (MapleCharacter visitor : visitors) { if (visitor != null) { visitor.getClient().announce(packet); @@ -82,27 +88,37 @@ public class HiredMerchant extends AbstractMapleMapObject { } public void addVisitor(MapleCharacter visitor) { - int i = this.getFreeSlot(); - if (i > -1) { - visitors[i] = visitor; - broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1)); - } - } - - public void removeVisitor(MapleCharacter visitor) { - int slot = getVisitorSlot(visitor); - if (slot < 0){ //Not found - return; - } - if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) { - visitors[slot] = null; - if (slot != -1) { - broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1)); + synchronized(visitors) { + int i = this.getFreeSlot(); + if (i > -1) { + visitors[i] = visitor; + broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1)); } } } - public int getVisitorSlot(MapleCharacter visitor) { + public void removeVisitor(MapleCharacter visitor) { + synchronized(visitors) { + int slot = getVisitorSlot(visitor); + if (slot < 0){ //Not found + return; + } + if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) { + visitors[slot] = null; + if (slot != -1) { + broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1)); + } + } + } + } + + public int getVisitorSlotThreadsafe(MapleCharacter visitor) { + synchronized(visitors) { + return getVisitorSlot(visitor); + } + } + + private int getVisitorSlot(MapleCharacter visitor) { for (int i = 0; i < 3; i++) { if (visitors[i] != null && visitors[i].getId() == visitor.getId()){ return i; @@ -112,21 +128,24 @@ public class HiredMerchant extends AbstractMapleMapObject { } public void removeAllVisitors(String message) { - for (int i = 0; i < 3; i++) { - if (visitors[i] != null) { - visitors[i].setHiredMerchant(null); - visitors[i].getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11)); - if (message.length() > 0) { - visitors[i].dropMessage(1, message); + synchronized(visitors) { + for (int i = 0; i < 3; i++) { + if (visitors[i] != null) { + visitors[i].setHiredMerchant(null); + visitors[i].getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11)); + if (message.length() > 0) { + visitors[i].dropMessage(1, message); + } + visitors[i] = null; } - visitors[i] = null; } } } public void buy(MapleClient c, int item, short quantity) { - MaplePlayerShopItem pItem = items.get(item); synchronized (items) { + MaplePlayerShopItem pItem = items.get(item); + Item newItem = pItem.getItem().copy(); newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity))); if ((newItem.getFlag() & ItemConstants.KARMA) == ItemConstants.KARMA) { @@ -142,11 +161,15 @@ public class HiredMerchant extends AbstractMapleMapObject { c.announce(MaplePacketCreator.enableActions()); return; } - int price = pItem.getPrice() * quantity; + int price = (int)Math.min((long)pItem.getPrice() * quantity, Integer.MAX_VALUE); if (c.getPlayer().getMeso() >= price) { if (MapleInventoryManipulator.addFromDrop(c, newItem, true)) { c.getPlayer().gainMeso(-price, false); - sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price)); + + synchronized (sold) { + sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price)); + } + pItem.setBundles((short) (pItem.getBundles() - quantity)); if (pItem.getBundles() < 1) { pItem.setDoesExist(false); @@ -187,7 +210,9 @@ public class HiredMerchant extends AbstractMapleMapObject { try { saveItems(true); - items.clear(); + synchronized (items) { + items.clear(); + } } catch (SQLException ex) { ex.printStackTrace(); } @@ -234,15 +259,19 @@ public class HiredMerchant extends AbstractMapleMapObject { con.close(); } - if (check(c.getPlayer(), getItems()) && !timeout) { - for (MaplePlayerShopItem mpsi : getItems()) { + List copyItems = getItems(); + if (check(c.getPlayer(), copyItems) && !timeout) { + for (MaplePlayerShopItem mpsi : copyItems) { if (mpsi.isExist() && (mpsi.getItem().getType() == MapleInventoryType.EQUIP.getType())) { MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), false); } else if (mpsi.isExist()) { MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), null, -1, mpsi.getItem().getFlag(), mpsi.getItem().getExpiration()); } } - items.clear(); + + synchronized (items) { + items.clear(); + } } try { @@ -251,7 +280,9 @@ public class HiredMerchant extends AbstractMapleMapObject { e.printStackTrace(); } - items.clear(); + synchronized (items) { + items.clear(); + } } catch (Exception e) { e.printStackTrace(); } @@ -264,7 +295,9 @@ public class HiredMerchant extends AbstractMapleMapObject { } public void clearItems() { - items.clear(); + synchronized (items) { + items.clear(); + } } public int getOwnerId() { @@ -276,15 +309,25 @@ public class HiredMerchant extends AbstractMapleMapObject { } public MapleCharacter[] getVisitors() { - return visitors; + synchronized(visitors) { + MapleCharacter[] copy = new MapleCharacter[3]; + for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i]; + + return copy; + } } public List getItems() { - return Collections.unmodifiableList(items); + synchronized (items) { + return Collections.unmodifiableList(items); + } } public void addItem(MaplePlayerShopItem item) { - items.add(item); + synchronized (items) { + items.add(item); + } + try { this.saveItems(false); } catch (SQLException ex) { @@ -293,7 +336,10 @@ public class HiredMerchant extends AbstractMapleMapObject { } public void removeFromSlot(int slot) { - items.remove(slot); + synchronized (items) { + items.remove(slot); + } + try { this.saveItems(false); } catch (SQLException ex) { @@ -301,8 +347,13 @@ public class HiredMerchant extends AbstractMapleMapObject { } } + public int getFreeSlotThreadsafe() { + synchronized(visitors) { + return getFreeSlot(); + } + } - public int getFreeSlot() { + private int getFreeSlot() { for (int i = 0; i < 3; i++) { if (visitors[i] == null) { return i; @@ -330,24 +381,38 @@ public class HiredMerchant extends AbstractMapleMapObject { public boolean isOwner(MapleCharacter chr) { return chr.getId() == ownerId; } + + public void sendMessage(MapleCharacter chr, String msg) { + String message = chr.getName() + " : " + msg; + byte slot = (byte) (getVisitorSlot(chr) + 1); + + synchronized (messages) { + messages.add(new Pair<>(message, slot)); + } + broadcastToVisitors(MaplePacketCreator.hiredMerchantChat(message, slot)); + } public void saveItems(boolean shutdown) throws SQLException { List> itemsWithType = new ArrayList<>(); + List bundles = new ArrayList<>(); - for (MaplePlayerShopItem pItems : items) { + for (MaplePlayerShopItem pItems : getItems()) { Item newItem = pItems.getItem(); - if (shutdown) { - newItem.setQuantity((short) (pItems.getItem().getQuantity() * pItems.getBundles())); + short newBundle = pItems.getBundles(); + + if (shutdown) { //is "shutdown" really necessary? + newItem.setQuantity((short) (pItems.getItem().getQuantity())); } else { - newItem.setQuantity(pItems.getItem().getQuantity()); + newItem.setQuantity((short) (pItems.getItem().getQuantity())); } - if (pItems.getBundles() > 0) { + if (newBundle > 0) { itemsWithType.add(new Pair<>(newItem, MapleInventoryType.getByType(newItem.getType()))); + bundles.add(newBundle); } } Connection con = DatabaseConnection.getConnection(); - ItemFactory.MERCHANT.saveItems(itemsWithType, this.ownerId, con); + ItemFactory.MERCHANT.saveItems(itemsWithType, bundles, this.ownerId, con); con.close(); } @@ -405,8 +470,21 @@ public class HiredMerchant extends AbstractMapleMapObject { return (int) ((System.currentTimeMillis() - start) / 1000); } + public void clearMessages() { + synchronized (messages) { + messages.clear(); + } + } + public List> getMessages() { - return messages; + synchronized (messages) { + List> msgList = new LinkedList<>(); + for(Pair m : messages) { + msgList.add(m); + } + + return msgList; + } } public int getMapId() { @@ -414,7 +492,9 @@ public class HiredMerchant extends AbstractMapleMapObject { } public List getSold() { - return sold; + synchronized (sold) { + return Collections.unmodifiableList(sold); + } } public int getMesos() { diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 0baa484756..528b9955e4 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -4959,7 +4959,7 @@ public class MaplePacketCreator { mplew.writeInt(item.getBundles()); mplew.writeInt(item.getPrice()); mplew.writeInt(hm.getOwnerId()); - mplew.write(hm.getFreeSlot() == -1 ? 1 : 0); + mplew.write(hm.getFreeSlotThreadsafe() == -1 ? 1 : 0); MapleCharacter chr = c.getChannelServer().getPlayerStorage().getCharacterById(hm.getOwnerId()); if ((chr != null) && (c.getChannel() == hm.getChannel())) { mplew.write(1); @@ -5004,7 +5004,7 @@ public class MaplePacketCreator { mplew.write(PlayerInteractionHandler.Action.ROOM.getCode()); mplew.write(0x05); mplew.write(0x04); - mplew.writeShort(hm.getVisitorSlot(chr) + 1); + mplew.writeShort(hm.getVisitorSlotThreadsafe(chr) + 1); mplew.writeInt(hm.getItemId()); mplew.writeMapleAsciiString("Hired Merchant"); for (int i = 0; i < 3; i++) { @@ -5016,10 +5016,12 @@ public class MaplePacketCreator { } mplew.write(-1); if (hm.isOwner(chr)) { - mplew.writeShort(hm.getMessages().size()); - for (int i = 0; i < hm.getMessages().size(); i++) { - mplew.writeMapleAsciiString(hm.getMessages().get(i).getLeft()); - mplew.write(hm.getMessages().get(i).getRight()); + List> msgList = hm.getMessages(); + + mplew.writeShort(msgList.size()); + for (int i = 0; i < msgList.size(); i++) { + mplew.writeMapleAsciiString(msgList.get(i).getLeft()); + mplew.write(msgList.get(i).getRight()); } } else { mplew.writeShort(0);