diff --git a/build/built-jar.properties b/build/built-jar.properties index eb2bbb7156..52bb8b603b 100644 --- a/build/built-jar.properties +++ b/build/built-jar.properties @@ -1,4 +1,4 @@ -#Thu, 07 Sep 2017 21:09:39 -0300 +#Mon, 11 Sep 2017 16:18:43 -0300 C\:\\Nexon\\MapleSolaxia\\MapleSolaxiaV2= diff --git a/build/classes/client/MapleCharacter$10.class b/build/classes/client/MapleCharacter$10.class index e4d584badb..6288a5b682 100644 Binary files a/build/classes/client/MapleCharacter$10.class and b/build/classes/client/MapleCharacter$10.class differ diff --git a/build/classes/client/MapleCharacter$11.class b/build/classes/client/MapleCharacter$11.class index aff0cca38c..896d1852e9 100644 Binary files a/build/classes/client/MapleCharacter$11.class and b/build/classes/client/MapleCharacter$11.class differ diff --git a/build/classes/client/MapleCharacter$12.class b/build/classes/client/MapleCharacter$12.class index b55d35b3eb..db65bed4c2 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 4e1bf41c99..28a9df55d0 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 1e7494e849..8320c4ad1a 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 2c32f8e7ff..64490039ff 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 63ce711b6e..8e2dcbf52a 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 6225754c96..cfd2a3d718 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 ad6887ed88..d6a712f722 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 7fa78d4ccf..bf52bb64a2 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 86bbd5007c..fb19c80949 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 f3130fe2f6..11fdd7ac6d 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 449e60db5b..51d48dc42c 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 2dbc6654bb..76aa3a3bcb 100644 Binary files a/build/classes/client/MapleCharacter.class and b/build/classes/client/MapleCharacter.class differ diff --git a/build/classes/client/MonsterBook.class b/build/classes/client/MonsterBook.class index 643c23d0ee..27da2f731d 100644 Binary files a/build/classes/client/MonsterBook.class and b/build/classes/client/MonsterBook.class differ diff --git a/build/classes/client/inventory/ItemFactory.class b/build/classes/client/inventory/ItemFactory.class index cefff5b7c7..f947434d16 100644 Binary files a/build/classes/client/inventory/ItemFactory.class and b/build/classes/client/inventory/ItemFactory.class differ diff --git a/build/classes/client/inventory/MapleInventory.class b/build/classes/client/inventory/MapleInventory.class index 391b267557..5298513a86 100644 Binary files a/build/classes/client/inventory/MapleInventory.class and b/build/classes/client/inventory/MapleInventory.class differ diff --git a/build/classes/constants/ServerConstants.class b/build/classes/constants/ServerConstants.class index 76d60f2dfc..2a82ec33ac 100644 Binary files a/build/classes/constants/ServerConstants.class and b/build/classes/constants/ServerConstants.class differ diff --git a/build/classes/net/server/channel/handlers/EnterMTSHandler.class b/build/classes/net/server/channel/handlers/EnterMTSHandler.class index a6bb2845b2..32b70e1dbe 100644 Binary files a/build/classes/net/server/channel/handlers/EnterMTSHandler.class and b/build/classes/net/server/channel/handlers/EnterMTSHandler.class differ diff --git a/build/classes/net/server/channel/handlers/SpawnPetHandler.class b/build/classes/net/server/channel/handlers/SpawnPetHandler.class index 92d19be4db..1b9ef92edc 100644 Binary files a/build/classes/net/server/channel/handlers/SpawnPetHandler.class and b/build/classes/net/server/channel/handlers/SpawnPetHandler.class differ diff --git a/build/classes/net/server/world/World$1.class b/build/classes/net/server/world/World$1.class index 2604d45938..178a4b554d 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 a08e8623d0..3909cebb55 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/CashShop$CashItem.class b/build/classes/server/CashShop$CashItem.class index ebd945202e..f1ff95903e 100644 Binary files a/build/classes/server/CashShop$CashItem.class and b/build/classes/server/CashShop$CashItem.class differ diff --git a/build/classes/server/CashShop$CashItemFactory.class b/build/classes/server/CashShop$CashItemFactory.class index d63e6359f0..e2e82156e5 100644 Binary files a/build/classes/server/CashShop$CashItemFactory.class and b/build/classes/server/CashShop$CashItemFactory.class differ diff --git a/build/classes/server/CashShop$SpecialCashItem.class b/build/classes/server/CashShop$SpecialCashItem.class index 8edc06c686..90d36697dd 100644 Binary files a/build/classes/server/CashShop$SpecialCashItem.class and b/build/classes/server/CashShop$SpecialCashItem.class differ diff --git a/build/classes/server/CashShop.class b/build/classes/server/CashShop.class index 978ab0c668..42512e0485 100644 Binary files a/build/classes/server/CashShop.class and b/build/classes/server/CashShop.class differ diff --git a/build/classes/server/MapleStorage$1.class b/build/classes/server/MapleStorage$1.class index 6be26693a1..56577554eb 100644 Binary files a/build/classes/server/MapleStorage$1.class and b/build/classes/server/MapleStorage$1.class differ diff --git a/build/classes/server/MapleStorage.class b/build/classes/server/MapleStorage.class index 9c0d792019..09d3bad26d 100644 Binary files a/build/classes/server/MapleStorage.class and b/build/classes/server/MapleStorage.class differ diff --git a/build/classes/server/life/MapleLifeFactory$BanishInfo.class b/build/classes/server/life/MapleLifeFactory$BanishInfo.class index da99b5b0b7..367e926624 100644 Binary files a/build/classes/server/life/MapleLifeFactory$BanishInfo.class and b/build/classes/server/life/MapleLifeFactory$BanishInfo.class differ diff --git a/build/classes/server/life/MapleLifeFactory$loseItem.class b/build/classes/server/life/MapleLifeFactory$loseItem.class index 90cb92865f..ebd1e0ee53 100644 Binary files a/build/classes/server/life/MapleLifeFactory$loseItem.class and b/build/classes/server/life/MapleLifeFactory$loseItem.class differ diff --git a/build/classes/server/life/MapleLifeFactory$selfDestruction.class b/build/classes/server/life/MapleLifeFactory$selfDestruction.class index 7d342cf72e..c89f4cb11a 100644 Binary files a/build/classes/server/life/MapleLifeFactory$selfDestruction.class and b/build/classes/server/life/MapleLifeFactory$selfDestruction.class differ diff --git a/build/classes/server/life/MapleLifeFactory.class b/build/classes/server/life/MapleLifeFactory.class index d02c73679b..44140cac11 100644 Binary files a/build/classes/server/life/MapleLifeFactory.class and b/build/classes/server/life/MapleLifeFactory.class differ diff --git a/build/classes/server/maps/MapleMap.class b/build/classes/server/maps/MapleMap.class index e464772d79..54c6b72b03 100644 Binary files a/build/classes/server/maps/MapleMap.class and b/build/classes/server/maps/MapleMap.class differ diff --git a/build/classes/tools/FilePrinter.class b/build/classes/tools/FilePrinter.class index 65c6c85bf8..54a30c3c80 100644 Binary files a/build/classes/tools/FilePrinter.class and b/build/classes/tools/FilePrinter.class differ diff --git a/dist/MapleSolaxia.jar b/dist/MapleSolaxia.jar index 161edee9cd..0bf1e8f864 100644 Binary files a/dist/MapleSolaxia.jar and b/dist/MapleSolaxia.jar differ diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 81bb263610..0e02a283f9 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -520,4 +520,11 @@ Corrigido itens saindo para fora do mapa. Nova posi 07 Setembro 2017, Adicionado Vega's Spell. -3rd job event agora possui tempo-limite e verifica se há jogadores já enfrentando o desafio. \ No newline at end of file +3rd job event agora possui tempo-limite e verifica se há jogadores já enfrentando o desafio. + +10 Setembro 2017, +Protegido contra acesso concorrente classes MonsterBook e MapleInventory. +Nova feature: autosaver. + +11 Setembro 2017, +Consertado pet ignore não recuperando dados dos pets em alguns casos. \ No newline at end of file diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml index 5af0ed1acc..3684d06bb3 100644 --- a/nbproject/private/private.xml +++ b/nbproject/private/private.xml @@ -2,6 +2,29 @@ - + + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/inventory/MaplePet.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/EnterMTSHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PlayerLoggedinHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MonsterBook.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/ItemConstants.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleStorage.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/world/World.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/ServerConstants.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleItemInformationProvider.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/inventory/MapleInventory.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/tools/MaplePacketCreator.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PetExcludeItemsHandler.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/inventory/ItemFactory.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/tools/FilePrinter.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/PlayerStorage.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/Server.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/CouponWorker.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleInventoryManipulator.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MapleCharacter.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/CharacterAutosaverWorker.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/CashShop.java + file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/scripting/event/EventManager.java + diff --git a/scripts/npc/2091005.js b/scripts/npc/2091005.js index f9bc8f97eb..1e9d00de64 100644 --- a/scripts/npc/2091005.js +++ b/scripts/npc/2091005.js @@ -208,7 +208,7 @@ function action(mode, type, selection) { var selStr = "You have #b" + cm.getPlayer().getDojoPoints() + "#k training points. Master prefers those with great talent. If you obtain more points than the average, you can receive a belt depending on your score.\r\n"; for (var i = 0; i < belts.length; i++) { if (cm.getPlayer().getItemQuantity(belts[i], true) > 0) { - selStr += "\r\n#L" + i + "##i" + belts[i] + "# #t" + belts[i] + "# (Obtained)"; + selStr += "\r\n#L" + i + "##i" + belts[i] + "# #t" + belts[i] + "# (Already on inventory)"; } else selStr += "\r\n#L" + i + "##i" + belts[i] + "# #t" + belts[i] + "#"; } @@ -220,6 +220,7 @@ function action(mode, type, selection) { if (cm.getPlayer().getDojoPoints() >= points) { if (cm.getPlayer().getLevel() > level) { cm.gainItem(belt, 1); + cm.getPlayer().setDojoPoints(cm.getPlayer().getDojoPoints() - points); cm.sendNext("There is the #i" + belt + "# #b#t" + belt + "##k. You have proven your valor to ascend on the Dojo ranks. Well done!"); } else diff --git a/scripts/npc/9010022.js b/scripts/npc/9010022.js index 31c1fea1ef..c09e542d3d 100644 --- a/scripts/npc/9010022.js +++ b/scripts/npc/9010022.js @@ -22,7 +22,7 @@ function action(mode, type, selection) { else status--; if (status == 0) { - if (cm.getLevel() < 20) { + if (cm.getLevel() < 25) { cm.sendDimensionalMirror("#-1# There is no place for you to transport to from here."); cm.dispose(); } else { diff --git a/sql/db_database.sql b/sql/db_database.sql index 131bb622e5..0972f34afc 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -21222,7 +21222,7 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `position`) VALUES (1052116, 2000002, 320, 212), (1052116, 2000001, 160, 216), (1052116, 2000000, 50, 220), -(9120002, 2061003, 40, 0, 100); +(9120002, 2061003, 40, 100); CREATE TABLE IF NOT EXISTS `skillmacros` ( `id` int(11) NOT NULL AUTO_INCREMENT, diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 7bd04b4ae9..fbe5b3234c 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -4529,26 +4529,31 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } - PreparedStatement ps2; - ResultSet rs2; - for(byte i = 0; i < 3; i++) { - MaplePet pet = ret.getPet(i); - if(pet == null) continue; - - int petId = pet.getUniqueId(); - ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?"); // Get pet details.. + PreparedStatement ps2, ps3; + ResultSet rs2, rs3; + + ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid > -1"); + ps3.setInt(1, charid); + rs3 = ps3.executeQuery(); + while(rs3.next()) { + int petId = rs3.getInt("petid"); + + ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?"); ps2.setInt(1, petId); - + ret.resetExcluded(petId); rs2 = ps2.executeQuery(); while(rs2.next()) { ret.addExcluded(petId, rs2.getInt("itemid")); } - + ps2.close(); rs2.close(); } + ps3.close(); + rs3.close(); + ret.commitExcludedItems(); if (channelserver) { @@ -5525,10 +5530,28 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } - // synchronize this call instead of trying to give access all at once (?) - public synchronized void saveToDB() { + public void saveToDB() { + if(ServerConstants.USE_AUTOSAVE) { + Runnable r = new Runnable() { + @Override + public void run() { + saveToDB(true); + } + }; + + Thread t = new Thread(r); //spawns a new thread to deal with this + t.start(); + } else { + saveToDB(true); + } + } + + public synchronized void saveToDB(boolean notAutosave) { Calendar c = Calendar.getInstance(); - FilePrinter.print(FilePrinter.SAVING_CHARACTER, "Attempting to save " + name + " at " + c.getTime().toString()); + + if(notAutosave) FilePrinter.print(FilePrinter.SAVING_CHARACTER, "Attempting to save " + name + " at " + c.getTime().toString()); + else FilePrinter.print(FilePrinter.AUTOSAVING_CHARACTER, "Attempting to autosave " + name + " at " + c.getTime().toString()); + Connection con = null; try { con = DatabaseConnection.getConnection(); @@ -5577,7 +5600,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } ps.setInt(22, meso.get()); ps.setInt(23, hpMpApUsed); - if (map == null || map.getId() == 610020000 || map.getId() == 610020001) { + if (map == null || map.getId() == 610020000 || map.getId() == 610020001) { // reset to first spawnpoint on those maps ps.setInt(24, 0); } else { MaplePortal closest = map.findClosestPlayerSpawnpoint(getPosition()); @@ -5648,7 +5671,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { petLock.unlock(); } - for(Entry> es: getExcluded().entrySet()) { + for(Entry> es: getExcluded().entrySet()) { // this set is already protected try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM petignores WHERE petid=?")) { ps2.setInt(1, es.getKey()); ps2.executeUpdate(); @@ -5667,8 +5690,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { deleteWhereCharacterId(con, "DELETE FROM keymap WHERE characterid = ?"); ps = con.prepareStatement("INSERT INTO keymap (characterid, `key`, `type`, `action`) VALUES (?, ?, ?, ?)"); ps.setInt(1, id); - for (Entry keybinding : keymap.entrySet()) { - ps.setInt(2, keybinding.getKey().intValue()); + + Set> keybindingItems = Collections.unmodifiableSet(keymap.entrySet()); + for (Entry keybinding : keybindingItems) { + ps.setInt(2, keybinding.getKey()); ps.setInt(3, keybinding.getValue().getType()); ps.setInt(4, keybinding.getValue().getAction()); ps.addBatch(); @@ -5691,14 +5716,13 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } ps.executeBatch(); + List> itemsWithType = new ArrayList<>(); - for (MapleInventory iv : inventory) { for (Item item : iv.list()) { itemsWithType.add(new Pair<>(item, iv.getType())); } } - ItemFactory.INVENTORY.saveItems(itemsWithType, id, con); deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?"); @@ -6283,7 +6307,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { if (slots <= 96) { inventory[type].setSlotLimit(slots); - saveToDB(); + this.saveToDB(); if (update) { client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots)); } @@ -6552,6 +6576,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { getMap().broadcastMessage(this, MaplePacketCreator.showPet(this, pet, true, hunger), true); removePet(pet, shift_left); + commitExcludedItems(); + client.announce(MaplePacketCreator.petStatUpdate(this)); client.announce(MaplePacketCreator.enableActions()); } diff --git a/src/client/MonsterBook.java b/src/client/MonsterBook.java index 3a53786194..ef76f911c7 100644 --- a/src/client/MonsterBook.java +++ b/src/client/MonsterBook.java @@ -25,9 +25,13 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import tools.DatabaseConnection; import tools.MaplePacketCreator; @@ -36,85 +40,140 @@ public final class MonsterBook { private int normalCard = 0; private int bookLevel = 1; private Map cards = new LinkedHashMap<>(); + private Lock lock = new ReentrantLock(); + private Set> getCardSet() { + lock.lock(); + try { + return Collections.unmodifiableSet(cards.entrySet()); + } finally { + lock.unlock(); + } + } + public void addCard(final MapleClient c, final int cardid) { c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignCardEffect(c.getPlayer().getId()), false); - for (Entry all : cards.entrySet()) { - if (all.getKey() == cardid) { - if (all.getValue() > 4) { - c.announce(MaplePacketCreator.addCard(true, cardid, all.getValue())); - } else { - all.setValue(all.getValue() + 1); - c.announce(MaplePacketCreator.addCard(false, cardid, all.getValue())); - c.announce(MaplePacketCreator.showGainCard()); - calculateLevel(); + + Integer qty; + lock.lock(); + try { + qty = cards.get(cardid); + + if(qty != null) { + if(qty < 5) { + cards.put(cardid, qty + 1); + } + } else { + cards.put(cardid, 1); + qty = 0; + + if (cardid / 1000 >= 2388) { + specialCard++; + } else { + normalCard++; } - return; } + } finally { + lock.unlock(); } - cards.put(cardid, 1); - c.announce(MaplePacketCreator.addCard(false, cardid, 1)); - c.announce(MaplePacketCreator.showGainCard()); - calculateLevel(); - if (cardid / 1000 >= 2388) { - specialCard++; + if(qty < 5) { + calculateLevel(); // current leveling system only accounts unique cards... + + c.announce(MaplePacketCreator.addCard(false, cardid, qty + 1)); + c.announce(MaplePacketCreator.showGainCard()); } else { - normalCard++; + c.announce(MaplePacketCreator.addCard(true, cardid, 5)); } - - //c.getPlayer().saveToDB(); //is it REALLY needed to save to DB every new entry? } private void calculateLevel() { - bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5)); + lock.lock(); + try { + bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5)); + } finally { + lock.unlock(); + } } public int getBookLevel() { - return bookLevel; + lock.lock(); + try { + return bookLevel; + } finally { + lock.unlock(); + } } public Map getCards() { - return cards; + lock.lock(); + try { + return Collections.unmodifiableMap(cards); + } finally { + lock.unlock(); + } } public int getTotalCards() { - return specialCard + normalCard; + lock.lock(); + try { + return specialCard + normalCard; + } finally { + lock.unlock(); + } } public int getNormalCard() { - return normalCard; + lock.lock(); + try { + return normalCard; + } finally { + lock.unlock(); + } } public int getSpecialCard() { - return specialCard; + lock.lock(); + try { + return specialCard; + } finally { + lock.unlock(); + } } public void loadCards(final int charid) throws SQLException { - Connection con = DatabaseConnection.getConnection(); - try (PreparedStatement ps = con.prepareStatement("SELECT cardid, level FROM monsterbook WHERE charid = ? ORDER BY cardid ASC")) { - ps.setInt(1, charid); - try (ResultSet rs = ps.executeQuery()) { - int cardid, level; - while (rs.next()) { - cardid = rs.getInt("cardid"); - level = rs.getInt("level"); - if (cardid / 1000 >= 2388) { - specialCard++; - } else { - normalCard++; + lock.lock(); + try { + Connection con = DatabaseConnection.getConnection(); + try (PreparedStatement ps = con.prepareStatement("SELECT cardid, level FROM monsterbook WHERE charid = ? ORDER BY cardid ASC")) { + ps.setInt(1, charid); + try (ResultSet rs = ps.executeQuery()) { + int cardid, level; + while (rs.next()) { + cardid = rs.getInt("cardid"); + level = rs.getInt("level"); + if (cardid / 1000 >= 2388) { + specialCard++; + } else { + normalCard++; + } + cards.put(cardid, level); } - cards.put(cardid, level); } } + + con.close(); + } finally { + lock.unlock(); } - con.close(); calculateLevel(); } public void saveCards(final int charid) { - if (cards.isEmpty()) { + Set> cardSet = getCardSet(); + + if (cardSet.isEmpty()) { return; } try { @@ -125,7 +184,7 @@ public final class MonsterBook { ps.close(); boolean first = true; StringBuilder query = new StringBuilder(); - for (Entry all : cards.entrySet()) { + for (Entry all : cardSet) { if (first) { query.append("INSERT INTO monsterbook VALUES ("); first = false; diff --git a/src/client/inventory/ItemFactory.java b/src/client/inventory/ItemFactory.java index caa41c75ae..5bd1d98401 100644 --- a/src/client/inventory/ItemFactory.java +++ b/src/client/inventory/ItemFactory.java @@ -141,7 +141,6 @@ public enum ItemFactory { ResultSet rs = null; lock.lock(); - try { StringBuilder query = new StringBuilder(); query.append("DELETE `inventoryitems`, `inventoryequipment` FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `"); diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java index 851450cd00..b527f8dc20 100644 --- a/src/client/inventory/MapleInventory.java +++ b/src/client/inventory/MapleInventory.java @@ -29,6 +29,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import tools.Pair; import client.MapleCharacter; @@ -36,6 +38,7 @@ import client.MapleClient; import constants.ItemConstants; import server.MapleItemInformationProvider; import server.MapleInventoryManipulator; +import tools.FilePrinter; /** * @@ -47,6 +50,7 @@ public class MapleInventory implements Iterable { private byte slotLimit; private MapleInventoryType type; private boolean checked = false; + private Lock lock = new ReentrantLock(); public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) { this.owner = mc; @@ -64,15 +68,34 @@ public class MapleInventory implements Iterable { } public byte getSlotLimit() { - return slotLimit; + lock.lock(); + try { + return slotLimit; + } finally { + lock.unlock(); + } } public void setSlotLimit(int newLimit) { - slotLimit = (byte) newLimit; + lock.lock(); + try { + slotLimit = (byte) newLimit; + } finally { + lock.unlock(); + } } + public Collection list() { + lock.lock(); + try { + return Collections.unmodifiableCollection(inventory.values()); + } finally { + lock.unlock(); + } + } + public Item findById(int itemId) { - for (Item item : inventory.values()) { + for (Item item : list()) { if (item.getItemId() == itemId) { return item; } @@ -81,10 +104,10 @@ public class MapleInventory implements Iterable { } public Item findByName(String name) { - for (Item item : inventory.values()) { + for (Item item : list()) { String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId()); if(itemName == null) { - System.out.println("[CRITICAL] Item " + item.getItemId() + " has no name."); + FilePrinter.printError(FilePrinter.EXCEPTION, "[CRITICAL] Item " + item.getItemId() + " has no name."); continue; } @@ -97,7 +120,7 @@ public class MapleInventory implements Iterable { public int countById(int itemId) { int qty = 0; - for (Item item : inventory.values()) { + for (Item item : list()) { if (item.getItemId() == itemId) { qty += item.getQuantity(); } @@ -138,7 +161,7 @@ public class MapleInventory implements Iterable { public List listById(int itemId) { List ret = new ArrayList<>(); - for (Item item : inventory.values()) { + for (Item item : list()) { if (item.getItemId() == itemId) { ret.add(item); } @@ -149,10 +172,6 @@ public class MapleInventory implements Iterable { return ret; } - public Collection list() { - return inventory.values(); - } - public short addItem(Item item) { short slotId = getNextFreeSlot(); if (slotId < 0 || item == null) { @@ -171,29 +190,34 @@ public class MapleInventory implements Iterable { } public void move(short sSlot, short dSlot, short slotMax) { - Item source = (Item) inventory.get(sSlot); - Item target = (Item) inventory.get(dSlot); - if (source == null) { - return; - } - if (target == null) { - source.setPosition(dSlot); - inventory.put(dSlot, source); - inventory.remove(sSlot); - } else if (target.getItemId() == source.getItemId() && !ItemConstants.isRechargable(source.getItemId())) { - if (type.getType() == MapleInventoryType.EQUIP.getType()) { + lock.lock(); + try { + Item source = (Item) inventory.get(sSlot); + Item target = (Item) inventory.get(dSlot); + if (source == null) { + return; + } + if (target == null) { + source.setPosition(dSlot); + inventory.put(dSlot, source); + inventory.remove(sSlot); + } else if (target.getItemId() == source.getItemId() && !ItemConstants.isRechargable(source.getItemId())) { + if (type.getType() == MapleInventoryType.EQUIP.getType()) { + swap(target, source); + } + if (source.getQuantity() + target.getQuantity() > slotMax) { + short rest = (short) ((source.getQuantity() + target.getQuantity()) - slotMax); + source.setQuantity(rest); + target.setQuantity(slotMax); + } else { + target.setQuantity((short) (source.getQuantity() + target.getQuantity())); + inventory.remove(sSlot); + } + } else { swap(target, source); } - if (source.getQuantity() + target.getQuantity() > slotMax) { - short rest = (short) ((source.getQuantity() + target.getQuantity()) - slotMax); - source.setQuantity(rest); - target.setQuantity(slotMax); - } else { - target.setQuantity((short) (source.getQuantity() + target.getQuantity())); - inventory.remove(sSlot); - } - } else { - swap(target, source); + } finally { + lock.unlock(); } } @@ -208,7 +232,12 @@ public class MapleInventory implements Iterable { } public Item getItem(short slot) { - return inventory.get(slot); + lock.lock(); + try { + return inventory.get(slot); + } finally { + lock.unlock(); + } } public void removeItem(short slot) { @@ -216,7 +245,7 @@ public class MapleInventory implements Iterable { } public void removeItem(short slot, short quantity, boolean allowZero) { - Item item = inventory.get(slot); + Item item = getItem(slot); if (item == null) {// TODO is it ok not to throw an exception here? return; } @@ -230,7 +259,12 @@ public class MapleInventory implements Iterable { } public void addSlot(short slot, Item item) { - inventory.put(slot, item); + lock.lock(); + try { + inventory.put(slot, item); + } finally { + lock.unlock(); + } if(ItemConstants.isRateCoupon(item.getItemId())) { owner.updateCouponRates(); @@ -238,7 +272,13 @@ public class MapleInventory implements Iterable { } public void removeSlot(short slot) { - Item item = inventory.remove(slot); + Item item; + lock.lock(); + try { + item = inventory.remove(slot); + } finally { + lock.unlock(); + } if(item != null && ItemConstants.isRateCoupon(item.getItemId())) { owner.updateCouponRates(); @@ -246,40 +286,67 @@ public class MapleInventory implements Iterable { } public boolean isFull() { - return inventory.size() >= slotLimit; + lock.lock(); + try { + return inventory.size() >= slotLimit; + } finally { + lock.unlock(); + } } public boolean isFull(int margin) { - return inventory.size() + margin >= slotLimit; + lock.lock(); + try { + return inventory.size() + margin >= slotLimit; + } finally { + lock.unlock(); + } } public boolean isFullAfterSomeItems(int margin, int used) { - return inventory.size() + margin >= slotLimit - used; + lock.lock(); + try { + return inventory.size() + margin >= slotLimit - used; + } finally { + lock.unlock(); + } } public short getNextFreeSlot() { if (isFull()) { return -1; } - for (short i = 1; i <= slotLimit; i++) { - if (!inventory.keySet().contains(i)) { - return i; + + lock.lock(); + try { + for (short i = 1; i <= slotLimit; i++) { + if (!inventory.containsKey(i)) { + return i; + } } + return -1; + } finally { + lock.unlock(); } - return -1; } public short getNumFreeSlot() { if (isFull()) { return 0; } - short free = 0; - for (short i = 1; i <= slotLimit; i++) { - if (!inventory.keySet().contains(i)) { - free++; - } - } - return free; + + lock.lock(); + try { + short free = 0; + for (short i = 1; i <= slotLimit; i++) { + if (!inventory.containsKey(i)) { + free++; + } + } + return free; + } finally { + lock.unlock(); + } } public static boolean checkSpot(MapleCharacter chr, Item item) { @@ -394,7 +461,7 @@ public class MapleInventory implements Iterable { @Override public Iterator iterator() { - return Collections.unmodifiableCollection(inventory.values()).iterator(); + return Collections.unmodifiableCollection(list()).iterator(); } public Collection allInventories() { @@ -404,7 +471,7 @@ public class MapleInventory implements Iterable { public Item findByCashId(int cashId) { boolean isRing = false; Equip equip = null; - for (Item item : inventory.values()) { + for (Item item : list()) { if (item.getType() == MapleInventoryType.EQUIP.getType()) { equip = (Equip) item; isRing = equip.getRingId() > -1; @@ -417,10 +484,20 @@ public class MapleInventory implements Iterable { } public boolean checked() { - return checked; + lock.lock(); + try { + return checked; + } finally { + lock.unlock(); + } } public void checked(boolean yes) { - checked = yes; + lock.lock(); + try { + checked = yes; + } finally { + lock.unlock(); + } } } \ No newline at end of file diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index bcadd516a4..80937e8543 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -44,6 +44,7 @@ public class ServerConstants { 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_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 to required secondary stat on equipments. public static final boolean USE_REFRESH_RANK_MOVE = true; 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) diff --git a/src/net/server/CharacterAutosaverWorker.java b/src/net/server/CharacterAutosaverWorker.java new file mode 100644 index 0000000000..a121fdb87b --- /dev/null +++ b/src/net/server/CharacterAutosaverWorker.java @@ -0,0 +1,50 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation 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 . +*/ + +package net.server; + +import net.server.world.World; +import client.MapleCharacter; +import constants.ServerConstants; + +/** + * @author Ronan + */ +public class CharacterAutosaverWorker implements Runnable { + private World wserv; + + @Override + public void run() { + if(!ServerConstants.USE_AUTOSAVE) return; + + PlayerStorage ps = wserv.getPlayerStorage(); + for(MapleCharacter chr: ps.getAllCharacters()) { + if(chr != null && chr.isLoggedin()) { + chr.saveToDB(false); + } + } + } + + public CharacterAutosaverWorker(World world) { + wserv = world; + } +} diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java index d932bc4b50..8629ed3747 100644 --- a/src/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/net/server/channel/handlers/EnterMTSHandler.java @@ -61,7 +61,6 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); chr.cancelExpirationTask(); chr.saveToDB(); - System.out.println("STRANGE SAVE TO DB"); chr.getMap().removePlayer(c.getPlayer()); try { c.announce(MaplePacketCreator.openCashShop(c, true)); diff --git a/src/net/server/channel/handlers/SpawnPetHandler.java b/src/net/server/channel/handlers/SpawnPetHandler.java index 8ca43b91e0..e1ff85376a 100644 --- a/src/net/server/channel/handlers/SpawnPetHandler.java +++ b/src/net/server/channel/handlers/SpawnPetHandler.java @@ -90,7 +90,8 @@ public final class SpawnPetHandler extends AbstractMaplePacketHandler { chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true); c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer())); c.announce(MaplePacketCreator.enableActions()); - + + chr.commitExcludedItems(); chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet)); } } diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 7f1ca4507d..e1f81313c5 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -45,8 +45,9 @@ import java.util.HashSet; import java.util.concurrent.ScheduledFuture; import server.TimerManager; -import net.server.PetFullnessWorker; +import net.server.CharacterAutosaverWorker; import net.server.MountTirednessWorker; +import net.server.PetFullnessWorker; import net.server.PlayerStorage; import net.server.Server; import net.server.channel.Channel; @@ -82,7 +83,9 @@ public class World { private Map activeMounts = new LinkedHashMap<>(); private ScheduledFuture mountsSchedule; private long mountUpdate; - + + private ScheduledFuture charactersSchedule; + public World(int world, int flag, String eventmsg, int exprate, int droprate, int mesorate, int bossdroprate) { this.id = world; this.flag = flag; @@ -99,6 +102,7 @@ public class World { petsSchedule = TimerManager.getInstance().register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000); mountsSchedule = TimerManager.getInstance().register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000); + charactersSchedule = TimerManager.getInstance().register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000); } public List getChannels() { diff --git a/src/server/CashShop.java b/src/server/CashShop.java index 2596b20249..dd9e279bbb 100644 --- a/src/server/CashShop.java +++ b/src/server/CashShop.java @@ -30,6 +30,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import provider.MapleData; import provider.MapleDataProvider; @@ -43,6 +45,7 @@ import client.inventory.ItemFactory; import client.inventory.MapleInventoryType; import client.inventory.MaplePet; import constants.ItemConstants; +import java.util.Collections; /* * @author Flav @@ -99,12 +102,13 @@ public class CashShop { item = new Item(itemId, (byte) 0, count, petid); } - if (ItemConstants.EXPIRING_ITEMS) - if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day. - item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 4)); - } else { - item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period)); - } + if (ItemConstants.EXPIRING_ITEMS) { + if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day. + item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 4)); + } else { + item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period)); + } + } item.setSN(sn); return item; } @@ -237,6 +241,7 @@ public class CashShop { private List inventory = new ArrayList<>(); private List wishList = new ArrayList<>(); private int notes = 0; + private Lock lock = new ReentrantLock(); public CashShop(int accountId, int characterId, int jobType) throws SQLException { this.accountId = accountId; @@ -325,13 +330,18 @@ public class CashShop { } public List getInventory() { - return inventory; + lock.lock(); + try { + return Collections.unmodifiableList(inventory); + } finally { + lock.unlock(); + } } public Item findByCashId(int cashId) { boolean isRing = false; Equip equip = null; - for (Item item : inventory) { + for (Item item : getInventory()) { if (item.getType() == 1) { equip = (Equip) item; isRing = equip.getRingId() > -1; @@ -345,11 +355,21 @@ public class CashShop { } public void addToInventory(Item item) { - inventory.add(item); + lock.lock(); + try { + inventory.add(item); + } finally { + lock.unlock(); + } } public void removeFromInventory(Item item) { - inventory.remove(item); + lock.lock(); + try { + inventory.remove(item); + } finally { + lock.unlock(); + } } public List getWishList() { @@ -458,7 +478,8 @@ public class CashShop { ps.close(); List> itemsWithType = new ArrayList<>(); - for (Item item : inventory) { + List inv = getInventory(); + for (Item item : inv) { itemsWithType.add(new Pair<>(item, MapleItemInformationProvider.getInstance().getInventoryType(item.getItemId()))); } @@ -466,7 +487,7 @@ public class CashShop { ps = con.prepareStatement("DELETE FROM `wishlists` WHERE `charid` = ?"); ps.setInt(1, characterId); ps.executeUpdate(); - ps.close(); + ps.close(); ps = con.prepareStatement("INSERT INTO `wishlists` VALUES (DEFAULT, ?, ?)"); ps.setInt(1, characterId); diff --git a/src/server/MapleStorage.java b/src/server/MapleStorage.java index 47be5f5d19..f1591dfe9c 100644 --- a/src/server/MapleStorage.java +++ b/src/server/MapleStorage.java @@ -36,6 +36,8 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; @@ -47,10 +49,11 @@ import tools.Pair; public class MapleStorage { private int id; - private List items; private int meso; private byte slots; private Map> typeItems = new HashMap<>(); + private List items; + private Lock lock = new ReentrantLock(); private MapleStorage(int id, byte slots, int meso) { this.id = id; @@ -135,7 +138,8 @@ public class MapleStorage { } List> itemsWithType = new ArrayList<>(); - for (Item item : items) { + List list = getItems(); + for (Item item : list) { itemsWithType.add(new Pair<>(item, MapleItemInformationProvider.getInstance().getInventoryType(item.getItemId()))); } @@ -146,30 +150,56 @@ public class MapleStorage { } public Item getItem(byte slot) { - return items.get(slot); + lock.lock(); + try { + return items.get(slot); + } finally { + lock.unlock(); + } } public Item takeOut(byte slot) { - Item ret = items.remove(slot); + Item ret; + + lock.lock(); + try { + ret = items.remove(slot); + } finally { + lock.unlock(); + } + MapleInventoryType type = MapleItemInformationProvider.getInstance().getInventoryType(ret.getItemId()); typeItems.put(type, new ArrayList<>(filterItems(type))); return ret; } public void store(Item item) { - items.add(item); + lock.lock(); + try { + items.add(item); + } finally { + lock.unlock(); + } + MapleInventoryType type = MapleItemInformationProvider.getInstance().getInventoryType(item.getItemId()); typeItems.put(type, new ArrayList<>(filterItems(type))); } public List getItems() { - return Collections.unmodifiableList(items); + lock.lock(); + try { + return Collections.unmodifiableList(items); + } finally { + lock.unlock(); + } } private List filterItems(MapleInventoryType type) { + List storageItems = getItems(); List ret = new LinkedList<>(); MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - for (Item item : items) { + + for (Item item : storageItems) { if (ii.getInventoryType(item.getItemId()) == type) { ret.add(item); } @@ -179,7 +209,8 @@ public class MapleStorage { public byte getSlot(MapleInventoryType type, byte slot) { byte ret = 0; - for (Item item : items) { + List storageItems = getItems(); + for (Item item : storageItems) { if (item == typeItems.get(type).get(slot)) { return ret; } @@ -190,21 +221,29 @@ public class MapleStorage { public void sendStorage(MapleClient c, int npcId) { final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - Collections.sort(items, new Comparator() { - @Override - public int compare(Item o1, Item o2) { - if (ii.getInventoryType(o1.getItemId()).getType() < ii.getInventoryType(o2.getItemId()).getType()) { - return -1; - } else if (ii.getInventoryType(o1.getItemId()) == ii.getInventoryType(o2.getItemId())) { - return 0; + + lock.lock(); + try { + Collections.sort(items, new Comparator() { + @Override + public int compare(Item o1, Item o2) { + if (ii.getInventoryType(o1.getItemId()).getType() < ii.getInventoryType(o2.getItemId()).getType()) { + return -1; + } else if (ii.getInventoryType(o1.getItemId()) == ii.getInventoryType(o2.getItemId())) { + return 0; + } + return 1; } - return 1; - } - }); - for (MapleInventoryType type : MapleInventoryType.values()) { - typeItems.put(type, new ArrayList<>(items)); + }); + } finally { + lock.unlock(); } - c.announce(MaplePacketCreator.getStorage(npcId, slots, items, meso)); + + List storageItems = getItems(); + for (MapleInventoryType type : MapleInventoryType.values()) { + typeItems.put(type, new ArrayList<>(storageItems)); + } + c.announce(MaplePacketCreator.getStorage(npcId, slots, storageItems, meso)); } public void sendStored(MapleClient c, MapleInventoryType type) { @@ -231,7 +270,12 @@ public class MapleStorage { } public boolean isFull() { - return items.size() >= slots; + lock.lock(); + try { + return items.size() >= slots; + } finally { + lock.unlock(); + } } public void close() { diff --git a/src/server/life/MapleLifeFactory.java b/src/server/life/MapleLifeFactory.java index 52c28415ea..045a3dfa88 100644 --- a/src/server/life/MapleLifeFactory.java +++ b/src/server/life/MapleLifeFactory.java @@ -55,100 +55,107 @@ public class MapleLifeFactory { } public static MapleMonster getMonster(int mid) { - MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid)); - if (stats == null) { - MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11)); - if (monsterData == null) { - return null; - } - MapleData monsterInfoData = monsterData.getChildByPath("info"); - stats = new MapleMonsterStats(); - stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData)); - stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1); - stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData)); - stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData)); - stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData)); - stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData)); - stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0)); - stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0)); - stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData)); - stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0)); - stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0); - stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0); - stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0); - stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0); - stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO")); - stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1)); - stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0)); - stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0); - - MapleData special = monsterInfoData.getChildByPath("coolDamage"); - if (special != null) { - int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData); - int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0); - stats.setCool(new Pair<>(coolDmg, coolProb)); - } - special = monsterInfoData.getChildByPath("loseItem"); - if (special != null) { - for (MapleData liData : special.getChildren()) { - stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x")))); + try { + MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid)); + if (stats == null) { + MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11)); + if (monsterData == null) { + return null; } - } - special = monsterInfoData.getChildByPath("selfDestruction"); - if (special != null) { - stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1))); - } - MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack"); - int firstAttack = 0; - if (firstAttackData != null) { - if (firstAttackData.getType() == MapleDataType.FLOAT) { - firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData)); - } else { - firstAttack = MapleDataTool.getInt(firstAttackData); - } - } - stats.setFirstAttack(firstAttack > 0); - stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000); - - stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0)); - stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0)); + MapleData monsterInfoData = monsterData.getChildByPath("info"); + stats = new MapleMonsterStats(); + stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData)); + stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1); + stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData)); + stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData)); + stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData)); + stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData)); + stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0)); + stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0)); + stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData)); + stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0)); + stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0); + stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0); + stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0); + stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0); + stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO")); + stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1)); + stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0)); + stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0); - for (MapleData idata : monsterData) { - if (!idata.getName().equals("info")) { - int delay = 0; - for (MapleData pic : idata.getChildren()) { - delay += MapleDataTool.getIntConvert("delay", pic, 0); + MapleData special = monsterInfoData.getChildByPath("coolDamage"); + if (special != null) { + int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData); + int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0); + stats.setCool(new Pair<>(coolDmg, coolProb)); + } + special = monsterInfoData.getChildByPath("loseItem"); + if (special != null) { + for (MapleData liData : special.getChildren()) { + stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x")))); } - stats.setAnimationTime(idata.getName(), delay); } - } - MapleData reviveInfo = monsterInfoData.getChildByPath("revive"); - if (reviveInfo != null) { - List revives = new LinkedList<>(); - for (MapleData data_ : reviveInfo) { - revives.add(MapleDataTool.getInt(data_)); + special = monsterInfoData.getChildByPath("selfDestruction"); + if (special != null) { + stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1))); } - stats.setRevives(revives); - } - decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, "")); - MapleData monsterSkillData = monsterInfoData.getChildByPath("skill"); - if (monsterSkillData != null) { - int i = 0; - List> skills = new ArrayList<>(); - while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) { - skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0)))); - i++; + MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack"); + int firstAttack = 0; + if (firstAttackData != null) { + if (firstAttackData.getType() == MapleDataType.FLOAT) { + firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData)); + } else { + firstAttack = MapleDataTool.getInt(firstAttackData); + } } - stats.setSkills(skills); + stats.setFirstAttack(firstAttack > 0); + stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000); + + stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0)); + stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0)); + + for (MapleData idata : monsterData) { + if (!idata.getName().equals("info")) { + int delay = 0; + for (MapleData pic : idata.getChildren()) { + delay += MapleDataTool.getIntConvert("delay", pic, 0); + } + stats.setAnimationTime(idata.getName(), delay); + } + } + MapleData reviveInfo = monsterInfoData.getChildByPath("revive"); + if (reviveInfo != null) { + List revives = new LinkedList<>(); + for (MapleData data_ : reviveInfo) { + revives.add(MapleDataTool.getInt(data_)); + } + stats.setRevives(revives); + } + decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, "")); + MapleData monsterSkillData = monsterInfoData.getChildByPath("skill"); + if (monsterSkillData != null) { + int i = 0; + List> skills = new ArrayList<>(); + while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) { + skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0)))); + i++; + } + stats.setSkills(skills); + } + MapleData banishData = monsterInfoData.getChildByPath("ban"); + if (banishData != null) { + stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp"))); + } + monsterStats.put(Integer.valueOf(mid), stats); } - MapleData banishData = monsterInfoData.getChildByPath("ban"); - if (banishData != null) { - stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp"))); - } - monsterStats.put(Integer.valueOf(mid), stats); + MapleMonster ret = new MapleMonster(mid, stats); + return ret; + } catch(NullPointerException npe) { + System.out.println("[SEVERE] MOB " + mid + " failed to load. Issue: " + npe.getMessage() + "\n\n"); + npe.printStackTrace(); + + return null; } - MapleMonster ret = new MapleMonster(mid, stats); - return ret; } private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) { diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 8e50c0163d..7f66af234e 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -2325,11 +2325,11 @@ public class MapleMap { } public void setMapPointBoundings(int px, int py, int h, int w) { - mapArea.setBounds(px, py, w, h); + mapArea.setBounds(px + 7, py, w - 14, h); } public void setMapLineBoundings(int vrTop, int vrBottom, int vrLeft, int vrRight) { - mapArea.setBounds(vrLeft, vrTop, vrRight - vrLeft, vrBottom - vrTop); + mapArea.setBounds(vrLeft + 7, vrTop, vrRight - vrLeft - 14, vrBottom - vrTop); } /** diff --git a/src/tools/FilePrinter.java b/src/tools/FilePrinter.java index ed0445a58d..81fb33a7bd 100644 --- a/src/tools/FilePrinter.java +++ b/src/tools/FilePrinter.java @@ -41,6 +41,7 @@ public class FilePrinter { FREDRICK = "fredrick/", NPC_UNCODED = "uncodedNPCs.txt", QUEST_UNCODED = "uncodedQuests.txt", + AUTOSAVING_CHARACTER = "saveCharAuto.txt", SAVING_CHARACTER = "saveChar.txt", USED_COMMANDS = "usedCommands.txt";//more to come (maps)