Major schedules & DB refactor

Refactored many calls for TimerManager's schedules throughout the source.
Switched all tables using MyISAM to InnoDB: on a multi-threaded environment such as this, table-locking is an instant no-no, and other gains MyISAM would have over InnoDB are minimal.
Altered getConnection() to properly throw an exception (good practice!) in case of no available connection instead of a mere null.
This commit is contained in:
ronancpl
2017-11-09 20:20:21 -02:00
parent 9677e6f3f5
commit c46ff82929
35 changed files with 600 additions and 374 deletions
+3 -2
View File
@@ -69,7 +69,8 @@ Hamachi is optional, though. You don't have to install Hamachi if you want to ma
Set the "MapleSolaxia" folder on a place of your preference. It is recommended for use "C:\Nexon\MapleSolaxia".
Setting up the SQL: open MySQL Query Browser, and define these parameters at startup and click OK:
Setting up the SQL: open MySQL Query Browser, define these parameters at startup and click OK.
Server Host: localhost Port: 3306 Username: root
Now it must be done CAREFULLY:
@@ -80,7 +81,7 @@ Now it is OPTIONAL, you don't need to run it if you don't want, as it will simpl
3. File -> Open Script... -> Browse for "C:\Nexon\MapleSolaxia\sql" -> db_shopupdate.sql, and execute it.
At the end of the execution of these SQLs, you should have installed a database schema named "maplesolaxia". REGISTER YOUR FIRST ACCOUNT to be used in-game by creating manually a entry on the table "accounts" at that database with a login and a password.
At the end of the execution of these SQLs, you should have installed a database schema named "maplesolaxia". REGISTER YOUR FIRST ACCOUNT to be used in-game by creating manually an entry on the table "accounts" at that database with a login and a password.
Configure the IP you want to use for your MapleStory server in "configuration.ini" file, or set it as "localhost" if you want to run it only on your machine. Alternatively, you can use the IP given by Hamachi to use on a Hamachi network, or you can use a non-Hamachi method of port-forwarding. Neither will be approached here.
BIN
View File
Binary file not shown.
+12 -2
View File
@@ -1,6 +1,6 @@
========== MapleSolaxiaV2 ==========
Credits:
Ronan - Freelance Developer
Ronan - Developer
Vcoc - Freelance Developer
---------------------------
@@ -81,17 +81,27 @@ Server potentials:
* Custom jail system (needs provided custom wz).
* Delete Character 100% (requires ENABLE_PIC activated).
* Autosaver (periodically saves on DB current state of every player in-game).
* Both fixed and randomized versions of HP/MP growth rate abailable, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature.
* Both fixed and randomized versions of HP/MP growth rate available, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature.
Admin/GM commands:
* Server commands layered by GM levels.
* Spawn Zakum/Horntail/Pinkbean 100%.
* New commands.
External tools:
* MapleArrowFetcher - Updates min/max quantity dropped on all arrows drop data, calculations based on mob level and whether it's a boss or not.
* MapleCouponInstaller - Retrieves coupon info from the WZ and makes a SQL table with it. The server will use that table to gather info regarding rates and intervals.
* MapleIdRetriever - Two behaviors: generates a SQL table with relation (id, name) of the handbook given as input. Given a file with names, outputs a file with ids.
* MapleMesoFetcher - Creates meso drop data for mobs with more than 4 items (thus overworld mobs), calculations based on mob level and whether it's a boss or not.
* MapleQuestItemFetcher - Searches the SQL tables and project files and reports in all relevant data regarding missing/erroneous quest items.
* MobBookIndexer - Generates a SQL table with all relations of cardid and mobid present in the mob book.
* MobBookUpdate - Generates a wz.xml that is a copy of the original MonsterBook.wz.xml, except it updates the drop data info in the book with those currently on DB.
Project:
* Organized project code.
* Highly configurable server (see all server flags at ServerConstants).
* 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.
---------------------------
+10 -1
View File
@@ -650,4 +650,13 @@ Implementado nova ferramenta: MapleQuestItemFetcher. Ela vasculha a DB e os XMLs
05 Novembro 2017,
Adicionado sistema de level cap para jobs correntes.
Revisto acesso concorrente sobre o componente EM da classe EIM.
Revisto acesso concorrente sobre o componente EM da classe EIM.
07 Novembro 2017,
Refatorado esquema de schedules por toda a source, diminuindo drasticamente as chamadas ao TimerManager.
Refatorado algumas chamadas à DB, busca somente aquilo que é necessário.
08 - 09 Novembro 2017,
Adicionado packet para extra slot pendant.
Corrigido possível bug em MapleMapFactory.
Modificado todas as DB tables agora utilizando InnoDB (ganhos do MyISAM em contraste se tornou ínfimo, para casos críticos).
+10 -10
View File
@@ -206,7 +206,7 @@ CREATE TABLE IF NOT EXISTS `temp_data` (
`chance` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`dropperid`, `itemid`),
KEY `mobid` (`dropperid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ;
INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES
(1, 9400121, 4000138, 1, 1, 0, 600000),
@@ -15929,7 +15929,7 @@ CREATE TABLE IF NOT EXISTS `monsterbook` (
`charid` int(11) unsigned NOT NULL,
`cardid` int(11) NOT NULL,
`level` int(1) DEFAULT '1'
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `monstercarddata` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -15937,7 +15937,7 @@ CREATE TABLE IF NOT EXISTS `monstercarddata` (
`mobid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=309 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=309 ;
INSERT INTO `monstercarddata` (`id`, `cardid`, `mobid`) VALUES
(1, 2380000, 100100),
@@ -16313,7 +16313,7 @@ CREATE TABLE IF NOT EXISTS `nxcode` (
`type` int(11) NOT NULL DEFAULT '0',
`item` int(11) NOT NULL DEFAULT '10000',
PRIMARY KEY (`code`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `nxcoupons` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -16418,7 +16418,7 @@ CREATE TABLE IF NOT EXISTS `questactions` (
`status` int(11) NOT NULL DEFAULT '0',
`data` blob NOT NULL,
PRIMARY KEY (`questactionid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `questprogress` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
@@ -16434,7 +16434,7 @@ CREATE TABLE IF NOT EXISTS `questrequirements` (
`status` int(11) NOT NULL DEFAULT '0',
`data` blob NOT NULL,
PRIMARY KEY (`questrequirementid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `queststatus` (
`queststatusid` int(10) unsigned NOT NULL AUTO_INCREMENT,
@@ -16456,7 +16456,7 @@ CREATE TABLE IF NOT EXISTS `reactordrops` (
`questid` int(5) NOT NULL DEFAULT '-1',
PRIMARY KEY (`reactordropid`),
KEY `reactorid` (`reactorid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 PACK_KEYS=1 AUTO_INCREMENT=841 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 PACK_KEYS=1 AUTO_INCREMENT=841 ;
INSERT INTO `reactordrops` (`reactordropid`, `reactorid`, `itemid`, `chance`, `questid`) VALUES
(1, 2001, 4031161, 1, 1008),
@@ -17339,7 +17339,7 @@ CREATE TABLE IF NOT EXISTS `shopitems` (
`pitch` int(11) NOT NULL DEFAULT '0',
`position` int(11) NOT NULL COMMENT 'sort is an arbitrary field designed to give leeway when modifying shops. The lowest number is 104 and it increments by 4 for each item to allow decent space for swapping/inserting/removing items.',
PRIMARY KEY (`shopitemid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=20047 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=20047 ;
INSERT INTO `shopitems` (`shopitemid`, `shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
(1, 11000, 1332005, 500, 0, 104),
@@ -20967,7 +20967,7 @@ CREATE TABLE IF NOT EXISTS `shops` (
`shopid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`npcid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`shopid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10000000 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10000000 ;
INSERT INTO `shops` (`shopid`, `npcid`) VALUES
(11000, 11000),
@@ -21256,7 +21256,7 @@ CREATE TABLE IF NOT EXISTS `skillmacros` (
`name` varchar(13) DEFAULT NULL,
`shout` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `skills` (
`id` int(11) NOT NULL AUTO_INCREMENT,
+2 -2
View File
@@ -19885,7 +19885,7 @@ USE `maplesolaxia`;
PRIMARY KEY (`id`),
UNIQUE KEY (`dropperid`, `itemid`),
KEY `mobid` (`dropperid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
#pass (sorted) data from one table to another
INSERT INTO drop_data (dropperid, itemid, minimum_quantity, maximum_quantity, questid, chance)
@@ -21223,7 +21223,7 @@ USE `maplesolaxia`;
`mobid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `monstercarddata` (`cardid`, `mobid`) (SELECT itemid, min(dropperid) FROM drop_data where itemid>=2380000 and itemid<2390000 group by itemid);
+98 -39
View File
@@ -257,6 +257,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private Map<Integer, MapleCoolDownValueHolder> coolDowns = new LinkedHashMap<>();
private EnumMap<MapleDisease, MapleDiseaseValueHolder> diseases = new EnumMap<>(MapleDisease.class);
private Map<Integer, MapleDoor> doors = new LinkedHashMap<>();
private Map<MapleQuest, Long> questExpirations = new LinkedHashMap<>();
private ScheduledFuture<?> dragonBloodSchedule;
private ScheduledFuture<?> hpDecreaseTask;
private ScheduledFuture<?> beholderHealingSchedule, beholderBuffSchedule, berserkSchedule;
@@ -264,14 +265,14 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private ScheduledFuture<?> buffExpireTask = null;
private ScheduledFuture<?> itemExpireTask = null;
private ScheduledFuture<?> diseaseExpireTask = null;
private ScheduledFuture<?> questExpireTask = null;
private ScheduledFuture<?> recoveryTask = null;
private ScheduledFuture<?> extraRecoveryTask = null;
private ScheduledFuture<?> chairRecoveryTask = null;
private ScheduledFuture<?> pendantOfSpirit = null; //1122017
private List<ScheduledFuture<?>> timers = new ArrayList<>();
private Lock chrLock = new ReentrantLock(true);
private Lock effLock = new ReentrantLock(true);
private Lock petLock = new ReentrantLock(true);
private Lock petLock = new ReentrantLock(true); // for quest tasks as well
private Lock prtLock = new ReentrantLock();
private Map<Integer, Set<Integer>> excluded = new LinkedHashMap<>();
private Set<Integer> excludedItems = new LinkedHashSet<>();
@@ -2088,13 +2089,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
addHP(-getMap().getHPDec());
lastHpDec = System.currentTimeMillis();
}
hpDecreaseTask = TimerManager.getInstance().schedule(new Runnable() {
}
private void startHpDecreaseTask(long lastHpTask) {
hpDecreaseTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
doHurtHp();
}
}, 10000);
}, 10000, 10000 - lastHpTask);
}
public void resetHpDecreaseTask() {
@@ -2103,16 +2106,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
long lastHpTask = System.currentTimeMillis() - lastHpDec;
if(lastHpTask >= 10000) {
doHurtHp();
} else {
hpDecreaseTask = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
doHurtHp();
}
}, 10000 - lastHpTask);
}
startHpDecreaseTask((lastHpTask > 10000) ? 10000 : lastHpTask);
}
public void dropMessage(String message) {
@@ -2127,10 +2121,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return nf.format(MapleGuild.CHANGE_EMBLEM_COST);
}
public List<ScheduledFuture<?>> getTimers() {
return timers;
}
private void enforceMaxHpMp() {
List<Pair<MapleStat, Integer>> stats = new ArrayList<>(2);
if (getMp() > getCurrentMaxMp()) {
@@ -7221,7 +7211,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public void showNote() {
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM notes WHERE `to`=? AND `deleted` = 0", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM notes WHERE `to` = ? AND `deleted` = 0", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
ps.setString(1, this.getName());
try (ResultSet rs = ps.executeQuery()) {
rs.last();
@@ -7466,15 +7456,83 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
updateQuest(newStatus);
}
public void questTimeLimit(final MapleQuest quest, int seconds) {
ScheduledFuture<?> sf = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
expireQuest(quest);
public void cancelQuestExpirationTask() {
petLock.lock();
try {
if (questExpireTask != null) {
questExpireTask.cancel(false);
questExpireTask = null;
}
}, seconds * 1000);
} finally {
petLock.unlock();
}
}
public void questExpirationTask() {
petLock.lock();
try {
if(!questExpirations.isEmpty()) {
if(questExpireTask == null) {
questExpireTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
runQuestExpireTask();
}
}, 10 * 1000);
}
}
} finally {
petLock.unlock();
}
}
private void runQuestExpireTask() {
petLock.lock();
try {
long timeNow = System.currentTimeMillis();
List<MapleQuest> expireList = new LinkedList<>();
for(Entry<MapleQuest, Long> qe : questExpirations.entrySet()) {
if(qe.getValue() <= timeNow) expireList.add(qe.getKey());
}
if(!expireList.isEmpty()) {
for(MapleQuest quest : expireList) {
expireQuest(quest);
questExpirations.remove(quest);
}
if(questExpirations.isEmpty()) {
questExpireTask.cancel(false);
questExpireTask = null;
}
}
} finally {
petLock.unlock();
}
}
private void registerQuestExpire(MapleQuest quest, long time) {
petLock.lock();
try {
if(questExpireTask == null) {
questExpireTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
runQuestExpireTask();
}
}, 10 * 1000);
}
questExpirations.put(quest, System.currentTimeMillis() + time);
} finally {
petLock.unlock();
}
}
public void questTimeLimit(final MapleQuest quest, int seconds) {
registerQuestExpire(quest, seconds * 1000);
announce(MaplePacketCreator.addQuestTimeLimit(quest.getId(), seconds * 1000));
timers.add(sf);
}
public void questTimeLimit2(final MapleQuest quest, long expires) {
@@ -7483,14 +7541,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if(timeLeft <= 0) {
expireQuest(quest);
} else {
ScheduledFuture<?> sf = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
expireQuest(quest);
}
}, timeLeft);
timers.add(sf);
registerQuestExpire(quest, timeLeft);
}
}
@@ -7903,10 +7954,19 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
cancelSkillCooldownTask();
cancelExpirationTask();
for (ScheduledFuture<?> sf : timers) {
sf.cancel(false);
petLock.lock();
try {
if (questExpireTask != null) {
questExpireTask.cancel(false);
questExpireTask = null;
questExpirations.clear();
questExpirations = null;
}
} finally {
petLock.unlock();
}
timers.clear();
if (maplemount != null) {
maplemount.empty();
maplemount = null;
@@ -7921,7 +7981,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
family = null;
client = null;
map = null;
timers = null;
}
}
+14 -34
View File
@@ -41,7 +41,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -70,7 +69,6 @@ import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager;
import server.life.MapleMonster;
import server.MapleTrade;
import server.TimerManager;
import server.maps.*;
import server.quest.MapleQuest;
import tools.LogHelper;
@@ -101,7 +99,6 @@ public class MapleClient {
private int gmlevel;
private Set<String> macs = new HashSet<>();
private Map<String, ScriptEngine> engines = new HashMap<>();
private ScheduledFuture<?> idleTask = null;
private byte characterSlots = 3;
private byte loginattempt = 0;
private String pin = null;
@@ -941,15 +938,13 @@ public class MapleClient {
}
private void clear() { //usable when defining client = null shortly after
Server.getInstance().unregisterLoginState(this);
this.accountName = null;
this.macs = null;
this.hwid = null;
this.birthday = null;
//this.engines = null;
if (this.idleTask != null) {
this.idleTask.cancel(true);
this.idleTask = null;
}
this.player = null;
this.receive = null;
this.send = null;
@@ -1005,25 +1000,17 @@ public class MapleClient {
lastPong = System.currentTimeMillis();
}
public void sendPing() {
final long then = System.currentTimeMillis();
announce(MaplePacketCreator.getPing());
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
try {
if (lastPong < then) {
if (session != null && session.isConnected()) {
session.close(false);
}
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}, 15000);
}
public void testPing(long timeThen) {
try {
if (lastPong < timeThen) {
if (session != null && session.isConnected()) {
session.close(false);
}
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
public String getHWID() {
return hwid;
@@ -1053,14 +1040,6 @@ public class MapleClient {
engines.remove(name);
}
public ScheduledFuture<?> getIdleTask() {
return idleTask;
}
public void setIdleTask(ScheduledFuture<?> idleTask) {
this.idleTask = idleTask;
}
public NPCConversationManager getCM() {
return NPCScriptManager.getInstance().getCM(this);
}
@@ -1321,6 +1300,7 @@ public class MapleClient {
player.cancelBuffExpireTask();
player.cancelDiseaseExpireTask();
player.cancelSkillCooldownTask();
player.cancelQuestExpirationTask();
//Cancelling magicdoor? Nope
//Cancelling mounts? Noty
if (player.getBuffedValue(MapleBuffStat.PUPPET) != null) {
+9 -6
View File
@@ -349,6 +349,10 @@ public class Commands {
break;
case "staff":
player.yellowMessage("MapleSolaxiaV2 Staff");
player.yellowMessage("Ronan - Developer");
player.yellowMessage("Vcoc - Freelance Developer");
player.yellowMessage("");
player.yellowMessage("MapleSolaxia Staff");
player.yellowMessage("Aria - Administrator");
player.yellowMessage("Twdtwd - Administrator");
@@ -358,9 +362,6 @@ public class Commands {
player.yellowMessage("SourMjolk - Game Master");
player.yellowMessage("Kanade - Game Master");
player.yellowMessage("Kitsune - Game Master");
player.yellowMessage("MapleSolaxiaV2 Staff");
player.yellowMessage("Ronan - Freelance Developer");
player.yellowMessage("Vcoc - Freelance Developer");
break;
case "lastrestart":
@@ -451,7 +452,7 @@ public class Commands {
output += "#b" + data.getRight() + "#k is dropped by:\r\n";
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM drop_data WHERE itemid = ? LIMIT 50");
PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE itemid = ? LIMIT 50");
ps.setInt(1, data.getLeft());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
@@ -1513,11 +1514,13 @@ public class Commands {
case "reloadmap":
MapleMap oldMap = c.getPlayer().getMap();
MapleMap newMap = c.getChannelServer().getMapFactory().getMap(player.getMapId());
MapleMap newMap = c.getChannelServer().getMapFactory().resetMap(player.getMapId());
int callerid = c.getPlayer().getId();
for (MapleCharacter ch : oldMap.getCharacters()) {
ch.changeMap(newMap);
if(ch.getId() != callerid) ch.dropMessage("You have been relocated due to map reloading. Sorry for the inconvenience.");
}
oldMap = null;
newMap.respawn();
break;
+1
View File
@@ -85,6 +85,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 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.
//Some Gameplay Enhancing Configurations
+83 -2
View File
@@ -42,6 +42,15 @@ import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
import constants.ServerConstants;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import server.TimerManager;
public class MapleServerHandler extends IoHandlerAdapter {
private PacketProcessor processor;
@@ -49,16 +58,26 @@ 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 Map<MapleClient, Long> idleSessions = new HashMap<>(100);
private Map<MapleClient, Long> tempIdleSessions = new HashMap<>();
private ScheduledFuture<?> idleManager = null;
public MapleServerHandler() {
this.processor = PacketProcessor.getProcessor(-1, -1);
idleManagerTask();
}
public MapleServerHandler(int world, int channel) {
this.processor = PacketProcessor.getProcessor(world, channel);
this.world = world;
this.channel = channel;
idleManagerTask();
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
System.out.println("disconnect by exception");
@@ -155,8 +174,70 @@ public class MapleServerHandler extends IoHandlerAdapter {
public void sessionIdle(final IoSession session, final IdleStatus status) throws Exception {
MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
if (client != null) {
client.sendPing();
registerIdleSession(client);
}
super.sessionIdle(session, status);
}
private void registerIdleSession(MapleClient c) {
if(idleLock.tryLock()) {
idleSessions.put(c, System.currentTimeMillis());
c.announce(MaplePacketCreator.getPing());
idleLock.unlock();
} else {
tempLock.lock();
try {
tempIdleSessions.put(c, System.currentTimeMillis());
c.announce(MaplePacketCreator.getPing());
} finally {
tempLock.unlock();
}
}
}
private void manageIdleSessions() {
long timeNow = System.currentTimeMillis();
long timeThen = timeNow - 15000;
idleLock.lock();
try {
for(Entry<MapleClient, Long> mc : idleSessions.entrySet()) {
if(timeNow - mc.getValue() >= 15000) {
mc.getKey().testPing(timeThen);
}
}
idleSessions.clear();
if(!tempIdleSessions.isEmpty()) {
tempLock.lock();
try {
for(Entry<MapleClient, Long> mc : tempIdleSessions.entrySet()) {
idleSessions.put(mc.getKey(), mc.getValue());
}
tempIdleSessions.clear();
} finally {
tempLock.unlock();
}
}
} finally {
idleLock.unlock();
}
}
private void idleManagerTask() {
this.idleManager = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
manageIdleSessions();
}
}, 10000);
}
private void cancelIdleManagerTask() {
this.idleManager.cancel(false);
this.idleManager = null;
}
}
+2 -2
View File
@@ -132,7 +132,7 @@ public enum SendOpcode {
NOTIFY_LEVELUP(0x69),
NOTIFY_MARRIAGE(0x6A),
NOTIFY_JOB_CHANGE(0x6B),
//SET_BUY_EQUIP_EXT(0x6C),//lol?
//SET_BUY_EQUIP_EXT(0x6C), //probably extra pendant slot for other versions?
MAPLE_TV_USE_RES(0x6D), //It's not blank, It's a popup nibs
AVATAR_MEGAPHONE_RESULT(0x6E),//bot useless..
SET_AVATAR_MEGAPHONE(0x6F),
@@ -145,7 +145,7 @@ public enum SendOpcode {
NEW_YEAR_CARD_RES(0x76),
RANDOM_MORPH_RES(0x77),
CANCEL_NAME_CHANGE_BY_OTHER(0x78),
SET_BUY_EQUIP_EXT(0x79),
SET_EXTRA_PENDANT_SLOT(0x79),
SCRIPT_PROGRESS_MESSAGE(0x7A),
DATA_CRC_CHECK_FAILED(0x7B),
MACRO_SYS_DATA_INIT(0x7C),
+60 -3
View File
@@ -37,6 +37,7 @@ import java.util.LinkedHashMap;
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;
@@ -63,6 +64,7 @@ import server.TimerManager;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.Pair;
import client.MapleClient;
import client.MapleCharacter;
import client.SkillFactory;
import constants.ItemConstants;
@@ -82,7 +84,8 @@ public class Server implements Runnable {
private static Server instance = null;
private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
private final Map<Integer, MapleGuild> guilds = new LinkedHashMap<>();
private final Lock shutdownLock = new ReentrantLock();
private final Map<MapleClient, Long> inLoginState = new LinkedHashMap<>();
private final Lock srvLock = new ReentrantLock();
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
private final Map<Integer, MapleAlliance> alliances = new LinkedHashMap<>();
private boolean online = false;
@@ -279,6 +282,7 @@ public class Server implements Runnable {
TimerManager tMan = TimerManager.getInstance();
tMan.start();
tMan.register(tMan.purge(), ServerConstants.PURGING_INTERVAL);//Purging ftw...
disconnectIdlesOnLoginTask();
long timeLeft = getTimeLeftForNextHour();
tMan.register(new CouponWorker(), ServerConstants.COUPON_INTERVAL, timeLeft);
@@ -720,11 +724,64 @@ public class Server implements Runnable {
return worlds;
}
public void registerLoginState(MapleClient c) {
srvLock.lock();
try {
inLoginState.put(c, System.currentTimeMillis() + 600000);
} finally {
srvLock.unlock();
}
}
public void unregisterLoginState(MapleClient c) {
srvLock.lock();
try {
inLoginState.remove(c);
} finally {
srvLock.unlock();
}
}
private void disconnectIdlesOnLoginState() {
srvLock.lock();
try {
List<MapleClient> toDisconnect = new LinkedList<>();
long timeNow = System.currentTimeMillis();
for(Entry<MapleClient, Long> mc : inLoginState.entrySet()) {
if(timeNow > mc.getValue()) {
toDisconnect.add(mc.getKey());
}
}
for(MapleClient c : toDisconnect) {
if(c.isLoggedIn()) {
c.disconnect(false, false);
} else {
c.getSession().close(true);
}
inLoginState.remove(c);
}
} finally {
srvLock.unlock();
}
}
private void disconnectIdlesOnLoginTask() {
TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
disconnectIdlesOnLoginState();
}
}, 300000);
}
public final Runnable shutdown(final boolean restart) {//no player should be online when trying to shutdown!
return new Runnable() {
@Override
public void run() {
shutdownLock.lock();
srvLock.lock();
try {
System.out.println((restart ? "Restarting" : "Shutting down") + " the server!\r\n");
@@ -788,7 +845,7 @@ public class Server implements Runnable {
getInstance().run();//DID I DO EVERYTHING?! D:
}
} finally {
shutdownLock.unlock();
srvLock.unlock();
}
}
};
@@ -297,7 +297,7 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler {
if (mc.getGuildId() <= 0) {
return;
}
Connection con = null;
Connection con;
try {
con = DatabaseConnection.getConnection();
PreparedStatement ps2;
@@ -65,6 +65,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler {
mc.cancelDiseaseExpireTask();
mc.cancelSkillCooldownTask();
mc.cancelExpirationTask();
mc.cancelQuestExpirationTask();
c.announce(MaplePacketCreator.openCashShop(c, false));
c.announce(MaplePacketCreator.showCashInventory(c));
@@ -74,6 +74,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
chr.cancelDiseaseExpireTask();
chr.cancelSkillCooldownTask();
chr.cancelExpirationTask();
chr.cancelQuestExpirationTask();
chr.saveToDB();
chr.getMap().removePlayer(c.getPlayer());
@@ -343,15 +343,15 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
}
} else if (op == 9) { //add to cart
int id = slea.readInt(); //id of the item
Connection con = null;
Connection con;
try {
con = DatabaseConnection.getConnection();
try (PreparedStatement ps1 = con.prepareStatement("SELECT * FROM mts_items WHERE id = ? AND seller <> ?")) {
ps1.setInt(1, id);//Previene que agregues al cart tus propios items
try (PreparedStatement ps1 = con.prepareStatement("SELECT id FROM mts_items WHERE id = ? AND seller <> ?")) {
ps1.setInt(1, id); //Dummy query, prevents adding to cart self owned items
ps1.setInt(2, c.getPlayer().getId());
try (ResultSet rs1 = ps1.executeQuery()) {
if (rs1.next()) {
PreparedStatement ps = con.prepareStatement("SELECT * FROM mts_cart WHERE cid = ? AND itemid = ?");
PreparedStatement ps = con.prepareStatement("SELECT cid FROM mts_cart WHERE cid = ? AND itemid = ?");
ps.setInt(1, c.getPlayer().getId());
ps.setInt(2, id);
try (ResultSet rs = ps.executeQuery()) {
@@ -267,6 +267,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
player.diseaseExpireTask();
player.skillCooldownTask();
player.expirationTask();
player.questExpirationTask();
if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) {
player.createDragon();
}
+1 -1
View File
@@ -208,7 +208,7 @@ public class MapleAlliance {
ps.close();
rs.close();
ps = con.prepareStatement("SELECT * FROM allianceguilds WHERE allianceid = ?");
ps = con.prepareStatement("SELECT guildid FROM allianceguilds WHERE allianceid = ?");
ps.setInt(1, id);
rs = ps.executeQuery();
@@ -26,10 +26,12 @@ import net.MaplePacketHandler;
import tools.data.input.SeekableLittleEndianAccessor;
public class KeepAliveHandler implements MaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
c.pongReceived();
}
@Override
public boolean validateState(MapleClient c) {
return true;
}
@@ -41,9 +41,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
return;
}
if (c.getIdleTask() != null) {
c.getIdleTask().cancel(true);
}
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), c.getChannel()).split(":");
try {
@@ -26,9 +26,7 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
return;
}
if (c.checkPic(pic)) {
if (c.getIdleTask() != null) {
c.getIdleTask().cancel(true);
}
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), c.getChannel()).split(":");
@@ -24,6 +24,7 @@ package net.server.handlers.login;
import java.util.Calendar;
import net.MaplePacketHandler;
import net.server.Server;
import server.TimerManager;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -73,12 +74,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
private static void login(MapleClient c){
c.announce(MaplePacketCreator.getAuthSuccess(c));//why the fk did I do c.getAccountName()?
final MapleClient client = c;
c.setIdleTask(TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
client.disconnect(false, false);
}
}, 600000));
Server.getInstance().registerLoginState(c);
}
}
@@ -49,9 +49,7 @@ public final class PickCharHandler extends AbstractMaplePacketHandler {
e.printStackTrace();
c.setChannel(1);
}
if (c.getIdleTask() != null) {
c.getIdleTask().cancel(true);
}
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), c.getChannel()).split(":");
try {
@@ -29,9 +29,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
String pic = slea.readMapleAsciiString();
if (c.getPic() == null || c.getPic().equals("")) {
c.setPic(pic);
if (c.getIdleTask() != null) {
c.getIdleTask().cancel(true);
}
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), c.getChannel()).split(":");
try {
@@ -24,7 +24,7 @@ package net.server.handlers.login;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import server.TimerManager;
import net.server.Server;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -40,12 +40,8 @@ public class SetGenderHandler extends AbstractMaplePacketHandler {
c.setGender(slea.readByte());
c.announce(MaplePacketCreator.getAuthSuccess(c));
final MapleClient client = c;
c.setIdleTask(TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
client.getSession().close(true);
}
}, 600000));
Server.getInstance().registerLoginState(c);
}
}
@@ -29,9 +29,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
return;
}
if (c.checkPic(pic)) {
if (c.getIdleTask() != null) {
c.getIdleTask().cancel(true);
}
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), c.getChannel()).split(":");
@@ -28,9 +28,7 @@ public final class ViewAllPicRegisterHandler extends AbstractMaplePacketHandler
slea.readMapleAsciiString();
String pic = slea.readMapleAsciiString();
c.setPic(pic);
if (c.getIdleTask() != null) {
c.getIdleTask().cancel(true);
}
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), channel).split(":");
try {
+1 -1
View File
@@ -1508,7 +1508,7 @@ public class MapleItemInformationProvider {
Connection con = null;
try {
con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM drop_data WHERE itemid = ? LIMIT 50");
PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE itemid = ? LIMIT 50");
ps.setInt(1, itemId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
+1 -1
View File
@@ -236,7 +236,7 @@ public class MapleShop {
con.close();
return null;
}
ps = con.prepareStatement("SELECT * FROM shopitems WHERE shopid = ? ORDER BY position DESC");
ps = con.prepareStatement("SELECT itemid, price, pitch FROM shopitems WHERE shopid = ? ORDER BY position DESC");
ps.setInt(1, shopId);
rs = ps.executeQuery();
List<Integer> recharges = new ArrayList<>(rechargeableItems);
@@ -111,7 +111,7 @@ public class MapleMonsterInformationProvider {
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("SELECT * FROM drop_data WHERE dropperid = ?");
ps = con.prepareStatement("SELECT itemid, chance, minimum_quantity, maximum_quantity, questid FROM drop_data WHERE dropperid = ?");
ps.setInt(1, monsterId);
rs = ps.executeQuery();
+42 -21
View File
@@ -98,6 +98,7 @@ public class MapleMap {
private Map<Integer, MaplePortal> portals = new HashMap<>();
private Map<Integer, Integer> backgroundTypes = new HashMap<>();
private Map<String, Integer> environment = new LinkedHashMap<>();
private Map<MapleMapItem, Long> droppedItems = new LinkedHashMap<>();
private LinkedList<WeakReference<MapleMapObject>> registeredDrops = new LinkedList<>();
private List<Rectangle> areas = new ArrayList<>();
private MapleFootholdTree footholds = null;
@@ -130,6 +131,7 @@ public class MapleMap {
private int mobCapacity = -1;
private ScheduledFuture<?> mapMonitor = null;
private ScheduledFuture<?> itemMonitor = null;
private ScheduledFuture<?> expireItemsTask = null;
private short itemMonitorTimeout;
private Pair<Integer, String> timeMob = null;
private short mobInterval = 5000;
@@ -368,7 +370,6 @@ public class MapleMap {
private void spawnRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery, SpawnCondition condition) {
chrRLock.lock();
try {
int curOID = getUsableOID();
mapobject.setObjectId(curOID);
@@ -585,6 +586,9 @@ public class MapleMap {
try {
itemMonitor.cancel(false);
itemMonitor = null;
expireItemsTask.cancel(false);
expireItemsTask = null;
} finally {
chrWLock.unlock();
}
@@ -621,6 +625,13 @@ public class MapleMap {
if(!registeredDrops.isEmpty()) cleanItemMonitor();
}
}, ServerConstants.ITEM_MONITOR_TIME, ServerConstants.ITEM_MONITOR_TIME);
expireItemsTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
makeDisappearExpiredItemDrops();
}
}, ServerConstants.ITEM_EXPIRE_CHECK, ServerConstants.ITEM_EXPIRE_CHECK);
itemMonitorTimeout = 1;
} finally {
@@ -637,7 +648,7 @@ public class MapleMap {
}
}
private void registerItemDrop(MapleMapItem mdrop) {
private void instantiateItemDrop(MapleMapItem mdrop) {
if(droppedItemCount.get() >= ServerConstants.ITEM_LIMIT_ON_MAP) {
MapleMapObject mapobj;
@@ -654,10 +665,9 @@ public class MapleMap {
makeDisappearItemFromMap(mapobj);
}
if(!everlast) TimerManager.getInstance().schedule(new ExpireMapItemJob(mdrop), ServerConstants.ITEM_EXPIRE_TIME);
objectWLock.lock();
try {
if(!everlast) registerItemDrop(mdrop);
registeredDrops.add(new WeakReference<>((MapleMapObject) mdrop));
} finally {
objectWLock.unlock();
@@ -666,6 +676,31 @@ public class MapleMap {
droppedItemCount.incrementAndGet();
}
private void registerItemDrop(MapleMapItem mdrop) {
droppedItems.put(mdrop, System.currentTimeMillis() + ServerConstants.ITEM_EXPIRE_TIME);
}
private void makeDisappearExpiredItemDrops() {
objectWLock.lock();
try {
List<MapleMapItem> toDisappear = new LinkedList<>();
long timeNow = System.currentTimeMillis();
for(Entry<MapleMapItem, Long> it : droppedItems.entrySet()) {
if(it.getValue() < timeNow) {
toDisappear.add(it.getKey());
}
}
for(MapleMapItem mmi : toDisappear) {
makeDisappearItemFromMap(mmi);
droppedItems.remove(mmi);
}
} finally {
objectWLock.unlock();
}
}
public void pickItemDrop(byte[] pickupPacket, MapleMapItem mdrop) {
broadcastMessage(pickupPacket, mdrop.getPosition());
@@ -686,7 +721,7 @@ public class MapleMap {
}
}, null);
registerItemDrop(mdrop);
instantiateItemDrop(mdrop);
activateItemReactors(mdrop, chr.getClient());
}
@@ -702,7 +737,7 @@ public class MapleMap {
}
}, null);
registerItemDrop(mdrop);
instantiateItemDrop(mdrop);
}
public final void disappearingItemDrop(final MapleMapObject dropper, final MapleCharacter owner, final Item item, final Point pos) {
@@ -1719,7 +1754,7 @@ public class MapleMap {
}, null);
broadcastMessage(MaplePacketCreator.dropItemFromMapObject(mdrop, dropper.getPosition(), droppos, (byte) 0));
registerItemDrop(mdrop);
instantiateItemDrop(mdrop);
activateItemReactors(mdrop, owner.getClient());
}
@@ -2694,20 +2729,6 @@ public class MapleMap {
}
}
private class ExpireMapItemJob implements Runnable {
private MapleMapItem mapitem;
public ExpireMapItemJob(MapleMapItem mapitem) {
this.mapitem = mapitem;
}
@Override
public void run() {
makeDisappearItemFromMap(mapitem);
}
}
private class ActivateItemReactor implements Runnable {
private MapleMapItem mapitem;
+217 -205
View File
@@ -69,6 +69,222 @@ public class MapleMapFactory {
this.mapsWLock = rrwl.writeLock();
}
public MapleMap resetMap(int mapid) {
mapsWLock.lock();
try {
maps.remove(Integer.valueOf(mapid));
} finally {
mapsWLock.unlock();
}
return getMap(mapid);
}
private synchronized MapleMap loadMapFromWz(int mapid, Integer omapid) {
MapleMap map;
mapsRLock.lock();
try {
map = maps.get(omapid);
} finally {
mapsRLock.unlock();
}
if (map != null) {
return map;
}
String mapName = getMapName(mapid);
MapleData mapData = source.getData(mapName);
MapleData infoData = mapData.getChildByPath("info");
String link = MapleDataTool.getString(infoData.getChildByPath("link"), "");
if (!link.equals("")) { //nexon made hundreds of dojo maps so to reduce the size they added links.
mapName = getMapName(Integer.parseInt(link));
mapData = source.getData(mapName);
}
float monsterRate = 0;
MapleData mobRate = infoData.getChildByPath("mobRate");
if (mobRate != null) {
monsterRate = ((Float) mobRate.getData()).floatValue();
}
map = new MapleMap(mapid, world, channel, MapleDataTool.getInt("returnMap", infoData), monsterRate);
map.setEventInstance(event);
String onFirstEnter = MapleDataTool.getString(infoData.getChildByPath("onFirstUserEnter"), String.valueOf(mapid));
map.setOnFirstUserEnter(onFirstEnter.equals("") ? String.valueOf(mapid) : onFirstEnter);
String onEnter = MapleDataTool.getString(infoData.getChildByPath("onUserEnter"), String.valueOf(mapid));
map.setOnUserEnter(onEnter.equals("") ? String.valueOf(mapid) : onEnter);
map.setFieldLimit(MapleDataTool.getInt(infoData.getChildByPath("fieldLimit"), 0));
map.setMobInterval((short) MapleDataTool.getInt(infoData.getChildByPath("createMobInterval"), 5000));
PortalFactory portalFactory = new PortalFactory();
for (MapleData portal : mapData.getChildByPath("portal")) {
map.addPortal(portalFactory.makePortal(MapleDataTool.getInt(portal.getChildByPath("pt")), portal));
}
MapleData timeMob = infoData.getChildByPath("timeMob");
if (timeMob != null) {
map.timeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")),
MapleDataTool.getString(timeMob.getChildByPath("message")));
}
int bounds[] = new int[4];
bounds[0] = MapleDataTool.getInt(infoData.getChildByPath("VRTop"));
bounds[1] = MapleDataTool.getInt(infoData.getChildByPath("VRBottom"));
if(bounds[0] == bounds[1]) { // old-style baked map
MapleData minimapData = mapData.getChildByPath("miniMap");
if(minimapData != null) {
bounds[0] = MapleDataTool.getInt(minimapData.getChildByPath("centerX")) * -1;
bounds[1] = MapleDataTool.getInt(minimapData.getChildByPath("centerY")) * -1;
bounds[2] = MapleDataTool.getInt(minimapData.getChildByPath("height"));
bounds[3] = MapleDataTool.getInt(minimapData.getChildByPath("width"));
map.setMapPointBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
}
} else {
bounds[2] = MapleDataTool.getInt(infoData.getChildByPath("VRLeft"));
bounds[3] = MapleDataTool.getInt(infoData.getChildByPath("VRRight"));
map.setMapLineBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
}
List<MapleFoothold> allFootholds = new LinkedList<>();
Point lBound = new Point();
Point uBound = new Point();
for (MapleData footRoot : mapData.getChildByPath("foothold")) {
for (MapleData footCat : footRoot) {
for (MapleData footHold : footCat) {
int x1 = MapleDataTool.getInt(footHold.getChildByPath("x1"));
int y1 = MapleDataTool.getInt(footHold.getChildByPath("y1"));
int x2 = MapleDataTool.getInt(footHold.getChildByPath("x2"));
int y2 = MapleDataTool.getInt(footHold.getChildByPath("y2"));
MapleFoothold fh = new MapleFoothold(new Point(x1, y1), new Point(x2, y2), Integer.parseInt(footHold.getName()));
fh.setPrev(MapleDataTool.getInt(footHold.getChildByPath("prev")));
fh.setNext(MapleDataTool.getInt(footHold.getChildByPath("next")));
if (fh.getX1() < lBound.x) {
lBound.x = fh.getX1();
}
if (fh.getX2() > uBound.x) {
uBound.x = fh.getX2();
}
if (fh.getY1() < lBound.y) {
lBound.y = fh.getY1();
}
if (fh.getY2() > uBound.y) {
uBound.y = fh.getY2();
}
allFootholds.add(fh);
}
}
}
MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
for (MapleFoothold fh : allFootholds) {
fTree.insert(fh);
}
map.setFootholds(fTree);
if (mapData.getChildByPath("area") != null) {
for (MapleData area : mapData.getChildByPath("area")) {
int x1 = MapleDataTool.getInt(area.getChildByPath("x1"));
int y1 = MapleDataTool.getInt(area.getChildByPath("y1"));
int x2 = MapleDataTool.getInt(area.getChildByPath("x2"));
int y2 = MapleDataTool.getInt(area.getChildByPath("y2"));
map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
}
}
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ?")) {
ps.setInt(1, omapid);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
map.addMapObject(new PlayerNPCs(rs));
}
}
}
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
for (MapleData life : mapData.getChildByPath("life")) {
String id = MapleDataTool.getString(life.getChildByPath("id"));
String type = MapleDataTool.getString(life.getChildByPath("type"));
AbstractLoadedMapleLife myLife = loadLife(life, id, type);
if (myLife instanceof MapleMonster) {
MapleMonster monster = (MapleMonster) myLife;
int mobTime = MapleDataTool.getInt("mobTime", life, 0);
int team = MapleDataTool.getInt("team", life, -1);
if (mobTime == -1) { //does not respawn, force spawn once
map.spawnMonster(monster);
} else {
map.addMonsterSpawn(monster, mobTime, team);
}
//should the map be reseted, use allMonsterSpawn list of monsters to spawn them again
map.addAllMonsterSpawn(monster, mobTime, team);
} else {
map.addMapObject(myLife);
}
}
if (mapData.getChildByPath("reactor") != null) {
for (MapleData reactor : mapData.getChildByPath("reactor")) {
String id = MapleDataTool.getString(reactor.getChildByPath("id"));
if (id != null) {
MapleReactor newReactor = loadReactor(reactor, id);
map.spawnReactor(newReactor);
}
}
}
try {
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(omapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(omapid)), ""));
} catch (Exception e) {
e.printStackTrace();
System.err.println("Not found mapid " + omapid);
map.setMapName("");
map.setStreetName("");
}
map.setClock(mapData.getChildByPath("clock") != null);
map.setEverlast(infoData.getChildByPath("everlast") != null);
map.setTown(infoData.getChildByPath("town") != null);
map.setHPDec(MapleDataTool.getIntConvert("decHP", infoData, 0));
map.setHPDecProtect(MapleDataTool.getIntConvert("protectItem", infoData, 0));
map.setForcedReturnMap(MapleDataTool.getInt(infoData.getChildByPath("forcedReturn"), 999999999));
map.setBoat(mapData.getChildByPath("shipObj") != null);
map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1));
map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0));
map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs?
HashMap<Integer, Integer> backTypes = new HashMap<>();
try {
for (MapleData layer : mapData.getChildByPath("back")) { // yolo
int layerNum = Integer.parseInt(layer.getName());
int type = MapleDataTool.getInt(layer.getChildByPath("type"), 0);
backTypes.put(layerNum, type);
}
} catch (Exception e) {
e.printStackTrace();
// swallow cause I'm cool
}
map.setBackgroundTypes(backTypes);
mapsWLock.lock();
try {
maps.put(omapid, map);
} finally {
mapsWLock.unlock();
}
return map;
}
public MapleMap getMap(int mapid) {
Integer omapid = Integer.valueOf(mapid);
MapleMap map;
@@ -80,211 +296,7 @@ public class MapleMapFactory {
mapsRLock.unlock();
}
if (map == null) {
synchronized (this) {
mapsRLock.lock();
try {
map = maps.get(omapid);
} finally {
mapsRLock.unlock();
}
if (map != null) {
return map;
}
String mapName = getMapName(mapid);
MapleData mapData = source.getData(mapName);
MapleData infoData = mapData.getChildByPath("info");
String link = MapleDataTool.getString(infoData.getChildByPath("link"), "");
if (!link.equals("")) { //nexon made hundreds of dojo maps so to reduce the size they added links.
mapName = getMapName(Integer.parseInt(link));
mapData = source.getData(mapName);
}
float monsterRate = 0;
MapleData mobRate = infoData.getChildByPath("mobRate");
if (mobRate != null) {
monsterRate = ((Float) mobRate.getData()).floatValue();
}
map = new MapleMap(mapid, world, channel, MapleDataTool.getInt("returnMap", infoData), monsterRate);
map.setEventInstance(event);
String onFirstEnter = MapleDataTool.getString(infoData.getChildByPath("onFirstUserEnter"), String.valueOf(mapid));
map.setOnFirstUserEnter(onFirstEnter.equals("") ? String.valueOf(mapid) : onFirstEnter);
String onEnter = MapleDataTool.getString(infoData.getChildByPath("onUserEnter"), String.valueOf(mapid));
map.setOnUserEnter(onEnter.equals("") ? String.valueOf(mapid) : onEnter);
map.setFieldLimit(MapleDataTool.getInt(infoData.getChildByPath("fieldLimit"), 0));
map.setMobInterval((short) MapleDataTool.getInt(infoData.getChildByPath("createMobInterval"), 5000));
PortalFactory portalFactory = new PortalFactory();
for (MapleData portal : mapData.getChildByPath("portal")) {
map.addPortal(portalFactory.makePortal(MapleDataTool.getInt(portal.getChildByPath("pt")), portal));
}
MapleData timeMob = infoData.getChildByPath("timeMob");
if (timeMob != null) {
map.timeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")),
MapleDataTool.getString(timeMob.getChildByPath("message")));
}
int bounds[] = new int[4];
bounds[0] = MapleDataTool.getInt(infoData.getChildByPath("VRTop"));
bounds[1] = MapleDataTool.getInt(infoData.getChildByPath("VRBottom"));
if(bounds[0] == bounds[1]) { // old-style baked map
MapleData minimapData = mapData.getChildByPath("miniMap");
if(minimapData != null) {
bounds[0] = MapleDataTool.getInt(minimapData.getChildByPath("centerX")) * -1;
bounds[1] = MapleDataTool.getInt(minimapData.getChildByPath("centerY")) * -1;
bounds[2] = MapleDataTool.getInt(minimapData.getChildByPath("height"));
bounds[3] = MapleDataTool.getInt(minimapData.getChildByPath("width"));
map.setMapPointBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
}
} else {
bounds[2] = MapleDataTool.getInt(infoData.getChildByPath("VRLeft"));
bounds[3] = MapleDataTool.getInt(infoData.getChildByPath("VRRight"));
map.setMapLineBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
}
List<MapleFoothold> allFootholds = new LinkedList<>();
Point lBound = new Point();
Point uBound = new Point();
for (MapleData footRoot : mapData.getChildByPath("foothold")) {
for (MapleData footCat : footRoot) {
for (MapleData footHold : footCat) {
int x1 = MapleDataTool.getInt(footHold.getChildByPath("x1"));
int y1 = MapleDataTool.getInt(footHold.getChildByPath("y1"));
int x2 = MapleDataTool.getInt(footHold.getChildByPath("x2"));
int y2 = MapleDataTool.getInt(footHold.getChildByPath("y2"));
MapleFoothold fh = new MapleFoothold(new Point(x1, y1), new Point(x2, y2), Integer.parseInt(footHold.getName()));
fh.setPrev(MapleDataTool.getInt(footHold.getChildByPath("prev")));
fh.setNext(MapleDataTool.getInt(footHold.getChildByPath("next")));
if (fh.getX1() < lBound.x) {
lBound.x = fh.getX1();
}
if (fh.getX2() > uBound.x) {
uBound.x = fh.getX2();
}
if (fh.getY1() < lBound.y) {
lBound.y = fh.getY1();
}
if (fh.getY2() > uBound.y) {
uBound.y = fh.getY2();
}
allFootholds.add(fh);
}
}
}
MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
for (MapleFoothold fh : allFootholds) {
fTree.insert(fh);
}
map.setFootholds(fTree);
if (mapData.getChildByPath("area") != null) {
for (MapleData area : mapData.getChildByPath("area")) {
int x1 = MapleDataTool.getInt(area.getChildByPath("x1"));
int y1 = MapleDataTool.getInt(area.getChildByPath("y1"));
int x2 = MapleDataTool.getInt(area.getChildByPath("x2"));
int y2 = MapleDataTool.getInt(area.getChildByPath("y2"));
map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
}
}
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ?")) {
ps.setInt(1, omapid);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
map.addMapObject(new PlayerNPCs(rs));
}
}
}
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
for (MapleData life : mapData.getChildByPath("life")) {
String id = MapleDataTool.getString(life.getChildByPath("id"));
String type = MapleDataTool.getString(life.getChildByPath("type"));
if (id.equals("9001105")) {
id = "9001108";//soz
}
AbstractLoadedMapleLife myLife = loadLife(life, id, type);
if (myLife instanceof MapleMonster) {
MapleMonster monster = (MapleMonster) myLife;
int mobTime = MapleDataTool.getInt("mobTime", life, 0);
int team = MapleDataTool.getInt("team", life, -1);
if (mobTime == -1) { //does not respawn, force spawn once
map.spawnMonster(monster);
} else {
map.addMonsterSpawn(monster, mobTime, team);
}
//should the map be reseted, use allMonsterSpawn list of monsters to spawn them again
map.addAllMonsterSpawn(monster, mobTime, team);
} else {
map.addMapObject(myLife);
}
}
if (mapData.getChildByPath("reactor") != null) {
for (MapleData reactor : mapData.getChildByPath("reactor")) {
String id = MapleDataTool.getString(reactor.getChildByPath("id"));
if (id != null) {
MapleReactor newReactor = loadReactor(reactor, id);
map.spawnReactor(newReactor);
}
}
}
try {
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(omapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(omapid)), ""));
} catch (Exception e) {
e.printStackTrace();
System.err.println("Not found mapid " + omapid);
map.setMapName("");
map.setStreetName("");
}
map.setClock(mapData.getChildByPath("clock") != null);
map.setEverlast(infoData.getChildByPath("everlast") != null);
map.setTown(infoData.getChildByPath("town") != null);
map.setHPDec(MapleDataTool.getIntConvert("decHP", infoData, 0));
map.setHPDecProtect(MapleDataTool.getIntConvert("protectItem", infoData, 0));
map.setForcedReturnMap(MapleDataTool.getInt(infoData.getChildByPath("forcedReturn"), 999999999));
map.setBoat(mapData.getChildByPath("shipObj") != null);
map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1));
map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0));
map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs?
HashMap<Integer, Integer> backTypes = new HashMap<>();
try {
for (MapleData layer : mapData.getChildByPath("back")) { // yolo
int layerNum = Integer.parseInt(layer.getName());
int type = MapleDataTool.getInt(layer.getChildByPath("type"), 0);
backTypes.put(layerNum, type);
}
} catch (Exception e) {
e.printStackTrace();
// swallow cause I'm cool
}
map.setBackgroundTypes(backTypes);
mapsWLock.lock();
try {
maps.put(omapid, map);
} finally {
mapsWLock.unlock();
}
}
}
return map;
return (map != null) ? map : loadMapFromWz(mapid, omapid);
}
public boolean isMapLoaded(int mapId) {
+4 -4
View File
@@ -26,16 +26,16 @@ public class DatabaseConnection {
}
int denies = 0;
while(true) { // There is no way it can pass with a null out of here
while(true) { // There is no way it can pass with a null out of here?
try {
return DriverManager.getConnection(ServerConstants.DB_URL, ServerConstants.DB_USER, ServerConstants.DB_PASS);
} catch (SQLException sqle) {
denies++;
if(denies == 3) {
// Give up, return null :3
FilePrinter.printError(FilePrinter.SQL_EXCEPTION, "SQL Driver refused to give a connection after " + denies + " tries.");
return null;
// Give up, throw exception. Nothing good will come from this.
FilePrinter.printError(FilePrinter.SQL_EXCEPTION, "SQL Driver refused to give a connection after " + denies + " tries. Problem: " + sqle.getMessage());
throw sqle;
}
}
}
+7
View File
@@ -313,6 +313,13 @@ public class MaplePacketCreator {
}
}
}
public static byte[] setExtraPendantSlot(boolean toggleExtraSlot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SET_EXTRA_PENDANT_SLOT.getValue());
mplew.writeBool(toggleExtraSlot);
return mplew.getPacket();
}
private static void addCharEntry(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr, boolean viewall) {
addCharStats(mplew, chr);