ThreadTracker + Attempt on NPC Disappearing fix

Engineered the ThreadTracker: server-embedded deadlock auditing tool, which will print error messages in case of found deadlocks (also showing all in-use locks on the time of the issue).
Changed the player's id on DB now starting from 20mil, thus preventing players from overwriting NPC/mobs with same oid in-game. Requires proper testing to see if the issue has been cleared.
This commit is contained in:
ronancpl
2017-11-16 12:22:32 -02:00
parent aecc3e300a
commit 2b38b62683
50 changed files with 1099 additions and 123 deletions
BIN
View File
Binary file not shown.
+1
View File
@@ -103,5 +103,6 @@ Project:
* Fixed/added some missing packets for MoveEnvironment, summons and others.
* Reviewed many Java object aspects that needed concurrency protection.
* Heavily reviewed future task management inside the project. Way less trivial schedules are spawned now, relieving task overload on the TimerManager.
* ThreadTracker: embedded auditing tool for run-time deadlock scanning throughout the server source (relies heavily on memory usage, designed only for debugging purposes).
---------------------------
+6 -1
View File
@@ -669,4 +669,9 @@ Corrigido GM shop sendo liberado pra jogadores em Amherst.
13 - 14 Novembro 2017,
Modificado ID de jogador agora começando em 20,000,000, evitando assim clash de id de jogador (que tb representa seu OID) com OIDs de objetos do mapa.
Nova ferramenta: MapleSkillMakerFetcher. A ferramenta lê info pertinente às descrições de itens feitos pelo Maker e as compila numa tabela, pra ser usada na DB.
Corrigida questline de mounts e skills de Aran.
Corrigida questline de mounts e skills de Aran.
15 Novembro 2017,
Implementado sistema de auditoria de deadlocks: ThreadTracker.
Corrigido bug onde jogadores recebem dano de mobs de mapas anteriores (que acarretavam em problemas com OID e natureza dos objetos).
Corrigido alguns problemas com exceção sendo lançada ao tentar desligar o server.
+4 -1
View File
@@ -2,6 +2,9 @@
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="2"/>
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
<group/>
<group>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/command/Commands.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/ServerConstants.java</file>
</group>
</open-files>
</project-private>
+1 -1
View File
@@ -22,7 +22,7 @@ function start(mode, type, selection) {
}
}
if (status == 0)
qm.sendYesNo("In order to rescue the princess, you must first navigate the Mushroom Forest. King Pepe set up a powerful barrier forbidding anyone from entering the castle. Please investigate this matter for us.");
qm.sendAcceptDecline("In order to rescue the princess, you must first navigate the Mushroom Forest. King Pepe set up a powerful barrier forbidding anyone from entering the castle. Please investigate this matter for us.");
if (status == 1)
qm.sendNext("You'll run into the barrier at the Mushroom Forest by heading east of where you are standing right now. Please be careful. I hear that the area is infested with crazy, fear-inducing monsters.");
if(status == 2){
+25 -6
View File
@@ -18793,7 +18793,6 @@ USE `maplesolaxia`;
(3400004,4000542,1,1,0,400000),
(3400005,4032508,1,1,2273,999999),
(3400006,4000543,1,1,0,400000),
(3400007,4032508,1,1,2273,999999),
(3400008,4000544,1,1,0,400000),
(4300001,1302009,1,1,0,700),
(4300001,1312007,1,1,0,700),
@@ -18939,7 +18938,7 @@ USE `maplesolaxia`;
(4300006,4000525,1,1,0,600000),
(4300006,4004000,1,1,0,10000),
(4300006,4020008,1,1,0,9000),
(4300006,4032506,1,1,2277,999999),
(4300006,4032506,1,1,2277,80000),
(4300007,1302009,1,1,0,700),
(4300007,1312007,1,1,0,700),
(4300007,1322016,1,1,0,700),
@@ -18976,7 +18975,7 @@ USE `maplesolaxia`;
(4300007,4000526,1,1,0,600000),
(4300007,4004000,1,1,0,10000),
(4300007,4020008,1,1,0,9000),
(4300007,4032506,1,1,2277,999999),
(4300007,4032506,1,1,2277,80000),
(4300008,1302009,1,1,0,700),
(4300008,1312007,1,1,0,700),
(4300008,1322016,1,1,0,700),
@@ -19013,7 +19012,7 @@ USE `maplesolaxia`;
(4300008,4000527,1,1,0,400000),
(4300008,4004000,1,1,0,10000),
(4300008,4020008,1,1,0,9000),
(4300008,4032506,1,1,2277,999999),
(4300008,4032506,1,1,2277,80000),
(4300009,1302009,1,1,0,700),
(4300009,1312007,1,1,0,700),
(4300009,1322016,1,1,0,700),
@@ -19207,7 +19206,6 @@ USE `maplesolaxia`;
(3400004,0,60,80,0,400000),
(3400005,0,80,85,0,400000),
(3400006,0,80,90,0,400000),
(3400007,0,90,100,0,400000),
(3400008,0,100,110,0,400000),
(4300001,0,100,120,0,400000),
(4300003,0,100,120,0,400000),
@@ -19792,7 +19790,28 @@ USE `maplesolaxia`;
(2230111, 4032147, 1, 1, 20723, 40000),
(9300378, 4001272, 1, 1, 0, 400000),
(9300378, 4032324, 1, 1, 21736, 40000),
(9300344, 4032322, 1, 1, 21731, 999999);
(9300344, 4032322, 1, 1, 21731, 999999),
(3400008, 1302008, 1, 1, 0, 8500),
(3400008, 1412004, 1, 1, 0, 8500),
(3400008, 1422007, 1, 1, 0, 8500),
(3400008, 1442009, 1, 1, 0, 8500),
(3400008, 1332010, 1, 1, 0, 8500),
(3400008, 1372001, 1, 1, 0, 8500),
(3400008, 1382002, 1, 1, 0, 8500),
(3400008, 1002013, 1, 1, 0, 8500),
(3400008, 1002152, 1, 1, 0, 8500),
(3400008, 1061047, 1, 1, 0, 8500),
(3400008, 1072090, 1, 1, 0, 8500),
(3400008, 1002137, 1, 1, 0, 8500),
(3400008, 1040023, 1, 1, 0, 8500),
(3400008, 1040072, 1, 1, 0, 8500),
(3400008, 1060062, 1, 1, 0, 8500),
(3400008, 1082049, 1, 1, 0, 8500),
(3400008, 1082072, 1, 1, 0, 8500),
(3400008, 1072081, 1, 1, 0, 8500),
(3400008, 1332031, 1, 1, 0, 8500),
(3400008, 1482003, 1, 1, 0, 8500),
(3400008, 4032508, 1, 1, 2273, 80000);
# (dropperid, itemid, minqty, maxqty, questid, chance)
+6 -5
View File
@@ -45,7 +45,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Comparator;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -154,6 +154,7 @@ import constants.skills.ThunderBreaker;
import net.server.channel.handlers.PartyOperationHandler;
import scripting.item.ItemScriptManager;
import server.maps.MapleMapItem;
import tools.locks.MonitoredEnums;
public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private static NumberFormat nf = new DecimalFormat("#,###,###,###");
@@ -270,10 +271,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private ScheduledFuture<?> extraRecoveryTask = null;
private ScheduledFuture<?> chairRecoveryTask = null;
private ScheduledFuture<?> pendantOfSpirit = null; //1122017
private Lock chrLock = new ReentrantLock(true);
private Lock effLock = new ReentrantLock(true);
private Lock petLock = new ReentrantLock(true); // for quest tasks as well
private Lock prtLock = new ReentrantLock();
private Lock chrLock = new MonitoredReentrantLock(MonitoredEnums.CHR, true);
private Lock effLock = new MonitoredReentrantLock(MonitoredEnums.EFF, true);
private Lock petLock = new MonitoredReentrantLock(MonitoredEnums.PET, true); // for quest tasks as well
private Lock prtLock = new MonitoredReentrantLock(MonitoredEnums.PRT);
private Map<Integer, Set<Integer>> excluded = new LinkedHashMap<>();
private Set<Integer> excludedItems = new LinkedHashSet<>();
private List<MapleRing> crushRings = new ArrayList<>();
+4 -3
View File
@@ -42,7 +42,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import javax.script.ScriptEngine;
@@ -77,6 +77,7 @@ import tools.FilePrinter;
import tools.HexTool;
import tools.MapleAESOFB;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public class MapleClient {
@@ -108,7 +109,7 @@ public class MapleClient {
private int picattempt = 0;
private byte gender = -1;
private boolean disconnecting = false;
private final Lock lock = new ReentrantLock(true);
private final Lock lock = new MonitoredReentrantLock(MonitoredEnums.CLIENT, true);
private int votePoints;
private int voteTime = -1;
private long lastNpcClick;
@@ -832,7 +833,7 @@ public class MapleClient {
removePlayer();
player.saveCooldowns();
player.saveToDB();
player.saveToDB(true);
player = null;
return;
+3 -2
View File
@@ -31,16 +31,17 @@ 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.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public final class MonsterBook {
private int specialCard = 0;
private int normalCard = 0;
private int bookLevel = 1;
private Map<Integer, Integer> cards = new LinkedHashMap<>();
private Lock lock = new ReentrantLock();
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.BOOK);
private Set<Entry<Integer, Integer>> getCardSet() {
lock.lock();
+3 -3
View File
@@ -346,7 +346,7 @@ public class Commands {
case "time":
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("-GMT3"));
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
player.yellowMessage("Solaxia Server Time: " + dateFormat.format(new Date()));
break;
@@ -741,7 +741,7 @@ public class Commands {
player.yellowMessage("Players on this map:");
for (MapleMapObject mmo : player.getMap().getPlayers()) {
MapleCharacter chr = (MapleCharacter) mmo;
player.dropMessage(5, ">> " + chr.getName());
player.dropMessage(5, ">> " + chr.getName() + " - " + chr.getId() + " - Oid: " + chr.getObjectId());
}
player.yellowMessage("NPCs on this map:");
for (MapleMapObject npcs : player.getMap().getMapObjects()) {
@@ -755,7 +755,7 @@ public class Commands {
if (mobs instanceof MapleMonster) {
MapleMonster mob = (MapleMonster) mobs;
if(mob.isAlive()){
player.dropMessage(5, ">> " + mob.getName() + " - " + mob.getId());
player.dropMessage(5, ">> " + mob.getName() + " - " + mob.getId() + " - Oid: " + mob.getObjectId());
}
}
}
+4 -2
View File
@@ -27,9 +27,11 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -45,7 +47,7 @@ public enum ItemFactory {
MERCHANT(6, false);
private final int value;
private final boolean account;
private static final ReentrantLock lock = new ReentrantLock(true);
private static final Lock lock = new MonitoredReentrantLock(MonitoredEnums.ITEM, true);
private ItemFactory(int value, boolean account) {
this.value = value;
+3 -2
View File
@@ -30,7 +30,7 @@ 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.locks.MonitoredReentrantLock;
import tools.Pair;
import client.MapleCharacter;
@@ -39,6 +39,7 @@ import constants.ItemConstants;
import server.MapleItemInformationProvider;
import server.MapleInventoryManipulator;
import tools.FilePrinter;
import tools.locks.MonitoredEnums;
/**
*
@@ -50,7 +51,7 @@ public class MapleInventory implements Iterable<Item> {
private byte slotLimit;
private MapleInventoryType type;
private boolean checked = false;
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.INVENTORY, true);
public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) {
this.owner = mc;
+4 -1
View File
@@ -8,10 +8,12 @@ public class ServerConstants {
public static String DB_URL = "";
public static String DB_USER = "";
public static String DB_PASS = "";
public static final boolean DB_EXPERIMENTAL_POOL = true; //[EXPERIMENTAL] Installs a connection pool to hub DB connections. Set false to default.
public static final boolean DB_EXPERIMENTAL_POOL = true; //[EXPERIMENTAL] Installs a connection pool to hub DB connections. Set false to default.
public static final boolean USE_THREAD_TRACKER = true; //[WARNING] This deadlock auditing thing will bloat the memory as fast as the time frame one takes to lose track of a raindrop on a tempesting day. Only for debug purposes.
//World And Version
public static short VERSION = 83;
public static String TIMEZONE = "-GMT3";
public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"};
//Login Configuration
@@ -85,6 +87,7 @@ public class ServerConstants {
//Dangling Items Configuration
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history.
public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reach timed out, a critical server deadlock has made present.
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
+4 -3
View File
@@ -43,13 +43,14 @@ import client.MapleClient;
import constants.ServerConstants;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.ScheduledFuture;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import server.TimerManager;
import tools.locks.MonitoredEnums;
public class MapleServerHandler extends IoHandlerAdapter {
@@ -58,8 +59,8 @@ public class MapleServerHandler extends IoHandlerAdapter {
private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm");
private static AtomicLong sessionId = new AtomicLong(7777);
private Lock idleLock = new ReentrantLock(true);
private Lock tempLock = new ReentrantLock(true);
private Lock idleLock = new MonitoredReentrantLock(MonitoredEnums.SHANDLER_IDLE, true);
private Lock tempLock = new MonitoredReentrantLock(MonitoredEnums.SHANDLER_TEMP, true);
private Map<MapleClient, Long> idleSessions = new HashMap<>(100);
private Map<MapleClient, Long> tempIdleSessions = new HashMap<>();
private ScheduledFuture<?> idleManager = null;
+11 -10
View File
@@ -35,27 +35,28 @@ public class MaplePacketEncoder implements ProtocolEncoder {
public void encode(final IoSession session, final Object message, final ProtocolEncoderOutput out) throws Exception {
final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
if (client != null) {
final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message;
final byte[] unencrypted = new byte[input.length];
System.arraycopy(input, 0, unencrypted, 0, input.length);
final byte[] ret = new byte[unencrypted.length + 4];
final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
MapleCustomEncryption.encryptData(unencrypted);
try {
client.lockClient();
try {
final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message;
final byte[] unencrypted = new byte[input.length];
System.arraycopy(input, 0, unencrypted, 0, input.length);
final byte[] ret = new byte[unencrypted.length + 4];
final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
MapleCustomEncryption.encryptData(unencrypted);
send_crypto.crypt(unencrypted);
System.arraycopy(header, 0, ret, 0, 4);
System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
out.write(IoBuffer.wrap(ret));
} finally {
client.unlockClient();
}
// System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
// out.write(ByteBuffer.wrap(ret));
} else {
} catch (NullPointerException npe) {
out.write(IoBuffer.wrap(((byte[]) message)));
}
}
+3 -2
View File
@@ -25,7 +25,8 @@ 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 tools.locks.MonitoredEnums;
import tools.locks.MonitoredReentrantLock;
/**
*
@@ -33,7 +34,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class PlayerBuffStorage {
private int id = (int) (Math.random() * 100);
private final Lock lock = new ReentrantLock(true);
private final Lock lock = new MonitoredReentrantLock(MonitoredEnums.BUFF_STORAGE, true);
private Map<Integer, List<PlayerBuffValueHolder>> buffs = new HashMap<>();
public void addBuffsToStorage(int chrid, List<PlayerBuffValueHolder> toStore) {
+3 -1
View File
@@ -26,12 +26,14 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import tools.locks.MonitoredEnums;
public class PlayerStorage {
private final ReentrantReadWriteLock locks = new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredEnums.PLAYER_STORAGE, true);
private final ReadLock rlock = locks.readLock();
private final WriteLock wlock = locks.writeLock();
private final Map<Integer, MapleCharacter> storage = new LinkedHashMap<>();
+17 -9
View File
@@ -33,15 +33,16 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.ScheduledFuture;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
@@ -70,11 +71,13 @@ import client.SkillFactory;
import constants.ItemConstants;
import constants.ServerConstants;
import java.util.Calendar;
import net.server.audit.ThreadTracker;
import server.quest.MapleQuest;
import tools.locks.MonitoredEnums;
public class Server implements Runnable {
private static final Set<Integer> activeFly = new HashSet<>();
private static final Map<Integer, Integer> couponRates = new LinkedHashMap<>();
private static final Map<Integer, Integer> couponRates = new HashMap<>(30);
private static final List<Integer> activeCoupons = new LinkedList<>();
private IoAcceptor acceptor;
@@ -83,11 +86,12 @@ public class Server implements Runnable {
private final Properties subnetInfo = new Properties();
private static Server instance = null;
private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
private final Map<Integer, MapleGuild> guilds = new LinkedHashMap<>();
private final Map<MapleClient, Long> inLoginState = new LinkedHashMap<>();
private final Lock srvLock = new ReentrantLock();
private final Map<Integer, MapleGuild> guilds = new HashMap<>(100);
private final Map<MapleClient, Long> inLoginState = new HashMap<>(100);
private final Lock srvLock = new MonitoredReentrantLock(MonitoredEnums.SERVER);
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
private final Map<Integer, MapleAlliance> alliances = new LinkedHashMap<>();
private final Map<Integer, MapleAlliance> alliances = new HashMap<>(100);
private boolean online = false;
public static long uptime = System.currentTimeMillis();
@@ -301,7 +305,9 @@ public class Server implements Runnable {
timeToTake = System.currentTimeMillis();
MapleQuest.loadAllQuest();
System.out.println("Quest loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().registerThreadTrackerTask();
try {
Integer worldCount = Math.min(ServerConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds")));
@@ -317,7 +323,7 @@ public class Server implements Runnable {
worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
worlds.add(world);
channels.add(new LinkedHashMap<Integer, String>());
channels.add(new HashMap<Integer, String>());
for (int j = 0; j < Integer.parseInt(p.getProperty("channels" + i)); j++) {
int channelid = j + 1;
Channel channel = new Channel(i, channelid);
@@ -807,6 +813,8 @@ public class Server implements Runnable {
}
}
}*/
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().cancelThreadTrackerTask();
TimerManager.getInstance().purge();
TimerManager.getInstance().stop();
+281
View File
@@ -0,0 +1,281 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.audit;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import server.TimerManager;
import tools.FilePrinter;
import tools.locks.MonitoredEnums;
import constants.ServerConstants;
/**
*
* @author RonanLana
*
* This tool has the main purpose of auditing deadlocks throughout the server and must be used only for debugging. The flag is USE_THREAD_TRACKER.
*/
public class ThreadTracker {
private static ThreadTracker instance = null;
private final Lock ttLock = new ReentrantLock(true);
private final Map<Long, List<MonitoredEnums>> threadTracker = new HashMap<>();
private final Map<Long, Integer> threadUpdate = new HashMap<>();
private final Map<Long, Thread> threads = new HashMap<>();
private final Map<Long, AtomicInteger> lockCount = new HashMap<>();
private final Map<Long, MonitoredEnums> lockIds = new HashMap<>();
private final Map<Long, Long> lockThreads = new HashMap<>();
private final Map<Long, Byte> lockUpdate = new HashMap<>();
private final Map<MonitoredEnums, Map<Long, Integer>> locks = new HashMap<>();
ScheduledFuture<?> threadTrackerSchedule;
private String printThreadTrackerState(String dateFormat) {
Map<MonitoredEnums, List<Integer>> lockValues = new HashMap<>();
Set<Long> executingThreads = new HashSet<>();
for(Map.Entry<Long, AtomicInteger> lock : lockCount.entrySet()) {
if(lock.getValue().get() != 0) {
executingThreads.add(lockThreads.get(lock.getKey()));
MonitoredEnums lockId = lockIds.get(lock.getKey());
List<Integer> list = lockValues.get(lockId);
if(list == null) {
list = new ArrayList<>();
lockValues.put(lockId, list);
}
list.add(lock.getValue().get());
}
}
String s = "----------------------------\r\n" + dateFormat + "\r\n ";
s += "Lock-thread usage count:";
for(Map.Entry<MonitoredEnums, List<Integer>> lock : lockValues.entrySet()) {
s += ("\r\n " + lock.getKey().name() + ": ");
for(Integer i : lock.getValue()) {
s += (i + " ");
}
}
s += "\r\n\r\nThread opened lock path:";
for(Long tid : executingThreads) {
s += "\r\n";
for(MonitoredEnums lockid : threadTracker.get(tid)) {
s += (lockid.name() + " ");
}
s += "|";
}
s += "\r\n\r\n";
return s;
}
private static String printThreadLog(List<MonitoredEnums> stillLockedPath, String dateFormat) {
String s = "----------------------------\r\n" + dateFormat + "\r\n ";
for(MonitoredEnums lock : stillLockedPath) {
s += (lock.name() + " ");
}
s += "\r\n\r\n";
return s;
}
private static String printThreadStack(StackTraceElement[] list, String dateFormat) {
String s = "----------------------------\r\n" + dateFormat + "\r\n";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
public void accessThreadTracker(boolean update, boolean lock, MonitoredEnums lockId, long lockOid) {
ttLock.lock();
try {
if(update) {
if(!lock) { // update tracker
List<Long> toRemove = new ArrayList<>();
for(Long l : threadUpdate.keySet()) {
int next = threadUpdate.get(l) + 1;
if(next == 4) {
List<MonitoredEnums> tt = threadTracker.get(l);
if(tt.isEmpty()) {
toRemove.add(l);
} else {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
String df = dateFormat.format(new Date());
FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df));
FilePrinter.print(FilePrinter.DEADLOCK_STACK, printThreadStack(threads.get(l).getStackTrace(), df));
}
}
threadUpdate.put(l, next);
}
for(Long l : toRemove) {
threadTracker.remove(l);
threadUpdate.remove(l);
threads.remove(l);
for(Map<Long, Integer> threadLock : locks.values()) {
threadLock.remove(l);
}
}
toRemove.clear();
for(Entry<Long, Byte> it : lockUpdate.entrySet()) {
byte val = (byte)(it.getValue() + 1);
if(val < 60) { // free the structure after 60 silent updates
lockUpdate.put(it.getKey(), val);
} else {
toRemove.add(it.getKey());
}
}
for(Long l : toRemove) {
lockCount.remove(l);
lockIds.remove(l);
lockThreads.remove(l);
lockUpdate.remove(l);
}
} else { // print status
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
FilePrinter.printError(FilePrinter.DEADLOCK_STATE, printThreadTrackerState(dateFormat.format(new Date())));
//FilePrinter.printError(FilePrinter.DEADLOCK_STATE, "[" + dateFormat.format(new Date()) + "] Presenting current lock path for lockid " + lockId.name() + ".\r\n" + printLockStatus(lockId) + "\r\n-------------------------------\r\n");
}
} else {
long tid = Thread.currentThread().getId();
if(lock) {
AtomicInteger c = lockCount.get(lockOid);
if(c == null) {
c = new AtomicInteger(0);
lockCount.put(lockOid, c);
lockIds.put(lockOid, lockId);
lockThreads.put(lockOid, tid);
lockUpdate.put(lockOid, (byte) 0);
}
c.incrementAndGet();
List<MonitoredEnums> list = threadTracker.get(tid);
if(list == null) {
list = new ArrayList<>(20);
threadTracker.put(tid, list);
threadUpdate.put(tid, 0);
threads.put(tid, Thread.currentThread());
}
list.add(lockId);
Map<Long, Integer> threadLock = locks.get(lockId);
if(threadLock == null) {
threadLock = new HashMap<>(5);
locks.put(lockId, threadLock);
}
Integer lc = threadLock.get(tid);
if(lc != null) {
threadLock.put(tid, lc + 1);
} else {
threadLock.put(tid, 1);
}
}
else {
AtomicInteger c = lockCount.get(lockOid);
c.decrementAndGet();
lockUpdate.put(lockOid, (byte) 0);
List<MonitoredEnums> list = threadTracker.get(tid);
for(int i = list.size() - 1; i >= 0; i--) {
if(lockId.getValue() == list.get(i).getValue()) {
list.remove(i);
break;
}
}
Map<Long, Integer> threadLock = locks.get(lockId);
threadLock.put(tid, threadLock.get(tid) - 1);
}
}
} finally {
ttLock.unlock();
}
}
private String printLockStatus(MonitoredEnums lockId) {
String s = "";
for(Long threadid : locks.get(lockId).keySet()) {
for(MonitoredEnums lockid : threadTracker.get(threadid)) {
s += (" " + lockid.name());
}
s += " |\r\n";
}
return s;
}
public void registerThreadTrackerTask() {
threadTrackerSchedule = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
accessThreadTracker(true, false, MonitoredEnums.UNDEFINED, -1);
}
}, 10000, 10000);
}
public void cancelThreadTrackerTask() {
threadTrackerSchedule.cancel(false);
}
public static ThreadTracker getInstance() {
if (instance == null) {
instance = new ThreadTracker();
}
return instance;
}
}
+5 -3
View File
@@ -33,7 +33,8 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -67,6 +68,7 @@ import tools.MaplePacketCreator;
import client.MapleCharacter;
import constants.ServerConstants;
import server.maps.MapleMiniDungeonInfo;
import tools.locks.MonitoredEnums;
public final class Channel {
@@ -90,11 +92,11 @@ public final class Channel {
private Map<Integer, Integer> dojoParty = new HashMap<>();
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
private ReentrantReadWriteLock merchantLock = new ReentrantReadWriteLock(true);
private ReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MERCHANT, true);
private ReadLock merchRlock = merchantLock.readLock();
private WriteLock merchWlock = merchantLock.writeLock();
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.CHANNEL, true);
public Channel(final int world, final int channel) {
this.world = world;
@@ -84,7 +84,7 @@ public final class ReportHandler extends AbstractMaplePacketHandler {
public void addReport(int reporterid, int victimid, int reason, String description, String chatlog) {
Calendar calendar = Calendar.getInstance();
Timestamp currentTimestamp = new java.sql.Timestamp(calendar.getTime().getTime());
Connection con = null;
Connection con;
try {
con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO reports (`reporttime`, `reporterid`, `victimid`, `reason`, `chatlog`, `description`) VALUES (?, ?, ?, ?, ?, ?)");
@@ -51,6 +51,7 @@ import server.life.MobAttackInfoFactory;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Randomizer;
@@ -78,9 +79,17 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
oid = slea.readInt();
try {
attacker = (MapleMonster) map.getMapObject(oid);
List<loseItem> loseItems;
MapleMapObject mmo = map.getMapObject(oid);
if(mmo instanceof MapleMonster) {
attacker = (MapleMonster) mmo;
if(attacker.getId() != monsteridfrom) {
attacker = null;
}
}
if (attacker != null) {
List<loseItem> loseItems;
if (attacker.isBuffed(MonsterStatus.NEUTRALISE)) {
return;
}
@@ -119,10 +128,8 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
} catch(ClassCastException e) {
//this happens due to mob on last map damaging player just before changing maps
if(ServerConstants.USE_DEBUG) {
e.printStackTrace();
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity.");
}
e.printStackTrace();
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity.");
return;
}
+3 -2
View File
@@ -36,12 +36,13 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import net.server.Server;
import net.server.channel.Channel;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public class MapleGuild {
public final static int CREATE_GUILD_COST = 1500000;
@@ -52,7 +53,7 @@ public class MapleGuild {
}
private final List<MapleGuildCharacter> members;
private final Lock membersLock = new ReentrantLock(true);
private final Lock membersLock = new MonitoredReentrantLock(MonitoredEnums.GUILD, true);
private String rankTitles[] = new String[5]; // 1 = master, 2 = jr, 5 = lowest member
private String name, notice;
+3 -2
View File
@@ -30,8 +30,9 @@ import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Comparator;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredEnums;
public class MapleParty {
private int id;
@@ -43,7 +44,7 @@ public class MapleParty {
private Map<Integer, Integer> histMembers = new HashMap<>();
private int nextEntry = 0;
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.PARTY, true);
public MapleParty(int id, MaplePartyCharacter chrfor) {
this.leaderId = chrfor.getId();
+8 -7
View File
@@ -43,7 +43,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.ScheduledFuture;
@@ -68,6 +68,7 @@ import server.maps.AbstractMapleMapObject;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -89,25 +90,25 @@ public class World {
private Map<Integer, MapleParty> parties = new HashMap<>();
private AtomicInteger runningPartyId = new AtomicInteger();
private Lock partyLock = new ReentrantLock(true);
private Lock partyLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_PARTY, true);
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
private Lock owlLock = new ReentrantLock();
private Lock owlLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_OWL);
private Lock activePetsLock = new ReentrantLock(true);
private Lock activePetsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_PETS, true);
private Map<Integer, Byte> activePets = new LinkedHashMap<>();
private ScheduledFuture<?> petsSchedule;
private long petUpdate;
private Lock activeMountsLock = new ReentrantLock(true);
private Lock activeMountsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_MOUNTS, true);
private Map<Integer, Byte> activeMounts = new LinkedHashMap<>();
private ScheduledFuture<?> mountsSchedule;
private long mountUpdate;
private Lock activePlayerShopsLock = new ReentrantLock(true);
private Lock activePlayerShopsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_PSHOPS, true);
private Map<Integer, MaplePlayerShop> activePlayerShops = new LinkedHashMap<>();
private Lock activeMerchantsLock = new ReentrantLock(true);
private Lock activeMerchantsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_MERCHS, true);
private Map<Integer, Pair<MapleHiredMerchant, Byte>> activeMerchants = new LinkedHashMap<>();
private long merchantUpdate;
@@ -35,7 +35,8 @@ import java.util.Set;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -69,6 +70,7 @@ import server.MapleItemInformationProvider;
import server.life.MapleLifeFactory;
import server.life.MapleNPC;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
/**
*
@@ -90,12 +92,12 @@ public class EventInstanceManager {
private List<Integer> mapIds = new LinkedList<>();
private List<Boolean> isInstanced = new LinkedList<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredEnums.EIM, true);
private final ReadLock rL = lock.readLock();
private final WriteLock wL = lock.writeLock();
private final Lock pL = new ReentrantLock(true);
private final Lock sL = new ReentrantLock();
private final Lock pL = new MonitoredReentrantLock(MonitoredEnums.EIM_PARTY, true);
private final Lock sL = new MonitoredReentrantLock(MonitoredEnums.EIM_SCRIPT, true);
private ScheduledFuture<?> event_schedule = null;
private boolean disposed = false;
+4 -3
View File
@@ -53,7 +53,8 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredEnums;
import tools.locks.MonitoredReentrantLock;
/**
*
@@ -74,8 +75,8 @@ public class EventManager {
private Integer readyId = 0;
private Properties props = new Properties();
private String name;
private Lock lobbyLock = new ReentrantLock();
private Lock queueLock = new ReentrantLock();
private Lock lobbyLock = new MonitoredReentrantLock(MonitoredEnums.EM_LOBBY);
private Lock queueLock = new MonitoredReentrantLock(MonitoredEnums.EM_QUEUE);
private static final int maxLobbys = 8; // an event manager holds up to this amount of concurrent lobbys
+3 -2
View File
@@ -31,7 +31,7 @@ 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 tools.locks.MonitoredReentrantLock;
import provider.MapleData;
import provider.MapleDataProvider;
@@ -46,6 +46,7 @@ import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.ItemConstants;
import java.util.Collections;
import tools.locks.MonitoredEnums;
/*
* @author Flav
@@ -241,7 +242,7 @@ public class CashShop {
private List<Item> inventory = new ArrayList<>();
private List<Integer> wishList = new ArrayList<>();
private int notes = 0;
private Lock lock = new ReentrantLock();
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.CASHSHOP);
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
this.accountId = accountId;
+3 -2
View File
@@ -32,13 +32,14 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import net.SendOpcode;
import server.maps.AbstractMapleMapObject;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.output.MaplePacketLittleEndianWriter;
import tools.locks.MonitoredEnums;
/**
*
@@ -55,7 +56,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
private List<String> bannedList = new ArrayList<>();
private List<Pair<MapleCharacter, String>> chatLog = new LinkedList<>();
private Map<Integer, Byte> chatSlot = new LinkedHashMap<>();
private Lock visitorLock = new ReentrantLock(true);
private Lock visitorLock = new MonitoredReentrantLock(MonitoredEnums.VISITOR_PSHOP, true);
public MaplePlayerShop(MapleCharacter owner, String description) {
this.setPosition(owner.getPosition());
+3 -2
View File
@@ -34,10 +34,11 @@ 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.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -50,7 +51,7 @@ public class MapleStorage {
private byte slots;
private Map<MapleInventoryType, List<Item>> typeItems = new HashMap<>();
private List<Item> items;
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.STORAGE, true);
private MapleStorage(int id, byte slots, int meso) {
this.id = id;
+6 -4
View File
@@ -51,7 +51,8 @@ import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import server.TimerManager;
@@ -62,6 +63,7 @@ import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.locks.MonitoredEnums;
public class MapleMonster extends AbstractLoadedMapleLife {
private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats.
@@ -84,9 +86,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private int team;
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
private ReentrantLock externalLock = new ReentrantLock();
private ReentrantLock monsterLock = new ReentrantLock(true);
private ReentrantLock statiLock = new ReentrantLock();
private Lock externalLock = new MonitoredReentrantLock(MonitoredEnums.MOB_EXT);
private Lock monsterLock = new MonitoredReentrantLock(MonitoredEnums.MOB, true);
private Lock statiLock = new MonitoredReentrantLock(MonitoredEnums.MOB_STATI);
public MapleMonster(int id, MapleMonsterStats stats) {
super(id);
+3 -1
View File
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -34,6 +35,7 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import tools.locks.MonitoredEnums;
/**
*
@@ -44,7 +46,7 @@ public class MobSkillFactory {
private static Map<String, MobSkill> mobSkills = new HashMap<String, MobSkill>();
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
private final static ReentrantReadWriteLock dataLock = new ReentrantReadWriteLock();
private final static ReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MOBSKILL_FACTORY);
private final static ReadLock rL = dataLock.readLock();
private final static WriteLock wL = dataLock.writeLock();
+3 -2
View File
@@ -27,7 +27,8 @@ import scripting.portal.PortalScriptManager;
import server.MaplePortal;
import tools.MaplePacketCreator;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredEnums;
import tools.locks.MonitoredReentrantLock;
public class MapleGenericPortal implements MaplePortal {
@@ -117,7 +118,7 @@ public class MapleGenericPortal implements MaplePortal {
if(scriptName != null) {
if(scriptLock == null) {
scriptLock = new ReentrantLock(false);
scriptLock = new MonitoredReentrantLock(MonitoredEnums.PORTAL, false);
}
} else {
scriptLock = null;
+3 -2
View File
@@ -37,7 +37,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
@@ -45,6 +45,7 @@ import server.MaplePlayerShopItem;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -63,7 +64,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
private List<SoldItem> sold = new LinkedList<>();
private AtomicBoolean open = new AtomicBoolean();
private MapleMap map;
private Lock visitorLock = new ReentrantLock(true);
private Lock visitorLock = new MonitoredReentrantLock(MonitoredEnums.VISITOR_MERCH, true);
public MapleHiredMerchant(final MapleCharacter owner, int itemId, String desc) {
this.setPosition(owner.getPosition());
+5 -3
View File
@@ -52,6 +52,7 @@ import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -78,13 +79,14 @@ import server.life.MonsterGlobalDropEntry;
import server.life.SpawnPoint;
import server.partyquest.MonsterCarnival;
import server.partyquest.MonsterCarnivalParty;
import server.partyquest.Pyramid;
//import server.partyquest.Pyramid;
import scripting.event.EventInstanceManager;
import server.life.MonsterListener;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.locks.MonitoredEnums;
public class MapleMap {
private static final List<MapleMapObjectType> rangedMapobjectTypes = Arrays.asList(MapleMapObjectType.SHOP, MapleMapObjectType.ITEM, MapleMapObjectType.NPC, MapleMapObjectType.MONSTER, MapleMapObjectType.DOOR, MapleMapObjectType.SUMMON, MapleMapObjectType.REACTOR);
@@ -161,11 +163,11 @@ public class MapleMap {
if (this.monsterRate == 0) {
this.monsterRate = 1;
}
final ReentrantReadWriteLock chrLock = new ReentrantReadWriteLock(true);
final ReentrantReadWriteLock chrLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MAP_CHRS, true);
chrRLock = chrLock.readLock();
chrWLock = chrLock.writeLock();
final ReentrantReadWriteLock objectLock = new ReentrantReadWriteLock(true);
final ReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MAP_OBJS, true);
objectRLock = objectLock.readLock();
objectWLock = objectLock.writeLock();
}
+3 -1
View File
@@ -33,6 +33,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -46,6 +47,7 @@ import server.life.MapleMonster;
import scripting.event.EventInstanceManager;
import tools.DatabaseConnection;
import tools.StringUtil;
import tools.locks.MonitoredEnums;
public class MapleMapFactory {
@@ -64,7 +66,7 @@ public class MapleMapFactory {
this.channel = channel;
this.event = eim;
ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
ReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredEnums.MAP_FACTORY);
this.mapsRLock = rrwl.readLock();
this.mapsWLock = rrwl.writeLock();
}
+4 -2
View File
@@ -24,8 +24,10 @@ import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import java.awt.Point;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public class MapleMapItem extends AbstractMapleMapObject {
@@ -35,7 +37,7 @@ public class MapleMapItem extends AbstractMapleMapObject {
protected byte type;
protected boolean pickedUp = false, playerDrop;
protected long dropTime;
private ReentrantLock itemLock = new ReentrantLock();
private Lock itemLock = new MonitoredReentrantLock(MonitoredEnums.MAP_ITEM);
public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, byte type, boolean playerDrop) {
setPosition(position);
+3 -2
View File
@@ -25,8 +25,9 @@ import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
/**
*
@@ -35,7 +36,7 @@ import tools.MaplePacketCreator;
public class MapleMiniDungeon {
List<MapleCharacter> players = new ArrayList<>();
ScheduledFuture<?> timeoutTask = null;
Lock lock = new ReentrantLock(true);
Lock lock = new MonitoredReentrantLock(MonitoredEnums.MINIDUNGEON, true);
int baseMap;
long expireTime;
+3 -2
View File
@@ -29,12 +29,13 @@ import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import scripting.reactor.ReactorScriptManager;
import server.TimerManager;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -53,7 +54,7 @@ public class MapleReactor extends AbstractMapleMapObject {
private boolean shouldCollect;
private boolean attackHit;
private ScheduledFuture<?> timeoutTask = null;
private Lock reactorLock = new ReentrantLock(true);
private Lock reactorLock = new MonitoredReentrantLock(MonitoredEnums.REACTOR, true);
public MapleReactor(MapleReactorStats stats, int rid) {
this.evstate = (byte)0;
+5 -1
View File
@@ -44,7 +44,11 @@ public class FilePrinter {
QUEST_UNCODED = "uncodedQuests.txt",
AUTOSAVING_CHARACTER = "saveCharAuto.txt",
SAVING_CHARACTER = "saveChar.txt",
USED_COMMANDS = "usedCommands.txt";//more to come (maps)
USED_COMMANDS = "usedCommands.txt",
DEADLOCK_ERROR = "deadlocks.txt",
DEADLOCK_STACK = "deadlocks/path.txt",
DEADLOCK_LOCKS = "deadlocks/locks.txt",
DEADLOCK_STATE = "deadlocks/state.txt";
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //for file system purposes, it's nice to use yyyy-MM-dd
private static final String FILE_PATH = "logs/" + sdf.format(Calendar.getInstance().getTime()) + "/"; // + sdf.format(Calendar.getInstance().getTime()) + "/"
+81
View File
@@ -0,0 +1,81 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
/**
*
* @author RonanLana
*/
public enum MonitoredEnums {
UNDEFINED(-1),
CHR(0),
EFF(1),
PET(2),
PRT(3),
CLIENT(4),
BOOK(5),
ITEM(6),
INVENTORY(7),
SHANDLER_IDLE(8),
SHANDLER_TEMP(9),
BUFF_STORAGE(10),
PLAYER_STORAGE(11),
SERVER(12),
MERCHANT(13),
CHANNEL(14),
GUILD(15),
PARTY(16),
WORLD_PARTY(17),
WORLD_OWL(18),
WORLD_PETS(19),
WORLD_MOUNTS(20),
WORLD_PSHOPS(21),
WORLD_MERCHS(21),
EIM(22),
EIM_PARTY(23),
EIM_SCRIPT(24),
EM_LOBBY(25),
EM_QUEUE(26),
CASHSHOP(27),
VISITOR_PSHOP(28),
STORAGE(29),
MOB_EXT(30),
MOB(31),
MOB_STATI(32),
MOBSKILL_FACTORY(33),
PORTAL(34),
VISITOR_MERCH(35),
MAP_CHRS(36),
MAP_OBJS(37),
MAP_FACTORY(38),
MAP_ITEM(39),
MINIDUNGEON(40),
REACTOR(41);
private final int i;
private MonitoredEnums(int val) {
this.i = val;
}
public int getValue() {
return i;
}
}
+147
View File
@@ -0,0 +1,147 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredReadLock extends ReentrantReadWriteLock.ReadLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredEnums id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredReadLock(MonitoredReentrantReadWriteLock lock) {
super(lock);
this.id = lock.id;
hashcode = this.hashCode();
}
@Override
public void lock() {
super.lock();
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}
+150
View File
@@ -0,0 +1,150 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredReentrantLock extends ReentrantLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredEnums id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredReentrantLock(MonitoredEnums id) {
super();
this.id = id;
hashcode = this.hashCode();
}
public MonitoredReentrantLock(MonitoredEnums id, boolean fair) {
super(fair);
this.id = id;
hashcode = this.hashCode();
}
@Override
public void lock() {
super.lock();
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}
@@ -0,0 +1,49 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
*
* @author RonanLana
*/
public class MonitoredReentrantReadWriteLock extends ReentrantReadWriteLock {
public final MonitoredEnums id;
public MonitoredReentrantReadWriteLock(MonitoredEnums id) {
super();
this.id = id;
}
public MonitoredReentrantReadWriteLock(MonitoredEnums id, boolean fair) {
super(fair);
this.id = id;
}
@Override
public ReadLock readLock() {
return super.readLock();
}
@Override
public WriteLock writeLock() {
return super.writeLock();
}
}
+145
View File
@@ -0,0 +1,145 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredWriteLock extends ReentrantReadWriteLock.WriteLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredEnums id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredWriteLock(MonitoredReentrantReadWriteLock lock) {
super(lock);
this.id = lock.id;
hashcode = this.hashCode();
}
@Override
public void lock() {
super.lock();
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}
Binary file not shown.
+24 -14
View File
@@ -1,14 +1,24 @@
Red Potion
Lemon
W. Ramen
Elixir
Mana Elixir
Mushroom Miso Ramen
Power Elixir
Lunar Gloves
LeFay Jester
Eclipse Earrings
Herculean Crown
Lockewood Hat
Pickpocket Pilfer
Eclipse Cloak
Gladius
Niam
Titan
Crescent Polearm
Iron Dagger
White Crusader Chainmail (Male)
Wizard Wand
Wizard Staff
Golden Pride
Blue Guiltian
Red Amorian Skirt (Female)
Red Salt Shoes
Green Pole-Feather Hat
Black Bennis Chainmail (Male)
Red Legolier (Male)
Blue Legolier Pants (Male)
Green Marker
Gold Brace
Green Hunter Boots
Dragon Toenail
Rouge Way
Guardian Katara
Justice Katara
Norman Grip
+20
View File
@@ -0,0 +1,20 @@
1302008
1412004
1422007
1442009
1332010
1372001
1382002
1002013
1002152
1061047
1072090
1002137
1040023
1040072
1060062
1082049
1082072
1072081
1332031
1482003
@@ -1,2 +1,2 @@
compile.on.save=true
user.properties.file=C:\\Users\\USER\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
@@ -41,9 +41,11 @@ import java.util.ArrayList;
* Set whether you are first installing the handbook on the SQL Server (TRUE) or just fetching whatever is on your "fetch.txt"
* file (FALSE) on the INSTALL_SQLTABLE property and build the project. With all done, run the Java executable.
*
* Expected installing time: 30 minutes
*
*/
public class MapleIdRetriever {
private final static boolean INSTALL_SQLTABLE = true;
private final static boolean INSTALL_SQLTABLE = false;
static String host = "jdbc:mysql://localhost:3306/maplesolaxia";
static String driver = "com.mysql.jdbc.Driver";
@@ -83,7 +85,12 @@ public class MapleIdRetriever {
if(tokens.length > 1) {
PreparedStatement ps = con.prepareStatement("INSERT INTO `handbook` (`id`, `name`) VALUES (?, ?)");
ps.setInt(1, Integer.parseInt(tokens[0]));
try {
ps.setInt(1, Integer.parseInt(tokens[0]));
} catch (NumberFormatException npe) { // odd...
String num = tokens[0].substring(1);
ps.setInt(1, Integer.parseInt(num));
}
ps.setString(2, tokens[1]);
ps.execute();
}