Services unrestrained to channels + Event scripts placeholder

Fixed an inconsistent scenario where player data would remain in world player storage even though they were no longer online.
Implemented missing functionality for "Safety Charm" which allows 30% MaxHP/MP heal on return.
Improved services facility, no longer tightly related to channels.
Implemented a world service for "save players" (services acts as a monitor).
Reviewed the event script initialization approach. Players no longer are retained from logging in on a channel whilst the events don't finish loadup.
Fixed certain quest items not showing up, which would happen due to them not being quest requisites.
Fixed NPC Pi crashing players when trying to craft arrows.
Fixed pet re-evolution quest not working on Robos.
Fixed boss HPBar not disappearing in certain situations.
Revised gathered mob info on linked mobs, no longer marshaling stats.
Fixed two possible deadlock scenarios within the cancel effect method.
Added lock auditing support for read-write locks.
Implemented code support for Cygnus intro clip.
Reviewed updateBuffEffect, now properly checking for pirate buffs in order to send the expected packet.
Reviewed unnecessary load of field objects, which would be doing so just for fetching the predicted map names.
Fixed mob buff tooltips not showing on "fake" mobs in the event of them turning into "real".
Reviewed usage of "unique" constraint on petid within the inventoryitems table.
Fixed portal in Ariant unexpectedly leading players who completed the "secret passageway" of Sleepywood into it.
Fixed a loop case in quest scripts from Magatia's broker having ore request.
This commit is contained in:
ronancpl
2019-10-29 01:48:58 -03:00
parent 54cdba01ae
commit 7d8d4691da
80 changed files with 944 additions and 496 deletions
+37
View File
@@ -2197,3 +2197,40 @@ Adicionado método que permite executar scripts de mapa no sistema de scripts de
Corrigido problema recente ao referenciar script com progresso "Touch the Sky" (script de portal utilizando métodos de scripts de mapa).
Adicionado fallback para scripts de NPC MapleTV.
Revisado aplicação e abordagem deste código-base nos arquivos XML legado.
15 - 16 Outubro 2019,
Corrigido caso inconsistente onde objeto de jogador seria mantido no storage de jogadores caso o jogador tentasse se deregistrar de um canal (sem desconectar) e não mais retornando na mesma sessão.
Adicionado código para funcionalidade de "Safety Charm", que permite jogadores curar 30% MaxHP/MaxMP ao retornar em campo.
Implementado extensão das facilidades de serviços, agora atuando desjuntamente de canais.
Implementado serviço de mundo para salvar jogadores, assim evitando geração de novas threads para cada jogador que está sendo persistido na DB.
18 Outubro 2019,
Revisado abordagem de inicialização de scripts de eventos em canais. Usuários não são mais bloqueados ao tentar logar devido ao canal não ter todos os eventos carregados.
19 Outubro 2019,
Corrigido certos itens de quest não aparecendo de mobs em casos onde o mesmo não é requisito para iniciar/completar.
Corrigido NPC Pi crashando jogadores ao tentar criar flechas.
Corrigido script de reevolução de pets não atuando em pet Robos, além de agora evitar repetir pets na conversão.
21 - 22 Outubro 2019,
Corrigido boss HPbar não desaparecendo em certos cenários. O mesmo deveria ocorrer assim que mob é retirado do mapa.
Revisado informações de mob, que não são propagáveis, sendo repassados à definição do novo mob.
Revisado carregamento de storages ao inicializar informações de conta podendo retornar nulo indevidamente em caso de exceção lançada.
Corrigido dois casos de deadlock ocorrendo ao tentar cancelar efeito de stat de jogador.
Adicionado auditoria de locks para os componentes que travam para leitura e escrita.
Adicionado clip de introdução de Cygnus.
Revisado updateBuffEffect, agora checando por determinados buffs de pirata e usando pacotes específicos para os mesmos.
Revisado geração desnecessária de objetos de mapa ao carregar nomes para comando "goto".
Corrigido buffs de mob em fakes desaparecendo assim que o mesmo muda de estado para real.
24 Outubro 2019,
Revisado uso de scriptids na geração de PlayerNPCs. Agora somente ids existentes na base especificada vai adiante.
27 Outubro 2019,
Revisado lógica booleana na detecção de foothold abaixo, não realizando múltiplas checagens sobre um mesmo atributo.
28 Outubro 2019,
Revisado lógica recente de uso de petid único levando a lançamento de exceção (transações de DB implica retirada de item de pet do CS storage na DB antes de colocar o mesmo no inventário).
Corrigido portal relacionado a quest em Ariant inesperadamente levando jogadores à passagem secreta de Sleepywood, em caso de conclusão de questline da mesma.
Corrigido loop em scripts de quest de associação a Alcadno/Zenumist em Magatia.
Adicionado utilização de dispose em quest scripts para o comando homônimo.
@@ -0,0 +1,25 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
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 start(ms) {
ms.displayCygnusIntro();
}
+5
View File
@@ -0,0 +1,5 @@
function start(ms) {
if (ms.getJobId() == 1000 && ms.getLevel() >= 10) {
ms.unlockUI();
}
}
+1 -1
View File
@@ -72,7 +72,7 @@ function action(mode, type, selection) {
} else if (status == 2){
cm.dispose();
if (beauty == 1){
if (cm.haveItem(5420002)){
if (cm.haveItem(5420002)){ // thanks MedicOP for noticing uncoded functionality for Hair Membership coupons
cm.setHair(hairnew[selection]);
cm.sendOk("Enjoy your new and improved hairstyle!");
} else if (cm.haveItem(5150001)){
-1
View File
@@ -18,7 +18,6 @@ function action(mode, type, selection) {
cm.sendNext("All right! Let's go!");
} else if (status == 1) {
cm.removeNPC(579711);
cm.removeNPC2(579711);
cm.updateInfo("fire", "0");
cm.playSound("cannonshooter/fire");
cm.sendDirectionInfo("Effect/Direction4.img/effect/cannonshooter/flying/0", 7000, 0, 0, -1, -1);
+13 -12
View File
@@ -173,18 +173,6 @@ function action(mode, type, selection) {
cost = costSet[selectedItem];
}
if (selectedType == 5){ //arrow refine
var itemSet = new Array(2060000,2061000,2060001,2061001,2060002,2061002);
var matSet = new Array(new Array (4003001,4003004),new Array (4003001,4003004),new Array (4011000,4003001,4003004),new Array (4011000,4003001,4003004),
new Array (4011001,4003001,4003005),new Array (4011001,4003001,4003005));
var matQtySet = new Array (new Array (1,1),new Array (1,1),new Array (1,3,10),new Array (1,3,10),new Array (1,5,15),new Array (1,5,15));
var costSet = new Array (0,0,0,0,0,0);
item = itemSet[selectedItem];
mats = matSet[selectedItem];
matQty = matQtySet[selectedItem];
cost = costSet[selectedItem];
}
var prompt = "So, you want me to make some #t" + item + "#s? In that case, how many do you want me to make?";
cm.sendGetNumber(prompt,1,1,100)
@@ -198,6 +186,19 @@ function action(mode, type, selection) {
else
qty = (selection > 0) ? selection : (selection < 0 ? -selection : 1);
// thanks kvmba for noticing arrow selection crashing players
if (selectedType == 5){ //arrow refine
var itemSet = new Array(2060000,2061000,2060001,2061001,2060002,2061002);
var matSet = new Array(new Array (4003001,4003004),new Array (4003001,4003004),new Array (4011000,4003001,4003004),new Array (4011000,4003001,4003004),
new Array (4011001,4003001,4003005),new Array (4011001,4003001,4003005));
var matQtySet = new Array (new Array (1,1),new Array (1,1),new Array (1,3,10),new Array (1,3,10),new Array (1,5,15),new Array (1,5,15));
var costSet = new Array (0,0,0,0,0,0);
item = itemSet[selectedItem];
mats = matSet[selectedItem];
matQty = matQtySet[selectedItem];
cost = costSet[selectedItem];
}
var prompt = "You want me to make ";
if (qty == 1)
prompt += "a #t" + item + "#?";
+3 -2
View File
@@ -77,7 +77,7 @@ function writeFeatureTab_Quests() {
addFeature("3rd job quiz with all 40-question pool available.");
addFeature("Item raising functional.");
addFeature("Cleared issue with player movement during NPC talk.");
addFeature("Reviewed usage of quest progress data as requirement.");
addFeature("Reviewed usage of quest progress data as requisite.");
}
function writeFeatureTab_PlayerSocialNetwork() {
@@ -287,6 +287,7 @@ function writeFeatureTab_Project() {
addFeature("Remodeled item scripts, properly using NPC dialogs.");
addFeature("ThreadTracker: runtime tool for deadlock detection.");
addFeature("Channel, World and Server-wide timer management.");
addFeature("Developed services as preemptive task scheduler.");
addFeature("Thoroughly reviewed encapsulation for player stats.");
addFeature("Heavily reviewed future task management, spawning much less threads and relieving task overload on the TimerManager.");
}
@@ -327,7 +328,7 @@ function action(mode, type, selection) {
status--;
if (status == 0) {
var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. I'm glad to say the development itself had continuously been agraciated by dozens of contributors and cheerers (truly thanks for the trusting vow, guys & gals!).\r\n\r\nTalking about results: many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n";
var sendStr = "HeavenMS was developed on the timespan of 4 years, based on where Solaxia left. I'm glad to say the development itself had continuously been agraciated by dozens of contributors and cheerers (truly thanks for the trusting vow, guys & gals!).\r\n\r\nTalking about results: many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n";
for(var i = 0; i < tabs.length; i++) {
sendStr += "#L" + i + "##b" + tabs[i] + "#k#l\r\n";
}
+1
View File
@@ -22,6 +22,7 @@
// Author: Ronan
function enter(ms) {
// thanks kvmba for noticing some issues running this script
ms.runMapScript();
return false;
}
+1 -4
View File
@@ -1,8 +1,5 @@
function enter(pi) {
if(pi.isQuestCompleted(20730) || pi.isQuestCompleted(21734)) { // puppeteer defeated, newfound secret path
pi.playPortalSound(); pi.warp(105040201,2);
return true;
}
// unexpected warp condition noticed thanks to IxianMace
pi.openNpc(1063011, "ThiefPassword");
return false;
+1 -1
View File
@@ -31,7 +31,7 @@ function start(mode, type, selection) {
} else if (status == 4) {
qm.forceStartQuest();
qm.forceCompleteQuest();
//qm.warp(913040100, 0);
qm.warp(913040100, 0);
qm.dispose();
}
}
+18 -11
View File
@@ -28,32 +28,39 @@
var status = -1;
var oreArray;
function start(mode, type, selection) {
}
function end(mode, type, selection) {
if (mode == -1 || (mode == 0 && type > 0)) {
if (mode == -1) {
qm.dispose();
} else {
oreArray = getOreArray();
if (status == -1) {
if (oreArray.length > 0) {
if (mode == 0 && type > 0) {
qm.dispose();
return;
}
if (mode == 1)
status++;
else
status--;
if (status == 0) {
oreArray = getOreArray();
if (oreArray.length > 0) {
qm.sendSimple("Oh, looks like someone's ready to make a deal. You want to join Zenumist so badly, huh? I really don't understand you, but that's just fine. What will you give me in return?\r\n" + getOreString(oreArray));
} else {
qm.sendOk("What is this, you don't have the ores with you. No ore, no deal.");
qm.sendOk("What is this, you don't have the #rjewel ores#k with you. No ore, no deal.");
qm.dispose();
return;
}
} else if (status == 0) {
} else if (status == 1) {
if (!qm.haveItem(oreArray[selection], 2)) {
qm.sendNext("What's this, you haven't got the ores. No ores no deal!");
qm.sendNext("What's this, you haven't got the #rjewel ores#k. No ores no deal!");
qm.dispose();
return;
}
qm.gainItem(oreArray[selection], -2); // Take 2 ores
qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Zenumist.");
qm.forceCompleteQuest();
} else if (status == 1) {
} else if (status == 2) {
qm.dispose();
}
}
+22 -15
View File
@@ -28,32 +28,39 @@
var status = -1;
var oreArray;
function start(mode, type, selection) {
}
function end(mode, type, selection) {
if (mode == -1 || (mode == 0 && type > 0)) {
if (mode == -1) {
qm.dispose();
} else {
oreArray = getOreArray();
if (status == -1) {
if (oreArray.length > 0) {
status++;
qm.sendSimple("Oh, looks like someone's ready to make a deal. You want to join Alcadno so badly, huh? I really don't understand you, but that's just fine. What will you give me in return?\r\n" + getOreString(oreArray));
} else {
qm.sendOk("What is this, you don't have the ores with you. No ore, no deal.");
if (mode == 0 && type > 0) {
qm.dispose();
return;
}
} else if (status == 0) {
if (mode == 1)
status++;
else
status--;
if (status == 0) {
oreArray = getOreArray();
if (oreArray.length > 0) {
qm.sendSimple("Oh, looks like someone's ready to make a deal. You want to join Zenumist so badly, huh? I really don't understand you, but that's just fine. What will you give me in return?\r\n" + getOreString(oreArray));
} else {
qm.sendOk("What is this, you don't have the #rjewel ores#k with you. No ore, no deal."); // script would loop undefinitely at completion, thanks IxianMace for noticing
qm.dispose();
return;
}
} else if (status == 1) {
if (!qm.haveItem(oreArray[selection], 2)) { // thanks resinate for noticing a function missing here
qm.sendNext("What's this, you haven't got the ores. No ores no deal!");
qm.sendNext("What's this, you haven't got the #rjewel ores#k. No ores no deal!");
qm.dispose();
return;
}
qm.gainItem(oreArray[selection], -2); // Take 2 ores
qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Alcadno.");
qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Zenumist.");
qm.forceCompleteQuest();
} else if (status == 1) {
} else if (status == 2) {
qm.dispose();
}
}
+30 -23
View File
@@ -47,39 +47,46 @@ function end(mode, type, selection) {
} else if (status == 1) {
qm.sendNextPrev("Then here we go...! #rHYAHH!#k");
} else if (status == 2) {
var pet = 0;
if (qm.getPlayer().getPet(0).getItemId() >= 5000029 && qm.getPlayer().getPet(0).getItemId() <= 5000033) {
var pet = 0;
} else if (qm.getPlayer().getPet(1).getItemId() >= 5000029 && qm.getPlayer().getPet(1).getItemId() <= 5000033) {
var pet = 1;
} else if (qm.getPlayer().getPet(2).getItemId() >= 5000029 && qm.getPlayer().getPet(2).getItemId() <= 5000033) {
var pet = 2;
} else {
var petidx = -1;
var petItemid;
for (var i = 0; i < 3; i++) {
var pet = qm.getPlayer().getPet(pet);
if (pet != null) {
var id = pet.getItemId();
if (id >= 5000029 && id <= 5000033) {
petItemid = 5000030;
petidx = i;
break;
} else if (id >= 5000048 && id <= 5000053) { // thanks Conrad for noticing Robo pets not being able to re-evolve
petItemid = 5000049;
petidx = i;
break;
}
}
}
if (petidx == -1) {
qm.sendOk("Something wrong, try again.");
qm.dispose();
return;
}
var id = qm.getPlayer().getPet(pet).getItemId();
if (id < 5000029 || id > 5000033) {
qm.sendOk("Something wrong, try again.");
qm.dispose();
return;
}
var rand = 1 + Math.floor(Math.random() * 10);
var pool = (petItemid == 5000030) ? 10 : 11;
do {
var rand = 1 + Math.floor(Math.random() * pool);
var after = 0;
if (rand >= 1 && rand <= 3) {
after = 5000030;
after = petItemid;
} else if (rand >= 4 && rand <= 6) {
after = 5000031;
after = petItemid + 1;
} else if (rand >= 7 && rand <= 9) {
after = 5000032;
after = petItemid + 2;
} else if (rand == 10) {
after = 5000033;
after = petItemid + 3;
} else {
qm.sendOk("Something wrong. Try again.");
qm.dispose();
return;
after = petItemid + 4;
}
} while (after == pet.getItemId());
/*if (name.equals(MapleItemInformationProvider.getInstance().getName(id))) {
name = MapleItemInformationProvider.getInstance().getName(after);
@@ -87,7 +94,7 @@ function end(mode, type, selection) {
qm.gainMeso(-10000);
qm.gainItem(5380000, -1);
qm.evolvePet(pet, after);
qm.evolvePet(petidx, after);
qm.sendOk("Woo! It worked again! #rYou may find your new pet under your 'CASH' inventory.\r #kIt used to be a #b#i" + id + "##t" + id + "##k, and now it's \r a#b #i" + after + "##t" + after + "##k! \r\n Come back with 10,000 mesos and another Rock of Evolution if you don't like it!\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n#v"+after+"# #t"+after+"#");
} else if (status == 3) {
+2 -2
View File
@@ -12968,7 +12968,7 @@ CREATE TABLE IF NOT EXISTS `inventoryitems` (
`position` int(11) NOT NULL DEFAULT '0',
`quantity` int(11) NOT NULL DEFAULT '0',
`owner` tinytext NOT NULL,
`petid` int(11) unsigned UNIQUE DEFAULT NULL,
`petid` int(11) NOT NULL DEFAULT '-1',
`flag` int(11) NOT NULL,
`expiration` bigint(20) NOT NULL DEFAULT '-1',
`giftFrom` varchar(26) NOT NULL,
@@ -16473,7 +16473,7 @@ CREATE TABLE IF NOT EXISTS `pets` (
PRIMARY KEY (`petid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
ALTER TABLE `inventoryitems` ADD CONSTRAINT `fk_itempetid` FOREIGN KEY (`petid`) REFERENCES `pets` (`petid`) ON DELETE SET NULL ;
ALTER TABLE `inventoryitems` ADD CONSTRAINT `fk_itempetid` FOREIGN KEY (`petid`) REFERENCES `pets` (`petid`) ON DELETE SET NULL ; # thanks Optimist for noticing queries over petid taking too long, shavit for pointing out an improvement using foreign key
CREATE TABLE IF NOT EXISTS `petignores` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+9 -9
View File
@@ -21,17 +21,17 @@ package client;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import server.maps.AbstractAnimatedMapleMapObject;
import server.maps.MapleMap;
@@ -51,13 +51,13 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
protected Map<MapleStat, Integer> statUpdates = new HashMap<>();
protected Lock effLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EFF, true);
protected ReadLock statRlock;
protected WriteLock statWlock;
protected MonitoredReadLock statRlock;
protected MonitoredWriteLock statWlock;
protected AbstractMapleCharacterObject() {
ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.CHARACTER_STA, true);
statRlock = locks.readLock();
statWlock = locks.writeLock();
MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.CHARACTER_STA, true);
statRlock = MonitoredReadLockFactory.createLock(locks);
statWlock = MonitoredWriteLockFactory.createLock(locks);
for (int i = 0; i < remainingSp.length; i++) {
remainingSp[i] = 0;
+33 -8
View File
@@ -171,9 +171,11 @@ import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.Warrior;
import constants.skills.ThunderBreaker;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.BaseService;
import net.server.channel.services.task.FaceExpressionService;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.FaceExpressionService;
import net.server.services.task.world.CharacterSaveService;
import net.server.services.type.WorldServices;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.util.ConcurrentHashSet;
public class MapleCharacter extends AbstractMapleCharacterObject {
@@ -214,6 +216,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private int localchairrate;
private boolean hidden, equipchanged = true, berserk, hasMerchant, hasSandboxItem = false, whiteChat = false, canRecvPartySearchInvite = true;
private boolean equippedMesoMagnet = false, equippedItemPouch = false, equippedPetItemIgnore = false;
private boolean usedSafetyCharm = false;
private float autopotHpAlert, autopotMpAlert;
private int linkedLevel = 0;
private String linkedName = null;
@@ -565,7 +568,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public void setSessionTransitionState() {
client.getSession().setAttribute(MapleClient.CLIENT_TRANSITION);
IoSession session = client.getSession();
session.setAttribute(MapleClient.CLIENT_TRANSITION);
Server.getInstance().setCharacteridInTransition(session, this.getId());
}
public boolean getCS() {
@@ -2846,7 +2851,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if(timeNow - lastExpression > 2000) {
lastExpression = timeNow;
FaceExpressionService service = (FaceExpressionService) client.getChannelServer().getServiceAccess(ServiceType.FACE_EXPRESSION);
FaceExpressionService service = (FaceExpressionService) client.getChannelServer().getServiceAccess(ChannelServices.FACE_EXPRESSION);
service.registerFaceExpression(map, this, emote);
}
}
@@ -3863,11 +3868,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public boolean cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) {
boolean ret;
prtLock.lock();
effLock.lock();
try {
ret = cancelEffect(effect, overwrite, startTime, true);
} finally {
effLock.unlock();
prtLock.unlock();
}
if (effect.isMagicDoor() && ret) {
@@ -4580,6 +4587,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
startChairTask();
}
prtLock.lock();
effLock.lock();
chrLock.lock();
try {
@@ -4596,7 +4604,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
toDeploy = new LinkedHashMap<>();
Map<Integer, Pair<MapleStatEffect, Long>> retrievedEffects = new LinkedHashMap<>();
Set<MapleBuffStat> retrievedStats = new LinkedHashSet<>();
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> statup : appliedStatups.entrySet()) {
MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey());
MapleBuffStatValueHolder statMbsvh = statup.getValue();
@@ -4655,6 +4662,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} finally {
chrLock.unlock();
effLock.unlock();
prtLock.unlock();
}
updateLocalStats();
@@ -5854,13 +5862,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
int amountNeeded, questStatus = this.getQuestStatus(questid);
if (questStatus == 0) {
amountNeeded = MapleQuest.getInstance(questid).getStartItemAmountNeeded(itemid);
if (amountNeeded == Integer.MIN_VALUE) {
return false;
}
} else if (questStatus != 1) {
return false;
} else {
amountNeeded = MapleQuest.getInstance(questid).getCompleteItemAmountNeeded(itemid);
if (amountNeeded == Integer.MAX_VALUE) {
return true;
}
}
return amountNeeded > 0 && getInventory(ItemConstants.getInventoryType(itemid)).countById(itemid) < amountNeeded;
return getInventory(ItemConstants.getInventoryType(itemid)).countById(itemid) < amountNeeded;
}
public int getRank() {
@@ -6208,6 +6222,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (timeNow < getNextBuybackTime() && avail) {
s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k";
s += "\r\n";
} else {
s += "Buyback #bavailable#k";
}
this.showHint(s);
@@ -7619,6 +7635,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (possesed > 0 && !GameConstants.isDojo(getMapId())) {
message("You have used a safety charm, so your EXP points have not been decreased.");
MapleInventoryManipulator.removeById(client, ItemConstants.getInventoryType(charmID[i]), charmID[i], 1, true, false);
usedSafetyCharm = true;
} else if (getJob() != MapleJob.BEGINNER) { //Hmm...
if (!FieldLimit.NO_EXP_DECREASE.check(getMap().getFieldLimit())) { // thanks Conrad for noticing missing FieldLimit check
int XPdummy = ExpTable.getExpNeededForLevel(getLevel());
@@ -7715,7 +7732,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
changeMap(returnMap);
cancelAllBuffs(false); // thanks Oblivium91 for finding out players still could revive in area and take damage before returning to town
if (usedSafetyCharm) { // thanks kvmba for noticing safety charm not providing 30% HP/MP
addMPHP((int) Math.ceil(this.getClientMaxHp() * 0.3), (int) Math.ceil(this.getClientMaxMp() * 0.3));
} else {
updateHp(50);
}
setStance(0);
}
@@ -8378,7 +8401,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
};
ThreadManager.getInstance().newTask(r); //spawns a new thread to deal with this
CharacterSaveService service = (CharacterSaveService) getWorldServer().getServiceAccess(WorldServices.SAVE_CHARACTER);
service.registerSaveCharacter(this.getId(), r);
} else {
saveCharToDB(true);
}
@@ -9958,6 +9982,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (qs.getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
}
// reminder: do not reset quest progress of infoNumbers, some quests cannot backtrack
}
}
+22 -6
View File
@@ -64,7 +64,6 @@ import org.apache.mina.core.session.IoSession;
import client.inventory.MapleInventoryType;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import scripting.AbstractPlayerInteraction;
import scripting.event.EventInstanceManager;
import scripting.event.EventManager;
@@ -120,7 +119,8 @@ public class MapleClient {
private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true);
private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true);
private final Lock announcerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ANNOUNCER, true);
private static final Lock loginLocks[] = new Lock[200]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here
private static final int lockCount = 200;
private static final Lock loginLocks[] = new Lock[lockCount]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here
private Calendar tempBanCalendar;
private int votePoints;
private int voteTime = -1;
@@ -131,7 +131,7 @@ public class MapleClient {
private int lang = 0;
static {
for (int i = 0; i < 200; i++) {
for (int i = 0; i < lockCount; i++) {
loginLocks[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true);
}
}
@@ -454,14 +454,14 @@ public class MapleClient {
}
public int finishLogin() {
Lock loginLock = loginLocks[this.getAccID() % 200];
Lock loginLock = loginLocks[this.getAccID() % lockCount];
loginLock.lock();
try {
if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN
loggedIn = false;
return 7;
}
updateLoginState(LOGIN_LOGGEDIN);
updateLoginState(MapleClient.LOGIN_LOGGEDIN);
} finally {
loginLock.unlock();
}
@@ -851,7 +851,7 @@ public class MapleClient {
if (rs.getTimestamp("lastlogin").getTime() + 30000 < Server.getInstance().getCurrentTime()) {
int accountId = accId;
state = LOGIN_NOTLOGGEDIN;
updateLoginState(LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa
updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa
this.setAccID(accountId);
}
}
@@ -1232,6 +1232,22 @@ public class MapleClient {
return disconnectForBeingAFaggot;
}
public void checkChar(int accid) { /// issue with multiple chars from same account login found by shavit, resinate
if (true) {
return;
}
for (World w : Server.getInstance().getWorlds()) {
for (MapleCharacter chr : w.getPlayerStorage().getAllCharacters()) {
if (accid == chr.getAccountID()) {
FilePrinter.print(FilePrinter.EXPLOITS, "Player: " + chr.getName() + " has been removed from " + GameConstants.WORLD_NAMES[w.getId()] + ". Possible Dupe attempt.");
chr.getClient().forceDisconnect();
w.getPlayerStorage().removePlayer(chr.getId());
}
}
}
}
public int getVotePoints(){
int points = 0;
try {
+12 -2
View File
@@ -82,7 +82,9 @@ public final class MonsterBook {
}
if(qty < 5) {
calculateLevel(); // current leveling system only accounts unique cards...
if (qty == 0) { // leveling system only accounts unique cards
calculateLevel();
}
c.announce(MaplePacketCreator.addCard(false, cardid, qty + 1));
c.announce(MaplePacketCreator.showGainCard());
@@ -94,7 +96,15 @@ public final class MonsterBook {
private void calculateLevel() {
lock.lock();
try {
bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5));
int collectionExp = (normalCard + specialCard);
int level = 0, expToNextlevel = 1;
do {
level++;
expToNextlevel += level * 10;
} while (collectionExp >= expToNextlevel);
bookLevel = level; // thanks IxianMace for noticing book level differing between book UI and character info UI
} finally {
lock.unlock();
}
@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.command.Command;
import client.MapleClient;
import scripting.npc.NPCScriptManager;
import scripting.quest.QuestScriptManager;
import tools.MaplePacketCreator;
public class DisposeCommand extends Command {
@@ -36,6 +37,7 @@ public class DisposeCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
NPCScriptManager.getInstance().dispose(c);
QuestScriptManager.getInstance().dispose(c);
c.announce(MaplePacketCreator.enableActions());
c.removeClickedNPC();
c.getPlayer().message("You've been disposed.");
@@ -42,21 +42,26 @@ public class MapOwnerClaimCommand extends Command {
if (YamlConfig.config.server.USE_MAP_OWNERSHIP_SYSTEM) {
if (chr.getEventInstance() == null) {
MapleMap map = chr.getMap();
if (map.countBosses() == 0) { // thanks Conrad for suggesting bosses prevent map leasing
MapleMap ownedMap = chr.getOwnedMap(); // thanks Conrad for suggesting not unlease a map as soon as player exits it
if (ownedMap != null) {
ownedMap.unclaimOwnership(chr);
if (chr.getMap() == ownedMap) {
if (map == ownedMap) {
chr.dropMessage(5, "This lawn is now free real estate.");
return;
}
}
if (chr.getMap().claimOwnership(chr)) {
if (map.claimOwnership(chr)) {
chr.dropMessage(5, "You have leased this lawn for a while, until you leave here or after 1 minute of inactivity.");
} else {
chr.dropMessage(5, "This lawn has already been leased by a player.");
}
} else {
chr.dropMessage(5, "This lawn is currently under a boss siege.");
}
} else {
chr.dropMessage(5, "This lawn cannot be leased.");
}
@@ -29,11 +29,10 @@ import client.MapleClient;
import constants.game.GameConstants;
import java.util.ArrayList;
import java.util.Collections;
import net.server.Server;
import server.maps.MaplePortal;
import server.maps.FieldLimit;
import server.maps.MapleMap;
import server.maps.MapleMapManager;
import server.maps.MapleMapFactory;
import server.maps.MapleMiniDungeonInfo;
import java.util.Comparator;
@@ -47,19 +46,28 @@ public class GotoCommand extends Command {
{
setDescription("");
MapleMapManager mapManager = Server.getInstance().getWorlds().get(0).getChannels().get(0).getMapFactory();
List<Entry<String, Integer>> towns = new ArrayList<>(GameConstants.GOTO_TOWNS.entrySet());
sortGotoEntries(towns);
try {
// thanks shavit for noticing goto areas getting loaded from wz needlessly, only for the name retrieval
for (Map.Entry<String, Integer> e : towns) {
GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (mapManager.getMap(e.getValue()).getMapName()) + "#k\r\n");
GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (MapleMapFactory.loadPlaceName(e.getValue())) + "#k\r\n");
}
List<Entry<String, Integer>> areas = new ArrayList<>(GameConstants.GOTO_AREAS.entrySet());
sortGotoEntries(areas);
for (Map.Entry<String, Integer> e : areas) {
GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (mapManager.getMap(e.getValue()).getMapName()) + "#k\r\n");
GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (MapleMapFactory.loadPlaceName(e.getValue())) + "#k\r\n");
}
} catch (Exception e) {
e.printStackTrace();
GOTO_TOWNS_INFO = "(none)";
GOTO_AREAS_INFO = "(none)";
}
}
public static String GOTO_TOWNS_INFO = "";
-4
View File
@@ -127,10 +127,6 @@ public class Item implements Comparable<Item> {
return petid;
}
public Integer getPetIdForDb() {
return petid > -1 ? petid : null;
}
@Override
public int compareTo(Item other) {
if (this.id < other.getItemId()) {
+2 -2
View File
@@ -234,7 +234,7 @@ public enum ItemFactory {
ps.setInt(6, item.getPosition());
ps.setInt(7, item.getQuantity());
ps.setString(8, item.getOwner());
ps.setObject(9, item.getPetIdForDb(), java.sql.Types.INTEGER);
ps.setInt(9, item.getPetId()); // thanks Daddy Egg for alerting a case of unique petid constraint breach getting raised
ps.setInt(10, item.getFlag());
ps.setLong(11, item.getExpiration());
ps.setString(12, item.getGiftFrom());
@@ -414,7 +414,7 @@ public enum ItemFactory {
ps.setInt(6, item.getPosition());
ps.setInt(7, item.getQuantity());
ps.setString(8, item.getOwner());
ps.setObject(9, item.getPetIdForDb(), java.sql.Types.INTEGER);
ps.setInt(9, item.getPetId());
ps.setInt(10, item.getFlag());
ps.setLong(11, item.getExpiration());
ps.setString(12, item.getGiftFrom());
+1 -1
View File
@@ -573,7 +573,7 @@ public class GameConstants {
}
public static boolean isFreeMarketRoom(int mapid) {
return mapid > 910000000 && mapid < 910000023;
return mapid / 1000000 == 910 && mapid > 910000000; // FM rooms subset, thanks to shavit
}
public static boolean isMerchantLocked(MapleMap map) {
+7 -6
View File
@@ -28,18 +28,19 @@ import java.util.Collection;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
public class PlayerStorage {
private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_STORAGE, true);
private final MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_STORAGE, true);
private final Map<Integer, MapleCharacter> storage = new LinkedHashMap<>();
private final Map<String, MapleCharacter> nameStorage = new LinkedHashMap<>();
private ReadLock rlock = locks.readLock();
private WriteLock wlock = locks.writeLock();
private MonitoredReadLock rlock = MonitoredReadLockFactory.createLock(locks);
private MonitoredWriteLock wlock = MonitoredWriteLockFactory.createLock(locks);
public void addPlayer(MapleCharacter chr) {
wlock.lock();
+12 -12
View File
@@ -41,15 +41,16 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import config.YamlConfig;
import net.server.audit.ThreadTracker;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
@@ -87,7 +88,6 @@ import client.SkillFactory;
import client.command.CommandsExecutor;
import client.inventory.Item;
import client.inventory.ItemFactory;
import client.inventory.MaplePet;
import client.inventory.manipulator.MapleCashidGenerator;
import client.newyear.NewYearCardRecord;
import constants.inventory.ItemConstants;
@@ -146,13 +146,13 @@ public class Server {
private final Lock srvLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER);
private final Lock disLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_DISEASES);
private final ReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true);
private final ReadLock wldRLock = wldLock.readLock();
private final WriteLock wldWLock = wldLock.writeLock();
private final MonitoredReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true);
private final MonitoredReadLock wldRLock = MonitoredReadLockFactory.createLock(wldLock);
private final MonitoredWriteLock wldWLock = MonitoredWriteLockFactory.createLock(wldLock);
private final ReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true);
private final ReadLock lgnRLock = lgnLock.readLock();
private final WriteLock lgnWLock = lgnLock.writeLock();
private final MonitoredReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true);
private final MonitoredReadLock lgnRLock = MonitoredReadLockFactory.createLock(lgnLock);
private final MonitoredWriteLock lgnWLock = MonitoredWriteLockFactory.createLock(lgnLock);
private final AtomicLong currentTime = new AtomicLong(0);
private long serverCurrentTime = 0;
@@ -1897,12 +1897,12 @@ public class Server {
}
}
resetServerWorlds();
ThreadManager.getInstance().stop();
TimerManager.getInstance().purge();
TimerManager.getInstance().stop();
resetServerWorlds();
System.out.println("Worlds + Channels are offline.");
acceptor.unbind();
acceptor = null;
@@ -78,6 +78,7 @@ public enum MonitoredLockType {
WORLD_PSHOPS,
WORLD_MERCHS,
WORLD_MAPOBJS,
WORLD_SAVECHARS,
WORLD_SUGGEST,
EIM,
EIM_PARTY,
+19 -18
View File
@@ -34,9 +34,11 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import config.YamlConfig;
import net.server.audit.LockCollector;
@@ -64,6 +66,10 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import client.MapleCharacter;
import net.server.services.ServicesManager;
import net.server.services.BaseService;
import net.server.services.type.ChannelServices;
import scripting.event.EventScriptManager;
import server.TimerManager;
import server.events.gm.MapleEvent;
@@ -73,13 +79,9 @@ import server.maps.MapleHiredMerchant;
import server.maps.MapleMap;
import server.maps.MapleMapManager;
import server.maps.MapleMiniDungeon;
import server.maps.MapleMiniDungeonInfo;
import tools.MaplePacketCreator;
import tools.Pair;
import client.MapleCharacter;
import net.server.channel.services.ServiceType;
import net.server.channel.services.ServicesManager;
import net.server.channel.services.task.BaseService;
import server.maps.MapleMiniDungeonInfo;
public final class Channel {
@@ -90,7 +92,7 @@ public final class Channel {
private String ip, serverMessage;
private MapleMapManager mapManager;
private EventScriptManager eventSM;
private ServicesManager services = new ServicesManager();
private ServicesManager services;
private Map<Integer, MapleHiredMerchant> hiredMerchants = new HashMap<>();
private final Map<Integer, Integer> storedVars = new HashMap<>();
private Set<Integer> playersAway = new HashSet<>();
@@ -121,9 +123,9 @@ public final class Channel {
private Set<Integer> ongoingCathedralGuests = null;
private long ongoingStartTime;
private ReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MERCHANT, true);
private ReadLock merchRlock = merchantLock.readLock();
private WriteLock merchWlock = merchantLock.writeLock();
private MonitoredReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MERCHANT, true);
private MonitoredReadLock merchRlock = MonitoredReadLockFactory.createLock(merchantLock);
private MonitoredWriteLock merchWlock = MonitoredWriteLockFactory.createLock(merchantLock);
private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[YamlConfig.config.server.CHANNEL_LOCKS];
@@ -155,8 +157,8 @@ public final class Channel {
eventSM = new EventScriptManager(this, getEvents());
eventSM.init();
} else {
String[] ev = {};
eventSM = new EventScriptManager(null, ev);
String[] ev = {"0_EXAMPLE"};
eventSM = new EventScriptManager(this, ev);
}
dojoStage = new int[20];
@@ -168,7 +170,7 @@ public final class Channel {
dojoTask[i] = null;
}
services = new ServicesManager();
services = new ServicesManager(ChannelServices.OVERALL);
System.out.println(" Channel " + getId() + ": Listening on port " + port);
} catch (Exception e) {
@@ -184,7 +186,6 @@ public final class Channel {
eventSM.cancel();
eventSM = null;
eventSM = new EventScriptManager(this, getEvents());
eventSM.init();
}
public final synchronized void shutdown() {
@@ -199,7 +200,7 @@ public final class Channel {
disconnectAwayPlayers();
players.disconnectAll();
eventSM.cancel();
eventSM.dispose();
eventSM = null;
mapManager.dispose();
@@ -277,7 +278,7 @@ public final class Channel {
return mapManager;
}
public BaseService getServiceAccess(ServiceType sv) {
public BaseService getServiceAccess(ChannelServices sv) {
return services.getAccess(sv).getService();
}
@@ -39,7 +39,8 @@ public final class NPCAnimationHandler extends AbstractMaplePacketHandler {
if (length == 6) { // NPC Talk
mplew.writeShort(SendOpcode.NPC_ACTION.getValue());
mplew.writeInt(slea.readInt());
mplew.writeShort(slea.readShort());
mplew.write(slea.readByte()); // 2 bytes, thanks resinate
mplew.write(slea.readByte());
c.announce(mplew.getPacket());
} else if (length > 6) { // NPC Move
byte[] bytes = slea.read(length - 9);
@@ -63,7 +63,6 @@ import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.game.GameConstants;
import constants.game.ScriptableNPCConstants;
import constants.net.ServerConstants;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
@@ -116,16 +115,13 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
}
Channel cserv = wserv.getChannel(c.getChannel());
if(cserv == null || !cserv.isActive()) {
if(cserv == null) {
c.setChannel(1);
cserv = wserv.getChannel(c.getChannel());
if(cserv == null) {
c.disconnect(true, false);
return;
} else if (!cserv.isActive()) {
c.announce(MaplePacketCreator.getAfterLoginError(7));
return;
}
}
@@ -133,13 +129,13 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
boolean newcomer = false;
IoSession session = c.getSession();
String remoteHwid;
if (player == null) {
if (!server.validateCharacteridInTransition(session, cid)) {
c.disconnect(true, false);
return;
}
String remoteHwid;
if (player == null) {
remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
if (remoteHwid == null) {
c.disconnect(true, false);
@@ -213,7 +213,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(oid, bouncedamage), false, true);
attacker.aggroMonsterDamage(chr, bouncedamage);
}
MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.BODY_PRESSURE);
MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.BODY_PRESSURE); // thanks Atoot for noticing an issue on Body Pressure neutralise
if (bPressure != null) {
Skill skill = SkillFactory.getSkill(Aran.BODY_PRESSURE);
if (!attacker.alreadyBuffedStats().contains(MonsterStatus.NEUTRALISE)) {
@@ -37,8 +37,12 @@ public class LoginStorage {
private ConcurrentHashMap<Integer, List<Long>> loginHistory = new ConcurrentHashMap<>();
public boolean registerLogin(int accountId) {
List<Long> accHist = loginHistory.putIfAbsent(accountId, new LinkedList<Long>());
if (accHist != null) {
List<Long> accHist = loginHistory.get(accountId);
if (accHist == null) {
accHist = new LinkedList<Long>();
loginHistory.put(accountId, accHist);
}
synchronized (accHist) {
if (accHist.size() > YamlConfig.config.server.MAX_ACCOUNT_LOGIN_ATTEMPT) {
long blockExpiration = Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION;
@@ -46,12 +50,7 @@ public class LoginStorage {
return false;
}
}
} else {
accHist = loginHistory.get(accountId);
}
synchronized (accHist) {
accHist.add(Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION);
return true;
}
@@ -25,8 +25,6 @@ import config.YamlConfig;
import java.io.File;
import net.server.world.MapleParty;
import net.server.coordinator.world.MapleInviteCoordinator.InviteType;
import net.server.coordinator.partysearch.PartySearchEchelon;
import net.server.coordinator.partysearch.PartySearchStorage;
import tools.MaplePacketCreator;
import tools.Pair;
@@ -38,11 +36,12 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import net.server.coordinator.world.MapleInviteCoordinator;
import provider.MapleData;
import provider.MapleDataProviderFactory;
@@ -58,9 +57,9 @@ public class MaplePartySearchCoordinator {
private Map<MapleJob, PartySearchEchelon> upcomers = new HashMap<>();
private List<MapleCharacter> leaderQueue = new LinkedList<>();
private final ReentrantReadWriteLock leaderQueueLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_QUEUE, true);
private final ReadLock leaderQueueRLock = leaderQueueLock.readLock();
private final WriteLock leaderQueueWLock = leaderQueueLock.writeLock();
private final MonitoredReentrantReadWriteLock leaderQueueLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_QUEUE, true);
private final MonitoredReadLock leaderQueueRLock = MonitoredReadLockFactory.createLock(leaderQueueLock);
private final MonitoredWriteLock leaderQueueWLock = MonitoredWriteLockFactory.createLock(leaderQueueLock);
private Map<Integer, MapleCharacter> searchLeaders = new HashMap<>();
private Map<Integer, LeaderSearchMetadata> searchSettings = new HashMap<>();
@@ -25,12 +25,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import java.lang.ref.WeakReference;
@@ -40,9 +40,9 @@ import java.lang.ref.WeakReference;
*/
public class PartySearchEchelon {
private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_ECHELON, true);
private final ReadLock psRLock = psLock.readLock();
private final WriteLock psWLock = psLock.writeLock();
private final MonitoredReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_ECHELON, true);
private final MonitoredReadLock psRLock = MonitoredReadLockFactory.createLock(psLock);
private final MonitoredWriteLock psWLock = MonitoredWriteLockFactory.createLock(psLock);
private Map<Integer, WeakReference<MapleCharacter>> echelon = new HashMap<>(20);
@@ -28,12 +28,14 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import tools.IntervalBuilder;
/**
@@ -45,9 +47,9 @@ public class PartySearchStorage {
private List<PartySearchCharacter> storage = new ArrayList<>(20);
private IntervalBuilder emptyIntervals = new IntervalBuilder();
private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_STORAGE, true);
private final ReadLock psRLock = psLock.readLock();
private final WriteLock psWLock = psLock.writeLock();
private final MonitoredReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_STORAGE, true);
private final MonitoredReadLock psRLock = MonitoredReadLockFactory.createLock(psLock);
private final MonitoredWriteLock psWLock = MonitoredWriteLockFactory.createLock(psLock);
public List<PartySearchCharacter> getStorageList() {
psRLock.lock();
@@ -44,7 +44,7 @@ public final class CharlistRequestHandler extends AbstractMaplePacketHandler {
int channel = slea.readByte() + 1;
Channel ch = wserv.getChannel(channel);
if(ch == null || !ch.isActive()) {
if(ch == null) {
c.announce(MaplePacketCreator.getServerStatus(2));
return;
}
@@ -150,6 +150,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
return;
}
if (c.finishLogin() == 0) {
c.checkChar(c.getAccID());
login(c);
} else {
c.announce(MaplePacketCreator.getLoginFailed(7));
@@ -17,7 +17,7 @@
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.services;
package net.server.services;
import config.YamlConfig;
import java.util.Collections;
@@ -17,7 +17,7 @@
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.services.task;
package net.server.services;
import config.YamlConfig;
@@ -17,7 +17,7 @@
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.services;
package net.server.services;
import java.util.List;
@@ -17,9 +17,7 @@
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.services;
import net.server.channel.services.task.BaseService;
package net.server.services;
/**
*
+30
View File
@@ -0,0 +1,30 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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.services;
/**
*
* @author Ronan
*/
public interface ServiceType <T extends Enum<?>> {
public abstract Service createService();
public int ordinal();
public T[] enumValues();
}
@@ -17,7 +17,7 @@
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.services;
package net.server.services;
/**
*
@@ -27,12 +27,12 @@ public class ServicesManager {
private Service[] services;
public ServicesManager() {
ServiceType[] serviceTypes = ServiceType.values();
public ServicesManager(ServiceType serviceBundle) {
Enum[] serviceTypes = serviceBundle.enumValues();
services = new Service[serviceTypes.length];
for (ServiceType type : serviceTypes) {
services[type.ordinal()] = type.createService();
for (Enum type : serviceTypes) {
services[type.ordinal()] = ((ServiceType) type).createService();
}
}
@@ -41,7 +41,7 @@ public class ServicesManager {
}
public void shutdown() {
for (int i = 0; i < ServiceType.values().length; i++) {
for (int i = 0; i < services.length; i++) {
services[i].dispose();
}
services = null;
@@ -17,11 +17,12 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
import net.server.services.BaseScheduler;
/**
*
@@ -37,6 +38,7 @@ public class EventService extends BaseService {
}
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(eventSchedulers[i] != null) {
@@ -17,8 +17,9 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import client.MapleCharacter;
import config.YamlConfig;
import java.util.Collections;
@@ -26,7 +27,7 @@ import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.BaseScheduler;
import net.server.services.BaseScheduler;
import server.maps.MapleMap;
import tools.MaplePacketCreator;
@@ -61,6 +62,7 @@ public class FaceExpressionService extends BaseService {
});
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(faceExpressionSchedulers[i] != null) {
@@ -17,8 +17,9 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
@@ -28,8 +29,8 @@ import java.util.Set;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.BaseScheduler;
import net.server.channel.services.SchedulerListener;
import net.server.services.BaseScheduler;
import net.server.services.SchedulerListener;
/**
*
@@ -45,6 +46,7 @@ public class MobAnimationService extends BaseService {
}
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobAnimationSchedulers[i] != null) {
@@ -17,11 +17,12 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
import net.server.services.BaseScheduler;
/**
*
@@ -37,6 +38,7 @@ public class MobClearSkillService extends BaseService {
}
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobClearSkillSchedulers[i] != null) {
@@ -17,11 +17,12 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
import net.server.services.BaseScheduler;
/**
*
@@ -37,6 +38,7 @@ public class MobMistService extends BaseService {
}
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobMistSchedulers[i] != null) {
@@ -17,8 +17,9 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import client.status.MonsterStatusEffect;
import config.YamlConfig;
import java.util.HashMap;
@@ -29,8 +30,8 @@ import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.BaseScheduler;
import net.server.channel.services.SchedulerListener;
import net.server.services.BaseScheduler;
import net.server.services.SchedulerListener;
/**
*
@@ -46,6 +47,7 @@ public class MobStatusService extends BaseService {
}
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobStatusSchedulers[i] != null) {
@@ -17,11 +17,12 @@
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.services.task;
package net.server.services.task.channel;
import net.server.services.BaseService;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
import net.server.services.BaseScheduler;
/**
*
@@ -37,6 +38,7 @@ public class OverallService extends BaseService { // thanks Alex for suggestin
}
}
@Override
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(channelSchedulers[i] != null) {
@@ -0,0 +1,62 @@
/*
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.services.task.world;
import net.server.audit.locks.MonitoredLockType;
import net.server.services.BaseScheduler;
import net.server.services.BaseService;
/**
*
* @author Ronan
*/
public class CharacterSaveService extends BaseService {
CharacterSaveScheduler chrSaveScheduler = new CharacterSaveScheduler();
@Override
public void dispose() {
if(chrSaveScheduler != null) {
chrSaveScheduler.dispose();
chrSaveScheduler = null;
}
}
public void registerSaveCharacter(int characterId, Runnable runAction) {
chrSaveScheduler.registerSaveCharacter(characterId, runAction);
}
private class CharacterSaveScheduler extends BaseScheduler {
public CharacterSaveScheduler() {
super(MonitoredLockType.WORLD_SAVECHARS);
}
public void registerSaveCharacter(Integer characterId, Runnable runAction) {
registerEntry(characterId, runAction, 0);
}
public void unregisterSaveCharacter(Integer characterId) {
interruptEntry(characterId);
}
}
}
@@ -0,0 +1,63 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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.services.type;
import net.server.services.ServiceType;
import net.server.services.task.channel.EventService;
import net.server.services.task.channel.FaceExpressionService;
import net.server.services.task.channel.MobAnimationService;
import net.server.services.task.channel.MobClearSkillService;
import net.server.services.task.channel.MobMistService;
import net.server.services.task.channel.MobStatusService;
import net.server.services.task.channel.OverallService;
import net.server.services.BaseService;
import net.server.services.Service;
/**
*
* @author Ronan
*/
public enum ChannelServices implements ServiceType {
MOB_STATUS(MobStatusService.class),
MOB_ANIMATION(MobAnimationService.class),
MOB_CLEAR_SKILL(MobClearSkillService.class),
MOB_MIST(MobMistService.class),
FACE_EXPRESSION(FaceExpressionService.class),
EVENT(EventService.class),
OVERALL(OverallService.class);
private Class<? extends BaseService> s;
private ChannelServices(Class<? extends BaseService> service) {
s = service;
}
@Override
public Service createService() {
return new Service(s);
}
@Override
public ChannelServices[] enumValues() {
return ChannelServices.values();
}
}
@@ -17,32 +17,35 @@
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.services;
package net.server.services.type;
import net.server.channel.services.task.*;
import net.server.services.ServiceType;
import net.server.services.BaseService;
import net.server.services.Service;
import net.server.services.task.world.CharacterSaveService;
/**
*
* @author Ronan
*/
public enum ServiceType {
public enum WorldServices implements ServiceType {
MOB_STATUS(MobStatusService.class),
MOB_ANIMATION(MobAnimationService.class),
MOB_CLEAR_SKILL(MobClearSkillService.class),
MOB_MIST(MobMistService.class),
FACE_EXPRESSION(FaceExpressionService.class),
EVENT(EventService.class),
OVERALL(OverallService.class);
SAVE_CHARACTER(CharacterSaveService.class);
private Class<? extends BaseService> s;
private ServiceType(Class<? extends BaseService> service) {
private WorldServices(Class<? extends BaseService> service) {
s = service;
}
@Override
public Service createService() {
return new Service(s);
}
@Override
public WorldServices[] enumValues() {
return WorldServices.values();
}
}
+23 -10
View File
@@ -29,7 +29,6 @@ import client.MapleCharacter;
import client.MapleFamily;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -50,9 +49,6 @@ import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.Set;
import java.util.HashSet;
@@ -74,9 +70,13 @@ import net.server.PlayerStorage;
import net.server.Server;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import net.server.channel.Channel;
import net.server.channel.CharacterIdChannelPair;
import net.server.coordinator.world.MapleInviteCoordinator;
@@ -87,6 +87,9 @@ import net.server.coordinator.partysearch.MaplePartySearchCoordinator;
import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter;
import net.server.guild.MapleGuildSummary;
import net.server.services.BaseService;
import net.server.services.ServicesManager;
import net.server.services.type.WorldServices;
import net.server.task.CharacterAutosaverTask;
import net.server.task.FamilyDailyResetTask;
import net.server.task.FishingTask;
@@ -123,12 +126,13 @@ public class World {
private Map<Integer, Pair<Integer, Integer>> relationshipCouples = new HashMap<>();
private Map<Integer, MapleGuildSummary> gsStore = new HashMap<>();
private PlayerStorage players = new PlayerStorage();
private ServicesManager services = new ServicesManager(WorldServices.SAVE_CHARACTER);
private MapleMatchCheckerCoordinator matchChecker = new MapleMatchCheckerCoordinator();
private MaplePartySearchCoordinator partySearch = new MaplePartySearchCoordinator();
private final ReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true);
private ReadLock chnRLock = chnLock.readLock();
private WriteLock chnWLock = chnLock.writeLock();
private final MonitoredReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true);
private MonitoredReadLock chnRLock = MonitoredReadLockFactory.createLock(chnLock);
private MonitoredWriteLock chnWLock = MonitoredWriteLockFactory.createLock(chnLock);
private Map<Integer, SortedMap<Integer, MapleCharacter>> accountChars = new HashMap<>();
private Map<Integer, MapleStorage> accountStorages = new HashMap<>();
@@ -145,9 +149,9 @@ public class World {
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
private List<Map<Integer, Integer>> cashItemBought = new ArrayList<>(9);
private final ReentrantReadWriteLock suggestLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_SUGGEST, true);
private ReadLock suggestRLock = suggestLock.readLock();
private WriteLock suggestWLock = suggestLock.writeLock();
private final MonitoredReentrantReadWriteLock suggestLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_SUGGEST, true);
private MonitoredReadLock suggestRLock = MonitoredReadLockFactory.createLock(suggestLock);
private MonitoredWriteLock suggestWLock = MonitoredWriteLockFactory.createLock(suggestLock);
private Map<Integer, Integer> disabledServerMessages = new HashMap<>(); // reuse owl lock
private MonitoredReentrantLock srvMessagesLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_SRVMESSAGES);
@@ -2064,6 +2068,14 @@ public class World {
partySearch.runPartySearch();
}
public BaseService getServiceAccess(WorldServices sv) {
return services.getAccess(sv).getService();
}
private void closeWorldServices() {
services.shutdown();
}
private void clearWorldData() {
List<MapleParty> pList;
partyLock.lock();
@@ -2077,6 +2089,7 @@ public class World {
p.disposeLocks();
}
closeWorldServices();
disposeLocks();
}
@@ -56,6 +56,7 @@ import tools.MaplePacketCreator;
import client.MapleCharacter;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleClient;
import client.MapleJob;
import client.MapleQuestStatus;
import client.SkillFactory;
import client.inventory.Equip;
@@ -94,6 +95,18 @@ public class AbstractPlayerInteraction {
return c.getPlayer();
}
public int getJobId() {
return getPlayer().getJob().getId();
}
public MapleJob getJob(){
return getPlayer().getJob();
}
public int getLevel() {
return getPlayer().getLevel();
}
public MapleMap getMap() {
return c.getPlayer().getMap();
}
@@ -36,8 +36,12 @@ import config.YamlConfig;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import server.maps.MaplePortal;
@@ -55,9 +59,6 @@ import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import java.awt.Point;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.server.coordinator.world.MapleEventRecallCoordinator;
@@ -91,9 +92,9 @@ public class EventInstanceManager {
private MapleExpedition expedition = null;
private List<Integer> mapIds = new LinkedList<>();
private final ReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredLockType.EIM, true);
private ReadLock rL = lock.readLock();
private WriteLock wL = lock.writeLock();
private final MonitoredReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredLockType.EIM, true);
private MonitoredReadLock rL = MonitoredReadLockFactory.createLock(lock);
private MonitoredWriteLock wL = MonitoredWriteLockFactory.createLock(lock);
private MonitoredReentrantLock pL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_PARTY, true);
private MonitoredReentrantLock sL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_SCRIPT, true);
+29 -8
View File
@@ -21,9 +21,11 @@
*/
package scripting.event;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -48,7 +50,8 @@ public class EventScriptManager extends AbstractScriptManager {
public EventManager em;
}
private Map<String, EventEntry> events = new LinkedHashMap<>();
private static EventEntry fallback;
private Map<String, EventEntry> events = new ConcurrentHashMap<>();
private boolean active = false;
public EventScriptManager(Channel cserv, String[] scripts) {
@@ -59,12 +62,15 @@ public class EventScriptManager extends AbstractScriptManager {
events.put(script, new EventEntry(iv, new EventManager(cserv, iv, script)));
}
}
init();
fallback = events.remove("0_EXAMPLE");
}
public EventManager getEventManager(String event) {
EventEntry entry = events.get(event);
if (entry == null) {
return null;
return fallback.em;
}
return entry.em;
}
@@ -73,7 +79,7 @@ public class EventScriptManager extends AbstractScriptManager {
return active;
}
public void init() {
public final void init() {
for (EventEntry entry : events.values()) {
try {
entry.iv.put("em", entry.em);
@@ -84,16 +90,17 @@ public class EventScriptManager extends AbstractScriptManager {
}
}
active = true;
active = events.size() > 1; // bootup loads only 1 script
}
private void reloadScripts() {
if (events.isEmpty()) {
Set<Entry<String, EventEntry>> eventEntries = new HashSet<>(events.entrySet());
if (eventEntries.isEmpty()) {
return;
}
Channel cserv = events.values().iterator().next().em.getChannelServer();
for (Entry<String, EventEntry> entry : events.entrySet()) {
Channel cserv = eventEntries.iterator().next().getValue().em.getChannelServer();
for (Entry<String, EventEntry> entry : eventEntries) {
String script = entry.getKey();
NashornScriptEngine iv = getScriptEngine("event/" + script + ".js");
events.put(script, new EventEntry(iv, new EventManager(cserv, iv, script)));
@@ -112,4 +119,18 @@ public class EventScriptManager extends AbstractScriptManager {
entry.em.cancel();
}
}
public void dispose() {
if (events.isEmpty()) {
return;
}
Set<EventEntry> eventEntries = new HashSet<>(events.values());
events.clear();
active = false;
for (EventEntry entry : eventEntries) {
entry.em.cancel();
}
}
}
+28
View File
@@ -36,6 +36,34 @@ public class MapScriptMethods extends AbstractPlayerInteraction {
super(c);
}
public void displayCygnusIntro() {
switch (c.getPlayer().getMapId()) {
case 913040100:
lockUI();
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene0"));
break;
case 913040101:
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene1"));
break;
case 913040102:
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene2"));
break;
case 913040103:
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene3"));
break;
case 913040104:
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene4"));
break;
case 913040105:
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene5"));
break;
case 913040106:
lockUI();
c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene6"));
break;
}
}
public void displayAranIntro() {
switch (c.getPlayer().getMapId()) {
case 914090010:
@@ -253,14 +253,6 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
return this.getText;
}
public int getJobId() {
return getPlayer().getJob().getId();
}
public MapleJob getJob(){
return getPlayer().getJob();
}
@Override
public boolean forceStartQuest(int id) {
return forceStartQuest(id, npc);
@@ -303,10 +295,6 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
getPlayer().gainExp(gain, true, true);
}
public int getLevel() {
return getPlayer().getLevel();
}
@Override
public void showEffect(String effect) {
getPlayer().getMap().broadcastMessage(MaplePacketCreator.environmentChange(effect, 3));
+5 -2
View File
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import config.YamlConfig;
@@ -60,7 +61,6 @@ import client.inventory.manipulator.MapleInventoryManipulator;
import client.status.MonsterStatus;
import client.status.MonsterStatusEffect;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import constants.skills.Aran;
import constants.skills.Assassin;
import constants.skills.Bandit;
@@ -1281,9 +1281,13 @@ public class MapleStatEffect {
long leftDuration = (starttime + localDuration) - Server.getInstance().getCurrentTime();
if (leftDuration > 0) {
if (isDash() || isInfusion()) {
target.announce(MaplePacketCreator.givePirateBuff(activeStats, (skill ? sourceid : -sourceid), (int) leftDuration));
} else {
target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int) leftDuration, activeStats));
}
}
}
private void applyBuffEffect(MapleCharacter applyfrom, MapleCharacter applyto, boolean primary) {
if (!isMonsterRiding() && !isCouponBuff() && !isMysticDoor() && !isHyperBody() && !isCombo()) { // last mystic door already dispelled if it has been used before.
@@ -1398,7 +1402,6 @@ public class MapleStatEffect {
//CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime);
//ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, localDuration);
applyto.registerEffect(this, starttime, starttime + localDuration, false);
if (mbuff != null) {
applyto.getMap().broadcastMessage(applyto, mbuff, false);
}
+17 -21
View File
@@ -22,6 +22,7 @@ import client.MapleClient;
import client.inventory.Item;
import client.inventory.ItemFactory;
import client.inventory.MapleInventoryType;
import constants.game.GameConstants;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -44,6 +45,7 @@ import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
import tools.FilePrinter;
/**
*
@@ -67,51 +69,45 @@ public class MapleStorage {
this.meso = meso;
}
private static MapleStorage create(int id, int world) {
try {
private static MapleStorage create(int id, int world) throws SQLException {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("INSERT INTO storages (accountid, world, slots, meso) VALUES (?, ?, 4, 0)")) {
ps.setInt(1, id);
ps.setInt(2, world);
ps.executeUpdate();
}
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return loadOrCreateFromDB(id, world);
}
public static MapleStorage loadOrCreateFromDB(int id, int world) {
MapleStorage ret = null;
int storeId;
try {
MapleStorage ret;
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT storageid, slots, meso FROM storages WHERE accountid = ? AND world = ?");
ps.setInt(1, id);
ps.setInt(2, world);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
con.close();
return create(id, world);
} else {
storeId = rs.getInt("storageid");
ret = new MapleStorage(storeId, (byte) rs.getInt("slots"), rs.getInt("meso"));
rs.close();
ps.close();
if (rs.next()) {
ret = new MapleStorage(rs.getInt("storageid"), (byte) rs.getInt("slots"), rs.getInt("meso"));
for (Pair<Item, MapleInventoryType> item : ItemFactory.STORAGE.loadItems(ret.id, false)) {
ret.items.add(item.getLeft());
}
} else {
ret = create(id, world);
}
rs.close();
ps.close();
con.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
return ret;
} catch (SQLException ex) { // exceptions leading to deploy null storages found thanks to Jefe
FilePrinter.printError(FilePrinter.STORAGE, ex, "SQL error occurred when trying to load storage for accountid " + id + ", world " + GameConstants.WORLD_NAMES[world]);
throw new RuntimeException(ex);
}
}
public byte getSlots() {
+3 -5
View File
@@ -102,18 +102,16 @@ public class MapleLifeFactory {
MapleData monsterInfoData = monsterData.getChildByPath("info");
List<MobAttackInfoHolder> attackInfos = new LinkedList<>();
MapleMonsterStats stats;
MapleMonsterStats stats = new MapleMonsterStats();
int linkMid = MapleDataTool.getIntConvert("link", monsterInfoData, 0);
if (linkMid == 0) {
stats = new MapleMonsterStats();
} else {
if (linkMid != 0) {
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> linkStats = getMonsterStats(linkMid);
if (linkStats == null) {
return null;
}
stats = linkStats.getLeft();
// thanks resinate for noticing non-propagable infos such as revives getting retrieved
attackInfos.addAll(linkStats.getRight());
}
+28 -25
View File
@@ -72,11 +72,11 @@ import tools.Randomizer;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.MobAnimationService;
import net.server.channel.services.task.MobClearSkillService;
import net.server.channel.services.task.MobStatusService;
import net.server.channel.services.task.OverallService;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.MobAnimationService;
import net.server.services.task.channel.MobClearSkillService;
import net.server.services.task.channel.MobStatusService;
import net.server.services.task.channel.OverallService;
import net.server.coordinator.world.MapleMonsterAggroCoordinator;
import server.MapleStatEffect;
import server.loot.MapleLootManager;
@@ -338,7 +338,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
if(animationTime > 0) {
MobAnimationService service = (MobAnimationService) map.getChannelServer().getServiceAccess(ServiceType.MOB_ANIMATION);
MobAnimationService service = (MobAnimationService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_ANIMATION);
return service.registerMobOnAnimationEffect(map.getId(), this.hashCode(), animationTime);
} else {
return true;
@@ -824,7 +824,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if(htKilled) {
reviveMap.killMonster(ht, killer, true);
ht.broadcastMobHpBar(killer);
}
}
@@ -1041,18 +1040,22 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return isBoss() && getTagColor() > 0;
}
public void broadcastMonsterStatus() {
Collection<MonsterStatusEffect> mseList = this.getStati().values();
for (MapleCharacter chr : map.getAllPlayers()) {
announceMonsterStatusInternal(chr.getClient(), mseList);
}
}
public void announceMonsterStatus(MapleClient client) {
statiLock.lock();
try {
if (stati.size() > 0) {
for (final MonsterStatusEffect mse : this.stati.values()) {
announceMonsterStatusInternal(client, this.getStati().values());
}
public void announceMonsterStatusInternal(MapleClient client, Collection<MonsterStatusEffect> mseList) {
for (MonsterStatusEffect mse : mseList) {
client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null));
}
}
} finally {
statiLock.unlock();
}
}
@Override
public void sendSpawnData(MapleClient client) {
@@ -1203,7 +1206,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (oldEffect != null) {
oldEffect.removeActiveStatus(stat);
if (oldEffect.getStati().isEmpty()) {
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS);
service.interruptMobStatus(mapid, oldEffect);
}
}
@@ -1310,7 +1313,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
statiLock.unlock();
}
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS);
service.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay);
return true;
}
@@ -1362,7 +1365,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
statiLock.unlock();
}
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS);
service.registerMobStatus(map.getId(), effect, cancelTask, duration);
}
@@ -1561,7 +1564,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
};
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ServiceType.MOB_CLEAR_SKILL);
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL);
service.registerMobClearSkillAction(mmap.getId(), r, cooltime);
}
@@ -1630,7 +1633,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
};
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ServiceType.MOB_CLEAR_SKILL);
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL);
service.registerMobClearSkillAction(mmap.getId(), r, cooltime);
} finally {
monsterLock.unlock();
@@ -1678,7 +1681,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
public void run() {
int curHp = hp.get();
if(curHp <= 1) {
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS);
service.interruptMobStatus(map.getId(), status);
return;
}
@@ -1687,7 +1690,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (damage >= curHp) {
damage = curHp - 1;
if (type == 1 || type == 2) {
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS);
service.interruptMobStatus(map.getId(), status);
}
}
@@ -1744,7 +1747,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
};
MobClearSkillService service = (MobClearSkillService) mmap.getChannelServer().getServiceAccess(ServiceType.MOB_CLEAR_SKILL);
MobClearSkillService service = (MobClearSkillService) mmap.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL);
service.registerMobClearSkillAction(mmap.getId(), r, milli);
}
} finally {
@@ -1780,7 +1783,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
public Map<MonsterStatus, MonsterStatusEffect> getStati() {
statiLock.lock();
try {
return Collections.unmodifiableMap(stati);
return new HashMap<>(stati);
} finally {
statiLock.unlock();
}
@@ -2227,7 +2230,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
};
// had to schedule this since mob wouldn't stick to puppet aggro who knows why
OverallService service = (OverallService) this.getMap().getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) this.getMap().getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(this.getMap().getId(), r, YamlConfig.config.server.UPDATE_INTERVAL);
}
+4
View File
@@ -361,12 +361,16 @@ public class MaplePlayerNPC extends AbstractMapleMapObject {
int j = 0;
for(int i = branchSid; i < nextBranchSid; i++) {
if(!usedScriptIds.contains(i)) {
if (MaplePlayerNPCFactory.isExistentScriptid(i)) { // thanks Ark, Zein, geno, Ariel, JrCl0wn for noticing client crashes due to use of missing scriptids
availables.add(i);
j++;
if(j == 20) {
break;
}
} else {
break; // after this point no more scriptids expected...
}
}
}
+8 -1
View File
@@ -19,6 +19,7 @@
*/
package server.life;
import constants.net.ServerConstants;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@@ -36,9 +37,15 @@ import provider.MapleDataTool;
*/
public class MaplePlayerNPCFactory {
private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>();
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
public static boolean isExistentScriptid(int scriptid) {
return npcData.getData(scriptid + ".img") != null;
}
public static void loadDeveloperRoomMetadata(MapleDataProvider npc) {
MapleData thisData = npc.getData("9977777.img");
if(thisData != null) {
@@ -60,7 +67,7 @@ public class MaplePlayerNPCFactory {
}
public static void loadFactoryMetadata() {
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
MapleDataProvider npc = npcData;
loadDeveloperRoomMetadata(npc);
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
+3 -3
View File
@@ -32,8 +32,8 @@ import client.status.MonsterStatus;
import constants.game.GameConstants;
import java.util.LinkedList;
import java.util.Map;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.OverallService;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.OverallService;
import tools.Randomizer;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
@@ -117,7 +117,7 @@ public class MobSkill {
}
};
OverallService service = (OverallService) monster.getMap().getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) monster.getMap().getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(monster.getMap().getId(), toRun, animationTime);
}
+7 -6
View File
@@ -27,11 +27,12 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
@@ -46,9 +47,9 @@ public class MobSkillFactory {
private static Map<String, MobSkill> mobSkills = new HashMap<String, MobSkill>();
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
private final static ReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY);
private final static ReadLock rL = dataLock.readLock();
private final static WriteLock wL = dataLock.writeLock();
private final static MonitoredReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY);
private final static MonitoredReadLock rL = MonitoredReadLockFactory.createLock(dataLock);
private final static MonitoredWriteLock wL = MonitoredWriteLockFactory.createLock(dataLock);
public static MobSkill getMobSkill(final int skillId, final int level) {
final String key = skillId + "" + level;
+2 -4
View File
@@ -56,14 +56,12 @@ public class MapleLootManager {
qItemAmount = qCompleteAmount;
}
if (qItemAmount <= 0) {
continue;
}
// thanks kvmba for noticing quest items with no required amount failing to be detected as such
int qItemStatus = chrInv.hasItem(dropEntry.itemId, qItemAmount);
if (qItemStatus == 2) {
continue;
} /*else if (restricted && qItemStatus == 1) {
} /*else if (restricted && qItemStatus == 1) { // one-of-a-kind loots should be available everytime, thanks onechord for noticing
continue;
}*/
} /*else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) { // thanks Conrad, Legalize for noticing eligible loots not being available to drop for non-killer parties
+3 -3
View File
@@ -28,8 +28,8 @@ import config.YamlConfig;
import tools.Pair;
import client.MapleCharacter;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.OverallService;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.OverallService;
/**
*
@@ -133,7 +133,7 @@ public class MapleDoor {
if (effectTimeLeft > 0) {
MapleMap town = destroyDoor.getTown();
OverallService service = (OverallService) town.getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) town.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(town.getId(), new Runnable() {
@Override
public void run() {
+7 -4
View File
@@ -20,11 +20,14 @@
package server.maps;
import java.awt.Point;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import client.MapleCharacter;
import client.MapleClient;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import net.server.world.MapleParty;
import tools.MaplePacketCreator;
@@ -41,9 +44,9 @@ public class MapleDoorObject extends AbstractMapleMapObject {
private int linkedPortalId;
private Point linkedPos;
private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_DOOR, true);
private ReentrantReadWriteLock.ReadLock rlock = locks.readLock();
private ReentrantReadWriteLock.WriteLock wlock = locks.writeLock();
private final MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_DOOR, true);
private MonitoredReadLock rlock = MonitoredReadLockFactory.createLock(locks);
private MonitoredWriteLock wlock = MonitoredWriteLockFactory.createLock(locks);
public MapleDoorObject(int owner, MapleMap destination, MapleMap origin, int townPortalId, Point targetPosition, Point toPosition) {
super();
+4 -2
View File
@@ -169,7 +169,8 @@ public class MapleFootholdTree {
}
Collections.sort(xMatches);
for (MapleFoothold fh : xMatches) {
if (!fh.isWall() && fh.getY1() != fh.getY2()) {
if (!fh.isWall()) {
if (fh.getY1() != fh.getY2()) {
int calcY;
double s1 = Math.abs(fh.getY2() - fh.getY1());
double s2 = Math.abs(fh.getX2() - fh.getX1());
@@ -185,12 +186,13 @@ public class MapleFootholdTree {
if (calcY >= p.y) {
return fh;
}
} else if (!fh.isWall()) {
} else {
if (fh.getY1() >= p.y) {
return fh;
}
}
}
}
return null;
}
+70 -39
View File
@@ -55,20 +55,21 @@ import java.util.Random;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import java.lang.ref.WeakReference;
import net.server.Server;
import net.server.coordinator.world.MapleMonsterAggroCoordinator;
import net.server.channel.Channel;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.FaceExpressionService;
import net.server.channel.services.task.MobMistService;
import net.server.channel.services.task.OverallService;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.FaceExpressionService;
import net.server.services.task.channel.MobMistService;
import net.server.services.task.channel.OverallService;
import net.server.world.World;
import scripting.map.MapScriptManager;
import server.MapleItemInformationProvider;
@@ -177,10 +178,10 @@ public class MapleMap {
private int timeExpand;
//locks
private ReadLock chrRLock;
private WriteLock chrWLock;
private ReadLock objectRLock;
private WriteLock objectWLock;
private MonitoredReadLock chrRLock;
private MonitoredWriteLock chrWLock;
private MonitoredReadLock objectRLock;
private MonitoredWriteLock objectWLock;
private Lock lootLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_LOOT, true);
@@ -196,13 +197,13 @@ public class MapleMap {
if (this.monsterRate == 0) {
this.monsterRate = 1;
}
final ReentrantReadWriteLock chrLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_CHRS, true);
chrRLock = chrLock.readLock();
chrWLock = chrLock.writeLock();
final MonitoredReentrantReadWriteLock chrLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_CHRS, true);
chrRLock = MonitoredReadLockFactory.createLock(chrLock);
chrWLock = MonitoredWriteLockFactory.createLock(chrLock);
final ReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_OBJS, true);
objectRLock = objectLock.readLock();
objectWLock = objectLock.writeLock();
final MonitoredReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_OBJS, true);
objectRLock = MonitoredReadLockFactory.createLock(objectLock);
objectWLock = MonitoredWriteLockFactory.createLock(objectLock);
aggroMonitor = new MapleMonsterAggroCoordinator();
}
@@ -570,7 +571,7 @@ public class MapleMap {
initial.x = xLimits.right;
}
Point ret = calcPointBelow(new Point(initial.x, initial.y - 85));
Point ret = calcPointBelow(new Point(initial.x, initial.y - 85)); // actual drop ranges: default - 120, explosive - 360
if (ret == null) {
ret = bsearchDropPos(initial, fallback);
}
@@ -758,6 +759,10 @@ public class MapleMap {
final List<MonsterDropEntry> otherQuestEntry = new ArrayList<>();
sortDropEntries(YamlConfig.config.server.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr);
if (dropEntry.isEmpty() && visibleQuestEntry.isEmpty()) { // thanks resinate
return;
}
registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob);
}
@@ -1333,6 +1338,18 @@ public class MapleMap {
return count;
}
public int countBosses() {
int count = 0;
for(MapleMonster mob: getAllMonsters()) {
if (mob.isBoss()) {
count++;
}
}
return count;
}
public boolean damageMonster(final MapleCharacter chr, final MapleMonster monster, final int damage) {
if (monster.getId() == 8800000) {
for (MapleMapObject object : chr.getMap().getMapObjects()) {
@@ -1388,6 +1405,9 @@ public class MapleMap {
spawnedMonstersOnMap.decrementAndGet();
removeMapObject(monster);
monster.disposeMapObject();
if (monster.hasBossHPBar()) { // thanks resinate for noticing boss HPbar not clearing after mob defeat in certain scenarios
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
}
return true;
} finally {
@@ -1462,7 +1482,6 @@ public class MapleMap {
if (mons != null) {
if (mons.getId() == 8800000) {
makeMonsterReal(mons);
mons.aggroUpdateController();
break;
}
}
@@ -1904,10 +1923,7 @@ public class MapleMap {
});
monster.aggroUpdateController();
if (monster.hasBossHPBar()) {
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
}
updateBossSpawn(monster);
spawnedMonstersOnMap.incrementAndGet();
addSelfDestructive(monster);
@@ -1946,7 +1962,7 @@ public class MapleMap {
public void dismissRemoveAfter(final MapleMonster monster) {
Runnable removeAfterAction = monster.popRemoveAfterAction();
if (removeAfterAction != null) {
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.forceRunOverallAction(mapid, removeAfterAction);
}
}
@@ -2009,6 +2025,7 @@ public class MapleMap {
}, null);
monster.aggroUpdateController();
updateBossSpawn(monster);
if ((monster.getTeam() == 1 || monster.getTeam() == 0) && (isCPQMap() || isCPQMap2())) {
List<MCSkill> teamS = null;
@@ -2026,10 +2043,6 @@ public class MapleMap {
}
}
if (monster.hasBossHPBar()) {
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
}
if (monster.getDropPeriodTime() > 0) { //9300102 - Watchhog, 9300061 - Moon Bunny (HPQ), 9300093 - Tylus
if (monster.getId() == 9300102) {
monsterItemDrop(monster, monster.getDropPeriodTime());
@@ -2078,10 +2091,7 @@ public class MapleMap {
});
monster.aggroUpdateController();
if (monster.hasBossHPBar()) {
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
}
updateBossSpawn(monster);
spawnedMonstersOnMap.incrementAndGet();
addSelfDestructive(monster);
@@ -2105,8 +2115,9 @@ public class MapleMap {
public void makeMonsterReal(final MapleMonster monster) {
monster.setFake(false);
broadcastMessage(MaplePacketCreator.makeMonsterReal(monster));
monster.broadcastMonsterStatus();
monster.aggroUpdateController();
updateBossSpawn(monster);
}
public void spawnReactor(final MapleReactor reactor) {
@@ -2208,7 +2219,7 @@ public class MapleMap {
}
};
MobMistService service = (MobMistService) this.getChannelServer().getServiceAccess(ServiceType.MOB_MIST);
MobMistService service = (MobMistService) this.getChannelServer().getServiceAccess(ChannelServices.MOB_MIST);
service.registerMobMistCancelAction(mapid, mistSchedule, duration);
}
@@ -2308,7 +2319,7 @@ public class MapleMap {
}
private void registerMapSchedule(Runnable r, long delay) {
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(mapid, r, delay);
}
@@ -2795,7 +2806,7 @@ public class MapleMap {
public void removePlayer(MapleCharacter chr) {
Channel cserv = chr.getClient().getChannelServer();
FaceExpressionService service = (FaceExpressionService) this.getChannelServer().getServiceAccess(ServiceType.FACE_EXPRESSION);
FaceExpressionService service = (FaceExpressionService) this.getChannelServer().getServiceAccess(ChannelServices.FACE_EXPRESSION);
service.unregisterFaceExpression(mapid, chr);
chr.unregisterChairBuff();
@@ -2914,6 +2925,21 @@ public class MapleMap {
}
}
private void updateBossSpawn(MapleMonster monster) {
if (monster.hasBossHPBar()) {
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
}
if (monster.isBoss()) {
if (unclaimOwnership() != null) {
String mobName = MapleMonsterInformationProvider.getInstance().getMobNameFromId(monster.getId());
if (mobName != null) {
mobName = mobName.trim();
this.dropMessage(5, "This lawn has been taken siege by " + mobName + "'s forces and will be kept hold until their defeat.");
}
}
}
}
public void broadcastBossHpMessage(MapleMonster mm, int bossHash, final byte[] packet) {
broadcastBossHpMessage(mm, bossHash, null, packet, Double.POSITIVE_INFINITY, null);
}
@@ -3556,7 +3582,7 @@ public class MapleMap {
if (reactor.getDelay() > 0) {
MapleMap reactorMap = reactor.getMap();
OverallService service = (OverallService) reactorMap.getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) reactorMap.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(reactorMap.getId(), new Runnable() {
@Override
public void run() {
@@ -4219,8 +4245,13 @@ public class MapleMap {
}
}
public MapleCharacter unclaimOwnership() {
MapleCharacter lastOwner = this.mapOwner;
return unclaimOwnership(lastOwner) ? lastOwner : null;
}
public boolean unclaimOwnership(MapleCharacter chr) {
if (mapOwner == chr) {
if (chr != null && mapOwner == chr) {
this.mapOwner = null;
chr.setOwnedMap(null);
@@ -4255,7 +4286,7 @@ public class MapleMap {
public void checkMapOwnerActivity() {
long timeNow = Server.getInstance().getCurrentTime();
if (timeNow - mapOwnerLastActivityTime > 60000) {
if (unclaimOwnership(mapOwner)) {
if (unclaimOwnership() != null) {
this.dropMessage(5, "This lawn is now free real estate.");
}
}
+19 -2
View File
@@ -309,9 +309,10 @@ public class MapleMapFactory {
}
}
}
try {
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), ""));
map.setMapName(loadPlaceName(mapid));
map.setStreetName(loadStreetName(mapid));
} catch (Exception e) {
if (mapid / 1000 != 1020) { // explorer job introduction scenes
e.printStackTrace();
@@ -434,4 +435,20 @@ public class MapleMapFactory {
return builder.toString();
}
public static String loadPlaceName(int mapid) throws Exception {
try {
return MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), "");
} catch (Exception e) {
return "";
}
}
public static String loadStreetName(int mapid) throws Exception {
try {
return MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), "");
} catch (Exception e) {
return "";
}
}
}
+9 -8
View File
@@ -21,11 +21,12 @@ package server.maps;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import scripting.event.EventInstanceManager;
public class MapleMapManager {
@@ -35,17 +36,17 @@ public class MapleMapManager {
private Map<Integer, MapleMap> maps = new HashMap<>();
private ReadLock mapsRLock;
private WriteLock mapsWLock;
private MonitoredReadLock mapsRLock;
private MonitoredWriteLock mapsWLock;
public MapleMapManager(EventInstanceManager eim, int world, int channel) {
this.world = world;
this.channel = channel;
this.event = eim;
ReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER);
this.mapsRLock = rrwl.readLock();
this.mapsWLock = rrwl.writeLock();
MonitoredReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER);
this.mapsRLock = MonitoredReadLockFactory.createLock(rrwl);
this.mapsWLock = MonitoredWriteLockFactory.createLock(rrwl);
}
public MapleMap resetMap(int mapid) {
+4 -4
View File
@@ -36,8 +36,8 @@ import server.TimerManager;
import tools.MaplePacketCreator;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.OverallService;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.OverallService;
import server.partyquest.GuardianSpawnPoint;
/**
@@ -371,7 +371,7 @@ public class MapleReactor extends AbstractMapleMapObject {
delayedRespawnRun = r;
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(map.getId(), r, this.getDelay());
}
@@ -379,7 +379,7 @@ public class MapleReactor extends AbstractMapleMapObject {
Runnable r = delayedRespawnRun;
if (r != null) {
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ServiceType.OVERALL);
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.forceRunOverallAction(map.getId(), r);
return true;
} else {
+5 -5
View File
@@ -225,7 +225,7 @@ public class MapleQuest {
int ixSize = ix.size();
for (int i = 0; i < ixSize; i++) {
String progress = chr.getClient().getAbstractPlayerInteraction().getQuestProgress(infoNumber, i);
String progress = chr.getAbstractPlayerInteraction().getQuestProgress(infoNumber, i);
String ixProgress = ix.get(i);
if (!progress.contentEquals(ixProgress)) {
@@ -387,19 +387,19 @@ public class MapleQuest {
public int getStartItemAmountNeeded(int itemid) {
MapleQuestRequirement req = startReqs.get(MapleQuestRequirementType.ITEM);
if(req == null)
return 0;
return Integer.MIN_VALUE;
ItemRequirement ireq = (ItemRequirement) req;
return ireq.getItemAmountNeeded(itemid);
return ireq.getItemAmountNeeded(itemid, false);
}
public int getCompleteItemAmountNeeded(int itemid) {
MapleQuestRequirement req = completeReqs.get(MapleQuestRequirementType.ITEM);
if(req == null)
return 0;
return Integer.MAX_VALUE;
ItemRequirement ireq = (ItemRequirement) req;
return ireq.getItemAmountNeeded(itemid);
return ireq.getItemAmountNeeded(itemid, true);
}
public int getMobAmountNeeded(int mid) {
+1 -1
View File
@@ -272,7 +272,7 @@ public class ItemAction extends MapleQuestAction {
}
// thanks onechord for noticing quests unnecessarily giving out "full inventory" from quests that also takes items from players
return chr.getClient().getAbstractPlayerInteraction().canHoldAllAfterRemoving(toAddItemids, toAddQuantity, toRemoveItemids, toRemoveQuantity);
return chr.getAbstractPlayerInteraction().canHoldAllAfterRemoving(toAddItemids, toAddQuantity, toRemoveItemids, toRemoveQuantity);
}
private boolean canGetItem(ItemData item, MapleCharacter chr) {
@@ -96,11 +96,12 @@ public class ItemRequirement extends MapleQuestRequirement {
return true;
}
public int getItemAmountNeeded(int itemid) {
if(items.containsKey(itemid)) {
return items.get(itemid);
public int getItemAmountNeeded(int itemid, boolean complete) {
Integer amount = items.get(itemid);
if (amount != null) {
return amount;
} else {
return complete ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
return 0;
}
}
+9 -8
View File
@@ -22,11 +22,12 @@ package tools;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
/**
*
@@ -36,13 +37,13 @@ public class IntervalBuilder {
private List<Line2D> intervalLimits = new ArrayList<>();
protected ReadLock intervalRlock;
protected WriteLock intervalWlock;
protected MonitoredReadLock intervalRlock;
protected MonitoredWriteLock intervalWlock;
public IntervalBuilder() {
ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.INTERVAL, true);
intervalRlock = locks.readLock();
intervalWlock = locks.writeLock();
MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.INTERVAL, true);
intervalRlock = MonitoredReadLockFactory.createLock(locks);
intervalWlock = MonitoredWriteLockFactory.createLock(locks);
}
private void refitOverlappedIntervals(int st, int en, int newFrom, int newTo) {
-2
View File
@@ -1086,7 +1086,6 @@ public class MaplePacketCreator {
mplew.writeShort(chr.getHp());
mplew.writeBool(false);
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.skip(18);
return mplew.getPacket();
}
@@ -1103,7 +1102,6 @@ public class MaplePacketCreator {
mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy)
mplew.writeInt(spawnPosition.y);
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.skip(18);
return mplew.getPacket();
}