diff --git a/README.md b/README.md
index 4c48aa4152..66bc810125 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## Head developer: Ronan C. P. Lana
-Besides myself for maintaining this repository, credits are to be given to Nexon(Duh!), the original MapleSolaxia staff and other colaborators, as just some changes/patches on the game were applied by myself, in which some of them diverged from the original v83 patch contents.
+Besides myself for maintaining this repository, credits are to be given to Nexon(Duh!), the original MapleSolaxia staff and other colaborators, as just some changes/patches on the game were applied by myself, in which some of them diverged from the original v83 patch contents (alright, not just "some patches" by now since a whole lot of major server core changes have been applied on this development).
Regarding distributability and usage of the code presented here: like it was before, this MapleStory server is open-source. By that, it is meant that anyone is **free to install, use, modify and redistribute the contents**, as long as there is **no kind of commercial trading involved** and the **credits to the original creators are maintained** within the codes.
diff --git a/docs/issues.txt b/docs/issues.txt
index 7b1be7e003..f67c136e9d 100644
--- a/docs/issues.txt
+++ b/docs/issues.txt
@@ -7,7 +7,6 @@ Vcoc - Freelance Developer
Known issues:
- Everytime two people click on an npc at the same time, one of them dcs and the other needs to @dispose to talk to the npc.
- If multiple people hit boxes/reactors at the same time, they both dc with invalid pointer error.
-- Passwords on minirooms are not encoded for players entering/logging into the map.
- Some criticals (e.g. from Aran skills) will not show up as crit for other players.
- Deadlocks may start appearing if the server stays online long enough with many players logged in.
- If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen.
diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt
index 0e94fe5bf8..7a96d18335 100644
--- a/docs/mychanges_ptbr.txt
+++ b/docs/mychanges_ptbr.txt
@@ -1172,4 +1172,26 @@ Refatorado vários métodos de liberamento de recursos do server, tais como em:
Implementado código que ofereçe suporte para abrir novos worlds e channels sob demanda.
Adicionado scheduler dedicado para ações de event managers.
Corrigido potencial de deadlock em alguns pontos do sistema de schedulers de canais.
-Refatorado vários temporizadores utilizados pelo EventManager e Channel, como o respawn de mobs e o disposeInstance.
\ No newline at end of file
+Refatorado vários temporizadores utilizados pelo EventManager e Channel, como o respawn de mobs e o disposeInstance.
+
+24 Julho 2018,
+Implementado código que ofereçe suporte para remover worlds e channels sob demanda.
+Corrigido alguns problemas com deadlocks ao tentar remover worlds e channels.
+Melhorado envio de packets de objetos de ambiente. Agora é necessário enviar somente um packet, que lista o estado corrente de todos os obstáculos de um mapa (usado bastante na CWKPQ).
+Corrigido mobs não sendo devidamente registrados em eventos e não sumindo em certos cenários (e.g. quando chamado por outros mobs/bosses).
+
+25 - 26 Julho 2018,
+Melhorado sistema de respawn de mobs. Agora a quantidade de mobs em campo depende do número de jogadores no mesmo mapa.
+Corrigido certos pontos em MapleClient não liberando devidamente o jogador da estrutura que mantem todos os jogadores online num mundo.
+Corrigido corda atuando erroneamente em mapas de Malaysia.
+Remodelado viagens para Malaysia, agora por dois trajetos: CBD e Boat Quay. Para voltar de Malaysia somente conversando com Audrey (NPC agente de viagens) agora.
+Implementado um temporizador para drops de mobs. Aparentemente GMS drops levavam em conta o tempo de despawn do mob antes de lançar itens em campo.
+Corrigido dispose de EIM finalizando incorretamente se a função "dispose" estiver sendo disparado via scripts.
+Protegido concorrentemente acesso a módulos de distribuição de APs, evitando assim exploits críticos com APs.
+Melhorado desempenho do HealOvertimeHandler por fazendo buscar informação de mapas com recovery rate maior do XML ao invés de testar um conjunto limitado de mapas.
+
+27 Julho 2018,
+Corrigido comando "maxhpmp" permitindo efetivar valores negativos ao alvo.
+Corrigido Duey não registrando nível e experiência de equipamentos ao enviá-los.
+Buff no buyback: por um curto período após respawnar o jogador não morre (mínimo 1 HP), assim permitindo um tempo mínimo de reação.
+Revisado task do item monitor em MapleMap mal-protegido contra acessos concorrentes.
\ No newline at end of file
diff --git a/scripts/event/0_EXAMPLE.js b/scripts/event/0_EXAMPLE.js
index 6e6d98ba06..b542d1a655 100644
--- a/scripts/event/0_EXAMPLE.js
+++ b/scripts/event/0_EXAMPLE.js
@@ -112,6 +112,10 @@ function playerDead(eim, player) {
// Happens when player dies
}
+function monsterRevive(mob, eim) {
+ // Happens when an opposing mob revives
+}
+
function playerRevive(eim, player) {
// Happens when player's revived.
// @Param : returns true/false
diff --git a/scripts/npc/1061010.js b/scripts/npc/1061010.js
index ba0685ada2..dd640d2da1 100644
--- a/scripts/npc/1061010.js
+++ b/scripts/npc/1061010.js
@@ -28,8 +28,6 @@ function action(mode, type, selection) {
else if(mapid == 108010401) cm.getPlayer().changeMap(107000402);
else if(mapid == 108010501) cm.getPlayer().changeMap(105070200);
- var em = cm.getEventManager("3rdjob");
- em.getInstance(cm.getPlayer().getName()).unregisterPlayer(cm.getPlayer());
cm.dispose();
}
}
diff --git a/scripts/npc/2042000.js b/scripts/npc/2042000.js
index 1d539fa0b3..5387617294 100644
--- a/scripts/npc/2042000.js
+++ b/scripts/npc/2042000.js
@@ -80,7 +80,7 @@ function action(mode, type, selection) {
if(allDone) {
cm.sendOk("Done. Thanks for showing up~.");
} else {
- cm.sendOk("Done. Be aware some of the items could not be synthetized because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee.");
+ cm.sendOk("Done. Be aware some of the items #rcould not be synthetized#k because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee.");
}
cm.dispose();
}
diff --git a/scripts/npc/9000020.js b/scripts/npc/9000020.js
index d9fda2f36b..1a35a90e59 100644
--- a/scripts/npc/9000020.js
+++ b/scripts/npc/9000020.js
@@ -21,10 +21,50 @@
*/
status = -1;
+
+var travelFrom = [777777777, 541000000];
+var travelFee = [3000, 10000];
+
+var travelMap = [800000000, 550000000];
+var travelPlace = ["Mushroom Shrine of Japan", "Trend Zone of Malaysia"];
+var travelPlaceShort = ["Mushroom Shrine", "Metropolis"];
+var travelPlaceCountry = ["Japan", "Malaysia"];
+var travelAgent = ["I", "#r#p9201135##k"];
+
+var travelDescription = ["If you desire to feel the essence of Japan, there's nothing like visiting the Shrine, a Japanese cultural melting pot. Mushroom Shrine is a mythical place that serves the incomparable Mushroom God from ancient times.",
+ "If you desire to feel the heat of the tropics on an upbeat environment, the residents of Malaysia are eager to welcome you. Also, the metropolis itself is the heart of the local economy, that place is known to always offer something to do or to visit around."];
+
+var travelDescription2 = ["Check out the female shaman serving the Mushroom God, and I strongly recommend trying Takoyaki, Yakisoba, and other delocious food sold in the streets of Japan. Now, let's head over to #bMushroom Shrine#k, a mythical place if there ever was one.",
+ "Once there, I strongly suggest you to schedule a visit to Kampung Village. Why? Surely you've come to know about the fantasy theme park Spooky World? No? It's simply put the greatest theme park around there, it's worth a visit! Now, let's head over to the #bTrend Zone of Malaysia#k."];
+
+var travelType;
+var travelStatus;
+
function start() {
+ travelStatus = getTravelingStatus(cm.getPlayer().getMapId());
action(1,0,0);
}
+function getTravelingStatus(mapid) {
+ for(var i = 0; i < travelMap.length; i++) {
+ if(mapid == travelMap[i]) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+function getTravelType(mapid) {
+ for(var i = 0; i < travelFrom.length; i++) {
+ if(mapid == travelFrom[i]) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
function action(mode, type, selection) {
status++;
if(mode != 1){
@@ -35,7 +75,8 @@ function action(mode, type, selection) {
return;
}
}
- if (cm.getPlayer().getMapId() == 800000000) {
+
+ if (travelStatus != -1) {
if (status == 0)
cm.sendSimple("How's the traveling? Are you enjoying it?#b\r\n#L0#Yes, I'm done with traveling. Can I go back to #m" + cm.getPlayer().peekSavedLocation("WORLDTOUR") + "#?\r\n#L1#No, I'd like to continue exploring this place.");
else if (status == 1) {
@@ -53,23 +94,24 @@ function action(mode, type, selection) {
cm.dispose();
}
} else {
- if (status == 0)
- cm.sendNext("If you're tired of the monotonous daily life, how about getting out for a change? there's nothing quite like soaking up a new culture, learning something new by the minute! It's time for you to get out and travel. We, at the Maple Travel Agency recommend you going on a #bWorld Tour#k! Are you worried about the travel expense? You shouldn't be! We, the #bMaple Travel Agency#k, havecarefully come up with a plan to let you travel for ONLY #b3,000 mesos#k!");
- else if (status == 1)
- cm.sendSimple("We currently offer this place for you traveling pleasure: #bMushroom Shrine of Japan#k. I'll be there serving you as the travel guide. Rest assured, the number of destinations will be increase over time. Now, would you like to head over to the Mushroom Shrine?#b\r\n#L0#Yes, take me to Mushroom Shrine (Japan)");
- else if (status == 2)
- cm.sendNext("Would you like to travel to #bMushroom Shrine of Japan#k? If you desire to feel the essence of Japan, there's nothing like visiting the Shrine, a Japanese cultural melting pot. Mushroom Shrine is a mythical place that serves the incomparable Mushroom God from ancient times.");
- else if (status == 3) {
- if(cm.getMeso() < 3000){
+ if (status == 0) {
+ travelType = getTravelType(cm.getPlayer().getMapId());
+ cm.sendNext("If you're tired of the monotonous daily life, how about getting out for a change? there's nothing quite like soaking up a new culture, learning something new by the minute! It's time for you to get out and travel. We, at the Maple Travel Agency recommend you going on a #bWorld Tour#k! Are you worried about the travel expense? You shouldn't be! We, the #bMaple Travel Agency#k, have carefully come up with a plan to let you travel for ONLY #b" + cm.numberWithCommas(travelFee[travelType]) + " mesos#k!");
+ } else if (status == 1) {
+ cm.sendSimple("We currently offer this place for you traveling pleasure: #b" + travelPlace[travelType] + "#k. " + travelAgent[travelType] + "'ll be there serving you as the travel guide. Rest assured, the number of destinations will be increase over time. Now, would you like to head over to the " + travelPlaceShort[travelType] + "?#b\r\n#L0#Yes, take me to " + travelPlaceShort[travelType] + " (" + travelPlaceCountry[travelType] + ")");
+ } else if (status == 2) {
+ cm.sendNext("Would you like to travel to #b" + travelPlace[travelType] + "#k? " + travelDescription[travelType]);
+ } else if (status == 3) {
+ if(cm.getMeso() < travelFee[travelType]){
cm.sendNext("You don't have enough mesos to take the travel.");
cm.dispose();
return;
}
- cm.sendNextPrev("Check out the female shaman serving the Mushroom God, and I strongly recommend trying Takoyaki, Yakisoba, and other delocious food sold in the streets of Japan. Now, let's head over to #bMushroom Shrine#k, a mythical place if there ever was one.");
+ cm.sendNextPrev(travelDescription2[travelType]);
} else if (status == 4) {
- cm.gainMeso(-3000);
+ cm.gainMeso(-travelFee[travelType]);
cm.getPlayer().saveLocation("WORLDTOUR");
- cm.warp(800000000, 0);
+ cm.warp(travelMap[travelType], 0);
cm.dispose();
}
}
diff --git a/scripts/npc/9201135.js b/scripts/npc/9201135.js
index 66f6bb89d2..84a20758ea 100644
--- a/scripts/npc/9201135.js
+++ b/scripts/npc/9201135.js
@@ -19,26 +19,55 @@
along with this program. If not, see .
*/
-var toMap = new Array(550000000, 551000000, 540000000,540000000);
-var inMap = new Array(540000000, 540000000, 551000000, 550000000);
-var cost = new Array(10000, 50000, 50000, 10000);
+var inMap = new Array(540000000, 550000000, 551000000);
+var toMap = new Array(550000000, new Array(551000000, 541000000), 550000000);
+var cost = new Array(42000, new Array(10000, 0), 10000);
+var toMapSp = new Array(0, new Array(2, 4), 4);
+
var location;
-var text = "Where would you like to travel?\n\n";
+var text;
+
+var travelCost;
+var travelMap;
+var travelSp;
+
+var startedTravel = false;
+
var status = 0;
function start() {
if (cm.getPlayer().getMap().getId() != 540000000) {
- for (var i = 0; i < toMap.length; i ++) {
- if (inMap[i] == cm.getPlayer().getMap().getId()) {
- location = i;
- break;
- }
- }
- text +="\t\r\n#b#L0##m" + toMap[location] + "# (" + cost[location] + "mesos)#l#k";
+ text = "Hey I'm #p9201135#, your tour guide here in #rMalaysia#k. Where would you like to travel?\n\n";
} else {
- text += "\t\r\n#b#L0##m" + toMap[0] + "# (" + cost[0] + "mesos)#l\n\t\r\n#L1##m" + toMap[1] + "# (" + cost[1] + "mesos)#l#k";
+ text = "Hey I'm #p9201135#, a tour guide on #rMalaysia#k. Since you're not registered in our special travel package with our partner #bMaple Travel Agency#k, the ride will be significantly more expensive. So, would you like to ride now?\n\n";
+ startedTravel = true;
}
- cm.sendSimple(text);
+
+ for (var i = 0; i < toMap.length; i ++) {
+ if (inMap[i] == cm.getPlayer().getMap().getId()) {
+ if(inMap[i] == 550000000) {
+ toMap[1][1] = cm.getPlayer().peekSavedLocation("WORLDTOUR");
+ }
+
+ location = i;
+ break;
+ }
+ }
+
+ if(toMap[location] instanceof Array) {
+ var maps = toMap[location];
+ var costs = cost[location];
+
+ for(var i = 0; i < maps.length; i++) {
+ text +="\t\r\n#b#L" + i + "##m" + maps[i] + "# " + (costs[i] > 0 ? "(" + costs[i] + "mesos)" : "") + "#l";
+ }
+ } else {
+ text +="\t\r\n#b#L0##m" + toMap[location] + "# " + (cost[location] > 0 ? "(" + cost[location] + "mesos)" : "") + "#l";
+ }
+
+ text += "#k";
+
+ cm.sendSimple(text);
}
function action(mode, type, selection) {
@@ -51,22 +80,45 @@ function action(mode, type, selection) {
return;
} else {
status++;
- }
+ }
if (status == 1) {
- if (cm.getPlayer().getMap().getId() == 540000000) {
- location = selection;
- }
if (toMap[location] == null) {
- cm.dipose();
+ cm.dispose();
return;
}
- cm.sendYesNo("Would you like to travel to #b#m"+toMap[location]+"##k? To head over to #b#m"+toMap[location]+"##k, it'll cost you cost you #b" + cost[location] + "#k. Would you like to go right now?");
+
+ if(toMap[location] instanceof Array) {
+ var maps = toMap[location];
+ var costs = cost[location];
+ var sps = toMapSp[location];
+
+ travelCost = costs[selection];
+ travelMap = maps[selection];
+ travelSp = sps[selection];
+ } else {
+ travelCost = cost[location];
+ travelMap = toMap[location];
+ travelSp = toMapSp[location];
+ }
+
+ if(travelCost > 0) {
+ cm.sendYesNo("Would you like to travel to #b#m" + travelMap + "##k? To head over to #b#m" + travelMap + "##k, it'll cost you #r" + cm.numberWithCommas(travelCost) + " mesos#k. Would you like to go right now?");
+ } else {
+ cm.sendNext("Had a great time in #rMalaysia#k? I hope so, have a safe travel back!");
+ }
} else if (status == 2) {
- if (cm.getMeso() < cost[location]) {
+ if (cm.getMeso() < travelCost) {
cm.sendNext("You do not seem to have enough mesos.");
} else {
- cm.warp(toMap[location]);
- cm.gainMeso(-cost[location]);
+ if(travelCost > 0) {
+ cm.gainMeso(-travelCost);
+ if(startedTravel) cm.getPlayer().saveLocation("WORLDTOUR");
+ }
+ else {
+ travelMap = cm.getPlayer().getSavedLocation("WORLDTOUR");
+ }
+
+ cm.warp(travelMap, travelSp);
}
cm.dispose();
}
diff --git a/scripts/npc/9270033.js b/scripts/npc/9270033.js
index fa038e915e..b12b211261 100644
--- a/scripts/npc/9270033.js
+++ b/scripts/npc/9270033.js
@@ -53,7 +53,7 @@ function action(mode, type, selection) {
}
} else if(status == 1) {
if(eim.isEventCleared()) {
- if(!eim.giveEventReward(cm.getPlayer)) {
+ if(!eim.giveEventReward(cm.getPlayer())) {
cm.sendOk("Please make a room on your inventory to receive the loot.");
cm.dispose();
return;
diff --git a/sql/db_database.sql b/sql/db_database.sql
index 6439a3e495..fca11f9286 100644
--- a/sql/db_database.sql
+++ b/sql/db_database.sql
@@ -12804,6 +12804,8 @@ CREATE TABLE IF NOT EXISTS `dueyitems` (
`quantity` int(10) unsigned NOT NULL DEFAULT '0',
`upgradeslots` int(11) DEFAULT '0',
`level` int(11) DEFAULT '0',
+ `itemlevel` int(11) DEFAULT '0',
+ `itemexp` int(11) DEFAULT '0',
`str` int(11) DEFAULT '0',
`dex` int(11) DEFAULT '0',
`int` int(11) DEFAULT '0',
diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java
index f87d2d82fb..bbeb373149 100644
--- a/src/client/MapleCharacter.java
+++ b/src/client/MapleCharacter.java
@@ -157,7 +157,7 @@ import net.server.channel.handlers.PartyOperationHandler;
import scripting.item.ItemScriptManager;
import server.maps.MapleMapItem;
import net.server.audit.locks.MonitoredLockType;
-import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
@@ -275,10 +275,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private ScheduledFuture> extraRecoveryTask = null;
private ScheduledFuture> chairRecoveryTask = null;
private ScheduledFuture> pendantOfSpirit = null; //1122017
- private Lock chrLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_CHR, true);
- private Lock effLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_EFF, true);
- private Lock petLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PET, true); // for quest tasks as well
- private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PRT);
+ private Lock chrLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CHR, true);
+ private Lock effLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EFF, true);
+ private Lock petLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PET, true); // for quest tasks as well
+ private Lock prtLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PRT);
private Map> excluded = new LinkedHashMap<>();
private Set excludedItems = new LinkedHashSet<>();
private static String[] ariantroomleader = new String[3];
@@ -445,8 +445,27 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return awayFromWorld.get();
}
- public void setAwayFromWorld(boolean away) {
- awayFromWorld.set(away);
+ public void setEnteredChannelWorld() {
+ awayFromWorld.set(false);
+ client.getChannelServer().removePlayerAway(id);
+ }
+
+ public void setAwayFromChannelWorld() {
+ setAwayFromChannelWorld(false);
+ }
+
+ public void setDisconnectedFromChannelWorld() {
+ setAwayFromChannelWorld(true);
+ }
+
+ private void setAwayFromChannelWorld(boolean disconnect) {
+ awayFromWorld.set(true);
+
+ if(!disconnect) {
+ client.getChannelServer().insertPlayerAway(id);
+ } else {
+ client.getChannelServer().removePlayerAway(id);
+ }
}
public long getPetLootCd() {
@@ -1548,9 +1567,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
// if this map has obstacle components moving, make it do so for this client
- for(Entry e: map.getEnvironment().entrySet()) {
- announce(MaplePacketCreator.environmentMove(e.getKey(), e.getValue()));
- }
+ announce(MaplePacketCreator.environmentMoveList(map.getEnvironment().entrySet()));
}
}
@@ -5273,6 +5290,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return lastBuyback + ServerConstants.BUYBACK_COOLDOWN_MINUTES * 60 * 1000;
}
+ private boolean isBuybackInvincible() {
+ return Server.getInstance().getCurrentTime() - lastBuyback < 4200;
+ }
+
private int getBuybackFee() {
float fee = ServerConstants.BUYBACK_FEE;
int grade = Math.min(Math.max(level, 30), 120) - 30;
@@ -5282,10 +5303,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public boolean couldBuyback() { // Ronan's buyback system
- long timeNow = System.currentTimeMillis();
+ long timeNow = Server.getInstance().getCurrentTime();
if(timeNow - lastDeathtime > ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000) {
- this.dropMessage(5, "You are unable to buyback now since the time available to decide has expired.");
+ this.dropMessage(5, "The time available to decide has expired, therefore you are unable to buyback now.");
return false;
}
@@ -7621,7 +7642,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
updatePartyMemberHP();
}
if (oldHp > hp && !isAlive()) {
- playerDead();
+ if(!isBuybackInvincible()) {
+ playerDead();
+ } else {
+ this.hp = 1;
+ }
}
}
diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java
index 1d82fb62ba..7028231540 100644
--- a/src/client/MapleClient.java
+++ b/src/client/MapleClient.java
@@ -75,7 +75,7 @@ import server.maps.*;
import server.quest.MapleQuest;
import net.server.audit.locks.MonitoredLockType;
-import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
public class MapleClient {
@@ -107,9 +107,9 @@ public class MapleClient {
private int picattempt = 0;
private byte gender = -1;
private boolean disconnecting = false;
- private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
- private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_ENCODER, true);
- private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_LOGIN, true);
+ private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true);
+ private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true);
+ private static final Lock loginLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true);
private int votePoints;
private int voteTime = -1;
private long lastNpcClick;
@@ -792,7 +792,7 @@ public class MapleClient {
private void removePlayer() {
try {
- player.setAwayFromWorld(true);
+ player.setDisconnectedFromChannelWorld();
player.notifyMapTransferToPartner(-1);
player.cancelAllBuffs(true);
player.cancelAllDebuffs();
@@ -836,10 +836,13 @@ public class MapleClient {
player.cancelMagicDoor();
+ final World wserv = getWorldServer();
if (channel == -1 || shutdown) {
if(chrg != null) chrg.setCharacter(null);
-
+
+ if(wserv != null) wserv.removePlayer(player);
removePlayer();
+
player.saveCooldowns();
player.saveCharToDB(true);
@@ -849,12 +852,11 @@ public class MapleClient {
removePlayer();
- final World worlda = getWorldServer();
try {
if (!cashshop) {
if (!this.serverTransition) { // meaning not changing channels
if (messengerid > 0) {
- worlda.leaveMessenger(messengerid, chrm);
+ wserv.leaveMessenger(messengerid, chrm);
}
/*
if (fid > 0) {
@@ -877,7 +879,7 @@ public class MapleClient {
}
if (party != null) {
chrp.setOnline(false);
- worlda.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
+ wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
if (map != null && party.getLeader().getId() == idz) {
MaplePartyCharacter lchr = null;
for (MaplePartyCharacter pchr : party.getMembers()) {
@@ -886,19 +888,19 @@ public class MapleClient {
}
}
if (lchr != null) {
- worlda.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
+ wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
}
}
}
if (bl != null) {
- worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
+ wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
}
} else {
if (!this.serverTransition) { // if dc inside of cash shop.
if (party != null) {
chrp.setOnline(false);
- worlda.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
+ wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
if (map != null && party.getLeader().getId() == idz) {
MaplePartyCharacter lchr = null;
for (MaplePartyCharacter pchr : party.getMembers()) {
@@ -907,12 +909,12 @@ public class MapleClient {
}
}
if (lchr != null) {
- worlda.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
+ wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
}
}
}
if (bl != null) {
- worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
+ wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
}
}
@@ -920,7 +922,7 @@ public class MapleClient {
FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e);
} finally {
if (!this.serverTransition) {
- worlda.removePlayer(player);
+ wserv.removePlayer(player);
//getChannelServer().removePlayer(player); already being done
player.saveCooldowns();
@@ -1309,7 +1311,15 @@ public class MapleClient {
return;
}
- String[] socket = Server.getInstance().getIP(getWorld(), channel).split(":");
+ String[] socket;
+ try {
+ socket = Server.getInstance().getIP(getWorld(), channel).split(":");
+ } catch (Exception e) {
+ announce(MaplePacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel."));
+ announce(MaplePacketCreator.enableActions());
+ return;
+ }
+
if (player.getTrade() != null) {
MapleTrade.cancelTrade(getPlayer());
}
@@ -1325,7 +1335,7 @@ public class MapleClient {
player.unregisterChairBuff();
server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs());
server.getPlayerBuffStorage().addDiseasesToStorage(player.getId(), player.getAllDiseases());
- player.setAwayFromWorld(true);
+ player.setDisconnectedFromChannelWorld();
player.notifyMapTransferToPartner(-1);
player.cancelAllBuffs(true);
player.cancelAllDebuffs();
diff --git a/src/client/MonsterBook.java b/src/client/MonsterBook.java
index 0175f3e633..1d2c6e41e3 100644
--- a/src/client/MonsterBook.java
+++ b/src/client/MonsterBook.java
@@ -32,10 +32,10 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.Semaphore;
-import net.server.audit.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import net.server.audit.locks.MonitoredLockType;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
public final class MonsterBook {
private static final Semaphore semaphore = new Semaphore(10);
@@ -44,7 +44,7 @@ public final class MonsterBook {
private int normalCard = 0;
private int bookLevel = 1;
private Map cards = new LinkedHashMap<>();
- private Lock lock = new MonitoredReentrantLock(MonitoredLockType.BOOK);
+ private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.BOOK);
private Set> getCardSet() {
lock.lock();
diff --git a/src/client/command/Commands.java b/src/client/command/Commands.java
index ce9e8453b3..1c5805bb2e 100644
--- a/src/client/command/Commands.java
+++ b/src/client/command/Commands.java
@@ -1804,29 +1804,31 @@ public class Commands {
if (sub.length >= 3) {
victim = c.getWorldServer().getPlayerStorage().getCharacterByName(sub[1]);
- statUpdate = Integer.valueOf(sub[2]);
+ statUpdate = Math.max(1, Integer.valueOf(sub[2]));
} else if(sub.length == 2) {
- statUpdate = Integer.valueOf(sub[1]);
+ statUpdate = Math.max(1, Integer.valueOf(sub[1]));
} else {
player.yellowMessage("Syntax: !maxhpmp [] ");
}
if(victim != null) {
+ List> statup = new ArrayList<>(4);
+
if(victim.getHp() > statUpdate) {
victim.setHp(statUpdate);
- victim.updateSingleStat(MapleStat.HP, statUpdate);
+ statup.add(new Pair<>(MapleStat.HP, statUpdate));
}
+ statup.add(new Pair<>(MapleStat.MAXHP, statUpdate));
if(victim.getMp() > statUpdate) {
victim.setMp(statUpdate);
- victim.updateSingleStat(MapleStat.MP, statUpdate);
+ statup.add(new Pair<>(MapleStat.MP, statUpdate));
}
+ statup.add(new Pair<>(MapleStat.MAXMP, statUpdate));
+ c.announce(MaplePacketCreator.updatePlayerStats(statup, victim));
victim.setMaxHp(statUpdate);
victim.setMaxMp(statUpdate);
- victim.updateSingleStat(MapleStat.MAXHP, statUpdate);
- victim.updateSingleStat(MapleStat.MAXMP, statUpdate);
-
victim.checkBerserk(victim.isHidden());
} else {
player.message("Player '" + sub[1] + "' could not be found on this world.");
@@ -2895,7 +2897,6 @@ public class Commands {
break;
- /*
case "removechannel":
if (sub.length < 2) {
player.dropMessage(5, "Syntax: @removechannel ");
@@ -2929,7 +2930,6 @@ public class Commands {
}
break;
- */
case "shutdown":
case "shutdownnow":
diff --git a/src/client/inventory/ItemFactory.java b/src/client/inventory/ItemFactory.java
index 9399d842b0..eb6661835c 100644
--- a/src/client/inventory/ItemFactory.java
+++ b/src/client/inventory/ItemFactory.java
@@ -28,10 +28,10 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
-import net.server.audit.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
/**
*
@@ -47,7 +47,7 @@ public enum ItemFactory {
MERCHANT(6, false);
private final int value;
private final boolean account;
- private static final Lock lock = new MonitoredReentrantLock(MonitoredLockType.ITEM, true);
+ private static final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.ITEM, true);
private ItemFactory(int value, boolean account) {
this.value = value;
diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java
index 1963dc9f83..d8f20304fc 100644
--- a/src/client/inventory/MapleInventory.java
+++ b/src/client/inventory/MapleInventory.java
@@ -32,7 +32,7 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.concurrent.locks.Lock;
-import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import tools.Pair;
import client.MapleCharacter;
@@ -53,7 +53,7 @@ public class MapleInventory implements Iterable- {
protected byte slotLimit;
protected MapleInventoryType type;
protected boolean checked = false;
- protected Lock lock = new MonitoredReentrantLock(MonitoredLockType.INVENTORY, true);
+ protected Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.INVENTORY, true);
public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) {
this.owner = mc;
diff --git a/src/client/processor/AssignAPProcessor.java b/src/client/processor/AssignAPProcessor.java
new file mode 100644
index 0000000000..603ead6066
--- /dev/null
+++ b/src/client/processor/AssignAPProcessor.java
@@ -0,0 +1,881 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ Copyleft (L) 2016 - 2018 RonanLana (HeavenMS)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package client.processor;
+
+import client.MapleCharacter;
+import client.MapleClient;
+import client.MapleJob;
+import client.MapleStat;
+import client.Skill;
+import client.SkillFactory;
+import client.autoban.AutobanFactory;
+import client.inventory.Equip;
+import client.inventory.Item;
+import client.inventory.MapleInventoryType;
+import constants.ServerConstants;
+import constants.skills.BlazeWizard;
+import constants.skills.Brawler;
+import constants.skills.DawnWarrior;
+import constants.skills.Magician;
+import constants.skills.Warrior;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import tools.MaplePacketCreator;
+import tools.Pair;
+import tools.Randomizer;
+import tools.data.input.SeekableLittleEndianAccessor;
+
+/**
+ *
+ * @author RonanLana (synchronization of AP transaction modules)
+ */
+public class AssignAPProcessor {
+
+ public static void APAutoAssignAction(SeekableLittleEndianAccessor slea, MapleClient c) {
+ MapleCharacter chr = c.getPlayer();
+ if (chr.getRemainingAp() < 1) return;
+
+ Collection
- equippedC = chr.getInventory(MapleInventoryType.EQUIPPED).list();
+
+ c.lockClient();
+ try {
+ int[] statGain = new int[4];
+ int[] statEqpd = new int[4];
+ statGain[0] = 0; statGain[1] = 0; statGain[2] = 0; statGain[3] = 0;
+
+ slea.skip(8);
+
+ if(ServerConstants.USE_SERVER_AUTOASSIGNER) {
+ // --------- Ronan Lana's AUTOASSIGNER ---------
+ // This method excels for assigning APs in such a way to cover all equipments AP requirements.
+ byte opt = slea.readByte(); // useful for pirate autoassigning
+
+ int str = 0, dex = 0, luk = 0, int_ = 0;
+ List eqpStrList = new ArrayList<>();
+ List eqpDexList = new ArrayList<>();
+ List eqpLukList = new ArrayList<>();
+
+ Equip nEquip;
+
+ for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item.
+ nEquip = (Equip)item;
+ if(nEquip.getStr() > 0) eqpStrList.add(nEquip.getStr());
+ str += nEquip.getStr();
+
+ if(nEquip.getDex() > 0) eqpDexList.add(nEquip.getDex());
+ dex += nEquip.getDex();
+
+ if(nEquip.getLuk() > 0) eqpLukList.add(nEquip.getLuk());
+ luk += nEquip.getLuk();
+
+ //if(nEquip.getInt() > 0) eqpIntList.add(nEquip.getInt()); //not needed...
+ int_ += nEquip.getInt();
+ }
+
+ statEqpd[0] = str;
+ statEqpd[1] = dex;
+ statEqpd[2] = luk;
+ statEqpd[3] = int_;
+
+ Collections.sort(eqpStrList, Collections.reverseOrder());
+ Collections.sort(eqpDexList, Collections.reverseOrder());
+ Collections.sort(eqpLukList, Collections.reverseOrder());
+
+ //Autoassigner looks up the 1st/2nd placed equips for their stats to calculate the optimal upgrade.
+ int eqpStr = getNthHighestStat(eqpStrList, (short) 0) + getNthHighestStat(eqpStrList, (short) 1);
+ int eqpDex = getNthHighestStat(eqpDexList, (short) 0) + getNthHighestStat(eqpDexList, (short) 1);
+ int eqpLuk = getNthHighestStat(eqpLukList, (short) 0) + getNthHighestStat(eqpLukList, (short) 1);
+
+ //c.getPlayer().message("----------------------------------------");
+ //c.getPlayer().message("SDL: s" + eqpStr + " d" + eqpDex + " l" + eqpLuk + " BASE STATS --> STR: " + chr.getStr() + " DEX: " + chr.getDex() + " INT: " + chr.getInt() + " LUK: " + chr.getLuk());
+ //c.getPlayer().message("SUM EQUIP STATS -> STR: " + str + " DEX: " + dex + " LUK: " + luk + " INT: " + int_);
+
+ MapleJob stance = c.getPlayer().getJobStyle(opt);
+ int prStat = 0, scStat = 0, trStat = 0, temp, tempAp = chr.getRemainingAp(), CAP;
+ if (tempAp < 1) return;
+
+ MapleStat primary, secondary, tertiary = MapleStat.LUK;
+ switch(stance) {
+ case MAGICIAN:
+ CAP = 165;
+ scStat = (chr.getLevel() + 3) - (chr.getLuk() + luk - eqpLuk);
+ if(scStat < 0) scStat = 0;
+ scStat = Math.min(scStat, tempAp);
+
+ if(tempAp > scStat) tempAp -= scStat;
+ else tempAp = 0;
+
+ prStat = tempAp;
+ int_ = prStat;
+ luk = scStat;
+ str = 0; dex = 0;
+
+ if(luk + chr.getLuk() > CAP) {
+ temp = luk + chr.getLuk() - CAP;
+ luk -= temp;
+ int_ += temp;
+ }
+
+ primary = MapleStat.INT;
+ secondary = MapleStat.LUK;
+ tertiary = MapleStat.DEX;
+
+ break;
+
+ case BOWMAN:
+ CAP = 125;
+ scStat = (chr.getLevel() + 5) - (chr.getStr() + str - eqpStr);
+ if(scStat < 0) scStat = 0;
+ scStat = Math.min(scStat, tempAp);
+
+ if(tempAp > scStat) tempAp -= scStat;
+ else tempAp = 0;
+
+ prStat = tempAp;
+ dex = prStat;
+ str = scStat;
+ int_ = 0; luk = 0;
+
+ if(str + chr.getStr() > CAP) {
+ temp = str + chr.getStr() - CAP;
+ str -= temp;
+ dex += temp;
+ }
+
+ primary = MapleStat.DEX;
+ secondary = MapleStat.STR;
+
+ break;
+
+ case GUNSLINGER:
+ case CROSSBOWMAN:
+ CAP = 120;
+ scStat = chr.getLevel() - (chr.getStr() + str - eqpStr);
+ if(scStat < 0) scStat = 0;
+ scStat = Math.min(scStat, tempAp);
+
+ if(tempAp > scStat) tempAp -= scStat;
+ else tempAp = 0;
+
+ prStat = tempAp;
+ dex = prStat;
+ str = scStat;
+ int_ = 0; luk = 0;
+
+ if(str + chr.getStr() > CAP) {
+ temp = str + chr.getStr() - CAP;
+ str -= temp;
+ dex += temp;
+ }
+
+ primary = MapleStat.DEX;
+ secondary = MapleStat.STR;
+
+ break;
+
+ case THIEF:
+ CAP = 160;
+
+ scStat = 0;
+ if(chr.getDex() < 80) {
+ scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
+ if(scStat < 0) scStat = 0;
+
+ scStat = Math.min(80 - chr.getDex(), scStat);
+ scStat = Math.min(tempAp, scStat);
+ tempAp -= scStat;
+ }
+
+ temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex);
+ if(temp < 0) temp = 0;
+ temp = Math.min(tempAp, temp);
+ scStat += temp;
+ tempAp -= temp;
+
+ // thieves will upgrade STR as well only if a level-based threshold is reached.
+ if(chr.getStr() >= Math.max(13, (int)(0.4 * chr.getLevel()))) {
+ if(chr.getStr() < 50) {
+ trStat = (chr.getLevel() - 10) - (chr.getStr() + str - eqpStr);
+ if(trStat < 0) trStat = 0;
+
+ trStat = Math.min(50 - chr.getStr(), trStat);
+ trStat = Math.min(tempAp, trStat);
+ tempAp -= trStat;
+ }
+
+ temp = (20 + (chr.getLevel() / 2)) - Math.max(50, trStat + chr.getStr() + str - eqpStr);
+ if(temp < 0) temp = 0;
+ temp = Math.min(tempAp, temp);
+ trStat += temp;
+ tempAp -= temp;
+ }
+
+ prStat = tempAp;
+ luk = prStat;
+ dex = scStat;
+ str = trStat;
+ int_ = 0;
+
+ if(dex + chr.getDex() > CAP) {
+ temp = dex + chr.getDex() - CAP;
+ dex -= temp;
+ luk += temp;
+ }
+ if(str + chr.getStr() > CAP) {
+ temp = str + chr.getStr() - CAP;
+ str -= temp;
+ luk += temp;
+ }
+
+ primary = MapleStat.LUK;
+ secondary = MapleStat.DEX;
+ tertiary = MapleStat.STR;
+
+ break;
+
+ case BRAWLER:
+ CAP = 120;
+
+ scStat = chr.getLevel() - (chr.getDex() + dex - eqpDex);
+ if(scStat < 0) scStat = 0;
+ scStat = Math.min(scStat, tempAp);
+
+ if(tempAp > scStat) tempAp -= scStat;
+ else tempAp = 0;
+
+ prStat = tempAp;
+ str = prStat;
+ dex = scStat;
+ int_ = 0; luk = 0;
+
+ if(dex + chr.getDex() > CAP) {
+ temp = dex + chr.getDex() - CAP;
+ dex -= temp;
+ str += temp;
+ }
+
+ primary = MapleStat.STR;
+ secondary = MapleStat.DEX;
+
+ break;
+
+ default: //warrior, beginner, ...
+ CAP = 80;
+
+ scStat = ((2 * chr.getLevel()) / 3) - (chr.getDex() + dex - eqpDex);
+ if(scStat < 0) scStat = 0;
+ scStat = Math.min(scStat, tempAp);
+
+ if(tempAp > scStat) tempAp -= scStat;
+ else tempAp = 0;
+
+ prStat = tempAp;
+ str = prStat;
+ dex = scStat;
+ int_ = 0; luk = 0;
+
+ if(dex + chr.getDex() > CAP) {
+ temp = dex + chr.getDex() - CAP;
+ dex -= temp;
+ str += temp;
+ }
+
+ primary = MapleStat.STR;
+ secondary = MapleStat.DEX;
+ }
+
+ //-------------------------------------------------------------------------------------
+
+ int extras = 0;
+
+ extras = gainStatByType(chr, primary, statGain, prStat + extras);
+ extras = gainStatByType(chr, secondary, statGain, scStat + extras);
+ extras = gainStatByType(chr, tertiary, statGain, trStat + extras);
+
+ if(extras > 0) { //redistribute surplus in priority order
+ extras = gainStatByType(chr, primary, statGain, extras);
+ extras = gainStatByType(chr, secondary, statGain, extras);
+ extras = gainStatByType(chr, tertiary, statGain, extras);
+ gainStatByType(chr, getQuaternaryStat(stance), statGain, extras);
+ }
+
+ int remainingAp = (chr.getRemainingAp() - getAccumulatedStatGain(statGain));
+ chr.setRemainingAp(remainingAp);
+ chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp);
+ c.announce(MaplePacketCreator.enableActions());
+
+ //----------------------------------------------------------------------------------------
+
+ c.announce(MaplePacketCreator.serverNotice(1, "Better AP applications detected:\r\nSTR: +" + statGain[0] + "\r\nDEX: +" + statGain[1] + "\r\nINT: +" + statGain[3] + "\r\nLUK: +" + statGain[2]));
+ } else {
+ if(slea.available() < 16) {
+ AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign.");
+
+ final MapleClient client = c;
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ client.disconnect(false, false);
+ }
+ });
+ t.start();
+
+ return;
+ }
+
+ for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item.
+ Equip nEquip = (Equip)item;
+
+ statEqpd[0] += nEquip.getStr();
+ statEqpd[1] += nEquip.getDex();
+ statEqpd[2] += nEquip.getLuk();
+ statEqpd[3] += nEquip.getInt();
+ }
+
+ int total = 0;
+ int extras = 0;
+ for (int i = 0; i < 2; i++) {
+ int type = slea.readInt();
+ int tempVal = slea.readInt();
+ if (tempVal < 0 || tempVal > chr.getRemainingAp()) {
+ return;
+ }
+ total += tempVal;
+ extras += gainStatByType(chr, MapleStat.getBy5ByteEncoding(type), statGain, tempVal);
+ }
+ int remainingAp = (chr.getRemainingAp() - total) + extras;
+ chr.setRemainingAp(remainingAp);
+ chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp);
+ c.announce(MaplePacketCreator.enableActions());
+ }
+ } finally {
+ c.unlockClient();
+ }
+ }
+
+ private static int getNthHighestStat(List statList, short rank) { // ranks from 0
+ return(statList.size() <= rank ? 0 : statList.get(rank));
+ }
+
+ private static int gainStatByType(MapleCharacter chr, MapleStat type, int[] statGain, int gain) {
+ if(gain <= 0) return 0;
+
+ int newVal = 0;
+ if (type.equals(MapleStat.STR)) {
+ newVal = chr.getStr() + gain;
+ if (newVal > ServerConstants.MAX_AP) {
+ statGain[0] += (gain - (newVal - ServerConstants.MAX_AP));
+ chr.setStr(ServerConstants.MAX_AP);
+ } else {
+ statGain[0] += gain;
+ chr.setStr(newVal);
+ }
+ } else if (type.equals(MapleStat.INT)) {
+ newVal = chr.getInt() + gain;
+ if (newVal > ServerConstants.MAX_AP) {
+ statGain[3] += (gain - (newVal - ServerConstants.MAX_AP));
+ chr.setInt(ServerConstants.MAX_AP);
+ } else {
+ statGain[3] += gain;
+ chr.setInt(newVal);
+ }
+ } else if (type.equals(MapleStat.LUK)) {
+ newVal = chr.getLuk() + gain;
+ if (newVal > ServerConstants.MAX_AP) {
+ statGain[2] += (gain - (newVal - ServerConstants.MAX_AP));
+ chr.setLuk(ServerConstants.MAX_AP);
+ } else {
+ statGain[2] += gain;
+ chr.setLuk(newVal);
+ }
+ } else if (type.equals(MapleStat.DEX)) {
+ newVal = chr.getDex() + gain;
+ if (newVal > ServerConstants.MAX_AP) {
+ statGain[1] += (gain - (newVal - ServerConstants.MAX_AP));
+ chr.setDex(ServerConstants.MAX_AP);
+ } else {
+ statGain[1] += gain;
+ chr.setDex(newVal);
+ }
+ }
+
+ if (newVal > ServerConstants.MAX_AP) {
+ chr.updateSingleStat(type, ServerConstants.MAX_AP);
+ return newVal - ServerConstants.MAX_AP;
+ }
+ chr.updateSingleStat(type, newVal);
+ return 0;
+ }
+
+ private static MapleStat getQuaternaryStat(MapleJob stance) {
+ if(stance != MapleJob.MAGICIAN) return MapleStat.INT;
+ return MapleStat.STR;
+ }
+
+ private static int getAccumulatedStatGain(int[] statGain) {
+ int acc = 0;
+
+ for(byte i = 0; i < statGain.length; i++) {
+ acc += statGain[i];
+ }
+
+ return acc;
+ }
+
+ public static boolean APResetAction(MapleClient c, int APFrom, int APTo) {
+ c.lockClient();
+ try {
+ List> statupdate = new ArrayList<>(2);
+ MapleCharacter player = c.getPlayer();
+
+ switch (APFrom) {
+ case 64: // str
+ if (player.getStr() < 5) {
+ player.message("You don't have the minimum STR required to swap.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+ player.addStat(1, -1);
+ break;
+ case 128: // dex
+ if (player.getDex() < 5) {
+ player.message("You don't have the minimum DEX required to swap.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+ player.addStat(2, -1);
+ break;
+ case 256: // int
+ if (player.getInt() < 5) {
+ player.message("You don't have the minimum INT required to swap.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+ player.addStat(3, -1);
+ break;
+ case 512: // luk
+ if (player.getLuk() < 5) {
+ player.message("You don't have the minimum LUK required to swap.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+ player.addStat(4, -1);
+ break;
+ case 2048: // HP
+ if(ServerConstants.USE_ENFORCE_HPMP_SWAP) {
+ if (APTo != 8192) {
+ player.message("You can only swap HP ability points to MP.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+ }
+ if (player.getHpMpApUsed() < 1) {
+ player.message("You don't have enough HPMP stat points to spend on AP Reset.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+
+ int hp = player.getMaxHp();
+ int level_ = player.getLevel();
+
+ boolean canWash_ = true;
+ if (hp < level_ * 14 + 148) {
+ canWash_ = false;
+ }
+
+ if (!canWash_) {
+ player.message("You don't have the minimum HP pool required to swap.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+
+ player.setHpMpApUsed(player.getHpMpApUsed() - 1);
+ int hplose = -takeHp(player.getJob());
+ int nextHp = Math.max(1, player.getHp() + hplose), nextMaxHp = Math.max(50, player.getMaxHp() + hplose);
+
+ player.setHp(nextHp);
+ player.setMaxHp(nextMaxHp);
+ statupdate.add(new Pair<>(MapleStat.HP, nextHp));
+ statupdate.add(new Pair<>(MapleStat.MAXHP, nextMaxHp));
+
+ break;
+ case 8192: // MP
+ if(ServerConstants.USE_ENFORCE_HPMP_SWAP) {
+ if (APTo != 2048) {
+ player.message("You can only swap MP ability points to HP.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+ }
+ if (player.getHpMpApUsed() < 1) {
+ player.message("You don't have enough HPMP stat points to spend on AP Reset.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+
+ int mp = player.getMaxMp();
+ int level = player.getLevel();
+ MapleJob job = player.getJob();
+
+ boolean canWash = true;
+ if (job.isA(MapleJob.SPEARMAN) && mp < 4 * level + 156) {
+ canWash = false;
+ } else if (job.isA(MapleJob.FIGHTER) && mp < 4 * level + 56) {
+ canWash = false;
+ } else if (job.isA(MapleJob.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) {
+ canWash = false;
+ } else if (mp < level * 14 + 148) {
+ canWash = false;
+ }
+
+ if (!canWash) {
+ player.message("You don't have the minimum MP pool required to swap.");
+ c.announce(MaplePacketCreator.enableActions());
+ return false;
+ }
+
+ player.setHpMpApUsed(player.getHpMpApUsed() - 1);
+ int mplose = -takeMp(job);
+ int nextMp = Math.max(0, player.getMp() + mplose), nextMaxMp = Math.max(5, player.getMaxMp() + mplose);
+
+ player.setMp(nextMp);
+ player.setMaxMp(nextMaxMp);
+ statupdate.add(new Pair<>(MapleStat.MP, nextMp));
+ statupdate.add(new Pair<>(MapleStat.MAXMP, nextMaxMp));
+
+ break;
+ default:
+ c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, player));
+ return false;
+ }
+
+ addStat(c, APTo, true);
+ c.announce(MaplePacketCreator.updatePlayerStats(statupdate, true, player));
+ return true;
+ } finally {
+ c.unlockClient();
+ }
+ }
+
+ public static void APAssignAction(MapleClient c, int num) {
+ c.lockClient();
+ try {
+ if (c.getPlayer().getRemainingAp() > 0) {
+ if (addStat(c, num, false)) {
+ c.getPlayer().setRemainingAp(c.getPlayer().getRemainingAp() - 1);
+ c.getPlayer().updateSingleStat(MapleStat.AVAILABLEAP, c.getPlayer().getRemainingAp());
+ }
+ }
+ c.announce(MaplePacketCreator.enableActions());
+ } finally {
+ c.unlockClient();
+ }
+ }
+
+ private static boolean addStat(MapleClient c, int apTo, boolean usedAPReset) {
+ switch (apTo) {
+ case 64: // Str
+ if (c.getPlayer().getStr() >= 32767) {
+ return false;
+ }
+ c.getPlayer().addStat(1, 1);
+ break;
+ case 128: // Dex
+ if (c.getPlayer().getDex() >= 32767) {
+ return false;
+ }
+ c.getPlayer().addStat(2, 1);
+ break;
+ case 256: // Int
+ if (c.getPlayer().getInt() >= 32767) {
+ return false;
+ }
+ c.getPlayer().addStat(3, 1);
+ break;
+ case 512: // Luk
+ if (c.getPlayer().getLuk() >= 32767) {
+ return false;
+ }
+ c.getPlayer().addStat(4, 1);
+ break;
+ case 2048: // HP
+ addHP(c.getPlayer(), addHP(c, usedAPReset));
+ break;
+ case 8192: // MP
+ addMP(c.getPlayer(), addMP(c, usedAPReset));
+ break;
+ default:
+ c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer()));
+ return false;
+ }
+ return true;
+ }
+
+ private static int addHP(MapleClient c, boolean usedAPReset) {
+ MapleCharacter player = c.getPlayer();
+ MapleJob job = player.getJob();
+ int MaxHP = player.getMaxHp();
+ if (player.getHpMpApUsed() > 9999 || MaxHP >= 30000) {
+ return MaxHP;
+ }
+
+ return MaxHP + calcHpChange(player, job, usedAPReset);
+ }
+
+ private static int calcHpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) {
+ int MaxHP = 0;
+
+ if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
+ if(!usedAPReset) {
+ Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.DAWNWARRIOR1) ? DawnWarrior.MAX_HP_INCREASE : Warrior.IMPROVED_MAXHP);
+ int sLvl = player.getSkillLevel(increaseHP);
+
+ if(sLvl > 0)
+ MaxHP += increaseHP.getEffect(sLvl).getY();
+ }
+
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if (usedAPReset) {
+ MaxHP += 20;
+ } else {
+ MaxHP += Randomizer.rand(18, 22);
+ }
+ } else {
+ MaxHP += 20;
+ }
+ } else if(job.isA(MapleJob.ARAN1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if (usedAPReset) {
+ MaxHP += 20;
+ } else {
+ MaxHP += Randomizer.rand(26, 30);
+ }
+ } else {
+ MaxHP += 28;
+ }
+ } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if (usedAPReset) {
+ MaxHP += 6;
+ } else {
+ MaxHP += Randomizer.rand(5, 9);
+ }
+ } else {
+ MaxHP += 6;
+ }
+ } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if (usedAPReset) {
+ MaxHP += 16;
+ } else {
+ MaxHP += Randomizer.rand(14, 18);
+ }
+ } else {
+ MaxHP += 16;
+ }
+ } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if (usedAPReset) {
+ MaxHP += 16;
+ } else {
+ MaxHP += Randomizer.rand(14, 18);
+ }
+ } else {
+ MaxHP += 16;
+ }
+ } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
+ if(!usedAPReset) {
+ Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP);
+ int sLvl = player.getSkillLevel(increaseHP);
+
+ if(sLvl > 0)
+ MaxHP += increaseHP.getEffect(sLvl).getY();
+ }
+
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if (usedAPReset) {
+ MaxHP += 18;
+ } else {
+ MaxHP += Randomizer.rand(16, 20);
+ }
+ } else {
+ MaxHP += 18;
+ }
+ } else if (usedAPReset) {
+ MaxHP += 8;
+ } else {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ MaxHP += Randomizer.rand(8, 12);
+ } else {
+ MaxHP += 10;
+ }
+ }
+
+ return MaxHP;
+ }
+
+ private static int addMP(MapleClient c, boolean usedAPReset) {
+ MapleCharacter player = c.getPlayer();
+ int MaxMP = player.getMaxMp();
+ MapleJob job = player.getJob();
+ if (player.getHpMpApUsed() > 9999 || player.getMaxMp() >= 30000) {
+ return MaxMP;
+ }
+
+ return MaxMP + calcMpChange(player, job, usedAPReset);
+ }
+
+ private static int calcMpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) {
+ int MaxMP = 0;
+
+ if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if(!usedAPReset) {
+ MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10));
+ } else {
+ MaxMP += 2;
+ }
+ } else {
+ MaxMP += 3;
+ }
+ } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
+ if(!usedAPReset) {
+ Skill increaseMP = SkillFactory.getSkill(job.isA(MapleJob.BLAZEWIZARD1) ? BlazeWizard.INCREASING_MAX_MP : Magician.IMPROVED_MAX_MP_INCREASE);
+ int sLvl = player.getSkillLevel(increaseMP);
+
+ if(sLvl > 0)
+ MaxMP += increaseMP.getEffect(sLvl).getY();
+ }
+
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if(!usedAPReset) {
+ MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20));
+ } else {
+ MaxMP += 18;
+ }
+ } else {
+ MaxMP += 18;
+ }
+ } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if(!usedAPReset) {
+ MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
+ } else {
+ MaxMP += 10;
+ }
+ } else {
+ MaxMP += 10;
+ }
+ } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if(!usedAPReset) {
+ MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
+ } else {
+ MaxMP += 10;
+ }
+ } else {
+ MaxMP += 10;
+ }
+ } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if(!usedAPReset) {
+ MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10));
+ } else {
+ MaxMP += 14;
+ }
+ } else {
+ MaxMP += 14;
+ }
+ } else {
+ if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
+ if(!usedAPReset) {
+ MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10));
+ } else {
+ MaxMP += 6;
+ }
+ } else {
+ MaxMP += 6;
+ }
+ }
+
+ return MaxMP;
+ }
+
+ private static void addHP(MapleCharacter player, int MaxHP) {
+ MaxHP = Math.min(30000, MaxHP);
+ player.setHpMpApUsed(player.getHpMpApUsed() + 1);
+ player.setMaxHp(MaxHP);
+ player.updateSingleStat(MapleStat.MAXHP, MaxHP);
+ }
+
+ private static void addMP(MapleCharacter player, int MaxMP) {
+ MaxMP = Math.min(30000, MaxMP);
+ player.setHpMpApUsed(player.getHpMpApUsed() + 1);
+ player.setMaxMp(MaxMP);
+ player.updateSingleStat(MapleStat.MAXMP, MaxMP);
+ }
+
+ private static int takeHp(MapleJob job) {
+ int MaxHP = 0;
+
+ if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
+ MaxHP += 54;
+ } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
+ MaxHP += 10;
+ } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
+ MaxHP += 20;
+ } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
+ MaxHP += 20;
+ } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
+ MaxHP += 42;
+ } else {
+ MaxHP += 12;
+ }
+
+ return MaxHP;
+ }
+
+ private static int takeMp(MapleJob job) {
+ int MaxMP = 0;
+
+ if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
+ MaxMP += 4;
+ } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
+ MaxMP += 31;
+ } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
+ MaxMP += 12;
+ } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
+ MaxMP += 12;
+ } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
+ MaxMP += 16;
+ } else {
+ MaxMP += 8;
+ }
+
+ return MaxMP;
+ }
+
+}
diff --git a/src/client/processor/AssignSPProcessor.java b/src/client/processor/AssignSPProcessor.java
new file mode 100644
index 0000000000..35fcd30250
--- /dev/null
+++ b/src/client/processor/AssignSPProcessor.java
@@ -0,0 +1,100 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ Copyleft (L) 2016 - 2018 RonanLana (HeavenMS)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package client.processor;
+
+import client.MapleCharacter;
+import client.MapleClient;
+import client.MapleStat;
+import client.Skill;
+import client.SkillFactory;
+import client.autoban.AutobanFactory;
+import constants.GameConstants;
+import constants.skills.Aran;
+import tools.FilePrinter;
+import tools.MaplePacketCreator;
+
+/**
+ *
+ * @author RonanLana (synchronization of SP transaction modules)
+ */
+public class AssignSPProcessor {
+
+ public static void SPAssignAction(MapleClient c, int skillid) {
+ c.lockClient();
+ try {
+ if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
+ c.getSession().write(MaplePacketCreator.enableActions());
+ return;
+ }
+
+ MapleCharacter player = c.getPlayer();
+ int remainingSp = player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000));
+ boolean isBeginnerSkill = false;
+ if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
+ AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
+ FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n");
+
+ final MapleClient client = c;
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ client.disconnect(true, false);
+ }
+ });
+ t.start();
+
+ return;
+ }
+ if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
+ int total = 0;
+ for (int i = 0; i < 3; i++) {
+ total += player.getSkillLevel(SkillFactory.getSkill(player.getJobType() * 10000000 + 1000 + i));
+ }
+ remainingSp = Math.min((player.getLevel() - 1), 6) - total;
+ isBeginnerSkill = true;
+ }
+ Skill skill = SkillFactory.getSkill(skillid);
+ int curLevel = player.getSkillLevel(skill);
+ if ((remainingSp > 0 && curLevel + 1 <= (skill.isFourthJob() ? player.getMasterLevel(skill) : skill.getMaxLevel()))) {
+ if (!isBeginnerSkill) {
+ player.setRemainingSp(player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)) - 1, GameConstants.getSkillBook(skillid/10000));
+ }
+ player.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)));
+ if (skill.getId() == Aran.FULL_SWING) {
+ player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ } else if (skill.getId() == Aran.OVER_SWING) {
+ player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ } else {
+ player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
+ }
+ }
+ } finally {
+ c.unlockClient();
+ }
+ }
+}
diff --git a/src/client/processor/DueyProcessor.java b/src/client/processor/DueyProcessor.java
index 6db886ffd8..3a03da5e77 100644
--- a/src/client/processor/DueyProcessor.java
+++ b/src/client/processor/DueyProcessor.java
@@ -149,6 +149,8 @@ public class DueyProcessor {
Equip eq = new Equip(rs.getInt("itemid"), (byte) 0, -1);
eq.setUpgradeSlots((byte) rs.getInt("upgradeslots"));
eq.setLevel((byte) rs.getInt("level"));
+ eq.setItemLevel((byte) rs.getInt("itemlevel"));
+ eq.setItemExp(rs.getInt("itemexp"));
eq.setStr((short) rs.getInt("str"));
eq.setDex((short) rs.getInt("dex"));
eq.setInt((short) rs.getInt("int"));
@@ -271,29 +273,31 @@ public class DueyProcessor {
rs.next();
PreparedStatement ps2;
if (item.getInventoryType().equals(MapleInventoryType.EQUIP)) {
- ps2 = con.prepareStatement("INSERT INTO dueyitems (PackageId, itemid, quantity, upgradeslots, level, str, dex, `int`, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, flag, owner) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ ps2 = con.prepareStatement("INSERT INTO dueyitems (PackageId, itemid, quantity, upgradeslots, level, itemlevel, itemexp, str, dex, `int`, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, flag, owner) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
Equip eq = (Equip) item;
ps2.setInt(2, eq.getItemId());
ps2.setInt(3, 1);
ps2.setInt(4, eq.getUpgradeSlots());
ps2.setInt(5, eq.getLevel());
- ps2.setInt(6, eq.getStr());
- ps2.setInt(7, eq.getDex());
- ps2.setInt(8, eq.getInt());
- ps2.setInt(9, eq.getLuk());
- ps2.setInt(10, eq.getHp());
- ps2.setInt(11, eq.getMp());
- ps2.setInt(12, eq.getWatk());
- ps2.setInt(13, eq.getMatk());
- ps2.setInt(14, eq.getWdef());
- ps2.setInt(15, eq.getMdef());
- ps2.setInt(16, eq.getAcc());
- ps2.setInt(17, eq.getAvoid());
- ps2.setInt(18, eq.getHands());
- ps2.setInt(19, eq.getSpeed());
- ps2.setInt(20, eq.getJump());
- ps2.setInt(21, eq.getFlag());
- ps2.setString(22, eq.getOwner());
+ ps2.setInt(6, eq.getItemLevel());
+ ps2.setInt(7, eq.getItemExp());
+ ps2.setInt(8, eq.getStr());
+ ps2.setInt(9, eq.getDex());
+ ps2.setInt(10, eq.getInt());
+ ps2.setInt(11, eq.getLuk());
+ ps2.setInt(12, eq.getHp());
+ ps2.setInt(13, eq.getMp());
+ ps2.setInt(14, eq.getWatk());
+ ps2.setInt(15, eq.getMatk());
+ ps2.setInt(16, eq.getWdef());
+ ps2.setInt(17, eq.getMdef());
+ ps2.setInt(18, eq.getAcc());
+ ps2.setInt(19, eq.getAvoid());
+ ps2.setInt(20, eq.getHands());
+ ps2.setInt(21, eq.getSpeed());
+ ps2.setInt(22, eq.getJump());
+ ps2.setInt(23, eq.getFlag());
+ ps2.setString(24, eq.getOwner());
} else {
ps2 = con.prepareStatement("INSERT INTO dueyitems (PackageId, itemid, quantity, flag, owner) VALUES (?, ?, ?, ?, ?)");
ps2.setInt(2, item.getItemId());
diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java
index 1a952fe7d3..61f686c598 100644
--- a/src/constants/ServerConstants.java
+++ b/src/constants/ServerConstants.java
@@ -46,8 +46,8 @@ public class ServerConstants {
public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info.
public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids.
- public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true;
public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range.
+ public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true;
public static final boolean USE_MTS = false;
public static final boolean USE_AUTOHIDE_GM = false; //When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152).
public static final boolean USE_BUYBACK_SYSTEM = true; //Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button.
@@ -80,6 +80,8 @@ public class ServerConstants {
public static final boolean USE_BANISHABLE_TOWN_SCROLL = true; //Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available.
public static final boolean USE_OLD_GMS_STYLED_PQ_NPCS = true; //Enables PQ NPCs with similar behaviour to old GMS style, that skips info about the PQs and immediately tries to register the party in.
public static final boolean USE_ENABLE_SOLO_EXPEDITIONS = true; //Enables start expeditions with any number of players. This will also bypass all the Zakum prequest.
+ public static final boolean USE_ENABLE_FULL_RESPAWN = true; //At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there.
+ public static final boolean USE_SPAWN_LOOT_ON_ANIMATION = false;//Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly).
//Announcement Configuration
public static final boolean USE_ANNOUNCE_SHOPITEMSOLD = false; //Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold.
@@ -122,10 +124,12 @@ public class ServerConstants {
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 KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before kites (cash item) disappears.
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 = 3 * 60 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present.
+ public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present.
+
+ //Map Monitor Configuration
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.
- public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
+ public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
//Channel Mob Disease Monitor Configuration
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
diff --git a/src/net/MapleServerHandler.java b/src/net/MapleServerHandler.java
index aa5409c5eb..16eeac4ce8 100644
--- a/src/net/MapleServerHandler.java
+++ b/src/net/MapleServerHandler.java
@@ -29,7 +29,7 @@ import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;
import net.server.audit.locks.MonitoredLockType;
-import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.Server;
import org.apache.mina.core.service.IoHandlerAdapter;
@@ -63,8 +63,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 MonitoredReentrantLock(MonitoredLockType.SRVHANDLER_IDLE, true);
- private Lock tempLock = new MonitoredReentrantLock(MonitoredLockType.SRVHANDLER_TEMP, true);
+ private Lock idleLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SRVHANDLER_IDLE, true);
+ private Lock tempLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SRVHANDLER_TEMP, true);
private Map idleSessions = new HashMap<>(100);
private Map tempIdleSessions = new HashMap<>();
private ScheduledFuture> idleManager = null;
diff --git a/src/net/opcodes/SendOpcode.java b/src/net/opcodes/SendOpcode.java
index 193c761d92..2786d6f7a4 100644
--- a/src/net/opcodes/SendOpcode.java
+++ b/src/net/opcodes/SendOpcode.java
@@ -169,7 +169,7 @@ public enum SendOpcode {
FIELD_EFFECT(0x8A),
FIELD_OBSTACLE_ONOFF(0x8B),
- FIELD_OBSTACLE_ONOFF_STATUS(0x8C),
+ FIELD_OBSTACLE_ONOFF_LIST(0x8C),
FIELD_OBSTACLE_ALL_RESET(0x8D),
BLOW_WEATHER(0x8E),
PLAY_JUKEBOX(0x8F),
diff --git a/src/net/server/PlayerBuffStorage.java b/src/net/server/PlayerBuffStorage.java
index 5efb6029e7..c936c98898 100644
--- a/src/net/server/PlayerBuffStorage.java
+++ b/src/net/server/PlayerBuffStorage.java
@@ -29,7 +29,7 @@ import java.util.concurrent.locks.Lock;
import server.life.MobSkill;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
-import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
/**
*
@@ -38,7 +38,7 @@ import net.server.audit.locks.MonitoredReentrantLock;
*/
public class PlayerBuffStorage {
private int id = (int) (Math.random() * 100);
- private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.BUFF_STORAGE, true);
+ private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.BUFF_STORAGE, true);
private Map> buffs = new HashMap<>();
private Map>> diseases = new HashMap<>();
diff --git a/src/net/server/PlayerStorage.java b/src/net/server/PlayerStorage.java
index ae6d595429..9bd6d1647d 100644
--- a/src/net/server/PlayerStorage.java
+++ b/src/net/server/PlayerStorage.java
@@ -23,8 +23,9 @@ package net.server;
import client.MapleClient;
import client.MapleCharacter;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.Iterator;
+import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -35,10 +36,10 @@ import net.server.audit.locks.MonitoredReentrantReadWriteLock;
public class PlayerStorage {
private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_STORAGE, true);
- private final ReadLock rlock = locks.readLock();
- private final WriteLock wlock = locks.writeLock();
private final Map storage = new LinkedHashMap<>();
private final Map nameStorage = new LinkedHashMap<>();
+ private ReadLock rlock = locks.readLock();
+ private WriteLock wlock = locks.writeLock();
public void addPlayer(MapleCharacter chr) {
wlock.lock();
@@ -90,17 +91,24 @@ public class PlayerStorage {
}
public final void disconnectAll() {
- wlock.lock();
- try {
- final Iterator chrit = storage.values().iterator();
- while (chrit.hasNext()) {
- MapleClient client = chrit.next().getClient();
- if(client != null) {
- client.disconnect(true, false);
- }
-
- chrit.remove();
+ List chrList;
+ rlock.lock();
+ try {
+ chrList = new ArrayList<>(storage.values());
+ } finally {
+ rlock.unlock();
+ }
+
+ for(MapleCharacter mc : chrList) {
+ MapleClient client = mc.getClient();
+ if(client != null) {
+ client.disconnect(true, false);
}
+ }
+
+ wlock.lock();
+ try {
+ storage.clear();
} finally {
wlock.unlock();
}
diff --git a/src/net/server/Server.java b/src/net/server/Server.java
index 088761dffd..45d189aca4 100644
--- a/src/net/server/Server.java
+++ b/src/net/server/Server.java
@@ -48,8 +48,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.ThreadTracker;
import net.server.audit.locks.MonitoredLockType;
-import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
@@ -111,8 +111,8 @@ public class Server {
private final List processDiseaseAnnouncePlayers = new LinkedList<>();
private final List registeredDiseaseAnnouncePlayers = new LinkedList<>();
- private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
- private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES);
+ private final Lock srvLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER);
+ private final Lock disLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_DISEASES);
private final ReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true);
private final ReadLock wldRLock = wldLock.readLock();
@@ -412,7 +412,7 @@ public class Server {
wldRLock.unlock();
}
- if(w == null || w.getPlayerStorage().getSize() > 0) {
+ if(w == null || !w.canUninstall()) {
return false;
}
@@ -1414,12 +1414,11 @@ public class Server {
}
}*/
+ List allChannels = getAllChannels();
+
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().cancelThreadTrackerTask();
-
- TimerManager.getInstance().purge();
- TimerManager.getInstance().stop();
-
- for (Channel ch : getAllChannels()) {
+
+ for (Channel ch : allChannels) {
while (!ch.finishedShutdown()) {
try {
Thread.sleep(1000);
@@ -1431,6 +1430,9 @@ public class Server {
}
resetServerWorlds();
+
+ TimerManager.getInstance().purge();
+ TimerManager.getInstance().stop();
System.out.println("Worlds + Channels are offline.");
acceptor.unbind();
diff --git a/src/net/server/audit/ThreadTracker.java b/src/net/server/audit/ThreadTracker.java
index 3566d2ed71..dc916553c4 100644
--- a/src/net/server/audit/ThreadTracker.java
+++ b/src/net/server/audit/ThreadTracker.java
@@ -142,12 +142,15 @@ public class ThreadTracker {
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));
+ StackTraceElement[] ste = threads.get(l).getStackTrace();
+ if(ste.length > 0) {
+ 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(ste, df));
+ }
}
}
diff --git a/src/net/server/audit/locks/MonitoredLockType.java b/src/net/server/audit/locks/MonitoredLockType.java
index 5397fadfee..ad26cea77b 100644
--- a/src/net/server/audit/locks/MonitoredLockType.java
+++ b/src/net/server/audit/locks/MonitoredLockType.java
@@ -87,6 +87,7 @@ public enum MonitoredLockType {
MAP_OBJS,
MAP_FACTORY,
MAP_ITEM,
+ MAP_LOOT,
MAP_BOUNDS,
MINIDUNGEON,
REACTOR,
diff --git a/src/net/server/audit/locks/MonitoredReadLock.java b/src/net/server/audit/locks/MonitoredReadLock.java
index dafe2286c5..b60599dd70 100644
--- a/src/net/server/audit/locks/MonitoredReadLock.java
+++ b/src/net/server/audit/locks/MonitoredReadLock.java
@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
- Copyleft (L) 2016 - 2018 RonanLana
+ Copyleft 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -19,143 +19,18 @@
*/
package net.server.audit.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 MonitoredLockType id;
- private final int hashcode;
- private final Lock state = new ReentrantLock(true);
- private final AtomicInteger reentrantCount = new AtomicInteger(0);
+public interface MonitoredReadLock {
- public MonitoredReadLock(MonitoredReentrantReadWriteLock lock) {
- super(lock);
- this.id = lock.id;
- hashcode = this.hashCode();
- }
+ public void lock();
- @Override
- public void 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();
- }
-
- super.lock();
- }
+ public void unlock();
- @Override
- public void unlock() {
- if(ServerConstants.USE_THREAD_TRACKER) {
- unregisterLocking();
- }
-
- super.unlock();
- }
+ public boolean tryLock();
- @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;
- }
- }
+ public MonitoredReadLock dispose();
- 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;
- }
-
- public void dispose() {
- state.lock();
- try {
- if(timeoutSchedule != null) {
- timeoutSchedule.cancel(false);
- timeoutSchedule = null;
- }
- } finally {
- state.unlock();
- }
- }
}
diff --git a/src/net/server/audit/locks/MonitoredReentrantLock.java b/src/net/server/audit/locks/MonitoredReentrantLock.java
index a0ae846af4..ad8af31fe7 100644
--- a/src/net/server/audit/locks/MonitoredReentrantLock.java
+++ b/src/net/server/audit/locks/MonitoredReentrantLock.java
@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
- Copyleft (L) 2016 - 2018 RonanLana
+ Copyleft 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -19,147 +19,18 @@
*/
package net.server.audit.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 MonitoredLockType id;
- private final int hashcode;
- private final Lock state = new ReentrantLock(true);
- private final AtomicInteger reentrantCount = new AtomicInteger(0);
-
- public MonitoredReentrantLock(MonitoredLockType id) {
- super();
- this.id = id;
- hashcode = this.hashCode();
- }
-
- public MonitoredReentrantLock(MonitoredLockType id, boolean fair) {
- super(fair);
- this.id = id;
- hashcode = this.hashCode();
- }
+public interface MonitoredReentrantLock {
- @Override
- public void 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();
- }
-
- super.lock();
- }
+ public void lock();
- @Override
- public void unlock() {
- if(ServerConstants.USE_THREAD_TRACKER) {
- unregisterLocking();
- }
-
- super.unlock();
- }
+ public void 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;
- }
- }
+ public boolean tryLock();
- 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();
- }
- }
+ public MonitoredReentrantLock dispose();
- 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;
- }
-
- public void dispose() {
- state.lock();
- try {
- if(timeoutSchedule != null) {
- timeoutSchedule.cancel(false);
- timeoutSchedule = null;
- }
- } finally {
- state.unlock();
- }
- }
}
diff --git a/src/net/server/audit/locks/MonitoredWriteLock.java b/src/net/server/audit/locks/MonitoredWriteLock.java
index e79db659bd..bef3ef3ba6 100644
--- a/src/net/server/audit/locks/MonitoredWriteLock.java
+++ b/src/net/server/audit/locks/MonitoredWriteLock.java
@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
- Copyleft (L) 2016 - 2018 RonanLana
+ Copyleft 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -19,142 +19,18 @@
*/
package net.server.audit.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 MonitoredLockType 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();
- }
+public interface MonitoredWriteLock {
- @Override
- public void 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();
- }
-
- super.lock();
- }
+ public void lock();
- @Override
- public void unlock() {
- if(ServerConstants.USE_THREAD_TRACKER) {
- unregisterLocking();
- }
-
- super.unlock();
- }
+ public void 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;
- }
- }
+ public boolean tryLock();
- 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();
- }
- }
+ public MonitoredWriteLock dispose();
- 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;
- }
-
- public void dispose() {
- state.lock();
- try {
- if(timeoutSchedule != null) {
- timeoutSchedule.cancel(false);
- timeoutSchedule = null;
- }
- } finally {
- state.unlock();
- }
- }
}
diff --git a/src/net/server/audit/locks/active/TrackerReadLock.java b/src/net/server/audit/locks/active/TrackerReadLock.java
new file mode 100644
index 0000000000..77f1723480
--- /dev/null
+++ b/src/net/server/audit/locks/active/TrackerReadLock.java
@@ -0,0 +1,169 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft (L) 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.active;
+
+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.audit.ThreadTracker;
+import net.server.audit.locks.MonitoredLockType;
+import net.server.audit.locks.MonitoredReadLock;
+import net.server.audit.locks.MonitoredReentrantReadWriteLock;
+import net.server.audit.locks.empty.EmptyReadLock;
+
+import tools.FilePrinter;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements MonitoredReadLock {
+ private ScheduledFuture> timeoutSchedule = null;
+ private StackTraceElement[] deadlockedState = null;
+ private final MonitoredLockType id;
+ private final int hashcode;
+ private final Lock state = new ReentrantLock(true);
+ private final AtomicInteger reentrantCount = new AtomicInteger(0);
+
+ public TrackerReadLock(MonitoredReentrantReadWriteLock lock) {
+ super(lock);
+ this.id = lock.id;
+ hashcode = this.hashCode();
+ }
+
+ @Override
+ public void 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();
+ }
+
+ super.lock();
+ }
+
+ @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;
+ }
+
+ @Override
+ public MonitoredReadLock dispose() {
+ state.lock();
+ try {
+ if(timeoutSchedule != null) {
+ timeoutSchedule.cancel(false);
+ timeoutSchedule = null;
+ }
+ } finally {
+ state.unlock();
+ }
+
+ //unlock();
+ return new EmptyReadLock();
+ }
+}
diff --git a/src/net/server/audit/locks/active/TrackerReentrantLock.java b/src/net/server/audit/locks/active/TrackerReentrantLock.java
new file mode 100644
index 0000000000..6ee53881c2
--- /dev/null
+++ b/src/net/server/audit/locks/active/TrackerReentrantLock.java
@@ -0,0 +1,171 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft (L) 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.active;
+
+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.audit.ThreadTracker;
+import net.server.audit.locks.MonitoredLockType;
+import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.empty.EmptyReentrantLock;
+import tools.FilePrinter;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class TrackerReentrantLock extends ReentrantLock implements MonitoredReentrantLock {
+ private ScheduledFuture> timeoutSchedule = null;
+ private StackTraceElement[] deadlockedState = null;
+ private final MonitoredLockType id;
+ private final int hashcode;
+ private final Lock state = new ReentrantLock(true);
+ private final AtomicInteger reentrantCount = new AtomicInteger(0);
+
+ public TrackerReentrantLock(MonitoredLockType id) {
+ super();
+ this.id = id;
+ hashcode = this.hashCode();
+ }
+
+ public TrackerReentrantLock(MonitoredLockType id, boolean fair) {
+ super(fair);
+ this.id = id;
+ hashcode = this.hashCode();
+ }
+
+ @Override
+ public void 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();
+ }
+
+ super.lock();
+ }
+
+ @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;
+ }
+
+ @Override
+ public MonitoredReentrantLock dispose() {
+ state.lock();
+ try {
+ if(timeoutSchedule != null) {
+ timeoutSchedule.cancel(false);
+ timeoutSchedule = null;
+ }
+ } finally {
+ state.unlock();
+ }
+
+ //unlock();
+ return new EmptyReentrantLock();
+ }
+}
diff --git a/src/net/server/audit/locks/active/TrackerWriteLock.java b/src/net/server/audit/locks/active/TrackerWriteLock.java
new file mode 100644
index 0000000000..8262a6d3ff
--- /dev/null
+++ b/src/net/server/audit/locks/active/TrackerWriteLock.java
@@ -0,0 +1,167 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft (L) 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.active;
+
+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.audit.ThreadTracker;
+import net.server.audit.locks.MonitoredLockType;
+import net.server.audit.locks.MonitoredReentrantReadWriteLock;
+import net.server.audit.locks.MonitoredWriteLock;
+import net.server.audit.locks.empty.EmptyWriteLock;
+import tools.FilePrinter;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implements MonitoredWriteLock {
+ private ScheduledFuture> timeoutSchedule = null;
+ private StackTraceElement[] deadlockedState = null;
+ private final MonitoredLockType id;
+ private final int hashcode;
+ private final Lock state = new ReentrantLock(true);
+ private final AtomicInteger reentrantCount = new AtomicInteger(0);
+
+ public TrackerWriteLock(MonitoredReentrantReadWriteLock lock) {
+ super(lock);
+ this.id = lock.id;
+ hashcode = this.hashCode();
+ }
+
+ @Override
+ public void 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();
+ }
+
+ super.lock();
+ }
+
+ @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;
+ }
+
+ @Override
+ public MonitoredWriteLock dispose() {
+ state.lock();
+ try {
+ if(timeoutSchedule != null) {
+ timeoutSchedule.cancel(false);
+ timeoutSchedule = null;
+ }
+ } finally {
+ state.unlock();
+ }
+
+ //unlock();
+ return new EmptyWriteLock();
+ }
+}
diff --git a/src/net/server/audit/locks/empty/EmptyReadLock.java b/src/net/server/audit/locks/empty/EmptyReadLock.java
new file mode 100644
index 0000000000..a94dfadb26
--- /dev/null
+++ b/src/net/server/audit/locks/empty/EmptyReadLock.java
@@ -0,0 +1,44 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.empty;
+
+import net.server.audit.locks.MonitoredReadLock;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class EmptyReadLock implements MonitoredReadLock {
+ @Override
+ public void lock() {}
+
+ @Override
+ public void unlock() {}
+
+ @Override
+ public boolean tryLock() {
+ return false;
+ }
+
+ @Override
+ public MonitoredReadLock dispose() {
+ return null;
+ }
+}
diff --git a/src/net/server/audit/locks/empty/EmptyReentrantLock.java b/src/net/server/audit/locks/empty/EmptyReentrantLock.java
new file mode 100644
index 0000000000..1ccd108795
--- /dev/null
+++ b/src/net/server/audit/locks/empty/EmptyReentrantLock.java
@@ -0,0 +1,44 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.empty;
+
+import net.server.audit.locks.MonitoredReentrantLock;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class EmptyReentrantLock implements MonitoredReentrantLock {
+ @Override
+ public void lock() {}
+
+ @Override
+ public void unlock() {}
+
+ @Override
+ public boolean tryLock() {
+ return false;
+ }
+
+ @Override
+ public MonitoredReentrantLock dispose() {
+ return null;
+ }
+}
diff --git a/src/net/server/audit/locks/empty/EmptyWriteLock.java b/src/net/server/audit/locks/empty/EmptyWriteLock.java
new file mode 100644
index 0000000000..cff88f441b
--- /dev/null
+++ b/src/net/server/audit/locks/empty/EmptyWriteLock.java
@@ -0,0 +1,44 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.empty;
+
+import net.server.audit.locks.MonitoredWriteLock;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class EmptyWriteLock implements MonitoredWriteLock {
+ @Override
+ public void lock() {}
+
+ @Override
+ public void unlock() {}
+
+ @Override
+ public boolean tryLock() {
+ return false;
+ }
+
+ @Override
+ public MonitoredWriteLock dispose() {
+ return null;
+ }
+}
diff --git a/src/net/server/audit/locks/factory/MonitoredReadLockFactory.java b/src/net/server/audit/locks/factory/MonitoredReadLockFactory.java
new file mode 100644
index 0000000000..728e8fa94b
--- /dev/null
+++ b/src/net/server/audit/locks/factory/MonitoredReadLockFactory.java
@@ -0,0 +1,33 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.factory;
+
+import net.server.audit.locks.MonitoredReentrantReadWriteLock;
+import net.server.audit.locks.active.TrackerReadLock;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class MonitoredReadLockFactory {
+ public static TrackerReadLock createLock(MonitoredReentrantReadWriteLock lock) {
+ return new TrackerReadLock(lock);
+ }
+}
diff --git a/src/net/server/audit/locks/factory/MonitoredReentrantLockFactory.java b/src/net/server/audit/locks/factory/MonitoredReentrantLockFactory.java
new file mode 100644
index 0000000000..6275fa24ec
--- /dev/null
+++ b/src/net/server/audit/locks/factory/MonitoredReentrantLockFactory.java
@@ -0,0 +1,37 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.factory;
+
+import net.server.audit.locks.MonitoredLockType;
+import net.server.audit.locks.active.TrackerReentrantLock;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class MonitoredReentrantLockFactory {
+ public static TrackerReentrantLock createLock(MonitoredLockType id) {
+ return new TrackerReentrantLock(id);
+ }
+
+ public static TrackerReentrantLock createLock(MonitoredLockType id, boolean fair) {
+ return new TrackerReentrantLock(id, fair);
+ }
+}
diff --git a/src/net/server/audit/locks/factory/MonitoredWriteLockFactory.java b/src/net/server/audit/locks/factory/MonitoredWriteLockFactory.java
new file mode 100644
index 0000000000..682983082d
--- /dev/null
+++ b/src/net/server/audit/locks/factory/MonitoredWriteLockFactory.java
@@ -0,0 +1,33 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package net.server.audit.locks.factory;
+
+import net.server.audit.locks.MonitoredReentrantReadWriteLock;
+import net.server.audit.locks.active.TrackerWriteLock;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class MonitoredWriteLockFactory {
+ public static TrackerWriteLock createLock(MonitoredReentrantReadWriteLock lock) {
+ return new TrackerWriteLock(lock);
+ }
+}
diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java
index a425bbf4f3..7a51af8685 100644
--- a/src/net/server/channel/Channel.java
+++ b/src/net/server/channel/Channel.java
@@ -29,6 +29,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -39,6 +40,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
@@ -95,6 +97,7 @@ public final class Channel {
private OverallScheduler channelSchedulers[] = new OverallScheduler[4];
private Map hiredMerchants = new HashMap<>();
private final Map storedVars = new HashMap<>();
+ private Set playersAway = new HashSet<>();
private List expeditions = new ArrayList<>();
private List expedType = new ArrayList<>();
private MapleEvent event;
@@ -128,7 +131,7 @@ public final class Channel {
private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[4];
- private MonitoredReentrantLock lock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL, true);
+ private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL, true);
public Channel(final int world, final int channel, long startTime) {
this.world = world;
@@ -165,7 +168,7 @@ public final class Channel {
}
for(int i = 0; i < 4; i++) {
- faceLock[i] = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_FACEEXPRS, true);
+ faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true);
mobStatusSchedulers[i] = new MobStatusScheduler();
mobAnimationSchedulers[i] = new MobAnimationScheduler();
@@ -208,6 +211,7 @@ public final class Channel {
eventSM = null;
closeChannelSchedules();
+ players = null;
acceptor.unbind();
@@ -263,10 +267,10 @@ public final class Channel {
channelSchedulers[i] = null;
}
- faceLock[i].dispose();
+ faceLock[i] = faceLock[i].dispose();
}
- lock.dispose();
+ lock = lock.dispose();
}
private void closeAllMerchants() {
@@ -291,7 +295,7 @@ public final class Channel {
public int getWorld() {
return world;
}
-
+
public void addPlayer(MapleCharacter chr) {
players.addPlayer(chr);
chr.announce(MaplePacketCreator.serverMessage(serverMessage));
@@ -355,6 +359,18 @@ public final class Channel {
}
return partym;
}
+
+ public void insertPlayerAway(int chrId) { // either they in CS or MTS
+ playersAway.add(chrId);
+ }
+
+ public void removePlayerAway(int chrId) {
+ playersAway.remove(chrId);
+ }
+
+ public boolean canUninstall() {
+ return players.getSize() == 0 && playersAway.isEmpty();
+ }
public class respawnMaps implements Runnable {
diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java
index 4b36d26253..3ab45935e1 100644
--- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java
+++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java
@@ -152,26 +152,27 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
AutobanFactory.MPCON.addPoint(player.getAutobanManager(), "Skill: " + attack.skill + "; Player MP: " + player.getMp() + "; MP Needed: " + attackEffect.getMpCon());
}
+ int mobCount = attackEffect.getMobCount();
if (attack.skill != Cleric.HEAL) {
if (player.isAlive()) {
- if(attack.skill == NightWalker.POISON_BOMB) // Poison Bomb
+ if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb
attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y));
- else if(attack.skill != Aran.BODY_PRESSURE) // prevent BP refreshing
+ } else if(attack.skill != Aran.BODY_PRESSURE) {// prevent BP refreshing
attackEffect.applyTo(player);
+
+ if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
+ || attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK
+ || attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) {
+
+ mobCount = 15;//:(
+ } else if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) {
+ mobCount = 12;
+ }
+ }
} else {
player.getClient().announce(MaplePacketCreator.enableActions());
}
}
- int mobCount = attackEffect.getMobCount();
- if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
- || attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK
- || attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) {
- mobCount = 15;//:(
- }
-
- if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) {
- mobCount = 12;
- }
if (attack.numAttacked > mobCount) {
AutobanFactory.MOB_COUNT.autoban(player, "Skill: " + attack.skill + "; Count: " + attack.numAttacked + " Max: " + attackEffect.getMobCount());
diff --git a/src/net/server/channel/handlers/AutoAssignHandler.java b/src/net/server/channel/handlers/AutoAssignHandler.java
index bb1960543a..b9e2717c4f 100644
--- a/src/net/server/channel/handlers/AutoAssignHandler.java
+++ b/src/net/server/channel/handlers/AutoAssignHandler.java
@@ -21,22 +21,9 @@
*/
package net.server.channel.handlers;
-import constants.ServerConstants;
-import client.MapleCharacter;
import client.MapleClient;
-import client.MapleJob;
-import client.MapleStat;
-import client.autoban.AutobanFactory;
-import client.inventory.Equip;
-import client.inventory.Item;
-import client.inventory.MapleInventory;
-import client.inventory.MapleInventoryType;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.ArrayList;
+import client.processor.AssignAPProcessor;
import net.AbstractMaplePacketHandler;
-import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -45,382 +32,8 @@ import tools.data.input.SeekableLittleEndianAccessor;
*/
public class AutoAssignHandler extends AbstractMaplePacketHandler {
- private static int getNthHighestStat(List statList, short rank) { // ranks from 0
- return(statList.size() <= rank ? 0 : statList.get(rank));
- }
-
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
- MapleCharacter chr = c.getPlayer();
- if (chr.getRemainingAp() < 1) return;
-
- int[] statGain = new int[4];
- int[] statEqpd = new int[4];
- statGain[0] = 0; statGain[1] = 0; statGain[2] = 0; statGain[3] = 0;
-
- slea.skip(8);
-
- if(ServerConstants.USE_SERVER_AUTOASSIGNER) {
- // --------- Ronan Lana's AUTOASSIGNER ---------
- // This method excels for assigning APs in such a way to cover all equipments AP requirements.
- byte opt = slea.readByte(); // useful for pirate autoassigning
-
- int str = 0, dex = 0, luk = 0, int_ = 0;
- List eqpStrList = new ArrayList<>();
- List eqpDexList = new ArrayList<>();
- List eqpLukList = new ArrayList<>();
-
- MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
- Collection
- equippedC = iv.list();
- Equip nEquip;
-
- for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item.
- nEquip = (Equip)item;
- if(nEquip.getStr() > 0) eqpStrList.add(nEquip.getStr());
- str += nEquip.getStr();
-
- if(nEquip.getDex() > 0) eqpDexList.add(nEquip.getDex());
- dex += nEquip.getDex();
-
- if(nEquip.getLuk() > 0) eqpLukList.add(nEquip.getLuk());
- luk += nEquip.getLuk();
-
- //if(nEquip.getInt() > 0) eqpIntList.add(nEquip.getInt()); //not needed...
- int_ += nEquip.getInt();
- }
-
- statEqpd[0] = str;
- statEqpd[1] = dex;
- statEqpd[2] = luk;
- statEqpd[3] = int_;
-
- Collections.sort(eqpStrList, Collections.reverseOrder());
- Collections.sort(eqpDexList, Collections.reverseOrder());
- Collections.sort(eqpLukList, Collections.reverseOrder());
-
- //Autoassigner looks up the 1st/2nd placed equips for their stats to calculate the optimal upgrade.
- int eqpStr = getNthHighestStat(eqpStrList, (short) 0) + getNthHighestStat(eqpStrList, (short) 1);
- int eqpDex = getNthHighestStat(eqpDexList, (short) 0) + getNthHighestStat(eqpDexList, (short) 1);
- int eqpLuk = getNthHighestStat(eqpLukList, (short) 0) + getNthHighestStat(eqpLukList, (short) 1);
-
- //c.getPlayer().message("----------------------------------------");
- //c.getPlayer().message("SDL: s" + eqpStr + " d" + eqpDex + " l" + eqpLuk + " BASE STATS --> STR: " + chr.getStr() + " DEX: " + chr.getDex() + " INT: " + chr.getInt() + " LUK: " + chr.getLuk());
- //c.getPlayer().message("SUM EQUIP STATS -> STR: " + str + " DEX: " + dex + " LUK: " + luk + " INT: " + int_);
-
- MapleJob stance = c.getPlayer().getJobStyle(opt);
- int prStat = 0, scStat = 0, trStat = 0, temp, tempAp = chr.getRemainingAp(), CAP;
-
- MapleStat primary, secondary, tertiary = MapleStat.LUK;
- switch(stance) {
- case MAGICIAN:
- CAP = 165;
- scStat = (chr.getLevel() + 3) - (chr.getLuk() + luk - eqpLuk);
- if(scStat < 0) scStat = 0;
- scStat = Math.min(scStat, tempAp);
-
- if(tempAp > scStat) tempAp -= scStat;
- else tempAp = 0;
-
- prStat = tempAp;
- int_ = prStat;
- luk = scStat;
- str = 0; dex = 0;
-
- if(luk + chr.getLuk() > CAP) {
- temp = luk + chr.getLuk() - CAP;
- luk -= temp;
- int_ += temp;
- }
-
- primary = MapleStat.INT;
- secondary = MapleStat.LUK;
- tertiary = MapleStat.DEX;
-
- break;
-
- case BOWMAN:
- CAP = 125;
- scStat = (chr.getLevel() + 5) - (chr.getStr() + str - eqpStr);
- if(scStat < 0) scStat = 0;
- scStat = Math.min(scStat, tempAp);
-
- if(tempAp > scStat) tempAp -= scStat;
- else tempAp = 0;
-
- prStat = tempAp;
- dex = prStat;
- str = scStat;
- int_ = 0; luk = 0;
-
- if(str + chr.getStr() > CAP) {
- temp = str + chr.getStr() - CAP;
- str -= temp;
- dex += temp;
- }
-
- primary = MapleStat.DEX;
- secondary = MapleStat.STR;
-
- break;
-
- case GUNSLINGER:
- case CROSSBOWMAN:
- CAP = 120;
- scStat = chr.getLevel() - (chr.getStr() + str - eqpStr);
- if(scStat < 0) scStat = 0;
- scStat = Math.min(scStat, tempAp);
-
- if(tempAp > scStat) tempAp -= scStat;
- else tempAp = 0;
-
- prStat = tempAp;
- dex = prStat;
- str = scStat;
- int_ = 0; luk = 0;
-
- if(str + chr.getStr() > CAP) {
- temp = str + chr.getStr() - CAP;
- str -= temp;
- dex += temp;
- }
-
- primary = MapleStat.DEX;
- secondary = MapleStat.STR;
-
- break;
-
- case THIEF:
- CAP = 160;
-
- scStat = 0;
- if(chr.getDex() < 80) {
- scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
- if(scStat < 0) scStat = 0;
-
- scStat = Math.min(80 - chr.getDex(), scStat);
- scStat = Math.min(tempAp, scStat);
- tempAp -= scStat;
- }
-
- temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex);
- if(temp < 0) temp = 0;
- temp = Math.min(tempAp, temp);
- scStat += temp;
- tempAp -= temp;
-
- // thieves will upgrade STR as well only if a level-based threshold is reached.
- if(chr.getStr() >= Math.max(13, (int)(0.4 * chr.getLevel()))) {
- if(chr.getStr() < 50) {
- trStat = (chr.getLevel() - 10) - (chr.getStr() + str - eqpStr);
- if(trStat < 0) trStat = 0;
-
- trStat = Math.min(50 - chr.getStr(), trStat);
- trStat = Math.min(tempAp, trStat);
- tempAp -= trStat;
- }
-
- temp = (20 + (chr.getLevel() / 2)) - Math.max(50, trStat + chr.getStr() + str - eqpStr);
- if(temp < 0) temp = 0;
- temp = Math.min(tempAp, temp);
- trStat += temp;
- tempAp -= temp;
- }
-
- prStat = tempAp;
- luk = prStat;
- dex = scStat;
- str = trStat;
- int_ = 0;
-
- if(dex + chr.getDex() > CAP) {
- temp = dex + chr.getDex() - CAP;
- dex -= temp;
- luk += temp;
- }
- if(str + chr.getStr() > CAP) {
- temp = str + chr.getStr() - CAP;
- str -= temp;
- luk += temp;
- }
-
- primary = MapleStat.LUK;
- secondary = MapleStat.DEX;
- tertiary = MapleStat.STR;
-
- break;
-
- case BRAWLER:
- CAP = 120;
-
- scStat = chr.getLevel() - (chr.getDex() + dex - eqpDex);
- if(scStat < 0) scStat = 0;
- scStat = Math.min(scStat, tempAp);
-
- if(tempAp > scStat) tempAp -= scStat;
- else tempAp = 0;
-
- prStat = tempAp;
- str = prStat;
- dex = scStat;
- int_ = 0; luk = 0;
-
- if(dex + chr.getDex() > CAP) {
- temp = dex + chr.getDex() - CAP;
- dex -= temp;
- str += temp;
- }
-
- primary = MapleStat.STR;
- secondary = MapleStat.DEX;
-
- break;
-
- default: //warrior, beginner, ...
- CAP = 80;
-
- scStat = ((2 * chr.getLevel()) / 3) - (chr.getDex() + dex - eqpDex);
- if(scStat < 0) scStat = 0;
- scStat = Math.min(scStat, tempAp);
-
- if(tempAp > scStat) tempAp -= scStat;
- else tempAp = 0;
-
- prStat = tempAp;
- str = prStat;
- dex = scStat;
- int_ = 0; luk = 0;
-
- if(dex + chr.getDex() > CAP) {
- temp = dex + chr.getDex() - CAP;
- dex -= temp;
- str += temp;
- }
-
- primary = MapleStat.STR;
- secondary = MapleStat.DEX;
- }
-
- //-------------------------------------------------------------------------------------
-
- int extras = 0;
-
- extras = gainStatByType(chr, primary, statGain, prStat + extras);
- extras = gainStatByType(chr, secondary, statGain, scStat + extras);
- extras = gainStatByType(chr, tertiary, statGain, trStat + extras);
-
- if(extras > 0) { //redistribute surplus in priority order
- extras = gainStatByType(chr, primary, statGain, extras);
- extras = gainStatByType(chr, secondary, statGain, extras);
- extras = gainStatByType(chr, tertiary, statGain, extras);
- gainStatByType(chr, getQuaternaryStat(stance), statGain, extras);
- }
-
- int remainingAp = (chr.getRemainingAp() - getAccumulatedStatGain(statGain));
- chr.setRemainingAp(remainingAp);
- chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp);
- c.announce(MaplePacketCreator.enableActions());
-
- //----------------------------------------------------------------------------------------
-
- c.announce(MaplePacketCreator.serverNotice(1, "Better AP applications detected:\r\nSTR: +" + statGain[0] + "\r\nDEX: +" + statGain[1] + "\r\nINT: +" + statGain[3] + "\r\nLUK: +" + statGain[2]));
- } else {
- if(slea.available() < 16) {
- AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign.");
- c.disconnect(false, false);
- return;
- }
-
- MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
- Collection
- equippedC = iv.list();
- for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item.
- Equip nEquip = (Equip)item;
-
- statEqpd[0] += nEquip.getStr();
- statEqpd[1] += nEquip.getDex();
- statEqpd[2] += nEquip.getLuk();
- statEqpd[3] += nEquip.getInt();
- }
-
- int total = 0;
- int extras = 0;
- for (int i = 0; i < 2; i++) {
- int type = slea.readInt();
- int tempVal = slea.readInt();
- if (tempVal < 0 || tempVal > c.getPlayer().getRemainingAp()) {
- return;
- }
- total += tempVal;
- extras += gainStatByType(chr, MapleStat.getBy5ByteEncoding(type), statGain, tempVal);
- }
- int remainingAp = (chr.getRemainingAp() - total) + extras;
- chr.setRemainingAp(remainingAp);
- chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp);
- c.announce(MaplePacketCreator.enableActions());
- }
- }
-
- private int gainStatByType(MapleCharacter chr, MapleStat type, int[] statGain, int gain) {
- if(gain <= 0) return 0;
-
- int newVal = 0;
- if (type.equals(MapleStat.STR)) {
- newVal = chr.getStr() + gain;
- if (newVal > ServerConstants.MAX_AP) {
- statGain[0] += (gain - (newVal - ServerConstants.MAX_AP));
- chr.setStr(ServerConstants.MAX_AP);
- } else {
- statGain[0] += gain;
- chr.setStr(newVal);
- }
- } else if (type.equals(MapleStat.INT)) {
- newVal = chr.getInt() + gain;
- if (newVal > ServerConstants.MAX_AP) {
- statGain[3] += (gain - (newVal - ServerConstants.MAX_AP));
- chr.setInt(ServerConstants.MAX_AP);
- } else {
- statGain[3] += gain;
- chr.setInt(newVal);
- }
- } else if (type.equals(MapleStat.LUK)) {
- newVal = chr.getLuk() + gain;
- if (newVal > ServerConstants.MAX_AP) {
- statGain[2] += (gain - (newVal - ServerConstants.MAX_AP));
- chr.setLuk(ServerConstants.MAX_AP);
- } else {
- statGain[2] += gain;
- chr.setLuk(newVal);
- }
- } else if (type.equals(MapleStat.DEX)) {
- newVal = chr.getDex() + gain;
- if (newVal > ServerConstants.MAX_AP) {
- statGain[1] += (gain - (newVal - ServerConstants.MAX_AP));
- chr.setDex(ServerConstants.MAX_AP);
- } else {
- statGain[1] += gain;
- chr.setDex(newVal);
- }
- }
-
- if (newVal > ServerConstants.MAX_AP) {
- chr.updateSingleStat(type, ServerConstants.MAX_AP);
- return newVal - ServerConstants.MAX_AP;
- }
- chr.updateSingleStat(type, newVal);
- return 0;
- }
-
- private MapleStat getQuaternaryStat(MapleJob stance) {
- if(stance != MapleJob.MAGICIAN) return MapleStat.INT;
- return MapleStat.STR;
- }
-
- private int getAccumulatedStatGain(int[] statGain) {
- int acc = 0;
-
- for(byte i = 0; i < statGain.length; i++) {
- acc += statGain[i];
- }
-
- return acc;
+ AssignAPProcessor.APAutoAssignAction(slea, c);
}
}
diff --git a/src/net/server/channel/handlers/ChangeChannelHandler.java b/src/net/server/channel/handlers/ChangeChannelHandler.java
index feece8abda..0f343fe5d1 100644
--- a/src/net/server/channel/handlers/ChangeChannelHandler.java
+++ b/src/net/server/channel/handlers/ChangeChannelHandler.java
@@ -37,12 +37,13 @@ public final class ChangeChannelHandler extends AbstractMaplePacketHandler {
int channel = slea.readByte() + 1;
c.getPlayer().getAutobanManager().setTimestamp(6, slea.readInt(), 2);
if(c.getChannel() == channel) {
- AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel.");
- c.disconnect(false, false);
- return;
+ AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel.");
+ c.disconnect(false, false);
+ return;
} else if (c.getPlayer().getCashShop().isOpened() || c.getPlayer().getMiniGame() != null || c.getPlayer().getPlayerShop() != null) {
return;
}
+
c.changeChannel(channel);
}
}
\ No newline at end of file
diff --git a/src/net/server/channel/handlers/DistributeAPHandler.java b/src/net/server/channel/handlers/DistributeAPHandler.java
index 5d9ac714fd..3831a15e5d 100644
--- a/src/net/server/channel/handlers/DistributeAPHandler.java
+++ b/src/net/server/channel/handlers/DistributeAPHandler.java
@@ -21,319 +21,18 @@
*/
package net.server.channel.handlers;
-import client.MapleCharacter;
import client.MapleClient;
-import client.MapleJob;
-import client.MapleStat;
-import client.Skill;
-import client.SkillFactory;
-import constants.ServerConstants;
-import constants.skills.BlazeWizard;
-import constants.skills.Brawler;
-import constants.skills.DawnWarrior;
-import constants.skills.Magician;
-import constants.skills.Warrior;
+import client.processor.AssignAPProcessor;
import net.AbstractMaplePacketHandler;
-import tools.MaplePacketCreator;
-import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
public final class DistributeAPHandler extends AbstractMaplePacketHandler {
- private static final int max = 32767;
-
+
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readInt();
int num = slea.readInt();
- if (c.getPlayer().getRemainingAp() > 0) {
- if (addStat(c, num, false)) {
- c.getPlayer().setRemainingAp(c.getPlayer().getRemainingAp() - 1);
- c.getPlayer().updateSingleStat(MapleStat.AVAILABLEAP, c.getPlayer().getRemainingAp());
- }
- }
- c.announce(MaplePacketCreator.enableActions());
- }
-
- public static boolean addStat(MapleClient c, int apTo, boolean usedAPReset) {
- switch (apTo) {
- case 64: // Str
- if (c.getPlayer().getStr() >= max) {
- return false;
- }
- c.getPlayer().addStat(1, 1);
- break;
- case 128: // Dex
- if (c.getPlayer().getDex() >= max) {
- return false;
- }
- c.getPlayer().addStat(2, 1);
- break;
- case 256: // Int
- if (c.getPlayer().getInt() >= max) {
- return false;
- }
- c.getPlayer().addStat(3, 1);
- break;
- case 512: // Luk
- if (c.getPlayer().getLuk() >= max) {
- return false;
- }
- c.getPlayer().addStat(4, 1);
- break;
- case 2048: // HP
- addHP(c.getPlayer(), addHP(c, usedAPReset));
- break;
- case 8192: // MP
- addMP(c.getPlayer(), addMP(c, usedAPReset));
- break;
- default:
- c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer()));
- return false;
- }
- return true;
- }
-
- private static int addHP(MapleClient c, boolean usedAPReset) {
- MapleCharacter player = c.getPlayer();
- MapleJob job = player.getJob();
- int MaxHP = player.getMaxHp();
- if (player.getHpMpApUsed() > 9999 || MaxHP >= 30000) {
- return MaxHP;
- }
- return MaxHP + calcHpChange(player, job, usedAPReset);
- }
-
- private static int calcHpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) {
- int MaxHP = 0;
-
- if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
- if(!usedAPReset) {
- Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.DAWNWARRIOR1) ? DawnWarrior.MAX_HP_INCREASE : Warrior.IMPROVED_MAXHP);
- int sLvl = player.getSkillLevel(increaseHP);
-
- if(sLvl > 0)
- MaxHP += increaseHP.getEffect(sLvl).getY();
- }
-
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if (usedAPReset) {
- MaxHP += 20;
- } else {
- MaxHP += Randomizer.rand(18, 22);
- }
- } else {
- MaxHP += 20;
- }
- } else if(job.isA(MapleJob.ARAN1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if (usedAPReset) {
- MaxHP += 20;
- } else {
- MaxHP += Randomizer.rand(26, 30);
- }
- } else {
- MaxHP += 28;
- }
- } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if (usedAPReset) {
- MaxHP += 6;
- } else {
- MaxHP += Randomizer.rand(5, 9);
- }
- } else {
- MaxHP += 6;
- }
- } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if (usedAPReset) {
- MaxHP += 16;
- } else {
- MaxHP += Randomizer.rand(14, 18);
- }
- } else {
- MaxHP += 16;
- }
- } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if (usedAPReset) {
- MaxHP += 16;
- } else {
- MaxHP += Randomizer.rand(14, 18);
- }
- } else {
- MaxHP += 16;
- }
- } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
- if(!usedAPReset) {
- Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP);
- int sLvl = player.getSkillLevel(increaseHP);
-
- if(sLvl > 0)
- MaxHP += increaseHP.getEffect(sLvl).getY();
- }
-
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if (usedAPReset) {
- MaxHP += 18;
- } else {
- MaxHP += Randomizer.rand(16, 20);
- }
- } else {
- MaxHP += 18;
- }
- } else if (usedAPReset) {
- MaxHP += 8;
- } else {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- MaxHP += Randomizer.rand(8, 12);
- } else {
- MaxHP += 10;
- }
- }
-
- return MaxHP;
- }
-
- private static int addMP(MapleClient c, boolean usedAPReset) {
- MapleCharacter player = c.getPlayer();
- int MaxMP = player.getMaxMp();
- MapleJob job = player.getJob();
- if (player.getHpMpApUsed() > 9999 || player.getMaxMp() >= 30000) {
- return MaxMP;
- }
-
- return MaxMP + calcMpChange(player, job, usedAPReset);
- }
-
- private static int calcMpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) {
- int MaxMP = 0;
-
- if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if(!usedAPReset) {
- MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10));
- } else {
- MaxMP += 2;
- }
- } else {
- MaxMP += 3;
- }
- } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
- if(!usedAPReset) {
- Skill increaseMP = SkillFactory.getSkill(job.isA(MapleJob.BLAZEWIZARD1) ? BlazeWizard.INCREASING_MAX_MP : Magician.IMPROVED_MAX_MP_INCREASE);
- int sLvl = player.getSkillLevel(increaseMP);
-
- if(sLvl > 0)
- MaxMP += increaseMP.getEffect(sLvl).getY();
- }
-
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if(!usedAPReset) {
- MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20));
- } else {
- MaxMP += 18;
- }
- } else {
- MaxMP += 18;
- }
- } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if(!usedAPReset) {
- MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
- } else {
- MaxMP += 10;
- }
- } else {
- MaxMP += 10;
- }
- } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if(!usedAPReset) {
- MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
- } else {
- MaxMP += 10;
- }
- } else {
- MaxMP += 10;
- }
- } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if(!usedAPReset) {
- MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10));
- } else {
- MaxMP += 14;
- }
- } else {
- MaxMP += 14;
- }
- } else {
- if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
- if(!usedAPReset) {
- MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10));
- } else {
- MaxMP += 6;
- }
- } else {
- MaxMP += 6;
- }
- }
-
- return MaxMP;
- }
-
- private static void addHP(MapleCharacter player, int MaxHP) {
- MaxHP = Math.min(30000, MaxHP);
- player.setHpMpApUsed(player.getHpMpApUsed() + 1);
- player.setMaxHp(MaxHP);
- player.updateSingleStat(MapleStat.MAXHP, MaxHP);
- }
-
- private static void addMP(MapleCharacter player, int MaxMP) {
- MaxMP = Math.min(30000, MaxMP);
- player.setHpMpApUsed(player.getHpMpApUsed() + 1);
- player.setMaxMp(MaxMP);
- player.updateSingleStat(MapleStat.MAXMP, MaxMP);
- }
-
- public static int takeHp(MapleCharacter player, MapleJob job) {
- int MaxHP = 0;
-
- if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
- MaxHP += 54;
- } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
- MaxHP += 10;
- } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
- MaxHP += 20;
- } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
- MaxHP += 20;
- } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
- MaxHP += 42;
- } else {
- MaxHP += 12;
- }
-
- return MaxHP;
- }
-
- public static int takeMp(MapleCharacter player, MapleJob job) {
- int MaxMP = 0;
-
- if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
- MaxMP += 4;
- } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
- MaxMP += 31;
- } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
- MaxMP += 12;
- } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
- MaxMP += 12;
- } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
- MaxMP += 16;
- } else {
- MaxMP += 8;
- }
-
- return MaxMP;
- }
+ AssignAPProcessor.APAssignAction(c, num);
+ }
}
diff --git a/src/net/server/channel/handlers/DistributeSPHandler.java b/src/net/server/channel/handlers/DistributeSPHandler.java
index c4bc963cb7..8e1e570c03 100644
--- a/src/net/server/channel/handlers/DistributeSPHandler.java
+++ b/src/net/server/channel/handlers/DistributeSPHandler.java
@@ -21,64 +21,17 @@
*/
package net.server.channel.handlers;
-import net.AbstractMaplePacketHandler;
-import tools.FilePrinter;
-import tools.MaplePacketCreator;
-import tools.data.input.SeekableLittleEndianAccessor;
-import client.MapleCharacter;
import client.MapleClient;
-import client.MapleStat;
-import client.Skill;
-import client.SkillFactory;
-import client.autoban.AutobanFactory;
-import constants.GameConstants;
-import constants.skills.Aran;
+import client.processor.AssignSPProcessor;
+import net.AbstractMaplePacketHandler;
+import tools.data.input.SeekableLittleEndianAccessor;
public final class DistributeSPHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readInt();
int skillid = slea.readInt();
- if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
- c.getSession().write(MaplePacketCreator.enableActions());
- return;
- }
- MapleCharacter player = c.getPlayer();
- int remainingSp = player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000));
- boolean isBeginnerSkill = false;
- if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
- AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
- FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n");
- c.disconnect(true, false);
- return;
- }
- if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
- int total = 0;
- for (int i = 0; i < 3; i++) {
- total += player.getSkillLevel(SkillFactory.getSkill(player.getJobType() * 10000000 + 1000 + i));
- }
- remainingSp = Math.min((player.getLevel() - 1), 6) - total;
- isBeginnerSkill = true;
- }
- Skill skill = SkillFactory.getSkill(skillid);
- int curLevel = player.getSkillLevel(skill);
- if ((remainingSp > 0 && curLevel + 1 <= (skill.isFourthJob() ? player.getMasterLevel(skill) : skill.getMaxLevel()))) {
- if (!isBeginnerSkill) {
- player.setRemainingSp(player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)) - 1, GameConstants.getSkillBook(skillid/10000));
- }
- player.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)));
- if (skill.getId() == Aran.FULL_SWING) {
- player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- } else if (skill.getId() == Aran.OVER_SWING) {
- player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- } else {
- player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
- }
- }
+ AssignSPProcessor.SPAssignAction(c, skillid);
}
}
\ No newline at end of file
diff --git a/src/net/server/channel/handlers/EnterCashShopHandler.java b/src/net/server/channel/handlers/EnterCashShopHandler.java
index 3becadcb9b..4d6c044de8 100644
--- a/src/net/server/channel/handlers/EnterCashShopHandler.java
+++ b/src/net/server/channel/handlers/EnterCashShopHandler.java
@@ -60,7 +60,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler {
mc.unregisterChairBuff();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs());
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(mc.getId(), mc.getAllDiseases());
- mc.setAwayFromWorld(true);
+ mc.setAwayFromChannelWorld();
mc.notifyMapTransferToPartner(-1);
mc.cancelAllBuffs(true);
mc.cancelAllDebuffs();
diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java
index d6bf850f16..c2833ba12d 100644
--- a/src/net/server/channel/handlers/EnterMTSHandler.java
+++ b/src/net/server/channel/handlers/EnterMTSHandler.java
@@ -83,7 +83,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
chr.unregisterChairBuff();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases());
- chr.setAwayFromWorld(true);
+ chr.setAwayFromChannelWorld();
chr.notifyMapTransferToPartner(-1);
chr.cancelAllBuffs(true);
chr.cancelAllDebuffs();
@@ -96,6 +96,8 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
chr.cancelQuestExpirationTask();
chr.saveCharToDB();
+
+ c.getChannelServer().removePlayer(chr);
chr.getMap().removePlayer(c.getPlayer());
try {
c.announce(MaplePacketCreator.openCashShop(c, true));
diff --git a/src/net/server/channel/handlers/HealOvertimeHandler.java b/src/net/server/channel/handlers/HealOvertimeHandler.java
index 04b426c268..72d8ee7abf 100644
--- a/src/net/server/channel/handlers/HealOvertimeHandler.java
+++ b/src/net/server/channel/handlers/HealOvertimeHandler.java
@@ -26,6 +26,7 @@ import client.MapleClient;
import client.autoban.AutobanFactory;
import client.autoban.AutobanManager;
import net.AbstractMaplePacketHandler;
+import server.maps.MapleMapFactory;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.MaplePacketCreator;
@@ -33,6 +34,8 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
+ if(!chr.isLoggedinWorld()) return;
+
AutobanManager abm = chr.getAutobanManager();
int timestamp = slea.readInt();
abm.setTimestamp(0, timestamp, 3);
@@ -41,8 +44,7 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
if (healHP != 0) {
if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing");
- int abHeal = 140;
- if(chr.getMapId() == 105040401 || chr.getMapId() == 105040402 || chr.getMapId() == 809000101 || chr.getMapId() == 809000201) abHeal += 40; // Sleepywood sauna and showa spa...
+ int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa...
if (healHP > abHeal) {
AutobanFactory.HIGH_HP_HEALING.autoban(chr, "Healing: " + healHP + "; Max is " + abHeal + ".");
return;
diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java
index b89692ebcc..0560264ec5 100644
--- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java
+++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java
@@ -42,6 +42,7 @@ import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
+import client.BuddyList;
import client.BuddylistEntry;
import client.CharacterNameAndId;
import client.MapleCharacter;
@@ -101,7 +102,17 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
int state = c.getLoginState();
boolean allowLogin = true;
+
Channel cserv = c.getChannelServer();
+ if(cserv == null) {
+ c.setChannel(1);
+ cserv = c.getChannelServer();
+
+ if(cserv == null) { // world server is out
+ c.disconnect(true, false);
+ return;
+ }
+ }
/* is this check really necessary?
if (state == MapleClient.LOGIN_SERVER_TRANSITION || state == MapleClient.LOGIN_NOTLOGGEDIN) {
@@ -126,7 +137,11 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
}
c.updateLoginState(MapleClient.LOGIN_LOGGEDIN);
+ World world = server.getWorld(c.getWorld());
+
cserv.addPlayer(player);
+ world.addPlayer(player);
+ player.setEnteredChannelWorld();
List buffs = server.getPlayerBuffStorage().getBuffsFromStorage(cid);
if (buffs != null) {
@@ -156,19 +171,16 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
player.getMap().addPlayer(player);
player.visitMap(player.getMap());
- World world = server.getWorld(c.getWorld());
- world.getPlayerStorage().addPlayer(player);
-
- player.setAwayFromWorld(false);
-
- int buddyIds[] = player.getBuddylist().getBuddyIds();
+ BuddyList bl = player.getBuddylist();
+ int buddyIds[] = bl.getBuddyIds();
world.loggedOn(player.getName(), player.getId(), c.getChannel(), buddyIds);
- for (CharacterIdChannelPair onlineBuddy : server.getWorld(c.getWorld()).multiBuddyFind(player.getId(), buddyIds)) {
- BuddylistEntry ble = player.getBuddylist().get(onlineBuddy.getCharacterId());
+ for (CharacterIdChannelPair onlineBuddy : world.multiBuddyFind(player.getId(), buddyIds)) {
+ BuddylistEntry ble = bl.get(onlineBuddy.getCharacterId());
ble.setChannel(onlineBuddy.getChannel());
- player.getBuddylist().put(ble);
+ bl.put(ble);
}
- c.announce(MaplePacketCreator.updateBuddylist(player.getBuddylist().getBuddies()));
+ c.announce(MaplePacketCreator.updateBuddylist(bl.getBuddies()));
+
c.announce(MaplePacketCreator.loadFamily(player));
if (player.getFamilyId() > 0) {
MapleFamily f = world.getFamily(player.getFamilyId());
diff --git a/src/net/server/channel/handlers/UseCashItemHandler.java b/src/net/server/channel/handlers/UseCashItemHandler.java
index 65fea04f2a..d0f4e489be 100644
--- a/src/net/server/channel/handlers/UseCashItemHandler.java
+++ b/src/net/server/channel/handlers/UseCashItemHandler.java
@@ -23,8 +23,6 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
-import client.MapleJob;
-import client.MapleStat;
import client.Skill;
import client.SkillFactory;
import client.creator.veteran.*;
@@ -36,6 +34,7 @@ import client.inventory.MaplePet;
import client.inventory.ModifyInventory;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.inventory.manipulator.MapleKarmaManipulator;
+import client.processor.AssignAPProcessor;
import constants.GameConstants;
import constants.ItemConstants;
import constants.ServerConstants;
@@ -79,9 +78,9 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
slea.readShort();
int itemId = slea.readInt();
int itemType = itemId / 10000;
- Item toUse = c.getPlayer().getInventory(MapleInventoryType.CASH).getItem(c.getPlayer().getInventory(MapleInventoryType.CASH).findById(itemId).getPosition());
+ Item toUse = player.getInventory(MapleInventoryType.CASH).getItem(player.getInventory(MapleInventoryType.CASH).findById(itemId).getPosition());
String medal = "";
- Item medalItem = c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49);
+ Item medalItem = player.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49);
if (medalItem != null) {
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
}
@@ -100,7 +99,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
player.changeMap(c.getChannelServer().getMapFactory().getMap(mapId));
} else {
MapleInventoryManipulator.addById(c, itemId, (short) 1);
- c.getPlayer().dropMessage(1, error1);
+ player.dropMessage(1, error1);
c.announce(MaplePacketCreator.enableActions());
}
} else {
@@ -149,131 +148,12 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
player.changeSkillLevel(skillSPTo, (byte) (curLevel + 1), player.getMasterLevel(skillSPTo), -1);
}
} else {
- List> statupdate = new ArrayList<>(2);
int APTo = slea.readInt();
int APFrom = slea.readInt();
- switch (APFrom) {
- case 64: // str
- if (player.getStr() < 5) {
- c.getPlayer().message("You don't have the minimum STR required to swap.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- player.addStat(1, -1);
- break;
- case 128: // dex
- if (player.getDex() < 5) {
- c.getPlayer().message("You don't have the minimum DEX required to swap.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- player.addStat(2, -1);
- break;
- case 256: // int
- if (player.getInt() < 5) {
- c.getPlayer().message("You don't have the minimum INT required to swap.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- player.addStat(3, -1);
- break;
- case 512: // luk
- if (player.getLuk() < 5) {
- c.getPlayer().message("You don't have the minimum LUK required to swap.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- player.addStat(4, -1);
- break;
- case 2048: // HP
- if(ServerConstants.USE_ENFORCE_HPMP_SWAP) {
- if (APTo != 8192) {
- c.getPlayer().message("You can only swap HP ability points to MP.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- }
- if (player.getHpMpApUsed() < 1) {
- c.getPlayer().message("You don't have enough HPMP stat points to spend on AP Reset.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- int hp = player.getMaxHp();
- int level_ = player.getLevel();
-
- boolean canWash_ = true;
- if (hp < level_ * 14 + 148) {
- canWash_ = false;
- }
-
- if (!canWash_) {
- c.getPlayer().message("You don't have the minimum HP pool required to swap.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- player.setHpMpApUsed(player.getHpMpApUsed() - 1);
- int hplose = -DistributeAPHandler.takeHp(player, player.getJob());
- int nextHp = Math.max(1, player.getHp() + hplose), nextMaxHp = Math.max(50, player.getMaxHp() + hplose);
-
- player.setHp(nextHp);
- player.setMaxHp(nextMaxHp);
- statupdate.add(new Pair<>(MapleStat.HP, nextHp));
- statupdate.add(new Pair<>(MapleStat.MAXHP, nextMaxHp));
-
- break;
- case 8192: // MP
- if(ServerConstants.USE_ENFORCE_HPMP_SWAP) {
- if (APTo != 2048) {
- c.getPlayer().message("You can only swap MP ability points to HP.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- }
- if (player.getHpMpApUsed() < 1) {
- c.getPlayer().message("You don't have enough HPMP stat points to spend on AP Reset.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- int mp = player.getMaxMp();
- int level = player.getLevel();
- MapleJob job = player.getJob();
-
- boolean canWash = true;
- if (job.isA(MapleJob.SPEARMAN) && mp < 4 * level + 156) {
- canWash = false;
- } else if (job.isA(MapleJob.FIGHTER) && mp < 4 * level + 56) {
- canWash = false;
- } else if (job.isA(MapleJob.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) {
- canWash = false;
- } else if (mp < level * 14 + 148) {
- canWash = false;
- }
-
- if (!canWash) {
- c.getPlayer().message("You don't have the minimum MP pool required to swap.");
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- player.setHpMpApUsed(player.getHpMpApUsed() - 1);
- int mplose = -DistributeAPHandler.takeMp(player, job);
- int nextMp = Math.max(0, player.getMp() + mplose), nextMaxMp = Math.max(5, player.getMaxMp() + mplose);
-
- player.setMp(nextMp);
- player.setMaxMp(nextMaxMp);
- statupdate.add(new Pair<>(MapleStat.MP, nextMp));
- statupdate.add(new Pair<>(MapleStat.MAXMP, nextMaxMp));
-
- break;
- default:
- c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer()));
- return;
+
+ if(!AssignAPProcessor.APResetAction(c, APFrom, APTo)) {
+ return;
}
- DistributeAPHandler.addStat(c, APTo, true);
- c.announce(MaplePacketCreator.updatePlayerStats(statupdate, true, c.getPlayer()));
}
remove(c, itemId);
} else if (itemType == 506) {
@@ -287,7 +167,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
eq.setOwner(player.getName());
} else if (itemId == 5060001 || itemId == 5061000 || itemId == 5061001 || itemId == 5061002 || itemId == 5061003) { // Sealing lock
MapleInventoryType type = MapleInventoryType.getByType((byte) slea.readInt());
- eq = c.getPlayer().getInventory(type).getItem((short) slea.readInt());
+ eq = player.getInventory(type).getItem((short) slea.readInt());
if (eq == null) { //Check if the type is EQUIPMENT?
return;
}
@@ -317,7 +197,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
} else if (itemId == 5060002) { // Incubator
byte inventory2 = (byte) slea.readInt();
short slot2 = (short) slea.readInt();
- Item item2 = c.getPlayer().getInventory(MapleInventoryType.getByType(inventory2)).getItem(slot2);
+ Item item2 = player.getInventory(MapleInventoryType.getByType(inventory2)).getItem(slot2);
if (item2 == null) // hacking
{
return;
@@ -388,11 +268,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
break;
case 6: //item megaphone
- String msg = medal + c.getPlayer().getName() + " : " + slea.readMapleAsciiString();
+ String msg = medal + player.getName() + " : " + slea.readMapleAsciiString();
whisper = slea.readByte() == 1;
Item item = null;
if (slea.readByte() == 1) { //item
- item = c.getPlayer().getInventory(MapleInventoryType.getByType((byte) slea.readInt())).getItem((short) slea.readInt());
+ item = player.getInventory(MapleInventoryType.getByType((byte) slea.readInt())).getItem((short) slea.readInt());
if (item == null) //hack
{
return;
@@ -412,7 +292,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
}
String[] msg2 = new String[lines];
for (int i = 0; i < lines; i++) {
- msg2[i] = medal + c.getPlayer().getName() + " : " + slea.readMapleAsciiString();
+ msg2[i] = medal + player.getName() + " : " + slea.readMapleAsciiString();
}
whisper = slea.readByte() == 1;
Server.getInstance().broadcastMessage(c.getWorld(), MaplePacketCreator.getMultiMegaphone(msg2, c.getChannel(), whisper));
@@ -420,10 +300,10 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
}
remove(c, itemId);
} else if (itemType == 508) { // graduation banner, thanks to tmskdl12
- MapleKite kite = new MapleKite(c.getPlayer(), slea.readMapleAsciiString(), itemId);
+ MapleKite kite = new MapleKite(player, slea.readMapleAsciiString(), itemId);
- if (!GameConstants.isFreeMarketRoom(c.getPlayer().getMapId())) {
- c.getPlayer().getMap().spawnKite(kite);
+ if (!GameConstants.isFreeMarketRoom(player.getMapId())) {
+ player.getMap().spawnKite(kite);
remove(c, itemId);
} else {
c.announce(MaplePacketCreator.sendCannotSpawnKite());
@@ -442,11 +322,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
remove(c, itemId);
} else if (itemType == 512) {
if (ii.getStateChangeItem(itemId) != 0) {
- for (MapleCharacter mChar : c.getPlayer().getMap().getCharacters()) {
+ for (MapleCharacter mChar : player.getMap().getCharacters()) {
ii.getItemEffect(ii.getStateChangeItem(itemId)).applyTo(mChar);
}
}
- player.getMap().startMapEffect(ii.getMsg(itemId).replaceFirst("%s", c.getPlayer().getName()).replaceFirst("%s", slea.readMapleAsciiString()), itemId);
+ player.getMap().startMapEffect(ii.getMsg(itemId).replaceFirst("%s", player.getName()).replaceFirst("%s", slea.readMapleAsciiString()), itemId);
remove(c, itemId);
} else if (itemType == 517) {
MaplePet pet = player.getPet(0);
@@ -512,7 +392,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
}
final int world = c.getWorld();
- Server.getInstance().broadcastMessage(world, MaplePacketCreator.getAvatarMega(c.getPlayer(), medal, c.getChannel(), itemId, strLines, (slea.readByte() != 0)));
+ Server.getInstance().broadcastMessage(world, MaplePacketCreator.getAvatarMega(player, medal, c.getChannel(), itemId, strLines, (slea.readByte() != 0)));
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
@@ -585,20 +465,20 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
} else if (itemType == 552) {
MapleInventoryType type = MapleInventoryType.getByType((byte) slea.readInt());
short slot = (short) slea.readInt();
- Item item = c.getPlayer().getInventory(type).getItem(slot);
+ Item item = player.getInventory(type).getItem(slot);
if (item == null || item.getQuantity() <= 0 || MapleKarmaManipulator.hasKarmaFlag(item) || !ii.isKarmaAble(item.getItemId())) {
c.announce(MaplePacketCreator.enableActions());
return;
}
if(MapleKarmaManipulator.hasUsedKarmaFlag(item)) {
- c.getPlayer().dropMessage(6, "Scissors of Karma was already used on this item.");
+ player.dropMessage(6, "Scissors of Karma was already used on this item.");
c.announce(MaplePacketCreator.enableActions());
return;
}
MapleKarmaManipulator.setKarmaFlag(item);
- c.getPlayer().forceUpdateItem(item);
+ player.forceUpdateItem(item);
remove(c, itemId);
c.announce(MaplePacketCreator.enableActions());
} else if (itemType == 552) { //DS EGG THING
@@ -607,8 +487,8 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
slea.readInt();
int itemSlot = slea.readInt();
slea.readInt();
- final Equip equip = (Equip) c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) itemSlot);
- if (equip.getVicious() == 2 || c.getPlayer().getInventory(MapleInventoryType.CASH).findById(5570000) == null) {
+ final Equip equip = (Equip) player.getInventory(MapleInventoryType.EQUIP).getItem((short) itemSlot);
+ if (equip.getVicious() == 2 || player.getInventory(MapleInventoryType.CASH).findById(5570000) == null) {
return;
}
equip.setVicious(equip.getVicious() + 1);
@@ -623,14 +503,14 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
}
final byte eSlot = (byte) slea.readInt();
- final Item eitem = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem(eSlot);
+ final Item eitem = player.getInventory(MapleInventoryType.EQUIP).getItem(eSlot);
if (slea.readInt() != 2) {
return;
}
final byte uSlot = (byte) slea.readInt();
- final Item uitem = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(uSlot);
+ final Item uitem = player.getInventory(MapleInventoryType.USE).getItem(uSlot);
if (eitem == null || uitem == null) {
return;
}
@@ -644,12 +524,12 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
//should have a check here against PE hacks
if(itemId / 1000000 != 5) itemId = 0;
- c.getPlayer().toggleBlockCashShop();
+ player.toggleBlockCashShop();
final int curlevel = toScroll.getLevel();
c.getSession().write(MaplePacketCreator.sendVegaScroll(0x40));
- final Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, uitem.getItemId(), false, itemId, c.getPlayer().isGM());
+ final Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, uitem.getItemId(), false, itemId, player.isGM());
c.getSession().write(MaplePacketCreator.sendVegaScroll(scrolled.getLevel() > curlevel ? 0x41 : 0x43));
//opcodes 0x42, 0x44: "this item cannot be used"; 0x39, 0x45: crashes
diff --git a/src/net/server/channel/worker/BaseScheduler.java b/src/net/server/channel/worker/BaseScheduler.java
index e4cec03f58..a95abdf053 100644
--- a/src/net/server/channel/worker/BaseScheduler.java
+++ b/src/net/server/channel/worker/BaseScheduler.java
@@ -27,11 +27,11 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.locks.Lock;
import net.server.Server;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
+import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import server.TimerManager;
import tools.Pair;
@@ -42,7 +42,7 @@ import tools.Pair;
public abstract class BaseScheduler {
private int idleProcs = 0;
private List listeners = new LinkedList<>();
- private final List externalLocks = new LinkedList<>();
+ private final List externalLocks = new LinkedList<>();
private Map