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)