Using Java ThreadPool + Mob Skills & Event Instance patch + Eqp Merge

Server source now uses Java ThreadPool, recycling used thread resources for next uses.
Added Grenade visual effect for other players.
Implemented an attempt towards unsynced mob behavior, where reportedly players were able to pin same mob in different sections of the map.
Solved several deadlock issues, mostly regarding character synchronized methods, event instance scripts and player/item vision-unvision.
Solved an issue where mobs would not cast some skills of it's skillset. Frequent behavior on low-leveled mobs.
Fixed a bug on 2nd Maker quest where players could complete it by merely disassembling an equipment.
New custom mechanic: equipment merge. Similarly to the Bazaar NPC, every equipment after the selected one is used up, and a fraction of their stat amounts are used as stat gains on the currently equipped items. If restrictions are enabled, players must be high-leveled and Maker lv3 to use it.
Skill Storm Break no longer uses up arrows.
Added a server flag to allow access for all Aran job skills from the beginning.
Implemented Battleship and Super Transformation questline scripts.
Fixed a desynchronization within pet position and cash inventory position, that could potentially lead to some inventory issues until relogin.
Improved timestamp handling in some handler classes. Spam detection is entirely a server-side matter, hence removed usage of client-sided timestamp content.
Refactored some pet response packets, improving some of their behaviors.
Fixed some quest issues: Maker lv1 and Omega Sector meteorite one.
This commit is contained in:
ronancpl
2018-11-10 17:48:35 -02:00
parent 00675ab95d
commit 5ee0cd1c98
120 changed files with 7424 additions and 6387 deletions
+3 -3
View File
@@ -9,7 +9,7 @@ Regarding distributability and usage of the code presented here: like it was bef
This is a NetBeans 8.0.2 Project, that MUST be built and run under JDK/JRE 7 (1.7.0_79+) in order to run properly. This means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application. This is a NetBeans 8.0.2 Project, that MUST be built and run under JDK/JRE 7 (1.7.0_79+) in order to run properly. This means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application.
In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of server source, in order to perceive it at it's full potential. My opinion, though! Refer to "README_wzchanges.txt" for more information on what has been changed from Nexon's v83 WZ files. In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of server source, in order to perceive it at it's full potential. My opinion, though!
The main objective of this project is to try as best as possible to recreate what once was the original MapleStory v83, while adding up some flavors that spices up the gameplay. In other words, aim to get the best of the MapleStory of that era. The main objective of this project is to try as best as possible to recreate what once was the original MapleStory v83, while adding up some flavors that spices up the gameplay. In other words, aim to get the best of the MapleStory of that era.
@@ -48,7 +48,7 @@ Client files & general tools: https://drive.google.com/drive/folders/0BzDsHSr-0V
* Fraysa's https://hostr.co/gJbLZITRVHmv * Fraysa's https://hostr.co/gJbLZITRVHmv
* MapleSilver's starting on window-mode. * Eric's MapleSilver starting on window-mode.
--- ---
### Development information ### Development information
@@ -175,7 +175,7 @@ Finally, select "Clean and Build project" to build the JAR file for the MapleSto
The client's set-up is quite straightforward: The client's set-up is quite straightforward:
1. From "ManagerMsv83.exe", install MapleStory on your folder of preference (e.g. "C:\Nexon\MapleStory") and follow their instructions. 1. From "ManagerMsv83.exe", install MapleStory on your folder of preference (e.g. "C:\Nexon\MapleStory") and follow their instructions.
2. Once done, erase these files: "HShield" (folder), "ASPLauncher.exe", "MapleStory.exe" and "patcher.exe". 2. Once done, erase these files: "HShield" (folder), "ASPLnchr.exe", "MapleStory.exe" and "Patcher.exe".
3. Extract into the client folder the "localhost.exe" from the provided link. 3. Extract into the client folder the "localhost.exe" from the provided link.
4. Overwrite the original WZ files with the ones provided from either one of those folders on the Google Drive: 4. Overwrite the original WZ files with the ones provided from either one of those folders on the Google Drive:
- "commit???_wz" (last published RELEASE, referring to commit of same number). - "commit???_wz" (last published RELEASE, referring to commit of same number).
+4
View File
@@ -89,6 +89,7 @@ Cash & Items:
* Storage with "Arrange Items" feature functional. * Storage with "Arrange Items" feature functional.
* Close-quarters evaluation mode for items (sandbox). * Close-quarters evaluation mode for items (sandbox).
* Further improved Karma scissors & Untradeable items mechanics. * Further improved Karma scissors & Untradeable items mechanics.
* Reviewed pet/item position data inconsistency within CASH inventory.
* Spikes on shoes. * Spikes on shoes.
* Vega's spell. * Vega's spell.
* Owl of Minerva. * Owl of Minerva.
@@ -175,6 +176,7 @@ Server potentials:
* Delete Character (requires ENABLE_PIC activated). * Delete Character (requires ENABLE_PIC activated).
* Smoothed up view-all-char feature, now showing properly all available characters and not disconnecting players too often. * Smoothed up view-all-char feature, now showing properly all available characters and not disconnecting players too often.
* Centralized getcurrenttime throughout several server handlers, boosting it's performance overall. * Centralized getcurrenttime throughout several server handlers, boosting it's performance overall.
* Centralized server timestamping, several timestamps received from clients are now unused, preventing some spammable exploits.
* Autosaver (periodically saves on DB current state of every player in-game). * Autosaver (periodically saves on DB current state of every player in-game).
* Both fixed and randomized versions of HP/MP growth rate available, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature. * Both fixed and randomized versions of HP/MP growth rate available, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature.
* Implemented methods to get the current Players' MaxHP/MaxMP method with equipment HP/MP gains already summed up. * Implemented methods to get the current Players' MaxHP/MaxMP method with equipment HP/MP gains already summed up.
@@ -191,6 +193,7 @@ Custom NPCs:
* Asia: scroll & rarities shop NPC. * Asia: scroll & rarities shop NPC.
* Abdula: lists droppers of needed skill/mastery books. * Abdula: lists droppers of needed skill/mastery books.
* Agent E: accessory crafter. * Agent E: accessory crafter.
* Dalair: automatized equipment-merger.
* Donation Box: automatized item-buyer. * Donation Box: automatized item-buyer.
* Coco & Ace of Hearts: C. scroll crafters. * Coco & Ace of Hearts: C. scroll crafters.
@@ -239,6 +242,7 @@ Project:
* Reviewed SQL data, eliminating duplicated entries on the tables. * Reviewed SQL data, eliminating duplicated entries on the tables.
* Improved login phase, using cache over DB queries. * Improved login phase, using cache over DB queries.
* Usage of HikariCP to improve the DB connection management. * Usage of HikariCP to improve the DB connection management.
* Usage of Java Threadpool to improve runnable call management.
* Developed many survey tools for content profiling. * Developed many survey tools for content profiling.
* Developed a robust anti-exploit login coordinator system. * Developed a robust anti-exploit login coordinator system.
* Protected many flaws with login management system. * Protected many flaws with login management system.
+43 -4
View File
@@ -2,12 +2,15 @@
Spiegelmann -> 2042000 Spiegelmann -> 2042000
Coco -> 9000017 Coco -> 9000017
Agent E -> 9000036 Agent E -> 9000036
Dalair -> 9000040
Donation Box -> 9000041 Donation Box -> 9000041
Abdula -> 9209000 Abdula -> 9209000 *
CUSTOM NPC SHOPS: CUSTOM NPC SHOPS (db_shopupdate.sql):
Asia -> 2082014 Asia -> 2082014 *
T-1337 -> 9201101 T-1337 -> 9201101 *
* : those won't get disabled when USE_ENABLE_CUSTOM_NPC_SCRIPTS = false
LOGS: LOGS:
@@ -1415,3 +1418,39 @@ Removido possibilidade de preparar engagements enquanto segurando aneis de casam
23 Outubro 2018, 23 Outubro 2018,
Adicionado script para quest de 4o job de Cygnus Knights. Adicionado script para quest de 4o job de Cygnus Knights.
25 - 27 Outubro 2018,
Scripts de eventos agora executam em uma thread separada, assim resolvendo possíveis problemas de deadlocks.
Realizada tentativa de solução para casos onde jogadores percebem mobs em pontos diferentes do mapa em certas situações.
Clareado um problema de deadlock relacionado a packets de visão/desaparecimento de jogadores e mobs.
Resolvido diversos problemas de deadlocks relacionados com event instances.
Implementado ThreadManager. Este sistema utiliza a abordagem de ThreadPool para reciclagem de threads utilizadas, assim eliminando realização de overhead ao criar novas threads a todo uso.
Resolvido certos casos onde requisição de item pickup de jogador não era devidamente registrado pelo servidor como ação válida.
29 - 31 Outubro 2018,
Corrigido mobs não lançando skills em determinados casos. Comportamento notável era mobs de nível baixo utilizando nenhuma skill.
Skill Storm Break não mais remove flechas ao ser utilizada.
Implementado método que força alteração na posição atual de mobs no mapa.
Mobs que forem atacados de muito longe terão suas posições atualizadas para todos os jogadores no mapa, buscando assim evitar dessincronizações entre clientes.
Adicionado drops de red bean porridge em alguns mobs de El Nath.
Implementado flag para opção de aquisição de belts de Dojo regular ou fácil.
Corrigido map banish de mobs não funcionando corretamente após as últimas atualizações do MoveLifeHandler.
Corrigido bug na quest do 2nd Maker que permitia conclusão de quest ao realizar disassemble em equipamentos.
Adicionado nova mecânica custom: amalgama de equipamentos. Jogadores acima do nível 160, que possuem Maker level 3, poderão utilizar esta mecânica para alavancar atributos dos itens equipados.
Refatorado método de ganho de fama para não mais usar modificador synchronized, assim evitando possível caso de deadlock.
Adicionado server flag que permite acesso a todas as skills de job de Aran ao iniciar novo job.
Corrigido horário irregular no sistema do servidor.
Adicionado scripts para skill quests de Battleship e Super Transformation.
03 Novembro 2018,
Corrigido caso onde pets estavam com posições de inventário desatualizadas, levando a certas inconsistências com evoluir/administrar pets.
05 Novembro 2018,
Alterado administração de timestamp nos handlers. Agora detecção de spam é inteiramente responsabilidade do servidor, onde a detecção de spam ocorre de acordo com quantidade de vezes que um pacote é recebido num dado intervalo de tempo estipulado internamente.
Refatorado packets que lidam com respostas a comandos de pets.
07 - 09 Novembro 2018,
Corrigido drop data de Ultra Gray com item inexistente.
Corrigido quest de meteoritos mudando estado da quest mesmo quando jogador não recebe o item devido a inventário cheio.
Corrigido quest de Maker lv1 retirando mesos além da quantidade esperada, ao realizar ações da quest.
Adicionado server flag que lida com utilização de features NPCs custom.
+28
View File
@@ -0,0 +1,28 @@
Source: http://metropi.forumotion.net/t32-npc-scripting-guide-from-ragezone
NPC Markups:
#b = Blue text.
#c[itemid]# Shows how many [itemid] the player has in their inventory.
#d = Purple text.
#e = Bold text.
#f[imagelocation]# - Shows an image inside the .wz files.
#g = Green text.
#h # - Shows the name of the player.
#i[itemid]# - Shows a picture of the item.
#k = Black text.
#l - Selection close.
#m[mapid]# - Shows the name of the map.
#n = Normal text (removes bold).
#o[mobid]# - Shows the name of the mob.
#p[npcid]# - Shows the name of the NPC.
#q[skillid]# - Shows the name of the skill.
#r = Red text.
#s[skillid]# - Shows the image of the skill.
#t[itemid]# - Shows the name of the item.
#v[itemid]# - Shows a picture of the item.
#x - Returns "0%" (need more information on this).
#z[itemid]# - Shows the name of the item.
#B[%]# - Shows a 'progress' bar.
#F[imagelocation]# - Shows an image inside the .wz files.
#L[number]# Selection open.
\r\n - Moves down a line.
+138
View File
@@ -0,0 +1,138 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* @Author Ronan
* Event - Kyrin's Test Quest
**/
var entryMap = 912010000;
var exitMap = 120000101;
var minMapId = 912010000;
var maxMapId = 912010200;
var eventTime = 4; //4 minutes
var lobbyRange = [0, 0];
function setLobbyRange() {
return lobbyRange;
}
function init() {
em.setProperty("noEntry","false");
}
function setup(level, lobbyid) {
var eim = em.newInstance("4jship_" + lobbyid);
eim.setProperty("level", level);
eim.setProperty("boss", "0");
eim.setProperty("canLeave", "0");
eim.getInstanceMap(entryMap).resetPQ(level);
respawnStages(eim);
eim.startEventTimer(eventTime * 60000);
eim.schedule("playerCanLeave", 1 * 60000);
eim.schedule("playerSurvived", 2 * 60000);
return eim;
}
function afterSetup(eim) {}
function respawnStages(eim) {}
function playerCanLeave(eim) {
eim.setIntProperty("canLeave", 1);
}
function playerSurvived(eim) {
if (eim.getLeader().isAlive()) {
eim.setIntProperty("canLeave", 2);
eim.dropMessage(5, "Kyrin: You have passed the test. Now for the closing part... Are you able reach the exit over there?");
} else {
eim.dropMessage(5, "Kyrin: You have failed the test. Aww, don't have such a sad face, just try it again later, ok?");
}
}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
player.changeMap(map, map.getPortal(0));
}
function playerUnregistered(eim, player) {}
function playerExit(eim, player) {
eim.unregisterPlayer(player);
eim.dispose();
em.setProperty("noEntry","false");
}
function playerLeft(eim, player) {}
function scheduledTimeout(eim) {
var player = eim.getPlayers().get(0);
playerExit(eim, player);
player.changeMap(exitMap);
}
function playerDisconnected(eim, player) {
playerExit(eim, player);
}
function changedMap(eim, chr, mapid) {
if(mapid < minMapId || mapid > maxMapId) playerExit(eim, chr);
}
function clearPQ(eim) {
eim.stopEventTimer();
eim.setEventCleared();
var player = eim.getPlayers().get(0);
eim.unregisterPlayer(player);
player.changeMap(exitMap);
eim.dispose();
em.setProperty("noEntry","false");
}
function monsterKilled(mob, eim) {}
function leftParty(eim, player) {}
function disbandParty(eim) {}
function monsterValue(eim, mobId) {
return 1;
}
function friendlyKilled(mob, eim) {
if(em.getProperty("noEntry") != "false") {
var player = eim.getPlayers().get(0);
playerExit(eim, player);
player.changeMap(exitMap);
}
}
function allMonstersDead(eim) {}
function cancelSchedule() {}
function dispose() {}
+140
View File
@@ -0,0 +1,140 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* @Author Ronan
* Event - Kyrin's Test Quest
**/
var entryMap = 912010100;
var exitMap = 120000101;
var minMapId = 912010100;
var maxMapId = 912010200;
var eventTime = 4; //4 minutes
var lobbyRange = [0, 0];
function setLobbyRange() {
return lobbyRange;
}
function init() {
em.setProperty("noEntry","false");
}
function setup(level, lobbyid) {
var eim = em.newInstance("4jsuper_" + lobbyid);
eim.setProperty("level", level);
eim.setProperty("boss", "0");
eim.setProperty("canLeave", "0");
eim.getInstanceMap(entryMap).resetPQ(level);
respawnStages(eim);
eim.startEventTimer(eventTime * 60000);
eim.schedule("playerCanLeave", 1 * 60000);
eim.schedule("playerSurvived", 2 * 60000);
return eim;
}
function afterSetup(eim) {}
function respawnStages(eim) {}
function playerCanLeave(eim) {
eim.setIntProperty("canLeave", 1);
}
function playerSurvived(eim) {
if (eim.getLeader().isAlive()) {
eim.setIntProperty("canLeave", 2);
eim.dropMessage(5, "Kyrin: You have passed the test. Now for the closing part... Are you able reach the exit over there?");
} else {
eim.dropMessage(5, "Kyrin: You have failed the test. Aww, don't have such a sad face, just try it again later, ok?");
}
}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
player.changeMap(map, map.getPortal(0));
}
function playerUnregistered(eim, player) {}
function playerExit(eim, player) {
eim.unregisterPlayer(player);
eim.dispose();
em.setProperty("noEntry","false");
}
function playerLeft(eim, player) {}
function scheduledTimeout(eim) {
var player = eim.getPlayers().get(0);
playerExit(eim, player);
player.changeMap(exitMap);
}
function playerDisconnected(eim, player) {
playerExit(eim, player);
}
function changedMap(eim, chr, mapid) {
if(mapid < minMapId || mapid > maxMapId) playerExit(eim, chr);
}
function changedLeader(eim, leader) {}
function clearPQ(eim) {
eim.stopEventTimer();
eim.setEventCleared();
var player = eim.getPlayers().get(0);
eim.unregisterPlayer(player);
player.changeMap(exitMap);
eim.dispose();
em.setProperty("noEntry","false");
}
function monsterKilled(mob, eim) {}
function leftParty(eim, player) {}
function disbandParty(eim) {}
function monsterValue(eim, mobId) {
return 1;
}
function friendlyKilled(mob, eim) {
if(em.getProperty("noEntry") != "false") {
var player = eim.getPlayers().get(0);
playerExit(eim, player);
player.changeMap(exitMap);
}
}
function allMonstersDead(eim) {}
function cancelSchedule() {}
function dispose() {}
+23 -11
View File
@@ -3,6 +3,7 @@ var maps = [104000000, 102000000, 100000000, 103000000, 120000000];
var cost = [1000, 1000, 1000, 1000, 800]; var cost = [1000, 1000, 1000, 1000, 800];
var selectedMap = -1; var selectedMap = -1;
var mesos; var mesos;
var hasCoupon = false;
function start() { function start() {
cm.sendNext("Hello, I drive the Regular Cab. If you want to go from town to town safely and fast, then ride our cab. We'll glady take you to your destination with an affordable price."); cm.sendNext("Hello, I drive the Regular Cab. If you want to go from town to town safely and fast, then ride our cab. We'll glady take you to your destination with an affordable price.");
@@ -33,22 +34,33 @@ function action(mode, type, selection) {
selStr += "\r\n#L" + i + "##m" + maps[i] + "# (" + (cm.getJobId() == 0 ? cost[i] / 10 : cost[i]) + " mesos)#l"; selStr += "\r\n#L" + i + "##m" + maps[i] + "# (" + (cm.getJobId() == 0 ? cost[i] / 10 : cost[i]) + " mesos)#l";
cm.sendSimple(selStr); cm.sendSimple(selStr);
} else if (status == 2) { } else if (status == 2) {
cm.sendYesNo("You don't have anything else to do here, huh? Do you really want to go to #b#m" + maps[selection] + "##k? It'll cost you #b"+ (cm.getJobId() == 0 ? cost[selection] / 10 : cost[selection]) + " mesos#k."); if (maps[selection] == 100000000 && cm.getMapId() == 101000000 && cm.haveItem(4032288)) {
cm.sendYesNo("Hmm, I see you have been recommended by Neinheart to come to Victoria Island to improve your knightly skills. Well, just this time the ride will be free of charges. Will you take the ride?");
hasCoupon = true;
} else {
cm.sendYesNo("You don't have anything else to do here, huh? Do you really want to go to #b#m" + maps[selection] + "##k? It'll cost you #b"+ (cm.getJobId() == 0 ? cost[selection] / 10 : cost[selection]) + " mesos#k.");
}
selectedMap = selection; selectedMap = selection;
} else if (status == 3) { } else if (status == 3) {
if (cm.getJobId() == 0) { if (!hasCoupon) {
mesos = cost[selectedMap] / 10; if (cm.getJobId() == 0) {
mesos = cost[selectedMap] / 10;
} else {
mesos = cost[selectedMap];
}
if (cm.getMeso() < mesos) {
cm.sendNext("You don't have enough mesos. Sorry to say this, but without them, you won't be able to ride the cab.");
cm.dispose();
return;
}
cm.gainMeso(-mesos);
} else { } else {
mesos = cost[selectedMap]; cm.gainItem(4032288, -1);
} }
if (cm.getMeso() < mesos) {
cm.sendNext("You don't have enough mesos. Sorry to say this, but without them, you won't be able to ride the cab.");
cm.dispose();
return;
}
cm.gainMeso(-mesos);
cm.warp(maps[selectedMap], 0); cm.warp(maps[selectedMap], 0);
cm.dispose(); cm.dispose();
} }
+1 -1
View File
@@ -79,7 +79,7 @@ function action(mode, type, selection) {
if(status == 0) { if(status == 0) {
hasCoin = cm.haveItem(coinId); hasCoin = cm.haveItem(coinId);
cm.sendNext("This is the vending machine of the Internet Cafe. Place your erasers or #t" + coinId + "# earned throughout the quests to redeem a prize. You can place #bany amount of erasers#k, however take note that bigger shots improves the reward possibilities!"); cm.sendNext("This is the vending machine of the Internet Cafe. Place your erasers or #t" + coinId + "# earned throughout the quests to redeem a prize. You can place #bany amount of erasers#k, however take note that placing #rdifferent erasers#k and #rbigger shots of any of them#k will improve the reward possibilities!");
} else if(status == 1) { } else if(status == 1) {
var sendStr; var sendStr;
currentTier = getRewardTier(); currentTier = getRewardTier();
+50 -2
View File
@@ -33,8 +33,39 @@ spawnPnpc = false;
spawnPnpcFee = 7000000; spawnPnpcFee = 7000000;
jobType = 5; jobType = 5;
var advQuest = 0;
function start() { function start() {
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { if (cm.isQuestStarted(6330)) {
if (cm.getEventInstance() != null) { // missing script for skill test found thanks to Lost(tm)
advQuest = 5; // string visibility thanks to iPunchEm & Glvelturall
cm.sendNext("Not bad at all. Let's discuss this outside!");
cm.setQuestProgress(6330, 0, 1);
} else if (cm.getQuestProgress(6330, 0) == 0) {
advQuest = 1;
cm.sendNext("You're ready, right? Now try to withstand my attacks for 2 minutes. I won't go easy on you. Good luck, because you will need it.");
} else {
advQuest = 3;
cm.teachSkill(5121003, 0, 10, -1);
cm.forceCompleteQuest(6330);
cm.sendNext("Congratulations. You have managed to pass my test. I'll teach you a new skill called \"Super Transformation\".\r\n\r\n #s5221003# #b#q5221003##k");
}
} else if (cm.isQuestStarted(6370)) {
if (cm.getEventInstance() != null) {
advQuest = 6;
cm.sendNext("Not bad at all. Let's discuss this outside!");
cm.setQuestProgress(6370, 0, 1);
} else if (cm.getQuestProgress(6370, 0) == 0) {
advQuest = 2;
cm.sendNext("You're ready, right? Now try to withstand my attacks for 2 minutes. I won't go easy on you. Good luck, because you will need it.");
} else {
advQuest = 4;
cm.teachSkill(5221006, 0, 10, -1);
cm.forceCompleteQuest(6370);
cm.sendNext("Congratulations. You have managed to pass my test. I'll teach you a new skill called \"Battleship\".\r\n\r\n #s5221006# #b#q5221006##k");
}
} else if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
spawnPnpc = true; spawnPnpc = true;
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
@@ -82,7 +113,24 @@ function action(mode, type, selection) {
start(); start();
return; return;
} else { } else {
if(spawnPnpc) { if (advQuest > 0) {
if (advQuest < 3) {
var em = cm.getEventManager(advQuest == 1 ? "4jship" : "4jsuper");
if(!em.startInstance(cm.getPlayer())) {
cm.sendOk("Someone is already challenging the test. Please try again later.");
}
} else if (advQuest < 5) {
if (advQuest == 3) {
cm.sendOk("It is similar to that of 'Transformation', but it's much more powerful than that. Keep training, and hope to see you around.");
} else {
cm.sendOk("Unlike most of the other skills you used as a Pirate, this one definitely is different. You can actually ride the 'Battleship' and attack enemies with it. Your DEF level will increase for the time you're on board, so that'll help you tremendously in combat situations. May you become the best Gunslinger out there...");
}
} else {
cm.warp(120000101);
}
cm.dispose();
} else if(spawnPnpc) {
if(mode > 0) { if(mode > 0) {
if(cm.getMeso() < spawnPnpcFee) { if(cm.getMeso() < spawnPnpcFee) {
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame."); cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
+5 -18
View File
@@ -12,12 +12,8 @@
var menu = new Array("Victoria Island"); var menu = new Array("Victoria Island");
var method; var method;
var hasCoupon = false;
function start() { function start() {
status = -1; status = -1;
if(cm.haveItem(4032288)) hasCoupon = true;
action(1, 0, 0); action(1, 0, 0);
} }
@@ -36,22 +32,13 @@ function action(mode, type, selection) {
} }
status++; status++;
if (status == 0) { if (status == 0) {
if(!hasCoupon) { var display = "";
var display = ""; for(var i=0; i < menu.length; i++) {
for(var i=0; i < menu.length; i++) { display += "\r\n#L"+i+"##b Victoria Island (1000 mesos)#k";
display += "\r\n#L"+i+"##b Victoria Island (1000 mesos)#k";
}
cm.sendSimple("Eh, Hello...again. Do you want to leave Ereve and go somewhere else? If so, you've come to the right place. I operate a ferry that goes from #bEreve#k to #bVictoria Island#k, I can take you to #bVictoria Island#k if you want... You'll have to pay a fee of #b1000#k Mesos.\r\n"+display);
} else {
cm.sendYesNo("Hmm, hi there. I see you have been recommended by Neinheart to go to Victoria Island to improve your knightly skills. Well, just this time the ride will be free of charges. Will you embark?");
} }
cm.sendSimple("Eh, Hello...again. Do you want to leave Ereve and go somewhere else? If so, you've come to the right place. I operate a ferry that goes from #bEreve#k to #bVictoria Island#k, I can take you to #bVictoria Island#k if you want... You'll have to pay a fee of #b1000#k Mesos.\r\n"+display);
} else if(status == 1) { } else if(status == 1) {
if(hasCoupon) { if(cm.getMeso() < 1000) {
cm.gainItem(4032288, -1);
cm.warp(200090031);
cm.dispose();
} else if(cm.getMeso() < 1000) {
cm.sendNext("Hmm... Are you sure you have #b1000#k Mesos? Check your Inventory and make sure you have enough. You must pay the fee or I can't let you get on..."); cm.sendNext("Hmm... Are you sure you have #b1000#k Mesos? Check your Inventory and make sure you have enough. You must pay the fee or I can't let you get on...");
cm.dispose(); cm.dispose();
} else { } else {
+1 -1
View File
@@ -10,6 +10,6 @@
**/ **/
function start() { function start() {
cm.sendOk("Ah, such lovely winds. This should be a perfect voyage as long as no stupid customer falls off for attempting some weird skill. Of course, I'm talking about you. Please refain from using your skills."); cm.sendOk("Ah, such lovely winds. This should be a perfect voyage as long as no stupid customer falls off for attempting some weird skill. Of course, I'm talking about you. Please refrain from using your skills.");
cm.dispose(); cm.dispose();
} }
+7 -3
View File
@@ -1,11 +1,15 @@
/* /*
NPC Name: Divine Bird NPC Name: Divine Bird
Map(s): Erev Map(s): Erev
Description: Buff Description: 3rd job KoC Buff
*/ */
importPackage(Packages.constants);
function start() { function start() {
cm.useItem(2022458); if (cm.getPlayer().isCygnus() && GameConstants.getJobBranch(cm.getJob()) > 2) {
cm.useItem(2022458);
}
cm.sendOk("Don't stop training. Every ounce of your energy is required to protect the world of Maple...."); cm.sendOk("Don't stop training. Every ounce of your energy is required to protect the world of Maple....");
cm.dispose(); cm.dispose();
} }
+9 -3
View File
@@ -49,18 +49,24 @@ function action(mode, type, selection) {
status--; status--;
if(status == 0) { if(status == 0) {
if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
cm.sendOk("The Monster Carnival is currently unavailable.");
cm.dispose();
return;
}
var selStr = "The Monster Carnival is currently unavailable, but instead I offer a steadfast #bore refining#k service for you, taxing #r" + ((feeMultiplier * 100) | 0) + "%#k over the usual fee to synthetize them. What will you do?#b"; var selStr = "The Monster Carnival is currently unavailable, but instead I offer a steadfast #bore refining#k service for you, taxing #r" + ((feeMultiplier * 100) | 0) + "%#k over the usual fee to synthetize them. What will you do?#b";
var options = new Array("Refine mineral ores","Refine jewel ores"); var options = new Array("Refine mineral ores","Refine jewel ores");
if(refineCrystals) { if(refineCrystals) {
options.push("Refine crystal ores"); options.push("Refine crystal ores");
} }
if(refineRocks) { if(refineRocks) {
options.push("Refine plates/jewels"); options.push("Refine plates/jewels");
} }
for (var i = 0; i < options.length; i++){ for (var i = 0; i < options.length; i++){
selStr += "\r\n#L" + i + "# " + options[i] + "#l"; selStr += "\r\n#L" + i + "# " + options[i] + "#l";
} }
cm.sendSimple(selStr); cm.sendSimple(selStr);
+7 -3
View File
@@ -47,10 +47,14 @@ function action(mode, type, selection) {
var progress = cm.getQuestProgress(3421, 0); var progress = cm.getQuestProgress(3421, 0);
if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) {
progress |= (1 << meteoriteId); if (cm.canHold(4031117, 1)) {
progress |= (1 << meteoriteId);
cm.gainItem(4031117, 1); cm.gainItem(4031117, 1);
cm.setQuestProgress(3421, 0, progress); cm.setQuestProgress(3421, 0, progress);
} else {
cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item.");
}
} }
} }
+7 -3
View File
@@ -47,10 +47,14 @@ function action(mode, type, selection) {
var progress = cm.getQuestProgress(3421, 0); var progress = cm.getQuestProgress(3421, 0);
if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) {
progress |= (1 << meteoriteId); if (cm.canHold(4031117, 1)) {
progress |= (1 << meteoriteId);
cm.gainItem(4031117, 1); cm.gainItem(4031117, 1);
cm.setQuestProgress(3421, 0, progress); cm.setQuestProgress(3421, 0, progress);
} else {
cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item.");
}
} }
} }
+7 -3
View File
@@ -47,10 +47,14 @@ function action(mode, type, selection) {
var progress = cm.getQuestProgress(3421, 0); var progress = cm.getQuestProgress(3421, 0);
if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) {
progress |= (1 << meteoriteId); if (cm.canHold(4031117, 1)) {
progress |= (1 << meteoriteId);
cm.gainItem(4031117, 1); cm.gainItem(4031117, 1);
cm.setQuestProgress(3421, 0, progress); cm.setQuestProgress(3421, 0, progress);
} else {
cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item.");
}
} }
} }
+7 -3
View File
@@ -47,10 +47,14 @@ function action(mode, type, selection) {
var progress = cm.getQuestProgress(3421, 0); var progress = cm.getQuestProgress(3421, 0);
if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) {
progress |= (1 << meteoriteId); if (cm.canHold(4031117, 1)) {
progress |= (1 << meteoriteId);
cm.gainItem(4031117, 1); cm.gainItem(4031117, 1);
cm.setQuestProgress(3421, 0, progress); cm.setQuestProgress(3421, 0, progress);
} else {
cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item.");
}
} }
} }
+7 -3
View File
@@ -47,10 +47,14 @@ function action(mode, type, selection) {
var progress = cm.getQuestProgress(3421, 0); var progress = cm.getQuestProgress(3421, 0);
if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) {
progress |= (1 << meteoriteId); if (cm.canHold(4031117, 1)) {
progress |= (1 << meteoriteId);
cm.gainItem(4031117, 1); cm.gainItem(4031117, 1);
cm.setQuestProgress(3421, 0, progress); cm.setQuestProgress(3421, 0, progress);
} else {
cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item.");
}
} }
} }
+7 -3
View File
@@ -47,10 +47,14 @@ function action(mode, type, selection) {
var progress = cm.getQuestProgress(3421, 0); var progress = cm.getQuestProgress(3421, 0);
if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) {
progress |= (1 << meteoriteId); if (cm.canHold(4031117, 1)) {
progress |= (1 << meteoriteId);
cm.gainItem(4031117, 1); cm.gainItem(4031117, 1);
cm.setQuestProgress(3421, 0, progress); cm.setQuestProgress(3421, 0, progress);
} else {
cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item.");
}
} }
} }
+5 -4
View File
@@ -25,26 +25,27 @@
* @Map(s): Dojo Hall * @Map(s): Dojo Hall
*/ */
importPackage(Packages.server.maps); importPackage(Packages.server.maps);
importPackage(Packages.constants);
var disabled = false; var disabled = false;
var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004); var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004);
var belt_level = Array(25, 35, 45, 60, 75); var belt_level = Array(25, 35, 45, 60, 75);
var belt_on_inventory; var belt_on_inventory;
var belt_points;
/* var belt_points = Array(200, 1800, 4000, 9200, 17000); */
var belt_points = Array(10, 90, 200, 460, 850); /* Watered down version */
var status = -1; var status = -1;
var selectedMenu = -1; var selectedMenu = -1;
var dojoWarp = 0; var dojoWarp = 0;
function start() { function start() {
if(disabled) { if (disabled) {
cm.sendOk("My master has requested that the dojo be #rclosed#k at this time so I can't let you in."); cm.sendOk("My master has requested that the dojo be #rclosed#k at this time so I can't let you in.");
cm.dispose(); cm.dispose();
return; return;
} }
belt_points = ServerConstants.USE_FAST_DOJO_UPGRADE ? Array(10, 90, 200, 460, 850) : Array(200, 1800, 4000, 9200, 17000);
belt_on_inventory = new Array(); belt_on_inventory = new Array();
for (var i = 0; i < belts.length; i++) { for (var i = 0; i < belts.length; i++) {
belt_on_inventory.push(cm.haveItemWithId(belts[i], true)); belt_on_inventory.push(cm.haveItemWithId(belts[i], true));
+2 -2
View File
@@ -14,7 +14,7 @@ function action(mode, type, selection) {
if (mode == -1) { if (mode == -1) {
cm.dispose(); cm.dispose();
} else { } else {
if (status == 0 && mode == 0) { if (mode == 0) {
cm.sendNext("Aye...are you scared of speed or heights? You can't trust my flying skills? Trust me, I've worked out all the kinks!"); cm.sendNext("Aye...are you scared of speed or heights? You can't trust my flying skills? Trust me, I've worked out all the kinks!");
cm.dispose(); cm.dispose();
return; return;
@@ -26,7 +26,7 @@ function action(mode, type, selection) {
if(status == 0){ if(status == 0){
cm.sendNext("I don't know how you found out about this, but you came to the right place! For those that wandered around Nihal Desert and are getting homesick, I am offering a flight straight to Victorial Island, non-stop! Don't worry about the flying ship--it's only fallen once or twice! Don't you feel claustrophobic being in a long flight on that small ship?"); cm.sendNext("I don't know how you found out about this, but you came to the right place! For those that wandered around Nihal Desert and are getting homesick, I am offering a flight straight to Victorial Island, non-stop! Don't worry about the flying ship--it's only fallen once or twice! Don't you feel claustrophobic being in a long flight on that small ship?");
} else if(status == 1){ } else if(status == 1){
cm.sendAcceptDecline("Please remember two things. One, this line is actually for overseas shipping, so #rI cannot gurantee exactly which town you'll land#k. Two, since I am putting you in this special flight, it'll be a bit expensive. The service charge is #e#b10,000 mesos#n#k. There's a flight thats about to take off. Are you interested in this direct flight?"); cm.sendYesNo("Please remember two things. One, this line is actually for overseas shipping, so #rI cannot gurantee exactly which town you'll land#k. Two, since I am putting you in this special flight, it'll be a bit expensive. The service charge is #e#b10,000 mesos#n#k. There's a flight thats about to take off. Are you interested in this direct flight?");
} else if(status == 2){ } else if(status == 2){
cm.sendNext("Okay, ready to takeoff~"); cm.sendNext("Okay, ready to takeoff~");
} else if(status == 3){ } else if(status == 3){
+6
View File
@@ -51,6 +51,12 @@ function action(mode, type, selection) {
} }
if (status == 0) { if (status == 0) {
if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k.");
cm.dispose();
return;
}
var selStr = "Hey traveler! Come, come closer... We offer a #bhuge opportunity of business#k to you. If you want to know what it is, keep listening..."; var selStr = "Hey traveler! Come, come closer... We offer a #bhuge opportunity of business#k to you. If you want to know what it is, keep listening...";
cm.sendNext(selStr); cm.sendNext(selStr);
} }
+12 -6
View File
@@ -39,6 +39,12 @@ var equip;
var maxEqp = 0; var maxEqp = 0;
function start() { function start() {
if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k.");
cm.dispose();
return;
}
cm.getPlayer().setCS(true); cm.getPlayer().setCS(true);
var selStr = "Hello, I am the #bAccessory NPC Crafter#k! My works are widely recognized to be too fine, up to the point at which all my items mimic not only the appearance but too the attributes of them! Everything I charge is some 'ingredients' to make them and, of course, a fee for my services. On what kind of equipment are you interessed?#b"; var selStr = "Hello, I am the #bAccessory NPC Crafter#k! My works are widely recognized to be too fine, up to the point at which all my items mimic not only the appearance but too the attributes of them! Everything I charge is some 'ingredients' to make them and, of course, a fee for my services. On what kind of equipment are you interessed?#b";
var options = ["Pendants","Face accessories","Eye accessories","Belts & medals","Rings"/*,"#t4032496#"*/]; var options = ["Pendants","Face accessories","Eye accessories","Belts & medals","Rings"/*,"#t4032496#"*/];
@@ -56,7 +62,7 @@ function action(mode, type, selection) {
if (status == 0) { if (status == 0) {
if (selection == 0) { //pendants if (selection == 0) { //pendants
var selStr = "Well, I've got these pendants on my repertoire:#b"; var selStr = "Well, I've got these pendants on my repertoire:#b";
items = [1122018,1122007,1122001,1122002,1122003,1122004,1122005,1122006,1122058]; items = [1122018,1122007,1122001,1122003,1122004,1122006,1122002,1122005,1122058];
for (var i = 0; i < items.length; i++) for (var i = 0; i < items.length; i++)
selStr += "\r\n#L" + i + "##t" + items[i] + "##b"; selStr += "\r\n#L" + i + "##t" + items[i] + "##b";
}else if (selection == 1) { //face accessory }else if (selection == 1) { //face accessory
@@ -103,9 +109,9 @@ function action(mode, type, selection) {
if (selectedType != 3) selectedItem = selection; if (selectedType != 3) selectedItem = selection;
if (selectedType == 0) { //pendant refine if (selectedType == 0) { //pendant refine
var matSet = [[4003004, 4030012, 4001356, 4000026], [4000026, 4001356, 4000073, 4001006], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [1122007, 4003002, 4000413]]; var matSet = [[4003004, 4030012, 4001356, 4000026], [4000026, 4001356, 4000073, 4001006], [4001343, 4011002, 4003004, 4003005], [4001343, 4011006, 4003004, 4003005], [4000091, 4011005, 4003004, 4003005], [4000091, 4011001, 4003004, 4003005], [4000469, 4011000, 4003004, 4003005], [4000469, 4011004, 4003004, 4003005], [1122007, 4003002, 4000413]];
var matQtySet = [[20, 20, 5, 1], [5, 5, 10, 1], [10, 4, 20, 4], [20, 8, 20, 8], [10, 4, 20, 4], [15, 6, 30, 6], [20, 8, 40, 8], [15, 6, 30, 6], [1, 1, 1]]; var matQtySet = [[20, 20, 5, 1], [5, 5, 10, 1], [10, 2, 20, 4], [10, 1, 20, 4], [15, 3, 30, 6], [15, 3, 30, 6], [20, 5, 20, 8], [20, 4, 40, 8], [1, 1, 1]];
var costSet = [150000, 500000, 200000, 400000, 200000, 300000, 400000, 300000, 2500000]; var costSet = [150000, 500000, 200000, 200000, 300000, 300000, 400000, 400000, 2500000];
}else if (selectedType == 1) { //face accessory refine }else if (selectedType == 1) { //face accessory refine
var matSet = [[4006000, 4003004],[4006000, 4003004,4000026],[4006000, 4003004,4000026,4000082,4003002],[4006000, 4003005],[4006000, 4003005,4000026],[4006000, 4003005,4000026,4000082,4003002],[4001006, 4011008],[4001006, 4011008],[4001006, 4011008],[4001006, 4011008]]; var matSet = [[4006000, 4003004],[4006000, 4003004,4000026],[4006000, 4003004,4000026,4000082,4003002],[4006000, 4003005],[4006000, 4003005,4000026],[4006000, 4003005,4000026,4000082,4003002],[4001006, 4011008],[4001006, 4011008],[4001006, 4011008],[4001006, 4011008]];
var matQtySet = [[5,5],[5,5,5],[5,5,5,5,1],[5,5],[5,5,5],[5,5,5,5,1],[1,1],[1,1],[1,1],[1,1]]; var matQtySet = [[5,5],[5,5,5],[5,5,5,5,1],[5,5],[5,5,5],[5,5,5,5,1],[1,1],[1,1],[1,1],[1,1]];
@@ -145,9 +151,9 @@ function action(mode, type, selection) {
var prompt = "You want me to make "; var prompt = "You want me to make ";
if(selectedType != 3) { if(selectedType != 3) {
if (qty == 1) if (qty == 1)
prompt += "a #t" + item + "#?"; prompt += "a #b#t" + item + "##k?";
else else
prompt += qty + " #t" + item + "#?"; prompt += "#b" + qty + " #t" + item + "##k?";
} }
else prompt += "a #bbelt#k or a #bmedal#k?"; else prompt += "a #bbelt#k or a #bmedal#k?";
+59 -18
View File
@@ -19,31 +19,72 @@
*/ */
/* Dalair /* Dalair
Medal NPC. Medal NPC.
NPC Equipment Merger:
* @author Ronan Lana
*/ */
importPackage(Packages.client.processor);
importPackage(Packages.constants);
var status; var status;
var mergeFee = 50000;
var name;
function start() { function start() {
status = -1; status = -1;
action(1, 0, 0); action(1, 0, 0);
} }
function action(mode, type, selection) { function action(mode, type, selection) {
if (mode == -1) { if (mode == -1) {
cm.dispose(); cm.dispose();
} else { } else {
if (mode == 0 && type > 0) { if (mode == 0 && type > 0) {
cm.dispose(); cm.dispose();
return; return;
}
if (mode == 1)
status++;
else
status--;
if(status == 0) {
cm.sendOk("The medal ranking system is currently unavailable.");
cm.dispose();
}
} }
if (mode == 1)
status++;
else
status--;
if(status == 0) {
if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
cm.sendOk("The medal ranking system is currently unavailable...");
cm.dispose();
return;
}
var levelLimit = !cm.getPlayer().isCygnus() ? 160 : 110;
var selStr = "The medal ranking system is currently unavailable... Therefore, I am providing the #bEquipment Merge#k service! ";
if (!ServerConstants.USE_STARTER_MERGE && (cm.getPlayer().getLevel() < levelLimit || MakerProcessor.getMakerSkillLevel(cm.getPlayer()) < 3)) {
selStr += "However, you must have #rMaker level 3#k and at least #rlevel 110#k (Cygnus Knight), #rlevel 160#k (other classes) and a fund of #r" + cm.numberWithCommas(mergeFee) + " mesos#k to use the service.";
cm.sendOk(selStr);
cm.dispose();
} else if (cm.getMeso() < mergeFee) {
selStr += "I'm sorry, but this service tax is of #r" + cm.numberWithCommas(mergeFee) + " mesos#k, which it seems you unfortunately don't have right now... Please, stop by again later.";
cm.sendOk(selStr);
cm.dispose();
} else {
selStr += "For the fee of #r" + cm.numberWithCommas(mergeFee) + "#k mesos, merge unnecessary equipments in your inventory into your currently equipped gears to get stat boosts into them, statups based on the attributes of the items used on the merge!";
cm.sendNext(selStr);
}
} else if(status == 1) {
selStr = "#rWARNING#b: Make sure you have your items ready to merge at the slots #rAFTER#b the item you have selected to merge.#k Any items #bunder#k the item selected will be merged thoroughly.\r\n\r\nNote that equipments receiving bonuses from merge are going to become #rUntradeable#k thereon, and equipments that already received the merge bonus #rcannot be used for merge#k.\r\n\r\n";
cm.sendGetText(selStr);
} else if(status == 2) {
name = cm.getText();
if (cm.getPlayer().mergeAllItemsFromName(name)) {
cm.gainMeso(-mergeFee);
cm.sendOk("Merging complete! Thanks for using the service and enjoy your new equipment stats.");
} else {
cm.sendOk("There is no #b'" + name + "'#k in your #bEQUIP#k inventory!");
}
cm.dispose();
}
}
} }
+6
View File
@@ -42,6 +42,12 @@ function action(mode, type, selection) {
} }
if (status == 0) { if (status == 0) {
if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
cm.sendOk("The medal ranking system is currently unavailable...");
cm.dispose();
return;
}
var selStr = "Hello, I am the #bBazaar NPC#k! Sell to me any item on your inventory you don't need. #rWARNING#b: Make sure you have your items ready to sell at the slots #rAFTER#b the item you have selected to sell.#k Any items #bunder#k the item selected will be sold thoroughly."; var selStr = "Hello, I am the #bBazaar NPC#k! Sell to me any item on your inventory you don't need. #rWARNING#b: Make sure you have your items ready to sell at the slots #rAFTER#b the item you have selected to sell.#k Any items #bunder#k the item selected will be sold thoroughly.";
for (var i = 0; i < options.length; i++) for (var i = 0; i < options.length; i++)
selStr += "\r\n#L" + i + "# " + options[i] + "#l"; selStr += "\r\n#L" + i + "# " + options[i] + "#l";
+4 -13
View File
@@ -20,7 +20,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
var status = 0; var status = 0;
var goToMansion = false;
var fee = 15000; var fee = 15000;
function start() { function start() {
@@ -37,19 +36,11 @@ function action(mode, type, selection) {
else { else {
status++; status++;
if (cm.getPlayer().getMapId() == 682000000) { if (cm.getPlayer().getMapId() == 682000000) {
if (status == 0) if (status == 0) {
cm.sendSimple("Where to, boss? \r\n#b#L0#New Leaf City (" + fee + " mesos)#l\r\n#L1#Haunted Mansion#l#k");
else if (status == 1) {
if (selection == 0) if (selection == 0)
cm.sendYesNo("You want to go to New Leaf City?"); cm.sendYesNo("Would you like to return back to #bcivilization#k? The fee is " + fee + " mesos.");
else { } else if (status == 1) {
goToMansion = true; if(cm.getMeso() >= fee) {
cm.sendYesNo("You're sure you want to enter the Mansion?");
}
} else if (status == 2) {
if(goToMansion) {
cm.warp(682000100, 0);
} else if(cm.getMeso() >= fee) {
cm.gainMeso(-fee); cm.gainMeso(-fee);
cm.warp(600000000); cm.warp(600000000);
} else { } else {
+4
View File
@@ -98,6 +98,7 @@ function writeFeatureTab_CashItems() {
addFeature("Storage with 'Arrange Items' feature functional."); addFeature("Storage with 'Arrange Items' feature functional.");
addFeature("Close-quarters evaluation mode for items."); addFeature("Close-quarters evaluation mode for items.");
addFeature("Reviewed Karma scissors & Untradeable items."); addFeature("Reviewed Karma scissors & Untradeable items.");
addFeature("Reviewed an pet position issue within CASH inventory.");
addFeature("Scroll for Spikes on Shoes."); addFeature("Scroll for Spikes on Shoes.");
addFeature("Scroll for Cold Protection."); addFeature("Scroll for Cold Protection.");
addFeature("Vega's spell."); addFeature("Vega's spell.");
@@ -187,6 +188,7 @@ function writeFeatureTab_Serverpotentials() {
addFeature("Delete Character."); addFeature("Delete Character.");
addFeature("Smooth view-all-char, now showing all account chars."); addFeature("Smooth view-all-char, now showing all account chars.");
addFeature("Centralized servertime, boosting handler performance."); addFeature("Centralized servertime, boosting handler performance.");
addFeature("Centralized timestamping, unused rcvd timestamps.");
addFeature("Autosaver (periodically saves player's data on DB)."); addFeature("Autosaver (periodically saves player's data on DB).");
addFeature("Fixed and randomized HP/MP growth rate available."); addFeature("Fixed and randomized HP/MP growth rate available.");
addFeature("Players' MaxHP/MaxMP method accounting equip gain."); addFeature("Players' MaxHP/MaxMP method accounting equip gain.");
@@ -211,6 +213,7 @@ function writeFeatureTab_CustomNPCs() {
addFeature("Asia: scroll & rarities shop NPC."); addFeature("Asia: scroll & rarities shop NPC.");
addFeature("Abdula: lists droppers of needed skill/mastery books."); addFeature("Abdula: lists droppers of needed skill/mastery books.");
addFeature("Agent E: accessory crafter."); addFeature("Agent E: accessory crafter.");
addFeature("Dalair: automatized equipment-merger.");
addFeature("Donation Box: automatized item-buyer."); addFeature("Donation Box: automatized item-buyer.");
addFeature("Coco & Ace of Hearts: C. scroll crafters."); addFeature("Coco & Ace of Hearts: C. scroll crafters.");
} }
@@ -240,6 +243,7 @@ function writeFeatureTab_Project() {
addFeature("Protected many flaws with login management system."); addFeature("Protected many flaws with login management system.");
addFeature("Developed a robust anti-exploit login coordinator."); addFeature("Developed a robust anti-exploit login coordinator.");
addFeature("Usage of HikariCP to improve DB connection calls."); addFeature("Usage of HikariCP to improve DB connection calls.");
addFeature("Usage of Java Threadpool to improve runnable calls.");
addFeature("Developed many survey tools for content profiling."); addFeature("Developed many survey tools for content profiling.");
addFeature("ThreadTracker: runtime tool for deadlock detection."); addFeature("ThreadTracker: runtime tool for deadlock detection.");
addFeature("Channel, World and Server-wide timer management."); addFeature("Channel, World and Server-wide timer management.");
+1 -1
View File
@@ -33,7 +33,7 @@ function enter(pi) {
} }
if(pi.haveItem(4032101 + jobtype, 1)) { if(pi.haveItem(4032101 + jobtype, 1)) {
pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight.");
return false; return false;
} }
+1 -1
View File
@@ -33,7 +33,7 @@ function enter(pi) {
} }
if(pi.haveItem(4032101 + jobtype, 1)) { if(pi.haveItem(4032101 + jobtype, 1)) {
pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight.");
return false; return false;
} }
+1 -1
View File
@@ -33,7 +33,7 @@ function enter(pi) {
} }
if(pi.haveItem(4032101 + jobtype, 1)) { if(pi.haveItem(4032101 + jobtype, 1)) {
pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight.");
return false; return false;
} }
+1 -1
View File
@@ -33,7 +33,7 @@ function enter(pi) {
} }
if(pi.haveItem(4032101 + jobtype, 1)) { if(pi.haveItem(4032101 + jobtype, 1)) {
pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight.");
return false; return false;
} }
+1 -1
View File
@@ -33,7 +33,7 @@ function enter(pi) {
} }
if(pi.haveItem(4032101 + jobtype, 1)) { if(pi.haveItem(4032101 + jobtype, 1)) {
pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight.");
return false; return false;
} }
+32
View File
@@ -0,0 +1,32 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
function enter(pi) {
var exit = pi.getEventInstance().getIntProperty("canLeave");
if (exit == 0) {
pi.message("You have to wait one minute before you can leave this place.");
return false;
} else if (exit == 2) {
pi.warp(912010200);
return true;
} else {
pi.warp(120000101);
return true;
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
function enter(pi) {
var exit = pi.getEventInstance().getIntProperty("canLeave");
if (exit == 0) {
pi.message("You have to wait one minute before you can leave this place.");
return false;
} else if (exit == 2) {
pi.warp(912010200);
return true;
} else {
pi.warp(120000101);
return true;
}
}
+5 -2
View File
@@ -20,6 +20,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
importPackage(Packages.client); importPackage(Packages.client);
importPackage(Packages.constants);
status = -1; status = -1;
@@ -47,8 +48,10 @@ function start(mode, type, selection) {
qm.changeJobById(2100); qm.changeJobById(2100);
qm.resetStats(); qm.resetStats();
//qm.teachSkill(21000000, 0, 10, -1); //learned later... if (ServerConstants.USE_FULL_ARAN_SKILLSET) {
//qm.teachSkill(21001003, 0, 20, -1); //learned later... qm.teachSkill(21000000, 0, 10, -1); //combo ability
qm.teachSkill(21001003, 0, 20, -1); //polearm booster
}
qm.completeQuest(); qm.completeQuest();
+8
View File
@@ -20,6 +20,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
importPackage(Packages.client); importPackage(Packages.client);
importPackage(Packages.constants);
var status = -1; var status = -1;
@@ -66,6 +67,13 @@ function end(mode, type, selection) {
qm.gainItem(1142130, true); qm.gainItem(1142130, true);
qm.changeJobById(2110); qm.changeJobById(2110);
if (ServerConstants.USE_FULL_ARAN_SKILLSET) {
qm.teachSkill(21100000, 0, 20, -1); //polearm mastery
qm.teachSkill(21100002, 0, 30, -1); //final charge
qm.teachSkill(21100004, 0, 20, -1); //combo smash
qm.teachSkill(21100005, 0, 20, -1); //combo drain
}
qm.completeQuest(); qm.completeQuest();
} }
+5
View File
@@ -20,6 +20,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
importPackage(Packages.client); importPackage(Packages.client);
importPackage(Packages.constants);
var status = -1; var status = -1;
@@ -50,6 +51,10 @@ function end(mode, type, selection) {
qm.gainItem(1142131, true); qm.gainItem(1142131, true);
qm.changeJobById(2111); qm.changeJobById(2111);
if (ServerConstants.USE_FULL_ARAN_SKILLSET) {
qm.teachSkill(21110002, 0, 20, -1); //full swing
}
qm.completeQuest(); qm.completeQuest();
} }
+35 -20
View File
@@ -1,30 +1,45 @@
importPackage(Packages.tools); var status = -1;
function end(mode, type, selection) { function end(mode, type, selection) {
var rnd; if (mode == -1) {
if (mode != 1) {
qm.dispose(); qm.dispose();
} else { } else {
if(qm.haveItem(4031092, 10)) { if(mode == 0 && type > 0) {
if(qm.canHold(4031092)) { qm.dispose();
qm.sendOk("Well done! You brought back all the #t4031092# that were missing. Here, get this scroll as a token of my gratitude..."); return;
qm.gainItem(4031092, -10); }
rnd = Math.floor(Math.random() * 4); if (mode == 1)
if(rnd == 0) qm.gainItem(2040704, 1); status++;
else if(rnd == 1) qm.gainItem(2040705, 1); else
else if(rnd == 2) qm.gainItem(2040707, 1); status--;
else qm.gainItem(2040708, 1);
qm.gainExp(2700); if (status == 0) {
qm.forceCompleteQuest(); if(qm.haveItem(4031092, 10)) {
if(qm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.USE).getNumFreeSlot() >= 1) {
qm.sendOk("Well done! You brought back all the #t4031092# that were missing. Here, take this scroll as a token of my gratitude...");
} else {
qm.sendOk("Free a space on your USE inventory before receiving your prize.");
qm.dispose();
return;
} }
else { } else {
qm.sendOk("Free a space on your USE inventory before receiving your prize."); qm.sendOk("Please return me 10 #t4031092# that went missing on this room.");
} qm.dispose();
} return;
}
} else if (status == 1) {
qm.gainItem(4031092, -10);
qm.dispose(); rnd = Math.floor(Math.random() * 4);
if(rnd == 0) qm.gainItem(2040704, 1);
else if(rnd == 1) qm.gainItem(2040705, 1);
else if(rnd == 2) qm.gainItem(2040707, 1);
else qm.gainItem(2040708, 1);
qm.gainExp(2700);
qm.forceCompleteQuest();
qm.dispose();
}
} }
} }
+12 -3
View File
@@ -19295,7 +19295,7 @@ USE `heavenms`;
(4230116, 2382042, 1, 1, 0, 10000), (4230116, 2382042, 1, 1, 0, 10000),
(4230117, 2382055, 1, 1, 0, 10000), (4230117, 2382055, 1, 1, 0, 10000),
(4230118, 2382067, 1, 1, 0, 10000), (4230118, 2382067, 1, 1, 0, 10000),
(4240000, 2383011, 1, 1, 0, 10000), (4240000, 2383011, 1, 1, 0, 20000),
(1210102, 4001354, 1, 1, 28209, 80000), (1210102, 4001354, 1, 1, 28209, 80000),
(1110101, 4031773, 1, 1, 2145, 80000), (1110101, 4031773, 1, 1, 2145, 80000),
(1130100, 4031773, 1, 1, 2145, 80000), (1130100, 4031773, 1, 1, 2145, 80000),
@@ -20292,7 +20292,15 @@ USE `heavenms`;
(9400110, 4000092, 1, 1, 0, 400000), (9400110, 4000092, 1, 1, 0, 400000),
(9400111, 4000093, 1, 1, 0, 400000), (9400111, 4000093, 1, 1, 0, 400000),
(3210201, 4031524, 1, 1, 8848, 200000), (3210201, 4031524, 1, 1, 8848, 200000),
(3110101, 4031527, 1, 1, 8849, 400000); (3110101, 4031527, 1, 1, 8849, 400000),
(5100000, 2022001, 1, 1, 0, 20000),
(5100001, 2022001, 1, 1, 0, 20000),
(5130105, 2022001, 1, 1, 0, 20000),
(5130106, 2022001, 1, 1, 0, 20000),
(6130102, 2022001, 1, 1, 0, 20000),
(6130103, 2022001, 1, 1, 0, 20000),
(6130200, 2022001, 1, 1, 0, 20000),
(6130201, 2022001, 1, 1, 0, 20000);
# (dropperid, itemid, minqty, maxqty, questid, chance) # (dropperid, itemid, minqty, maxqty, questid, chance)
@@ -20478,7 +20486,8 @@ USE `heavenms`;
(8142100, 2040205, 1, 1, 0, 750), (8142100, 2040205, 1, 1, 0, 750),
(9400580, 2048010, 1, 1, 0, 750), (9400580, 2048010, 1, 1, 0, 750),
(5110302, 2048010, 1, 1, 0, 750), (5110302, 2048010, 1, 1, 0, 750),
(4230118, 4240000, 1, 1, 0, 750), (4230118, 2048011, 1, 1, 0, 750),
(4240000, 2048011, 1, 1, 0, 750),
(6400100, 2048011, 1, 1, 0, 750), (6400100, 2048011, 1, 1, 0, 750),
(5130107, 2048012, 1, 1, 0, 750), (5130107, 2048012, 1, 1, 0, 750),
(6230601, 2048012, 1, 1, 0, 750), (6230601, 2048012, 1, 1, 0, 750),
+305 -107
View File
@@ -72,6 +72,7 @@ import server.MapleStatEffect;
import server.MapleStorage; import server.MapleStorage;
import server.MapleTrade; import server.MapleTrade;
import server.TimerManager; import server.TimerManager;
import server.ThreadManager;
import server.events.MapleEvents; import server.events.MapleEvents;
import server.events.RescueGaga; import server.events.RescueGaga;
import server.events.gm.MapleFitness; import server.events.gm.MapleFitness;
@@ -109,6 +110,7 @@ import tools.packets.Wedding;
import client.autoban.AutobanManager; import client.autoban.AutobanManager;
import client.creator.CharacterFactoryRecipe; import client.creator.CharacterFactoryRecipe;
import client.inventory.Equip; import client.inventory.Equip;
import client.inventory.Equip.StatUpgrade;
import client.inventory.Item; import client.inventory.Item;
import client.inventory.ItemFactory; import client.inventory.ItemFactory;
import client.inventory.MapleInventory; import client.inventory.MapleInventory;
@@ -159,12 +161,10 @@ import server.life.MobSkillFactory;
import server.maps.MapleMapItem; import server.maps.MapleMapItem;
import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import server.movement.AbsoluteLifeMovement;
import server.movement.LifeMovementFragment;
public class MapleCharacter extends AbstractMapleCharacterObject { public class MapleCharacter extends AbstractMapleCharacterObject {
private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
private static final String LEVEL_200 = "[Congrats] %s has reached Level 200! Congratulate %s on such an amazing achievement!"; private static final String LEVEL_200 = "[Congrats] %s has reached Level %d! Congratulate %s on such an amazing achievement!";
private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay", private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay",
"nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm", "nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm",
"operate", "master", "sysop", "party", "GameMaster", "community", "message", "event", "test", "meso", "Scania", "yata", "AsiaSoft", "henesys"}; "operate", "master", "sysop", "party", "GameMaster", "community", "message", "event", "test", "meso", "Scania", "yata", "AsiaSoft", "henesys"};
@@ -853,10 +853,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
List<Pair<MapleBuffStat, Integer>> ldsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.DARKSIGHT, 0)); List<Pair<MapleBuffStat, Integer>> ldsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.DARKSIGHT, 0));
getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(id, ldsstat), false); getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(id, ldsstat), false);
for (MapleMonster mon : this.getControlledMonsters()) { for (MapleMonster mon : this.getControlledMonsters()) {
mon.setController(null); mon.lockMonster();
mon.setControllerHasAggro(false); try {
mon.setControllerKnowsAboutAggro(false); mon.setController(null);
mon.getMap().updateMonsterController(mon); mon.setControllerHasAggro(false);
mon.setControllerKnowsAboutAggro(false);
mon.getMap().updateMonsterController(mon);
} finally {
mon.unlockMonster();
}
} }
} }
announce(MaplePacketCreator.enableActions()); announce(MaplePacketCreator.enableActions());
@@ -1178,11 +1183,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
public void broadcastStance() { public void broadcastStance() {
AbsoluteLifeMovement alm = new AbsoluteLifeMovement(0, getPosition(), 0, getStance()); map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, this.getIdleMovement()), false);
alm.setPixelsPerSecond(new Point(0, 0));
List<LifeMovementFragment> moveUpdate = Collections.singletonList((LifeMovementFragment) alm);
map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, moveUpdate), false);
} }
public MapleMap getWarpMap(int map) { public MapleMap getWarpMap(int map) {
@@ -1592,11 +1593,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
map.addPlayer(this); map.addPlayer(this);
visitMap(map); visitMap(map);
silentPartyUpdateInternal(getParty()); // EIM script calls inside
prtLock.lock(); prtLock.lock();
try { try {
if (party != null) { if (party != null) {
mpc.setMapId(to.getId()); mpc.setMapId(to.getId());
silentPartyUpdateInternal();
client.announce(MaplePacketCreator.updateParty(client.getChannel(), party, PartyOperation.SILENT_UPDATE, null)); client.announce(MaplePacketCreator.updateParty(client.getChannel(), party, PartyOperation.SILENT_UPDATE, null));
updatePartyMemberHPInternal(); updatePartyMemberHPInternal();
} }
@@ -1605,8 +1606,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
if (getMap().getHPDec() > 0) resetHpDecreaseTask(); if (getMap().getHPDec() > 0) resetHpDecreaseTask();
} } else {
else {
FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + "."); FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + ".");
} }
@@ -1708,19 +1708,29 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
public void checkMonsterAggro(MapleMonster monster) { public void checkMonsterAggro(MapleMonster monster) {
if (!monster.isControllerHasAggro()) { monster.lockMonster();
if (monster.getController() == this) { try {
monster.setControllerHasAggro(true); if (!monster.isControllerHasAggro()) {
} else { if (monster.getController() == this) {
monster.switchController(this, true); monster.setControllerHasAggro(true);
} else {
monster.switchController(this, true);
}
} }
} finally {
monster.unlockMonster();
} }
} }
public void controlMonster(MapleMonster monster, boolean aggro) { public void controlMonster(MapleMonster monster, boolean aggro) {
monster.setController(this); monster.lockMonster();
controlled.add(monster); try {
client.announce(MaplePacketCreator.controlMonster(monster, false, aggro)); monster.setController(this);
controlled.add(monster);
client.announce(MaplePacketCreator.controlMonster(monster, false, aggro));
} finally {
monster.unlockMonster();
}
} }
private boolean useItem(final int id) { private boolean useItem(final int id) {
@@ -1768,7 +1778,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (ob instanceof MapleMapItem) { if (ob instanceof MapleMapItem) {
MapleMapItem mapitem = (MapleMapItem) ob; MapleMapItem mapitem = (MapleMapItem) ob;
if (System.currentTimeMillis() - mapitem.getDropTime() < 900 || !mapitem.canBePickedBy(this)) { if (System.currentTimeMillis() - mapitem.getDropTime() < 400 || !mapitem.canBePickedBy(this)) {
client.announce(MaplePacketCreator.enableActions()); client.announce(MaplePacketCreator.enableActions());
return; return;
} }
@@ -1778,6 +1788,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
mpcs = getPartyMembersOnSameMap(); mpcs = getPartyMembersOnSameMap();
} }
String itemScriptName = null;
mapitem.lockItem(); mapitem.lockItem();
try { try {
if (mapitem.isPickedUp()) { if (mapitem.isPickedUp()) {
@@ -1852,12 +1863,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} else if (mItem.getItemId() / 10000 == 243) { } else if (mItem.getItemId() / 10000 == 243) {
MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mItem.getItemId()); MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mItem.getItemId());
if (info.runOnPickup()) { if (info.runOnPickup()) {
ItemScriptManager ism = ItemScriptManager.getInstance(); itemScriptName = info.getScript();
String scriptName = info.getScript();
if (ism.scriptExists(scriptName)) {
ism.getItemScript(client, scriptName);
}
} else { } else {
if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) { if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) {
client.announce(MaplePacketCreator.enableActions()); client.announce(MaplePacketCreator.enableActions());
@@ -1890,6 +1896,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} finally { } finally {
mapitem.unlockItem(); mapitem.unlockItem();
} }
if (itemScriptName != null) {
ItemScriptManager ism = ItemScriptManager.getInstance();
if (ism.scriptExists(itemScriptName)) {
ism.runItemScript(client, itemScriptName);
}
}
} }
client.announce(MaplePacketCreator.enableActions()); client.announce(MaplePacketCreator.enableActions());
} }
@@ -2913,16 +2926,21 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
} }
private synchronized int applyFame(int delta) { private Pair<Integer, Integer> applyFame(int delta) {
int newFame = fame + delta; petLock.lock();
if (newFame < -30000) { try {
delta = -(30000 + fame); int newFame = fame + delta;
} else if (newFame > 30000) { if (newFame < -30000) {
delta = 30000 - fame; delta = -(30000 + fame);
} } else if (newFame > 30000) {
delta = 30000 - fame;
}
fame += delta; fame += delta;
return delta; return new Pair<>(fame, delta);
} finally {
petLock.unlock();
}
} }
public void gainFame(int delta) { public void gainFame(int delta) {
@@ -2930,9 +2948,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
public boolean gainFame(int delta, MapleCharacter fromPlayer, int mode) { public boolean gainFame(int delta, MapleCharacter fromPlayer, int mode) {
delta = applyFame(delta); Pair<Integer, Integer> fameRes = applyFame(delta);
delta = fameRes.getRight();
if (delta != 0) { if (delta != 0) {
int thisFame = fame; int thisFame = fameRes.getLeft();
updateSingleStat(MapleStat.FAME, thisFame); updateSingleStat(MapleStat.FAME, thisFame);
if (fromPlayer != null) { if (fromPlayer != null) {
@@ -3571,10 +3590,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
for (MapleCharacter chr : townChars) { for (MapleCharacter chr : townChars) {
destroyDoor.getTownDoor().sendDestroyData(chr.getClient()); destroyDoor.getTownDoor().sendDestroyData(chr.getClient());
} }
if(destroyDoor.getTownPortal().getId() == 0x80) { if (destroyDoor.getTownPortal().getId() == 0x80) {
for (MapleCharacter chr : townChars) { for (MapleCharacter chr : townChars) {
MapleDoor door = chr.getMainTownDoor(); MapleDoor door = chr.getMainTownDoor();
if(door != null) { if (door != null) {
destroyDoor.getTownDoor().sendSpawnData(chr.getClient()); destroyDoor.getTownDoor().sendSpawnData(chr.getClient());
} }
} }
@@ -4180,29 +4199,33 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
public void applyPartyDoor(MapleDoor door, boolean partyUpdate) { public void applyPartyDoor(MapleDoor door, boolean partyUpdate) {
MapleParty chrParty;
prtLock.lock(); prtLock.lock();
try { try {
if (!partyUpdate) { if (!partyUpdate) {
pdoor = door; pdoor = door;
} }
if (party != null) { chrParty = getParty();
party.addDoor(id, door); if (chrParty != null) {
silentPartyUpdateInternal(); chrParty.addDoor(id, door);
} }
} finally { } finally {
prtLock.unlock(); prtLock.unlock();
} }
silentPartyUpdateInternal(chrParty);
} }
private MapleDoor removePartyDoor(boolean partyUpdate) { private MapleDoor removePartyDoor(boolean partyUpdate) {
MapleDoor ret = null; MapleDoor ret = null;
MapleParty chrParty;
prtLock.lock(); prtLock.lock();
try { try {
if (party != null) { chrParty = getParty();
party.removeDoor(id); if (chrParty != null) {
silentPartyUpdateInternal(); chrParty.removeDoor(id);
} }
if (!partyUpdate) { if (!partyUpdate) {
@@ -4213,6 +4236,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
prtLock.unlock(); prtLock.unlock();
} }
silentPartyUpdateInternal(chrParty);
return ret; return ret;
} }
@@ -5796,18 +5820,16 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (level == maxClassLevel) { if (level == maxClassLevel) {
if (!this.isGM()) { if (!this.isGM()) {
if (ServerConstants.PLAYERNPC_AUTODEPLOY) { if (ServerConstants.PLAYERNPC_AUTODEPLOY) {
Thread t = new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), MapleCharacter.this); MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), MapleCharacter.this);
} }
}); });
t.start();
} }
final String names = (getMedalText() + name); final String names = (getMedalText() + name);
getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, names))); getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, maxClassLevel, names)));
} }
} }
@@ -5874,22 +5896,27 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
MapleParty party;
boolean partyLeader;
prtLock.lock(); prtLock.lock();
try { try {
if (party != null) { party = getParty();
if(isPartyLeader()) party.assignNewLeader(client); partyLeader = party != null && isPartyLeader();
PartyOperationHandler.leaveParty(party, mpc, client);
showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k.");
}
} finally { } finally {
prtLock.unlock(); prtLock.unlock();
} }
if (party != null) {
if(partyLeader) party.assignNewLeader(client);
PartyOperationHandler.leaveParty(party, mpc, client);
showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k.");
}
} }
}; };
Thread t = new Thread(r); ThreadManager.getInstance().newTask(r);
t.start();
} }
levelUpMessages(); levelUpMessages();
@@ -7567,8 +7594,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
}; };
Thread t = new Thread(r); //spawns a new thread to deal with this ThreadManager.getInstance().newTask(r); //spawns a new thread to deal with this
t.start();
} else { } else {
saveCharToDB(true); saveCharToDB(true);
} }
@@ -8439,23 +8465,34 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public int sellAllItemsFromName(byte invTypeId, String name) { public int sellAllItemsFromName(byte invTypeId, String name) {
//player decides from which inventory items should be sold. //player decides from which inventory items should be sold.
MapleInventoryType type = MapleInventoryType.getByType(invTypeId); MapleInventoryType type = MapleInventoryType.getByType(invTypeId);
Item it = getInventory(type).findByName(name); MapleInventory inv = getInventory(type);
if(it == null) { inv.lockInventory();
return(-1); try {
} Item it = inv.findByName(name);
if(it == null) {
return(-1);
}
return(sellAllItemsFromPosition(ii, type, it.getPosition())); return(sellAllItemsFromPosition(ii, type, it.getPosition()));
} finally {
inv.unlockInventory();
}
} }
public int sellAllItemsFromPosition(MapleItemInformationProvider ii, MapleInventoryType type, short pos) { public int sellAllItemsFromPosition(MapleItemInformationProvider ii, MapleInventoryType type, short pos) {
int mesoGain = 0; int mesoGain = 0;
for(short i = pos; i <= getInventory(type).getSlotLimit(); i++) { MapleInventory inv = getInventory(type);
if(getInventory(type).getItem(i) == null) continue; inv.lockInventory();
mesoGain += standaloneSell(getClient(), ii, type, i, getInventory(type).getItem(i).getQuantity()); try {
for(short i = pos; i <= inv.getSlotLimit(); i++) {
if(inv.getItem(i) == null) continue;
mesoGain += standaloneSell(getClient(), ii, type, i, inv.getItem(i).getQuantity());
}
} finally {
inv.unlockInventory();
} }
return(mesoGain); return(mesoGain);
@@ -8465,36 +8502,199 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (quantity == 0xFFFF || quantity == 0) { if (quantity == 0xFFFF || quantity == 0) {
quantity = 1; quantity = 1;
} }
Item item = getInventory(type).getItem((short) slot);
if (item == null){ //Basic check
return(0);
}
int itemid = item.getItemId(); MapleInventory inv = getInventory(type);
if (ItemConstants.isRechargeable(itemid)) { inv.lockInventory();
quantity = item.getQuantity(); try {
} else if (ItemConstants.isWeddingToken(itemid) || ItemConstants.isWeddingRing(itemid)) { Item item = inv.getItem((short) slot);
return(0); if (item == null){ //Basic check
} return(0);
}
if (quantity < 0) { int itemid = item.getItemId();
return(0); if (ItemConstants.isRechargeable(itemid)) {
} quantity = item.getQuantity();
short iQuant = item.getQuantity(); } else if (ItemConstants.isWeddingToken(itemid) || ItemConstants.isWeddingRing(itemid)) {
if (iQuant == 0xFFFF) { return(0);
iQuant = 1; }
}
if (quantity <= iQuant && iQuant > 0) { if (quantity < 0) {
MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false); return(0);
int recvMesos = ii.getPrice(itemid, quantity); }
if (recvMesos > 0) { short iQuant = item.getQuantity();
gainMeso(recvMesos, false); if (iQuant == 0xFFFF) {
return(recvMesos); iQuant = 1;
}
if (quantity <= iQuant && iQuant > 0) {
MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
int recvMesos = ii.getPrice(itemid, quantity);
if (recvMesos > 0) {
gainMeso(recvMesos, false);
return(recvMesos);
}
}
return(0);
} finally {
inv.unlockInventory();
}
}
private static boolean hasMergeFlag(Item item) {
return (item.getFlag() & ItemConstants.MERGE_UNTRADEABLE) == ItemConstants.MERGE_UNTRADEABLE;
}
private static void setMergeFlag(Item item) {
int flag = item.getFlag();
flag |= ItemConstants.MERGE_UNTRADEABLE;
flag |= ItemConstants.UNTRADEABLE;
item.setFlag((byte) flag);
}
private List<Equip> getUpgradeableEquipped() {
List<Equip> list = new LinkedList<>();
for (Item item : getInventory(MapleInventoryType.EQUIPPED)) {
if (ii.isUpgradeable(item.getItemId())) {
list.add((Equip) item);
} }
} }
return(0); return list;
}
private static List<Equip> getEquipsWithStat(List<Pair<Equip, Map<StatUpgrade, Short>>> equipped, StatUpgrade stat) {
List<Equip> equippedWithStat = new LinkedList<>();
for (Pair<Equip, Map<StatUpgrade, Short>> eq : equipped) {
if (eq.getRight().containsKey(stat)) {
equippedWithStat.add(eq.getLeft());
}
}
return equippedWithStat;
}
public boolean mergeAllItemsFromName(String name) {
MapleInventoryType type = MapleInventoryType.EQUIP;
MapleInventory inv = getInventory(type);
inv.lockInventory();
try {
Item it = inv.findByName(name);
if(it == null) {
return false;
}
Map<StatUpgrade, Float> statups = new LinkedHashMap<>();
mergeAllItemsFromPosition(statups, it.getPosition());
List<Pair<Equip, Map<StatUpgrade, Short>>> upgradeableEquipped = new LinkedList<>();
Map<Equip, List<Pair<StatUpgrade, Integer>>> equipUpgrades = new LinkedHashMap<>();
for (Equip eq : getUpgradeableEquipped()) {
upgradeableEquipped.add(new Pair<>(eq, eq.getStats()));
equipUpgrades.put(eq, new LinkedList<Pair<StatUpgrade, Integer>>());
}
/*
for (Entry<StatUpgrade, Float> e : statups.entrySet()) {
System.out.println(e);
}
*/
for (Entry<StatUpgrade, Float> e : statups.entrySet()) {
Double ev = Math.sqrt(e.getValue());
Set<Equip> extraEquipped = new LinkedHashSet<>(equipUpgrades.keySet());
List<Equip> statEquipped = getEquipsWithStat(upgradeableEquipped, e.getKey());
float extraRate = (float)(0.2 * Math.random());
if (!statEquipped.isEmpty()) {
float statRate = 1.0f - extraRate;
int statup = (int) Math.ceil((ev * statRate) / statEquipped.size());
for (Equip statEq : statEquipped) {
equipUpgrades.get(statEq).add(new Pair<>(e.getKey(), statup));
extraEquipped.remove(statEq);
}
}
if (!extraEquipped.isEmpty()) {
int statup = (int) Math.round((ev * extraRate) / extraEquipped.size());
if (statup > 0) {
for (Equip extraEq : extraEquipped) {
equipUpgrades.get(extraEq).add(new Pair<>(e.getKey(), statup));
}
}
}
}
dropMessage(6, "EQUIPMENT MERGE operation results:");
for (Entry<Equip, List<Pair<StatUpgrade, Integer>>> eqpUpg : equipUpgrades.entrySet()) {
List<Pair<StatUpgrade, Integer>> eqpStatups = eqpUpg.getValue();
if (!eqpStatups.isEmpty()) {
Equip eqp = eqpUpg.getKey();
setMergeFlag(eqp);
String showStr = " '" + MapleItemInformationProvider.getInstance().getName(eqp.getItemId()) + "': ";
String upgdStr = eqp.gainStats(eqpStatups).getLeft();
this.forceUpdateItem(eqp);
showStr += upgdStr;
dropMessage(6, showStr);
}
}
return true;
} finally {
inv.unlockInventory();
}
}
public void mergeAllItemsFromPosition(Map<StatUpgrade, Float> statups, short pos) {
MapleInventory inv = getInventory(MapleInventoryType.EQUIP);
inv.lockInventory();
try {
for(short i = pos; i <= inv.getSlotLimit(); i++) {
standaloneMerge(statups, getClient(), MapleInventoryType.EQUIP, i, inv.getItem(i));
}
} finally {
inv.unlockInventory();
}
}
private void standaloneMerge(Map<StatUpgrade, Float> statups, MapleClient c, MapleInventoryType type, short slot, Item item) {
short quantity;
if (item == null || (quantity = item.getQuantity()) < 1 || ii.isCash(item.getItemId()) || !ii.isUpgradeable(item.getItemId()) || hasMergeFlag(item)){
return;
}
Equip e = (Equip) item;
for (Entry<StatUpgrade, Short> s : e.getStats().entrySet()) {
Float newVal = statups.get(s.getKey());
float incVal = s.getValue().floatValue();
switch (s.getKey()) {
case incPAD:
case incMAD:
case incPDD:
case incMDD:
incVal = (float) Math.log(incVal);
break;
}
if (newVal != null) {
newVal += incVal;
} else {
newVal = incVal;
}
statups.put(s.getKey(), newVal);
}
MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
} }
public void setShop(MapleShop shop) { public void setShop(MapleShop shop) {
@@ -8599,17 +8799,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
public void silentPartyUpdate() { public void silentPartyUpdate() {
prtLock.lock(); silentPartyUpdateInternal(getParty());
try {
silentPartyUpdateInternal();
} finally {
prtLock.unlock();
}
} }
private void silentPartyUpdateInternal() { private void silentPartyUpdateInternal(MapleParty chrParty) {
if (party != null) { if (chrParty != null) {
getWorldServer().updateParty(party.getId(), PartyOperation.SILENT_UPDATE, getMPC()); getWorldServer().updateParty(chrParty.getId(), PartyOperation.SILENT_UPDATE, getMPC());
} }
} }
@@ -8662,7 +8857,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} }
} }
public void runTirednessSchedule() { public boolean runTirednessSchedule() {
if(maplemount != null) { if(maplemount != null) {
int tiredness = maplemount.incrementAndGetTiredness(); int tiredness = maplemount.incrementAndGetTiredness();
@@ -8671,8 +8866,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
maplemount.setTiredness(99); maplemount.setTiredness(99);
this.dispelSkill(this.getJobType() * 10000000 + 1004); this.dispelSkill(this.getJobType() * 10000000 + 1004);
this.dropMessage(6, "Your mount grew tired! Treat it some revitalizer before riding it again!"); this.dropMessage(6, "Your mount grew tired! Treat it some revitalizer before riding it again!");
return false;
} }
} }
return true;
} }
public void startMapEffect(String msg, int itemId) { public void startMapEffect(String msg, int itemId) {
@@ -8956,7 +9154,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (!this.isHidden() || client.getPlayer().gmLevel() > 1) { if (!this.isHidden() || client.getPlayer().gmLevel() > 1) {
client.announce(MaplePacketCreator.spawnPlayerMapObject(client, this, false)); client.announce(MaplePacketCreator.spawnPlayerMapObject(client, this, false));
if(hasBuffFromSourceid(getJobMapChair(job))) { if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock this function
client.announce(MaplePacketCreator.giveForeignChairSkillEffect(id)); client.announce(MaplePacketCreator.giveForeignChairSkillEffect(id));
} }
} }
+26 -3
View File
@@ -74,6 +74,7 @@ import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager; import scripting.quest.QuestScriptManager;
import server.life.MapleMonster; import server.life.MapleMonster;
import server.MapleTrade; import server.MapleTrade;
import server.ThreadManager;
import server.maps.*; import server.maps.*;
import server.quest.MapleQuest; import server.quest.MapleQuest;
@@ -159,7 +160,7 @@ public class MapleClient {
} }
public void sendCharList(int server) { public void sendCharList(int server) {
this.announce(MaplePacketCreator.getCharList(this, server)); this.announce(MaplePacketCreator.getCharList(this, server, 0));
} }
public List<MapleCharacter> loadCharacters(int serverId) { public List<MapleCharacter> loadCharacters(int serverId) {
@@ -878,11 +879,33 @@ public class MapleClient {
} }
} }
public final synchronized void disconnect(boolean shutdown, boolean cashshop) {//once per MapleClient instance public final void disconnect(final boolean shutdown, final boolean cashshop) {
if (isDisconnecting()) {
ThreadManager.getInstance().newTask(new Runnable() {
@Override
public void run() {
disconnectInternal(shutdown, cashshop);
}
});
}
}
public final void forceDisconnect() {
if (isDisconnecting()) {
disconnectInternal(true, false);
}
}
private synchronized boolean isDisconnecting() {
if (disconnecting) { if (disconnecting) {
return; return false;
} }
disconnecting = true; disconnecting = true;
return true;
}
private void disconnectInternal(boolean shutdown, boolean cashshop) {//once per MapleClient instance
if (player != null && player.isLoggedin() && player.getClient() != null) { if (player != null && player.isLoggedin() && player.getClient() != null) {
final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId(); final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId();
//final int fid = player.getFamilyId(); //final int fid = player.getFamilyId();
+6 -1
View File
@@ -6,6 +6,7 @@
package client.autoban; package client.autoban;
import client.MapleCharacter; import client.MapleCharacter;
import constants.ServerConstants;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import net.server.Server; import net.server.Server;
@@ -109,11 +110,15 @@ public class AutobanManager {
if (this.timestamp[type] == time) { if (this.timestamp[type] == time) {
this.timestampcounter[type]++; this.timestampcounter[type]++;
if (this.timestampcounter[type] >= times) { if (this.timestampcounter[type] >= times) {
chr.getClient().disconnect(false, false); if (ServerConstants.USE_AUTOBAN) {
chr.getClient().disconnect(false, false);
}
FilePrinter.print(FilePrinter.EXPLOITS, "Player " + chr + " was caught spamming TYPE " + type + " and has been disconnected."); FilePrinter.print(FilePrinter.EXPLOITS, "Player " + chr + " was caught spamming TYPE " + type + " and has been disconnected.");
} }
return; return;
} }
this.timestamp[type] = time; this.timestamp[type] = time;
this.timestampcounter[type] = 0;
} }
} }
@@ -40,7 +40,7 @@ public class TimeCommand extends Command {
@Override @Override
public void execute(MapleClient client, String[] params) { public void execute(MapleClient client, String[] params) {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
client.getPlayer().yellowMessage("HeavenMS Server Time: " + dateFormat.format(new Date())); client.getPlayer().yellowMessage("HeavenMS Server Time: " + dateFormat.format(new Date()));
} }
} }
@@ -43,7 +43,9 @@ public class SummonCommand extends Command {
} }
MapleCharacter victim = c.getChannelServer().getPlayerStorage().getCharacterByName(params[0]); MapleCharacter victim = c.getChannelServer().getPlayerStorage().getCharacterByName(params[0]);
if (victim == null) {//If victim isn't on current channel, loop all channels on current world. if (victim == null) {
//If victim isn't on current channel, loop all channels on current world.
for (Channel ch : Server.getInstance().getChannelsFromWorld(c.getWorld())) { for (Channel ch : Server.getInstance().getChannelsFromWorld(c.getWorld())) {
victim = ch.getPlayerStorage().getCharacterByName(params[0]); victim = ch.getPlayerStorage().getCharacterByName(params[0]);
if (victim != null) { if (victim != null) {
@@ -61,9 +63,10 @@ public class SummonCommand extends Command {
victim.getEventInstance().unregisterPlayer(victim); victim.getEventInstance().unregisterPlayer(victim);
} }
} }
//Attempt to join the warpers instance. //Attempt to join the warpers instance.
if (player.getEventInstance() != null && changingEvent) { if (player.getEventInstance() != null && changingEvent) {
if (player.getClient().getChannel() == victim.getClient().getChannel()) {//just in case.. you never know... if (player.getClient().getChannel() == victim.getClient().getChannel()) {
player.getEventInstance().registerPlayer(victim); player.getEventInstance().registerPlayer(victim);
victim.changeMap(player.getEventInstance().getMapInstance(player.getMapId()), player.getMap().findClosestPortal(player.getPosition())); victim.changeMap(player.getEventInstance().getMapInstance(player.getMapId()), player.getMap().findClosestPortal(player.getPosition()));
} else { } else {
@@ -71,7 +71,8 @@ public class DebugCommand extends Command {
List<MapleMapObject> monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER)); List<MapleMapObject> monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
for (MapleMapObject monstermo : monsters) { for (MapleMapObject monstermo : monsters) {
MapleMonster monster = (MapleMonster) monstermo; MapleMonster monster = (MapleMonster) monstermo;
player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((monster.getController() != null) ? monster.getController().getName() : "<none>")); MapleCharacter controller = monster.getController();
player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((controller != null) ? controller.getName() : "<none>"));
} }
break; break;
@@ -27,6 +27,7 @@ import client.MapleCharacter;
import client.command.Command; import client.command.Command;
import client.MapleClient; import client.MapleClient;
import net.server.Server; import net.server.Server;
import server.ThreadManager;
public class ServerAddChannelCommand extends Command { public class ServerAddChannelCommand extends Command {
{ {
@@ -44,7 +45,7 @@ public class ServerAddChannelCommand extends Command {
final int worldid = Integer.parseInt(params[0]); final int worldid = Integer.parseInt(params[0]);
new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
int chid = Server.getInstance().addChannel(worldid); int chid = Server.getInstance().addChannel(worldid);
@@ -64,6 +65,6 @@ public class ServerAddChannelCommand extends Command {
} }
} }
} }
}).start(); });
} }
} }
@@ -27,6 +27,7 @@ import client.MapleCharacter;
import client.command.Command; import client.command.Command;
import client.MapleClient; import client.MapleClient;
import net.server.Server; import net.server.Server;
import server.ThreadManager;
public class ServerAddWorldCommand extends Command { public class ServerAddWorldCommand extends Command {
{ {
@@ -37,7 +38,7 @@ public class ServerAddWorldCommand extends Command {
public void execute(MapleClient c, String[] params) { public void execute(MapleClient c, String[] params) {
final MapleCharacter player = c.getPlayer(); final MapleCharacter player = c.getPlayer();
new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
int wid = Server.getInstance().addWorld(); int wid = Server.getInstance().addWorld();
@@ -54,6 +55,6 @@ public class ServerAddWorldCommand extends Command {
} }
} }
} }
}).start(); });
} }
} }
@@ -27,6 +27,7 @@ import client.MapleCharacter;
import client.command.Command; import client.command.Command;
import client.MapleClient; import client.MapleClient;
import net.server.Server; import net.server.Server;
import server.ThreadManager;
public class ServerRemoveChannelCommand extends Command { public class ServerRemoveChannelCommand extends Command {
{ {
@@ -43,7 +44,7 @@ public class ServerRemoveChannelCommand extends Command {
} }
final int worldId = Integer.parseInt(params[0]); final int worldId = Integer.parseInt(params[0]);
new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
if(Server.getInstance().removeChannel(worldId)) { if(Server.getInstance().removeChannel(worldId)) {
@@ -56,6 +57,6 @@ public class ServerRemoveChannelCommand extends Command {
} }
} }
} }
}).start(); });
} }
} }
@@ -27,6 +27,7 @@ import client.MapleCharacter;
import client.command.Command; import client.command.Command;
import client.MapleClient; import client.MapleClient;
import net.server.Server; import net.server.Server;
import server.ThreadManager;
public class ServerRemoveWorldCommand extends Command { public class ServerRemoveWorldCommand extends Command {
{ {
@@ -43,7 +44,7 @@ public class ServerRemoveWorldCommand extends Command {
return; return;
} }
new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
if(Server.getInstance().removeWorld()) { if(Server.getInstance().removeWorld()) {
@@ -60,6 +61,6 @@ public class ServerRemoveWorldCommand extends Command {
} }
} }
} }
}).start(); });
} }
} }
+73 -41
View File
@@ -24,8 +24,10 @@ package client.inventory;
import client.MapleClient; import client.MapleClient;
import constants.ServerConstants; import constants.ServerConstants;
import constants.ExpTable; import constants.ExpTable;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import server.MapleItemInformationProvider; import server.MapleItemInformationProvider;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.Pair; import tools.Pair;
@@ -47,7 +49,7 @@ public class Equip extends Item {
} }
} }
private static enum StatUpgrade { public static enum StatUpgrade {
incDEX(0), incSTR(1), incINT(2), incLUK(3), incDEX(0), incSTR(1), incINT(2), incLUK(3),
incMHP(4), incMMP(5), incPAD(6), incMAD(7), incMHP(4), incMMP(5), incPAD(6), incMAD(7),
@@ -332,49 +334,30 @@ public class Equip extends Item {
if(jump > 0) getUnitStatUpgrade(stats, StatUpgrade.incJump, jump, false); if(jump > 0) getUnitStatUpgrade(stats, StatUpgrade.incJump, jump, false);
} }
private void gainLevel(MapleClient c) { public Map<StatUpgrade, Short> getStats() {
List<Pair<StatUpgrade, Integer>> stats = new LinkedList<>(); Map<StatUpgrade, Short> stats = new HashMap<>(5);
if(isElemental) { if(dex > 0) stats.put(StatUpgrade.incDEX, dex);
List<Pair<String, Integer>> elementalStats = MapleItemInformationProvider.getInstance().getItemLevelupStats(getItemId(), itemLevel); if(str > 0) stats.put(StatUpgrade.incSTR, str);
if(_int > 0) stats.put(StatUpgrade.incINT,_int);
if(luk > 0) stats.put(StatUpgrade.incLUK, luk);
if(hp > 0) stats.put(StatUpgrade.incMHP, hp);
if(mp > 0) stats.put(StatUpgrade.incMMP, mp);
if(watk > 0) stats.put(StatUpgrade.incPAD, watk);
if(matk > 0) stats.put(StatUpgrade.incMAD, matk);
if(wdef > 0) stats.put(StatUpgrade.incPDD, wdef);
if(mdef > 0) stats.put(StatUpgrade.incMDD, mdef);
if(avoid > 0) stats.put(StatUpgrade.incEVA, avoid);
if(acc > 0) stats.put(StatUpgrade.incACC, acc);
if(speed > 0) stats.put(StatUpgrade.incSpeed, speed);
if(jump > 0) stats.put(StatUpgrade.incJump, jump);
for(Pair<String, Integer> p: elementalStats) { return stats;
if(p.getRight() > 0) stats.add(new Pair<>(StatUpgrade.valueOf(p.getLeft()), p.getRight())); }
}
}
if(!stats.isEmpty()) {
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
}
else {
isUpgradeable = false;
improveDefaultStats(stats);
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
if(isUpgradeable) {
while(stats.isEmpty()) {
improveDefaultStats(stats);
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
}
}
}
itemLevel++;
boolean gotVicious = false, gotSlot = false;
String lvupStr = "'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "' is now level " + itemLevel + "! ";
String showStr = "#e'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "'#b is now #elevel #r" + itemLevel + "#k#b!";
public Pair<String, Pair<Boolean, Boolean>> gainStats(List<Pair<StatUpgrade, Integer>> stats) {
boolean gotSlot = false, gotVicious = false;
String lvupStr = "";
Integer statUp, maxStat = ServerConstants.MAX_EQUIPMNT_STAT; Integer statUp, maxStat = ServerConstants.MAX_EQUIPMNT_STAT;
for (Pair<StatUpgrade, Integer> stat : stats) { for (Pair<StatUpgrade, Integer> stat : stats) {
switch (stat.getLeft()) { switch (stat.getLeft()) {
@@ -460,6 +443,55 @@ public class Equip extends Item {
} }
} }
return new Pair<>(lvupStr, new Pair<>(gotSlot, gotVicious));
}
private void gainLevel(MapleClient c) {
List<Pair<StatUpgrade, Integer>> stats = new LinkedList<>();
if(isElemental) {
List<Pair<String, Integer>> elementalStats = MapleItemInformationProvider.getInstance().getItemLevelupStats(getItemId(), itemLevel);
for(Pair<String, Integer> p: elementalStats) {
if(p.getRight() > 0) stats.add(new Pair<>(StatUpgrade.valueOf(p.getLeft()), p.getRight()));
}
}
if(!stats.isEmpty()) {
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
} else {
isUpgradeable = false;
improveDefaultStats(stats);
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
if(isUpgradeable) {
while(stats.isEmpty()) {
improveDefaultStats(stats);
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
}
}
}
itemLevel++;
String lvupStr = "'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "' is now level " + itemLevel + "! ";
String showStr = "#e'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "'#b is now #elevel #r" + itemLevel + "#k#b!";
Pair<String, Pair<Boolean, Boolean>> res = gainStats(stats);
lvupStr += res.getLeft();
boolean gotSlot = res.getRight().getLeft();
boolean gotVicious = res.getRight().getRight();
if(gotVicious) { if(gotVicious) {
//c.getPlayer().dropMessage(6, "A new Vicious Hammer opportunity has been found on the '" + MapleItemInformationProvider.getInstance().getName(getItemId()) + "'!"); //c.getPlayer().dropMessage(6, "A new Vicious Hammer opportunity has been found on the '" + MapleItemInformationProvider.getInstance().getName(getItemId()) + "'!");
lvupStr += "+VICIOUS "; lvupStr += "+VICIOUS ";
+1
View File
@@ -73,6 +73,7 @@ public class Item implements Comparable<Item> {
public void setPosition(short position) { public void setPosition(short position) {
this.position = position; this.position = position;
if (this.pet != null) this.pet.setPosition(position);
} }
public void setQuantity(short quantity) { public void setQuantity(short quantity) {
+1 -1
View File
@@ -215,7 +215,7 @@ public class MaplePet extends Item {
enjoyed = false; enjoyed = false;
} }
owner.getMap().broadcastMessage(MaplePacketCreator.commandResponse(owner.getId(), slot, type, enjoyed)); owner.getMap().broadcastMessage(MaplePacketCreator.petFoodResponse(owner.getId(), slot, enjoyed, false));
saveToDb(); saveToDb();
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition()); Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
@@ -188,6 +188,7 @@ public class MapleInventoryManipulator {
short newQ = (short) Math.min(oldQ + quantity, slotMax); short newQ = (short) Math.min(oldQ + quantity, slotMax);
quantity -= (newQ - oldQ); quantity -= (newQ - oldQ);
eItem.setQuantity(newQ); eItem.setQuantity(newQ);
item.setPosition(eItem.getPosition());
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, eItem)))); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, eItem))));
} }
} else { } else {
@@ -209,6 +210,8 @@ public class MapleInventoryManipulator {
item.setQuantity((short) (quantity + newQ)); item.setQuantity((short) (quantity + newQ));
return false; return false;
} }
nItem.setPosition(newSlot);
item.setPosition(newSlot);
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem)))); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem(); if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
} }
@@ -223,6 +226,8 @@ public class MapleInventoryManipulator {
c.announce(MaplePacketCreator.getShowInventoryFull()); c.announce(MaplePacketCreator.getShowInventoryFull());
return false; return false;
} }
nItem.setPosition(newSlot);
item.setPosition(newSlot);
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem)))); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem(); if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
@@ -234,6 +239,7 @@ public class MapleInventoryManipulator {
c.announce(MaplePacketCreator.getShowInventoryFull()); c.announce(MaplePacketCreator.getShowInventoryFull());
return false; return false;
} }
item.setPosition(newSlot);
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item)))); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item))));
if(MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem(); if(MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem();
} else { } else {
+2 -2
View File
@@ -43,6 +43,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import server.ThreadManager;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.Randomizer; import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
@@ -333,13 +334,12 @@ public class AssignAPProcessor {
AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign."); AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign.");
final MapleClient client = c; final MapleClient client = c;
Thread t = new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
client.disconnect(false, false); client.disconnect(false, false);
} }
}); });
t.start();
return; return;
} }
+2 -2
View File
@@ -30,6 +30,7 @@ import client.SkillFactory;
import client.autoban.AutobanFactory; import client.autoban.AutobanFactory;
import constants.GameConstants; import constants.GameConstants;
import constants.skills.Aran; import constants.skills.Aran;
import server.ThreadManager;
import tools.FilePrinter; import tools.FilePrinter;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
@@ -55,13 +56,12 @@ public class AssignSPProcessor {
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n"); 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; final MapleClient client = c;
Thread t = new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
client.disconnect(true, false); client.disconnect(true, false);
} }
}); });
t.start();
return; return;
} }
+7 -2
View File
@@ -20,6 +20,7 @@
package client.processor; package client.processor;
import client.MapleClient; import client.MapleClient;
import client.MapleCharacter;
import client.inventory.Equip; import client.inventory.Equip;
import client.inventory.Item; import client.inventory.Item;
import client.inventory.MapleInventoryType; import client.inventory.MapleInventoryType;
@@ -199,7 +200,7 @@ public class MakerProcessor {
c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded)); c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false); c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
if(toCreate == 4260003 && c.getPlayer().getQuestStatus(6033) == 1) { if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) {
c.getAbstractPlayerInteraction().setQuestProgress(6033, 1); c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
} }
} else { } else {
@@ -282,6 +283,10 @@ public class MakerProcessor {
return null; return null;
} }
public static int getMakerSkillLevel(MapleCharacter chr) {
return chr.getSkillLevel((chr.getJob().getId() / 1000) * 10000000 + 1007);
}
private static short getCreateStatus(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) { private static short getCreateStatus(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) {
if(recipe == null) { if(recipe == null) {
return -1; return -1;
@@ -299,7 +304,7 @@ public class MakerProcessor {
return 3; return 3;
} }
if(c.getPlayer().getSkillLevel((c.getPlayer().getJob().getId() / 1000) * 10000000 + 1007) < recipe.getReqSkillLevel()) { if(getMakerSkillLevel(c.getPlayer()) < recipe.getReqSkillLevel()) {
return 4; return 4;
} }
+1
View File
@@ -45,6 +45,7 @@ public final class ItemConstants {
public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this
public final static int PET_COME = 0x80; public final static int PET_COME = 0x80;
public final static int ACCOUNT_SHARING = 0x100; public final static int ACCOUNT_SHARING = 0x100;
public final static int MERGE_UNTRADEABLE = 0x200;
public final static boolean EXPIRING_ITEMS = true; public final static boolean EXPIRING_ITEMS = true;
public final static Set<Integer> permanentItemids = new HashSet<>(); public final static Set<Integer> permanentItemids = new HashSet<>();
+8 -2
View File
@@ -82,7 +82,7 @@ public class ServerConstants {
public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats. public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats.
public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it. public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it.
public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs. public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
public static final boolean USE_ENFORCE_JOB_SP_RANGE = true; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved. public static final boolean USE_ENFORCE_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved.
public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players. public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players.
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants. public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants.
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
@@ -115,6 +115,10 @@ public class ServerConstants {
public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items. public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items.
public static final boolean USE_MAKER_FEE_HEURISTICS = true; //Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than requested by the client-side). public static final boolean USE_MAKER_FEE_HEURISTICS = true; //Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than requested by the client-side).
//Custom Configuration
public static final boolean USE_ENABLE_CUSTOM_NPC_SCRIPT = true;//Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay), quests or NPC shops.
public static final boolean USE_STARTER_MERGE = false; //Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites).
//Commands Configuration //Commands Configuration
public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command. public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command.
public static final boolean USE_WHOLE_SERVER_RANKING = false; //Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of separated by worlds. public static final boolean USE_WHOLE_SERVER_RANKING = false; //Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of separated by worlds.
@@ -133,7 +137,7 @@ public class ServerConstants {
public static final int PARTY_EXPERIENCE_MOD = 1; //Change for event stuff. public static final int PARTY_EXPERIENCE_MOD = 1; //Change for event stuff.
//Miscellaneous Configuration //Miscellaneous Configuration
public static String TIMEZONE = "-GMT3"; public static String TIMEZONE = "GMT-3";
public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values). public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values).
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions. public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled. public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
@@ -178,6 +182,7 @@ public class ServerConstants {
public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss. public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss.
//Other Skills Configuration //Other Skills Configuration
public static final boolean USE_FULL_ARAN_SKILLSET = false; //Enables starter availability to all Aran job skills. Suggestion thanks to Masterrulax.
public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will. public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will.
public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Suggestion thanks to Celestial. public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Suggestion thanks to Celestial.
public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs. public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs.
@@ -245,6 +250,7 @@ public class ServerConstants {
public static final long EVENT_LOBBY_DELAY = 10; //Cooldown duration in seconds before reopening an event lobby. public static final long EVENT_LOBBY_DELAY = 10; //Cooldown duration in seconds before reopening an event lobby.
//Dojo Configuration //Dojo Configuration
public static final boolean USE_FAST_DOJO_UPGRADE = true; //Reduced Dojo training points amount required for a belt upgrade.
public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo? public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo?
public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack
public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack
+1
View File
@@ -33,6 +33,7 @@ public class WindArcher {
public static final int BOW_BOOSTER = 13101001; public static final int BOW_BOOSTER = 13101001;
public static final int FINAL_ATTACK = 13101002; public static final int FINAL_ATTACK = 13101002;
public static final int SOUL_ARROW = 13101003; public static final int SOUL_ARROW = 13101003;
public static final int STORM_BREAK = 13101005;
public static final int WIND_WALK = 13101006; public static final int WIND_WALK = 13101006;
public static final int HURRICANE = 13111002; public static final int HURRICANE = 13111002;
public static final int PUPPET = 13111004; public static final int PUPPET = 13111004;
+1
View File
@@ -136,6 +136,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.NPC_TALK, new NPCTalkHandler()); registerHandler(RecvOpcode.NPC_TALK, new NPCTalkHandler());
registerHandler(RecvOpcode.NPC_TALK_MORE, new NPCMoreTalkHandler()); registerHandler(RecvOpcode.NPC_TALK_MORE, new NPCMoreTalkHandler());
registerHandler(RecvOpcode.QUEST_ACTION, new QuestActionHandler()); registerHandler(RecvOpcode.QUEST_ACTION, new QuestActionHandler());
registerHandler(RecvOpcode.GRENADE_EFFECT, new GrenadeEffectHandler());
registerHandler(RecvOpcode.NPC_SHOP, new NPCShopHandler()); registerHandler(RecvOpcode.NPC_SHOP, new NPCShopHandler());
registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler()); registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler());
registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler()); registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler());
+2 -1
View File
@@ -114,7 +114,8 @@ public enum RecvOpcode {
TROCK_ADD_MAP(0x66), TROCK_ADD_MAP(0x66),
REPORT(0x6A), REPORT(0x6A),
QUEST_ACTION(0x6B), QUEST_ACTION(0x6B),
//lolno //USER_CALC_DAMAGE_STAT_SET_REQUEST(0x6C),
GRENADE_EFFECT(0x6D),
SKILL_MACRO(0x6E), SKILL_MACRO(0x6E),
USE_ITEM_REWARD(0x70), USE_ITEM_REWARD(0x70),
MAKER_SKILL(0x71), MAKER_SKILL(0x71),
+1
View File
@@ -228,6 +228,7 @@ public enum SendOpcode {
GIVE_FOREIGN_BUFF(0xC7), GIVE_FOREIGN_BUFF(0xC7),
CANCEL_FOREIGN_BUFF(0xC8), CANCEL_FOREIGN_BUFF(0xC8),
UPDATE_PARTYMEMBER_HP(0xC9), UPDATE_PARTYMEMBER_HP(0xC9),
THROW_GRENADE(0xCC),
CANCEL_CHAIR(0xCD), CANCEL_CHAIR(0xCD),
SHOW_ITEM_GAIN_INCHAT(0xCE), SHOW_ITEM_GAIN_INCHAT(0xCE),
DOJO_WARP_UP(0xCF), DOJO_WARP_UP(0xCF),
+1 -1
View File
@@ -102,7 +102,7 @@ public class PlayerStorage {
for(MapleCharacter mc : chrList) { for(MapleCharacter mc : chrList) {
MapleClient client = mc.getClient(); MapleClient client = mc.getClient();
if(client != null) { if(client != null) {
client.disconnect(true, false); client.forceDisconnect();
} }
} }
+38 -1
View File
@@ -85,8 +85,10 @@ import client.newyear.NewYearCardRecord;
import constants.ItemConstants; import constants.ItemConstants;
import constants.GameConstants; import constants.GameConstants;
import constants.ServerConstants; import constants.ServerConstants;
import java.util.TimeZone;
import server.CashShop.CashItemFactory; import server.CashShop.CashItemFactory;
import server.MapleSkillbookInformationProvider; import server.MapleSkillbookInformationProvider;
import server.ThreadManager;
import server.TimerManager; import server.TimerManager;
import server.life.MaplePlayerNPCFactory; import server.life.MaplePlayerNPCFactory;
import server.quest.MapleQuest; import server.quest.MapleQuest;
@@ -147,6 +149,10 @@ public class Server {
private boolean online = false; private boolean online = false;
public static long uptime = System.currentTimeMillis(); public static long uptime = System.currentTimeMillis();
public int getCurrentTimestamp() {
return (int) (Server.getInstance().getCurrentTime() - Server.uptime);
}
public long getCurrentTime() { // returns a slightly delayed time value, under frequency of UPDATE_INTERVAL public long getCurrentTime() { // returns a slightly delayed time value, under frequency of UPDATE_INTERVAL
return serverCurrentTime; return serverCurrentTime;
} }
@@ -825,6 +831,33 @@ public class Server {
return rankSystem; return rankSystem;
} }
private static void clearUnreferencedPetIds() {
PreparedStatement ps = null;
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)");
ps.executeUpdate();
ps.close();
con.close();
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
try {
if(ps != null && !ps.isClosed()) {
ps.close();
}
if(con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public void init() { public void init() {
Properties p = loadWorldINI(); Properties p = loadWorldINI();
if(p == null) { if(p == null) {
@@ -833,10 +866,11 @@ public class Server {
System.out.println("HeavenMS v" + ServerConstants.VERSION + " starting up.\r\n"); System.out.println("HeavenMS v" + ServerConstants.VERSION + " starting up.\r\n");
if(ServerConstants.SHUTDOWNHOOK) if(ServerConstants.SHUTDOWNHOOK)
Runtime.getRuntime().addShutdownHook(new Thread(shutdown(false))); Runtime.getRuntime().addShutdownHook(new Thread(shutdown(false)));
TimeZone.setDefault(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
Connection c = null; Connection c = null;
try { try {
c = DatabaseConnection.getConnection(); c = DatabaseConnection.getConnection();
@@ -856,6 +890,7 @@ public class Server {
sqle.printStackTrace(); sqle.printStackTrace();
} }
clearUnreferencedPetIds();
MapleCashidGenerator.loadExistentCashIdsFromDb(); MapleCashidGenerator.loadExistentCashIdsFromDb();
IoBuffer.setUseDirectBuffer(false); IoBuffer.setUseDirectBuffer(false);
@@ -863,6 +898,7 @@ public class Server {
acceptor = new NioSocketAcceptor(); acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory())); acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory()));
ThreadManager.getInstance().start();
TimerManager tMan = TimerManager.getInstance(); TimerManager tMan = TimerManager.getInstance();
tMan.start(); tMan.start();
tMan.register(tMan.purge(), ServerConstants.PURGING_INTERVAL);//Purging ftw... tMan.register(tMan.purge(), ServerConstants.PURGING_INTERVAL);//Purging ftw...
@@ -1716,6 +1752,7 @@ public class Server {
resetServerWorlds(); resetServerWorlds();
ThreadManager.getInstance().stop();
TimerManager.getInstance().purge(); TimerManager.getInstance().purge();
TimerManager.getInstance().stop(); TimerManager.getInstance().stop();
+2 -2
View File
@@ -153,7 +153,7 @@ public class ThreadTracker {
StackTraceElement[] ste = threads.get(l).getStackTrace(); StackTraceElement[] ste = threads.get(l).getStackTrace();
if(ste.length > 0) { if(ste.length > 0) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
String df = dateFormat.format(new Date()); String df = dateFormat.format(new Date());
FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df)); FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df));
@@ -195,7 +195,7 @@ public class ThreadTracker {
} }
} else { // print status } else { // print status
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
FilePrinter.printError(FilePrinter.DEADLOCK_STATE, printThreadTrackerState(dateFormat.format(new Date()))); FilePrinter.printError(FilePrinter.DEADLOCK_STATE, printThreadTrackerState(dateFormat.format(new Date())));
//FilePrinter.printError(FilePrinter.DEADLOCK_STATE, "[" + dateFormat.format(new Date()) + "] Presenting current lock path for lockid " + lockId.name() + ".\r\n" + printLockStatus(lockId) + "\r\n-------------------------------\r\n"); //FilePrinter.printError(FilePrinter.DEADLOCK_STATE, "[" + dateFormat.format(new Date()) + "] Presenting current lock path for lockid " + lockId.name() + ".\r\n" + printLockStatus(lockId) + "\r\n-------------------------------\r\n");
@@ -62,7 +62,7 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements
if(ServerConstants.USE_THREAD_TRACKER) { if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) { if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
//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"); //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); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
@@ -64,7 +64,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen
if(ServerConstants.USE_THREAD_TRACKER) { if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) { if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
//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"); //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); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
@@ -60,7 +60,7 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement
if(ServerConstants.USE_THREAD_TRACKER) { if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) { if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
//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"); //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); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
@@ -41,7 +41,7 @@ public class EmptyReadLock implements MonitoredReadLock {
private static String printThreadStack(StackTraceElement[] list) { private static String printThreadStack(StackTraceElement[] list) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
String df = dateFormat.format(new Date()); String df = dateFormat.format(new Date());
String s = "\r\n" + df + "\r\n"; String s = "\r\n" + df + "\r\n";
@@ -41,7 +41,7 @@ public class EmptyReentrantLock implements MonitoredReentrantLock {
private static String printThreadStack(StackTraceElement[] list) { private static String printThreadStack(StackTraceElement[] list) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
String df = dateFormat.format(new Date()); String df = dateFormat.format(new Date());
String s = "\r\n" + df + "\r\n"; String s = "\r\n" + df + "\r\n";
@@ -41,7 +41,7 @@ public class EmptyWriteLock implements MonitoredWriteLock {
private static String printThreadStack(StackTraceElement[] list) { private static String printThreadStack(StackTraceElement[] list) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); dateFormat.setTimeZone(TimeZone.getDefault());
String df = dateFormat.format(new Date()); String df = dateFormat.format(new Date());
String s = "\r\n" + df + "\r\n"; String s = "\r\n" + df + "\r\n";
@@ -259,8 +259,9 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
else if(attack.skill == Shadower.BOOMERANG_STEP) else if(attack.skill == Shadower.BOOMERANG_STEP)
distanceToDetect += 60000; distanceToDetect += 60000;
if(distance > distanceToDetect) { if (distance > distanceToDetect) {
AutobanFactory.DISTANCE_HACK.alert(player, "Distance Sq to monster: " + distance + " SID: " + attack.skill + " MID: " + monster.getId()); AutobanFactory.DISTANCE_HACK.alert(player, "Distance Sq to monster: " + distance + " SID: " + attack.skill + " MID: " + monster.getId());
monster.refreshMobPosition();
} }
int totDamageToOneMonster = 0; int totDamageToOneMonster = 0;
@@ -338,7 +339,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
} }
} }
if (job == 2111 || job == 2112) { if (player.isAran()) {
if (player.getBuffedValue(MapleBuffStat.WK_CHARGE) != null) { if (player.getBuffedValue(MapleBuffStat.WK_CHARGE) != null) {
Skill snowCharge = SkillFactory.getSkill(Aran.SNOW_CHARGE); Skill snowCharge = SkillFactory.getSkill(Aran.SNOW_CHARGE);
if (totDamageToOneMonster > 0) { if (totDamageToOneMonster > 0) {
@@ -26,6 +26,7 @@ import net.AbstractMaplePacketHandler;
import server.life.MapleMonster; import server.life.MapleMonster;
import server.maps.MapleMap; import server.maps.MapleMap;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleCharacter;
public final class AutoAggroHandler extends AbstractMaplePacketHandler { public final class AutoAggroHandler extends AbstractMaplePacketHandler {
@@ -38,18 +39,26 @@ public final class AutoAggroHandler extends AbstractMaplePacketHandler {
int oid = slea.readInt(); int oid = slea.readInt();
MapleMonster monster = map.getMonsterByOid(oid); MapleMonster monster = map.getMonsterByOid(oid);
if (monster != null && monster.getController() != null) { if (monster != null) {
if (!monster.isControllerHasAggro()) { MapleCharacter currentController = monster.getController();
if (map.getCharacterById(monster.getController().getId()) == null) { monster.lockMonster();
monster.switchController(c.getPlayer(), true); try {
if (currentController != null) {
if (!monster.isControllerHasAggro()) {
if (map.getCharacterById(currentController.getId()) == null) {
monster.switchController(c.getPlayer(), true);
} else {
monster.switchController(currentController, true);
}
} else if (map.getCharacterById(currentController.getId()) == null) {
monster.switchController(c.getPlayer(), true);
}
} else { } else {
monster.switchController(monster.getController(), true); monster.switchController(c.getPlayer(), true);
} }
} else if (map.getCharacterById(monster.getController().getId()) == null) { } finally {
monster.switchController(c.getPlayer(), true); monster.unlockMonster();
} }
} else if (monster != null && monster.getController() == null) {
monster.switchController(c.getPlayer(), true);
} }
} }
} }
@@ -25,6 +25,7 @@ import net.AbstractMaplePacketHandler;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient; import client.MapleClient;
import client.autoban.AutobanFactory; import client.autoban.AutobanFactory;
import net.server.Server;
/** /**
* *
@@ -35,7 +36,8 @@ public final class ChangeChannelHandler extends AbstractMaplePacketHandler {
@Override @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int channel = slea.readByte() + 1; int channel = slea.readByte() + 1;
c.getPlayer().getAutobanManager().setTimestamp(6, slea.readInt(), 2); slea.readInt();
c.getPlayer().getAutobanManager().setTimestamp(6, Server.getInstance().getCurrentTimestamp(), 3);
if(c.getChannel() == channel) { if(c.getChannel() == channel) {
AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel."); AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel.");
c.disconnect(false, false); c.disconnect(false, false);
@@ -0,0 +1,57 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package net.server.channel.handlers;
import client.MapleClient;
import client.MapleCharacter;
import constants.skills.Gunslinger;
import constants.skills.NightWalker;
import net.AbstractMaplePacketHandler;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import java.awt.Point;
import tools.FilePrinter;
/*
* @author GabrielSin
*/
public class GrenadeEffectHandler extends AbstractMaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
Point position = new Point(slea.readInt(), slea.readInt());
int keyDown = slea.readInt();
int skillId = slea.readInt();
switch (skillId) {
case NightWalker.POISON_BOMB:
case Gunslinger.GRENADE:
int skillLevel = chr.getSkillLevel(skillId);
if (skillLevel > 0) {
chr.getMap().broadcastMessage(chr, MaplePacketCreator.throwGrenade(chr.getId(), position, keyDown, skillId, skillLevel), position);
}
break;
default:
FilePrinter.printError(FilePrinter.UNHANDLED_EVENT, "The skill id: " + skillId + " is not coded in " + this.getClass().getName() + ".");
}
}
}
@@ -38,12 +38,12 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
if(!chr.isLoggedinWorld()) return; if(!chr.isLoggedinWorld()) return;
AutobanManager abm = chr.getAutobanManager(); AutobanManager abm = chr.getAutobanManager();
int timestamp = (int) (Server.getInstance().getCurrentTime() - Server.uptime); int timestamp = Server.getInstance().getCurrentTimestamp();
slea.skip(8); slea.skip(8);
short healHP = slea.readShort(); short healHP = slea.readShort();
if (healHP != 0) { if (healHP != 0) {
abm.setTimestamp(8, timestamp, 3); // thanks Vcoc for pointing out d/c happening here abm.setTimestamp(8, timestamp, 4); // thanks Vcoc for pointing out d/c happening here
if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing"); if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing");
int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa... int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa...
@@ -58,7 +58,7 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
} }
short healMP = slea.readShort(); short healMP = slea.readShort();
if (healMP != 0 && healMP < 1000) { if (healMP != 0 && healMP < 1000) {
abm.setTimestamp(9, timestamp, 3); abm.setTimestamp(9, timestamp, 4);
if ((abm.getLastSpam(1) + 1500) > timestamp) AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing"); if ((abm.getLastSpam(1) + 1500) > timestamp) AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing");
chr.addMP(healMP); chr.addMP(healMP);
abm.spam(1, timestamp); abm.spam(1, timestamp);
@@ -31,6 +31,7 @@ import client.MapleClient;
import client.inventory.Item; import client.inventory.Item;
import client.inventory.MapleInventory; import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType; import client.inventory.MapleInventoryType;
import net.server.Server;
import server.MapleItemInformationProvider; import server.MapleItemInformationProvider;
public final class InventoryMergeHandler extends AbstractMaplePacketHandler { public final class InventoryMergeHandler extends AbstractMaplePacketHandler {
@@ -38,7 +39,8 @@ public final class InventoryMergeHandler extends AbstractMaplePacketHandler {
@Override @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer(); MapleCharacter chr = c.getPlayer();
chr.getAutobanManager().setTimestamp(2, slea.readInt(), 3); slea.readInt();
chr.getAutobanManager().setTimestamp(2, Server.getInstance().getCurrentTimestamp(), 4);
if(!ServerConstants.USE_ITEM_SORT) { if(!ServerConstants.USE_ITEM_SORT) {
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
@@ -24,7 +24,6 @@ package net.server.channel.handlers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import constants.ServerConstants;
import net.AbstractMaplePacketHandler; import net.AbstractMaplePacketHandler;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
@@ -35,7 +34,9 @@ import client.inventory.Equip;
import client.inventory.MapleInventory; import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType; import client.inventory.MapleInventoryType;
import client.inventory.ModifyInventory; import client.inventory.ModifyInventory;
import constants.ServerConstants;
import server.MapleItemInformationProvider; import server.MapleItemInformationProvider;
import net.server.Server;
/** /**
* *
@@ -187,7 +188,8 @@ public final class InventorySortHandler extends AbstractMaplePacketHandler {
@Override @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer(); MapleCharacter chr = c.getPlayer();
chr.getAutobanManager().setTimestamp(3, slea.readInt(), 3); slea.readInt();
chr.getAutobanManager().setTimestamp(3, Server.getInstance().getCurrentTimestamp(), 4);
if(!ServerConstants.USE_ITEM_SORT) { if(!ServerConstants.USE_ITEM_SORT) {
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
@@ -61,7 +61,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
} }
MapleMonster monster = (MapleMonster) mmo; MapleMonster monster = (MapleMonster) mmo;
List<MapleCharacter> banishPlayers = new LinkedList<>(); List<MapleCharacter> banishPlayers = null;
byte pNibbles = slea.readByte(); byte pNibbles = slea.readByte();
byte rawActivity = slea.readByte(); byte rawActivity = slea.readByte();
@@ -70,7 +70,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
short pOption = slea.readShort(); short pOption = slea.readShort();
slea.skip(8); slea.skip(8);
if (rawActivity >= 0) { if (rawActivity >= 0) {
rawActivity = (byte) (rawActivity & 0xFF >> 1); rawActivity = (byte) (rawActivity & 0xFF >> 1);
} }
@@ -78,7 +78,9 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
boolean isSkill = inRangeInclusive(rawActivity, 21, 25); boolean isSkill = inRangeInclusive(rawActivity, 21, 25);
byte attackId = (byte) (isAttack ? rawActivity - 12 : -1); byte attackId = (byte) (isAttack ? rawActivity - 12 : -1);
boolean nextMovementCouldBeSkill = (pNibbles & 0x0F) != 0; isSkill |= (pNibbles != 0);
boolean nextMovementCouldBeSkill = true;
boolean pUnk = (pNibbles & 0xF0) != 0; boolean pUnk = (pNibbles & 0xF0) != 0;
boolean currentController = (monster.getController() == player); boolean currentController = (monster.getController() == player);
@@ -86,6 +88,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
MobSkill toUse = null; MobSkill toUse = null;
int useSkillId = 0, useSkillLevel = 0; int useSkillId = 0, useSkillLevel = 0;
int rnd = 0;
if (isSkill) { if (isSkill) {
int noSkills = monster.getNoSkills(); int noSkills = monster.getNoSkills();
if (noSkills > 0) { if (noSkills > 0) {
@@ -95,11 +98,10 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
useSkillId = skillToUse.getLeft(); useSkillId = skillToUse.getLeft();
useSkillLevel = skillToUse.getRight(); useSkillLevel = skillToUse.getRight();
toUse = MobSkillFactory.getMobSkill(useSkillId, useSkillLevel); toUse = MobSkillFactory.getMobSkill(useSkillId, useSkillLevel);
} else {
rnd = rndSkill;
nextMovementCouldBeSkill = false; nextMovementCouldBeSkill = false;
} }
} else {
nextMovementCouldBeSkill = false;
} }
int mobMp; int mobMp;
@@ -107,9 +109,10 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
mobMp = monster.getMp(); mobMp = monster.getMp();
int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(toUse); int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(toUse);
if(animationTime > 0) { if(animationTime > 0 && toUse.getSkillId() != 129) {
toUse.applyDelayedEffect(player, monster, true, banishPlayers, animationTime); toUse.applyDelayedEffect(player, monster, true, animationTime);
} else { } else {
banishPlayers = new LinkedList<>();
toUse.applyEffect(player, monster, true, banishPlayers); toUse.applyEffect(player, monster, true, banishPlayers);
} }
} else { } else {
@@ -138,36 +141,45 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
short start_y = slea.readShort(); // hmm... short start_y = slea.readShort(); // hmm...
Point startPos = new Point(start_x, start_y - 2); Point startPos = new Point(start_x, start_y - 2);
List<LifeMovementFragment> res = parseMovement(slea); List<LifeMovementFragment> res = parseMovement(slea);
if (!currentController) {
if (monster.isAttackedBy(player)) {
monster.switchController(player, true);
} else {
return;
}
} else if (rawActivity == -1 && monster.isControllerKnowsAboutAggro() && !monster.isMobile() && !monster.isFirstAttack()) {
monster.setControllerHasAggro(false);
monster.setControllerKnowsAboutAggro(false);
}
boolean aggro = monster.isControllerHasAggro(); boolean aggro;
monster.lockMonster();
try {
if (!currentController) {
if (monster.isAttackedBy(player)) {
monster.switchController(player, true);
} else {
return;
}
} else if (rawActivity == -1 && monster.isControllerKnowsAboutAggro() && !monster.isMobile() && !monster.isFirstAttack()) {
monster.setControllerHasAggro(false);
monster.setControllerKnowsAboutAggro(false);
}
aggro = monster.isControllerHasAggro();
if (aggro) {
monster.setControllerKnowsAboutAggro(true);
}
} finally {
monster.unlockMonster();
}
if (toUse != null) { if (toUse != null) {
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, toUse.getSkillId(), toUse.getSkillLevel())); c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, toUse.getSkillId(), toUse.getSkillLevel()));
} else { } else {
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro)); c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro));
} }
if (aggro) {
monster.setControllerKnowsAboutAggro(true);
}
if (res != null) { if (res != null) {
map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition()); map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition());
updatePosition(res, monster, -2); updatePosition(res, monster, -2);
map.moveMonster(monster, monster.getPosition()); map.moveMonster(monster, monster.getPosition());
} }
for (MapleCharacter chr : banishPlayers) { if (banishPlayers != null) {
chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg()); for (MapleCharacter chr : banishPlayers) {
chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg());
}
} }
} }
@@ -54,8 +54,9 @@ public final class PetCommandHandler extends AbstractMaplePacketHandler {
if (Randomizer.nextInt(101) <= petCommand.getProbability()) { if (Randomizer.nextInt(101) <= petCommand.getProbability()) {
pet.gainClosenessFullness(chr, petCommand.getIncrease(), 0, command); pet.gainClosenessFullness(chr, petCommand.getIncrease(), 0, command);
chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, false, command, false));
} else { } else {
chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, command, false)); chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, true, command, false));
} }
} }
} }
@@ -21,6 +21,7 @@
*/ */
package net.server.channel.handlers; package net.server.channel.handlers;
import net.AbstractMaplePacketHandler;
import client.MapleCharacter; import client.MapleCharacter;
import client.MapleClient; import client.MapleClient;
import client.inventory.MapleInventory; import client.inventory.MapleInventory;
@@ -28,8 +29,8 @@ import client.inventory.MapleInventoryType;
import client.inventory.MaplePet; import client.inventory.MaplePet;
import client.autoban.AutobanManager; import client.autoban.AutobanManager;
import client.inventory.Item; import client.inventory.Item;
import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleInventoryManipulator;
import net.server.Server;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
@@ -44,7 +45,8 @@ public final class PetFoodHandler extends AbstractMaplePacketHandler {
return; return;
} }
abm.spam(2); abm.spam(2);
abm.setTimestamp(1, slea.readInt(), 3); slea.readInt(); // timestamp issue detected thanks to Masterrulax
abm.setTimestamp(1, Server.getInstance().getCurrentTimestamp(), 3);
if (chr.getNoPets() == 0) { if (chr.getNoPets() == 0) {
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
return; return;
@@ -179,7 +179,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
} }
} }
if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 4111004) { if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 4111004 || attack.skill == 13101005) {
int visProjectile = projectile; //visible projectile sent to players int visProjectile = projectile; //visible projectile sent to players
if (ItemConstants.isThrowingStar(projectile)) { if (ItemConstants.isThrowingStar(projectile)) {
MapleInventory cash = chr.getInventory(MapleInventoryType.CASH); MapleInventory cash = chr.getInventory(MapleInventoryType.CASH);
@@ -192,7 +192,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
} }
} }
} }
} else if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) { } else if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 13101005) {
visProjectile = 0; visProjectile = 0;
} }
@@ -49,7 +49,7 @@ public final class ScriptedItemHandler extends AbstractMaplePacketHandler {
if (item == null || item.getItemId() != itemId || item.getQuantity() < 1 || !ism.scriptExists(info.getScript())) { if (item == null || item.getItemId() != itemId || item.getQuantity() < 1 || !ism.scriptExists(info.getScript())) {
return; return;
} }
ism.getItemScript(c, info.getScript()); ism.runItemScript(c, info.getScript());
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
//NPCScriptManager.getInstance().start(c, info.getNpc(), null, null); //NPCScriptManager.getInstance().start(c, info.getNpc(), null, null);
} }
@@ -30,7 +30,6 @@ import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleCharacter; import client.MapleCharacter;
import client.MapleClient; import client.MapleClient;
import client.MapleStat;
import client.Skill; import client.Skill;
import client.SkillFactory; import client.SkillFactory;
import constants.ServerConstants; import constants.ServerConstants;
@@ -41,14 +40,15 @@ import constants.skills.Hero;
import constants.skills.Paladin; import constants.skills.Paladin;
import constants.skills.Priest; import constants.skills.Priest;
import constants.skills.SuperGM; import constants.skills.SuperGM;
import net.server.Server;
public final class SpecialMoveHandler extends AbstractMaplePacketHandler { public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
@Override @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer(); MapleCharacter chr = c.getPlayer();
chr.getAutobanManager().setTimestamp(4, slea.readInt(), 3); slea.readInt();
chr.getAutobanManager().setTimestamp(4, Server.getInstance().getCurrentTimestamp(), 5);
int skillid = slea.readInt(); int skillid = slea.readInt();
/* /*
@@ -99,9 +99,14 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showMagnet(mobId, success), false); chr.getMap().broadcastMessage(chr, MaplePacketCreator.showMagnet(mobId, success), false);
MapleMonster monster = chr.getMap().getMonsterByOid(mobId); MapleMonster monster = chr.getMap().getMonsterByOid(mobId);
if (monster != null) { if (monster != null) {
if (!monster.isBoss()) { if (!monster.isBoss()) {
monster.switchController(chr, monster.isControllerHasAggro()); monster.lockMonster();
} try {
monster.switchController(chr, monster.isControllerHasAggro());
} finally {
monster.unlockMonster();
}
}
} }
} }
byte direction = slea.readByte(); byte direction = slea.readByte();
@@ -32,6 +32,8 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author Generic * @author Generic
*/ */
public final class TouchReactorHandler extends AbstractMaplePacketHandler { public final class TouchReactorHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int oid = slea.readInt(); int oid = slea.readInt();
MapleReactor reactor = c.getPlayer().getMap().getReactorByOid(oid); MapleReactor reactor = c.getPlayer().getMap().getReactorByOid(oid);
@@ -27,6 +27,7 @@ import client.inventory.MapleInventoryType;
import client.autoban.AutobanManager; import client.autoban.AutobanManager;
import constants.ItemConstants; import constants.ItemConstants;
import net.AbstractMaplePacketHandler; import net.AbstractMaplePacketHandler;
import net.server.Server;
import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleInventoryManipulator;
import server.life.MapleMonster; import server.life.MapleMonster;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
@@ -41,7 +42,8 @@ public final class UseCatchItemHandler extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer(); MapleCharacter chr = c.getPlayer();
AutobanManager abm = chr.getAutobanManager(); AutobanManager abm = chr.getAutobanManager();
abm.setTimestamp(5, slea.readInt(), 3); slea.readInt();
abm.setTimestamp(5, Server.getInstance().getCurrentTimestamp(), 4);
slea.readShort(); slea.readShort();
int itemId = slea.readInt(); int itemId = slea.readInt();
int monsterid = slea.readInt(); int monsterid = slea.readInt();
@@ -35,7 +35,6 @@ import constants.GameConstants;
/** /**
* @author Ronan * @author Ronan
*/ */
public final class UseOwlOfMinervaHandler extends AbstractMaplePacketHandler { public final class UseOwlOfMinervaHandler extends AbstractMaplePacketHandler {
@Override @Override
+3 -1
View File
@@ -1402,7 +1402,9 @@ public class World {
int dpVal = dp.getValue() + 1; int dpVal = dp.getValue() + 1;
if(dpVal == ServerConstants.MOUNT_EXHAUST_COUNT) { if(dpVal == ServerConstants.MOUNT_EXHAUST_COUNT) {
chr.runTirednessSchedule(); if (!chr.runTirednessSchedule()) {
continue;
}
dpVal = 0; dpVal = 0;
} }
+13 -13
View File
@@ -512,21 +512,21 @@ public class AbstractPlayerInteraction {
petId = MaplePet.createPet(id); petId = MaplePet.createPet(id);
if(from != null) { if(from != null) {
evolved = MaplePet.loadFromDb(id, (short) 0, petId); evolved = MaplePet.loadFromDb(id, (short) 0, petId);
Point pos = getPlayer().getPosition(); Point pos = getPlayer().getPosition();
pos.y -= 12; pos.y -= 12;
evolved.setPos(pos); evolved.setPos(pos);
evolved.setFh(getPlayer().getMap().getFootholds().findBelow(evolved.getPos()).getId()); evolved.setFh(getPlayer().getMap().getFootholds().findBelow(evolved.getPos()).getId());
evolved.setStance(0); evolved.setStance(0);
evolved.setSummoned(true); evolved.setSummoned(true);
evolved.setName(from.getName().compareTo(MapleItemInformationProvider.getInstance().getName(from.getItemId())) != 0 ? from.getName() : MapleItemInformationProvider.getInstance().getName(id)); evolved.setName(from.getName().compareTo(MapleItemInformationProvider.getInstance().getName(from.getItemId())) != 0 ? from.getName() : MapleItemInformationProvider.getInstance().getName(id));
evolved.setCloseness(from.getCloseness()); evolved.setCloseness(from.getCloseness());
evolved.setFullness(from.getFullness()); evolved.setFullness(from.getFullness());
evolved.setLevel(from.getLevel()); evolved.setLevel(from.getLevel());
evolved.setExpiration(System.currentTimeMillis() + expires); evolved.setExpiration(System.currentTimeMillis() + expires);
evolved.saveToDb(); evolved.saveToDb();
} }
//MapleInventoryManipulator.addById(c, id, (short) 1, null, petId, expires == -1 ? -1 : System.currentTimeMillis() + expires); //MapleInventoryManipulator.addById(c, id, (short) 1, null, petId, expires == -1 ? -1 : System.currentTimeMillis() + expires);
+162 -265
View File
@@ -69,6 +69,7 @@ import net.server.coordinator.MapleEventRecallCoordinator;
import scripting.AbstractPlayerInteraction; import scripting.AbstractPlayerInteraction;
import scripting.event.worker.EventScriptScheduler; import scripting.event.worker.EventScriptScheduler;
import server.MapleItemInformationProvider; import server.MapleItemInformationProvider;
import server.ThreadManager;
import server.life.MapleLifeFactory; import server.life.MapleLifeFactory;
import server.life.MapleNPC; import server.life.MapleNPC;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
@@ -228,51 +229,42 @@ public class EventInstanceManager {
} }
public synchronized void registerPlayer(MapleCharacter chr) { public synchronized void registerPlayer(final MapleCharacter chr) {
if (chr == null || !chr.isLoggedinWorld() || disposed) { if (chr == null || !chr.isLoggedinWorld() || disposed) {
return; return;
} }
wL.lock();
try { try {
wL.lock(); if(chars.containsKey(chr.getId())) {
try { return;
if(chars.containsKey(chr.getId())) {
return;
}
chars.put(chr.getId(), chr);
chr.setEventInstance(this);
} finally {
wL.unlock();
} }
sL.lock(); chars.put(chr.getId(), chr);
try { chr.setEventInstance(this);
em.getIv().invokeFunction("playerEntry", this, chr); } finally {
} finally { wL.unlock();
sL.unlock(); }
}
} catch (ScriptException | NoSuchMethodException ex) { try {
ex.printStackTrace(); em.getIv().invokeFunction("playerEntry", EventInstanceManager.this, chr);
} } catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
public void exitPlayer(MapleCharacter chr) { public void exitPlayer(final MapleCharacter chr) {
if (chr == null || !chr.isLoggedin()){ if (chr == null || !chr.isLoggedin()){
return; return;
} }
try {
unregisterPlayer(chr);
sL.lock(); unregisterPlayer(chr);
try {
em.getIv().invokeFunction("playerExit", this, chr); try {
} finally { em.getIv().invokeFunction("playerExit", EventInstanceManager.this, chr);
sL.unlock(); } catch (ScriptException | NoSuchMethodException ex) {
} ex.printStackTrace();
} catch (ScriptException | NoSuchMethodException ex) { }
ex.printStackTrace();
}
} }
public void dropMessage(int type, String message) { public void dropMessage(int type, String message) {
@@ -297,15 +289,10 @@ public class EventInstanceManager {
event_schedule = TimerManager.getInstance().schedule(new Runnable() { event_schedule = TimerManager.getInstance().schedule(new Runnable() {
@Override @Override
public void run() { public void run() {
try { dismissEventTimer();
dismissEventTimer();
sL.lock(); try {
try { em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this);
em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement scheduledTimeout function.", ex); Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement scheduledTimeout function.", ex);
} }
@@ -314,31 +301,25 @@ public class EventInstanceManager {
} }
public void addEventTimer(long time) { public void addEventTimer(long time) {
if(event_schedule != null) { if (event_schedule != null) {
if(event_schedule.cancel(false)) { if (event_schedule.cancel(false)) {
long nextTime = getTimeLeft() + time; long nextTime = getTimeLeft() + time;
eventTime += time; eventTime += time;
event_schedule = TimerManager.getInstance().schedule(new Runnable() { event_schedule = TimerManager.getInstance().schedule(new Runnable() {
@Override @Override
public void run() { public void run() {
try { dismissEventTimer();
dismissEventTimer();
sL.lock(); try {
try { em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this);
em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement scheduledTimeout function.", ex); Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement scheduledTimeout function.", ex);
} }
} }
}, nextTime); }, nextTime);
} }
} } else {
else {
startEventTimer(time); startEventTimer(time);
} }
} }
@@ -358,6 +339,7 @@ public class EventInstanceManager {
event_schedule.cancel(false); event_schedule.cancel(false);
event_schedule = null; event_schedule = null;
} }
dismissEventTimer(); dismissEventTimer();
} }
@@ -370,7 +352,7 @@ public class EventInstanceManager {
} }
public void registerParty(MapleCharacter chr) { public void registerParty(MapleCharacter chr) {
if(chr.isPartyLeader()) { if (chr.isPartyLeader()) {
registerParty(chr.getParty(), chr.getMap()); registerParty(chr.getParty(), chr.getMap());
} }
} }
@@ -390,20 +372,16 @@ public class EventInstanceManager {
private void registerExpeditionTeam(MapleExpedition exped, int recruitMap) { private void registerExpeditionTeam(MapleExpedition exped, int recruitMap) {
expedition = exped; expedition = exped;
for(MapleCharacter chr: exped.getMembers()) { for (MapleCharacter chr: exped.getMembers()) {
if(chr.getMapId() == recruitMap) if (chr.getMapId() == recruitMap) {
registerPlayer(chr); registerPlayer(chr);
}
} }
} }
public void unregisterPlayer(MapleCharacter chr) { public void unregisterPlayer(final MapleCharacter chr) {
try { try {
sL.lock(); em.getIv().invokeFunction("playerUnregistered", EventInstanceManager.this, chr);
try {
em.getIv().invokeFunction("playerUnregistered", EventInstanceManager.this, chr);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement playerUnregistered function.", ex); Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement playerUnregistered function.", ex);
} }
@@ -462,180 +440,140 @@ public class EventInstanceManager {
} }
} }
public void movePlayer(MapleCharacter chr) { public void movePlayer(final MapleCharacter chr) {
try {
sL.lock();
try {
em.getIv().invokeFunction("moveMap", this, chr);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
}
public void changedMap(MapleCharacter chr, int mapId) {
try {
sL.lock();
try {
em.getIv().invokeFunction("changedMap", this, chr, mapId);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {} // optional
}
public void afterChangedMap(MapleCharacter chr, int mapId) {
try {
sL.lock();
try {
em.getIv().invokeFunction("afterChangedMap", this, chr, mapId);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {} // optional
}
public void changedLeader(MapleCharacter ldr) {
try { try {
sL.lock(); em.getIv().invokeFunction("moveMap", EventInstanceManager.this, chr);
try { } catch (ScriptException | NoSuchMethodException ex) {
em.getIv().invokeFunction("changedLeader", this, ldr); ex.printStackTrace();
} finally { }
sL.unlock(); }
}
} catch (ScriptException | NoSuchMethodException ex) { public void changedMap(final MapleCharacter chr, final int mapId) {
ex.printStackTrace(); try {
} em.getIv().invokeFunction("changedMap", EventInstanceManager.this, chr, mapId);
} catch (ScriptException | NoSuchMethodException ex) {} // optional
}
public void afterChangedMap(final MapleCharacter chr, final int mapId) {
try {
em.getIv().invokeFunction("afterChangedMap", EventInstanceManager.this, chr, mapId);
} catch (ScriptException | NoSuchMethodException ex) {} // optional
}
public synchronized void changedLeader(final MapleCharacter ldr) {
try {
em.getIv().invokeFunction("changedLeader", EventInstanceManager.this, ldr);
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
leaderId = ldr.getId(); leaderId = ldr.getId();
} }
public void monsterKilled(MapleMonster mob, boolean hasKiller) { public void monsterKilled(final MapleMonster mob, final boolean hasKiller) {
int scriptResult = 0;
sL.lock(); sL.lock();
try { try {
mobs.remove(mob); mobs.remove(mob);
if(eventStarted) { if(eventStarted) {
try { scriptResult = 1;
em.getIv().invokeFunction("monsterKilled", mob, this, hasKiller);
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
if (mobs.isEmpty()) { if (mobs.isEmpty()) {
try { scriptResult = 2;
em.getIv().invokeFunction("allMonstersDead", this, hasKiller);
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
} }
} finally { } finally {
sL.unlock(); sL.unlock();
} }
if (scriptResult > 0) {
try {
em.getIv().invokeFunction("monsterKilled", mob, EventInstanceManager.this, hasKiller);
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
if (scriptResult > 1) {
try {
em.getIv().invokeFunction("allMonstersDead", EventInstanceManager.this, hasKiller);
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
}
}
} }
public void friendlyKilled(MapleMonster mob, boolean hasKiller) { public void friendlyKilled(final MapleMonster mob, final boolean hasKiller) {
try { try {
sL.lock(); em.getIv().invokeFunction("friendlyKilled", mob, EventInstanceManager.this, hasKiller);
try {
em.getIv().invokeFunction("friendlyKilled", mob, this, hasKiller);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {} //optional } catch (ScriptException | NoSuchMethodException ex) {} //optional
} }
public void playerKilled(MapleCharacter chr) { public void playerKilled(final MapleCharacter chr) {
try { ThreadManager.getInstance().newTask(new Runnable() {
sL.lock(); @Override
try { public void run() {
em.getIv().invokeFunction("playerDead", this, chr); try {
} finally { em.getIv().invokeFunction("playerDead", EventInstanceManager.this, chr);
sL.unlock(); } catch (ScriptException | NoSuchMethodException ex) {} // optional
} }
} catch (ScriptException | NoSuchMethodException ex) {} // optional });
} }
public void reviveMonster(MapleMonster mob) { public void reviveMonster(final MapleMonster mob) {
try { try {
sL.lock(); em.getIv().invokeFunction("monsterRevive", EventInstanceManager.this, mob);
try { } catch (ScriptException | NoSuchMethodException ex) {} // optional
em.getIv().invokeFunction("monsterRevive", this, mob);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {} // optional
} }
public boolean revivePlayer(MapleCharacter chr) { public boolean revivePlayer(final MapleCharacter chr) {
try { try {
Object b; Object b = em.getIv().invokeFunction("playerRevive", EventInstanceManager.this, chr);
if (b instanceof Boolean) {
sL.lock(); return (Boolean) b;
try {
b = em.getIv().invokeFunction("playerRevive", this, chr);
} finally {
sL.unlock();
} }
} catch (ScriptException | NoSuchMethodException ex) {} // optional
if (b instanceof Boolean) {
return (Boolean) b;
}
} catch (ScriptException | NoSuchMethodException ex) {} // optional
return true; return true;
} }
public void playerDisconnected(MapleCharacter chr) { public void playerDisconnected(final MapleCharacter chr) {
try { try {
sL.lock(); em.getIv().invokeFunction("playerDisconnected", EventInstanceManager.this, chr);
try { } catch (ScriptException | NoSuchMethodException ex) {
em.getIv().invokeFunction("playerDisconnected", this, chr); ex.printStackTrace();
} finally { }
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
MapleEventRecallCoordinator.getInstance().storeEventInstance(chr.getId(), this); MapleEventRecallCoordinator.getInstance().storeEventInstance(chr.getId(), this);
} }
/** public void monsterKilled(MapleCharacter chr, final MapleMonster mob) {
* try {
* @param chr
* @param mob
*/
public void monsterKilled(MapleCharacter chr, MapleMonster mob) {
try {
Integer kc = killCount.get(chr);
int inc; int inc;
sL.lock(); if (ServerConstants.JAVA_8) {
try { inc = (int)em.getIv().invokeFunction("monsterValue", EventInstanceManager.this, mob.getId());
if(ServerConstants.JAVA_8) } else {
inc = (int)em.getIv().invokeFunction("monsterValue", this, mob.getId()); inc = ((Double) em.getIv().invokeFunction("monsterValue", EventInstanceManager.this, mob.getId())).intValue();
else
inc = ((Double) em.getIv().invokeFunction("monsterValue", this, mob.getId())).intValue();
} finally {
sL.unlock();
} }
if (kc == null) { if (inc != 0) {
kc = inc; Integer kc = killCount.get(chr);
} else { if (kc == null) {
kc += inc; kc = inc;
} } else {
killCount.put(chr, kc); kc += inc;
if (expedition != null){ }
expedition.monsterKilled(chr, mob); killCount.put(chr, kc);
} if (expedition != null){
} catch (ScriptException | NoSuchMethodException ex) { expedition.monsterKilled(chr, mob);
ex.printStackTrace(); }
} }
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
public int getKillCount(MapleCharacter chr) { public int getKillCount(MapleCharacter chr) {
@@ -651,14 +589,7 @@ public class EventInstanceManager {
rL.unlock(); rL.unlock();
} }
Thread t = new Thread( new Runnable() { dispose(false);
@Override
public void run() {
dispose(false);
}
});
t.start();
} }
public synchronized void dispose(boolean shutdown) { // should not trigger any event script method after disposed public synchronized void dispose(boolean shutdown) { // should not trigger any event script method after disposed
@@ -666,12 +597,7 @@ public class EventInstanceManager {
disposed = true; disposed = true;
try { try {
sL.lock(); em.getIv().invokeFunction("dispose", EventInstanceManager.this);
try {
em.getIv().invokeFunction("dispose", this);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
@@ -738,13 +664,8 @@ public class EventInstanceManager {
@Override @Override
public void run() { public void run() {
try { try {
sL.lock(); if(em == null) return;
try { em.getIv().invokeFunction(methodName, EventInstanceManager.this);
if(em == null) return;
em.getIv().invokeFunction(methodName, EventInstanceManager.this);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
@@ -848,56 +769,36 @@ public class EventInstanceManager {
} }
} }
public void leftParty(MapleCharacter chr) { public void leftParty(final MapleCharacter chr) {
try { try {
sL.lock(); em.getIv().invokeFunction("leftParty", EventInstanceManager.this, chr);
try { } catch (ScriptException | NoSuchMethodException ex) {
em.getIv().invokeFunction("leftParty", this, chr); ex.printStackTrace();
} finally { }
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
public void disbandParty() { public void disbandParty() {
try { try {
sL.lock(); em.getIv().invokeFunction("disbandParty", EventInstanceManager.this);
try { } catch (ScriptException | NoSuchMethodException ex) {
em.getIv().invokeFunction("disbandParty", this); ex.printStackTrace();
} finally { }
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
public void clearPQ() { public void clearPQ() {
try { try {
sL.lock(); em.getIv().invokeFunction("clearPQ", EventInstanceManager.this);
try { } catch (ScriptException | NoSuchMethodException ex) {
em.getIv().invokeFunction("clearPQ", this); ex.printStackTrace();
} finally { }
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
public void removePlayer(MapleCharacter chr) { public void removePlayer(final MapleCharacter chr) {
try { try {
sL.lock(); em.getIv().invokeFunction("playerExit", EventInstanceManager.this, chr);
try { } catch (ScriptException | NoSuchMethodException ex) {
em.getIv().invokeFunction("playerExit", this, chr); ex.printStackTrace();
} finally { }
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
} }
public boolean isLeader(MapleCharacter chr) { public boolean isLeader(MapleCharacter chr) {
@@ -1158,15 +1059,11 @@ public class EventInstanceManager {
} }
} }
public final void startEvent() { public final synchronized void startEvent() {
eventStarted = true;
try { try {
sL.lock(); em.getIv().invokeFunction("afterSetup", EventInstanceManager.this);
try {
eventStarted = true;
em.getIv().invokeFunction("afterSetup", this);
} finally {
sL.unlock();
}
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
+6 -6
View File
@@ -60,6 +60,8 @@ import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import server.ThreadManager;
//import jdk.nashorn.api.scripting.ScriptUtils;
/** /**
* *
@@ -647,12 +649,11 @@ public class EventManager {
if(p != null) { if(p != null) {
List<MaplePartyCharacter> lmpc; List<MaplePartyCharacter> lmpc;
/* /*if(ServerConstants.JAVA_8) {
if(ServerConstants.JAVA_8)
lmpc = new ArrayList<>(((Map<String, MaplePartyCharacter>)(ScriptUtils.convert(p, Map.class))).values()); lmpc = new ArrayList<>(((Map<String, MaplePartyCharacter>)(ScriptUtils.convert(p, Map.class))).values());
else } else {
lmpc = new ArrayList<>((List<MaplePartyCharacter>) p); lmpc = new ArrayList<>((List<MaplePartyCharacter>) p);
*/ }*/
lmpc = new ArrayList<>((List<MaplePartyCharacter>) p); lmpc = new ArrayList<>((List<MaplePartyCharacter>) p);
party.setEligibleMembers(lmpc); party.setEligibleMembers(lmpc);
@@ -809,8 +810,7 @@ public class EventManager {
} }
private void fillEimQueue() { private void fillEimQueue() {
Thread t = new Thread(new EventManagerWorker()); //call new thread to fill up readied instances queue ThreadManager.getInstance().newTask(new EventManagerWorker()); //call new thread to fill up readied instances queue
t.start();
} }
private EventInstanceManager getReadyInstance() { private EventInstanceManager getReadyInstance() {
@@ -32,6 +32,7 @@ import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import server.ThreadManager;
/** /**
* *
@@ -105,68 +106,62 @@ public class EventScriptScheduler {
public void registerEntry(final Runnable scheduledAction, final long duration) { public void registerEntry(final Runnable scheduledAction, final long duration) {
Thread t = new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
schedulerLock.lock(); schedulerLock.lock();
try { try {
idleProcs = 0; idleProcs = 0;
if(schedulerTask == null) { if(schedulerTask == null) {
if(disposed) return; if(disposed) return;
schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC); schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC);
} }
registeredEntries.put(scheduledAction, Server.getInstance().getCurrentTime() + duration); registeredEntries.put(scheduledAction, Server.getInstance().getCurrentTime() + duration);
} finally { } finally {
schedulerLock.unlock(); schedulerLock.unlock();
} }
} }
}); });
t.start();
} }
public void cancelEntry(final Runnable scheduledAction) { public void cancelEntry(final Runnable scheduledAction) {
Thread t = new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
schedulerLock.lock(); schedulerLock.lock();
try { try {
registeredEntries.remove(scheduledAction); registeredEntries.remove(scheduledAction);
} finally { } finally {
schedulerLock.unlock(); schedulerLock.unlock();
} }
} }
}); });
t.start();
} }
public void dispose() { public void dispose() {
Thread t = new Thread(new Runnable() { ThreadManager.getInstance().newTask(new Runnable() {
@Override @Override
public void run() { public void run() {
schedulerLock.lock(); schedulerLock.lock();
try { try {
if(schedulerTask != null) { if(schedulerTask != null) {
schedulerTask.cancel(false); schedulerTask.cancel(false);
schedulerTask = null; schedulerTask = null;
} }
registeredEntries.clear(); registeredEntries.clear();
disposed = true; disposed = true;
} finally { } finally {
schedulerLock.unlock(); schedulerLock.unlock();
} }
disposeLocks(); disposeLocks();
} }
}); });
t.start();
} }
private void disposeLocks() { private void disposeLocks() {
+1 -1
View File
@@ -59,7 +59,7 @@ public class ItemScriptManager {
return scriptFile.exists(); return scriptFile.exists();
} }
public void getItemScript(MapleClient c, String scriptName) { public void runItemScript(MapleClient c, String scriptName) {
if (scripts.containsKey(scriptName)) { if (scripts.containsKey(scriptName)) {
try { try {
scripts.get(scriptName).invokeFunction("start", new ItemScriptMethods(c)); scripts.get(scriptName).invokeFunction("start", new ItemScriptMethods(c));
+1 -1
View File
@@ -62,7 +62,7 @@ public class MapScriptManager {
return scriptFile.exists(); return scriptFile.exists();
} }
public void getMapScript(MapleClient c, String scriptName, boolean firstUser) { public void runMapScript(MapleClient c, String scriptName, boolean firstUser) {
if (scripts.containsKey(scriptName)) { if (scripts.containsKey(scriptName)) {
try { try {
scripts.get(scriptName).invokeFunction("start", new MapScriptMethods(c)); scripts.get(scriptName).invokeFunction("start", new MapScriptMethods(c));
+6 -4
View File
@@ -646,7 +646,7 @@ public class MapleItemInformationProvider {
} }
public static boolean rollSuccessChance(double prop) { public static boolean rollSuccessChance(double prop) {
return Math.random() > testYourLuck(prop / 100.0, ServerConstants.SCROLL_CHANCE_RATE); return Math.random() >= testYourLuck(prop / 100.0, ServerConstants.SCROLL_CHANCE_RATE);
} }
private static short getMaximumShortMaxIfOverflow(int value1, int value2) { private static short getMaximumShortMaxIfOverflow(int value1, int value2) {
@@ -1238,9 +1238,11 @@ public class MapleItemInformationProvider {
boolean bRestricted = false; boolean bRestricted = false;
if(itemId != 0) { if(itemId != 0) {
MapleData data = getItemData(itemId); MapleData data = getItemData(itemId);
bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1; if (data != null) {
if (!bRestricted) { bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1;
bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; if (!bRestricted) {
bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1;
}
} }
} }
+72
View File
@@ -0,0 +1,72 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package server;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
*
* @author Ronan
*/
public class ThreadManager {
private static ThreadManager instance = new ThreadManager();
public static ThreadManager getInstance() {
return instance;
}
private ThreadPoolExecutor tpe;
private ThreadManager() {}
private class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
Thread t = new Thread(r);
t.start();
}
}
public void newTask(Runnable r) {
tpe.execute(r);
}
public void start() {
RejectedExecutionHandler reh = new RejectedExecutionHandlerImpl();
ThreadFactory tf = Executors.defaultThreadFactory();
tpe = new ThreadPoolExecutor(20, 1000, 77, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50), tf, reh);
}
public void stop() {
tpe.shutdown();
try {
tpe.awaitTermination(5, TimeUnit.MINUTES);
} catch (InterruptedException ie) {}
}
}
+57 -26
View File
@@ -573,14 +573,27 @@ public class MapleMonster extends AbstractLoadedMapleLife {
} }
} }
private void removeController() {
this.lockMonster();
try {
MapleCharacter chrController = getController();
if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
chrController.stopControllingMonster(this);
}
setController(null);
setControllerHasAggro(false);
setControllerKnowsAboutAggro(false);
} finally {
this.unlockMonster();
}
}
public MapleCharacter killBy(final MapleCharacter killer) { public MapleCharacter killBy(final MapleCharacter killer) {
distributeExperience(killer != null ? killer.getId() : 0); distributeExperience(killer != null ? killer.getId() : 0);
MapleCharacter chrController = getController(); removeController();
if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
chrController.stopControllingMonster(this);
}
final List<Integer> toSpawn = this.getRevives(); // this doesn't work (?) final List<Integer> toSpawn = this.getRevives(); // this doesn't work (?)
if (toSpawn != null) { if (toSpawn != null) {
@@ -647,8 +660,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
} }
} }
for(int i = 8810017; i >= 8810010; i--) for(int i = 8810017; i >= 8810010; i--) {
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true); reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
}
} }
if(eim != null) { if(eim != null) {
@@ -787,20 +801,24 @@ public class MapleMonster extends AbstractLoadedMapleLife {
} }
public void switchController(MapleCharacter newController, boolean immediateAggro) { public void switchController(MapleCharacter newController, boolean immediateAggro) {
MapleCharacter controllers = getController(); this.lockMonster();
if (controllers == newController) { try {
return; MapleCharacter controllers = getController();
if (controllers == newController) {
return;
}
removeController();
newController.controlMonster(this, immediateAggro);
setController(newController);
if (immediateAggro) {
setControllerHasAggro(true);
}
setControllerKnowsAboutAggro(false);
} finally {
this.unlockMonster();
} }
if (controllers != null) {
controllers.stopControllingMonster(this);
controllers.getClient().announce(MaplePacketCreator.stopControllingMonster(getObjectId()));
}
newController.controlMonster(this, immediateAggro);
setController(newController);
if (immediateAggro) {
setControllerHasAggro(true);
}
setControllerKnowsAboutAggro(false);
} }
public void addListener(MonsterListener listener) { public void addListener(MonsterListener listener) {
@@ -863,20 +881,21 @@ public class MapleMonster extends AbstractLoadedMapleLife {
} }
@Override @Override
public void sendSpawnData(MapleClient c) { public void sendSpawnData(MapleClient client) {
if (!isAlive()) { if (hp.get() <= 0) { // mustn't monsterLock this function
return; return;
} }
if (isFake()) { if (fake) {
c.announce(MaplePacketCreator.spawnFakeMonster(this, 0)); client.announce(MaplePacketCreator.spawnFakeMonster(this, 0));
} else { } else {
c.announce(MaplePacketCreator.spawnMonster(this, false)); client.announce(MaplePacketCreator.spawnMonster(this, false));
} }
statiLock.lock(); statiLock.lock();
try { try {
if (stati.size() > 0) { if (stati.size() > 0) {
for (final MonsterStatusEffect mse : this.stati.values()) { for (final MonsterStatusEffect mse : this.stati.values()) {
c.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null)); client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null));
} }
} }
} finally { } finally {
@@ -884,7 +903,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
} }
if (hasBossHPBar()) { if (hasBossHPBar()) {
c.announceBossHpBar(this, this.hashCode(), makeBossHPBarPacket()); client.announceBossHpBar(this, this.hashCode(), makeBossHPBarPacket());
} }
} }
@@ -1156,6 +1175,18 @@ public class MapleMonster extends AbstractLoadedMapleLife {
map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration); map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration);
} }
public void refreshMobPosition() {
updateMobPosition(getPosition());
}
public void updateMobPosition(Point newPoint) {
removeController();
setPosition(newPoint);
map.broadcastMessage(MaplePacketCreator.moveMonster(this.getObjectId(), false, -1, 0, 0, 0, this.getPosition(), this.getIdleMovement()));
map.moveMonster(this, this.getPosition());
map.updateMonsterController(this);
}
private void debuffMobStat(MonsterStatus stat) { private void debuffMobStat(MonsterStatus stat) {
statiLock.lock(); statiLock.lock();
try { try {
+2 -6
View File
@@ -44,12 +44,8 @@ public class MapleNPC extends AbstractLoadedMapleLife {
@Override @Override
public void sendSpawnData(MapleClient client) { public void sendSpawnData(MapleClient client) {
if (this.getId() > 9010010 && this.getId() < 9010014) { client.announce(MaplePacketCreator.spawnNPC(this));
client.announce(MaplePacketCreator.spawnNPCRequestController(this, false)); client.announce(MaplePacketCreator.spawnNPCRequestController(this, true));
} else {
client.announce(MaplePacketCreator.spawnNPC(this));
client.announce(MaplePacketCreator.spawnNPCRequestController(this, true));
}
} }
@Override @Override
+2 -2
View File
@@ -103,12 +103,12 @@ public class MobSkill {
this.limit = limit; this.limit = limit;
} }
public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, final List<MapleCharacter> banishPlayers, int animationTime) { public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, int animationTime) {
Runnable toRun = new Runnable() { Runnable toRun = new Runnable() {
@Override @Override
public void run() { public void run() {
if(monster.isAlive()) { if(monster.isAlive()) {
applyEffect(player, monster, skill, banishPlayers); applyEffect(player, monster, skill, null);
} }
} }
}; };

Some files were not shown because too many files have changed in this diff Show More