Mystic Doors review + Togglable SrvMessage-BossHP + Map-Event patch

Reviewed Mystic Doors.
Fixed several issues showing up on Duey in uncommon scenarios.
Fixed a concurrency issue with XMLDomMapleData.
Scheduled forward the "lock disposal" action within the source. Now, it's expected that, after a set while, no method should require usage of a disposed lock and, during that while, a supposed "disposed lock" is still available to run (although no new processes is expected to require use of these locks).
Fixed concurrency issues with player's current event instance, generating several inconsistencies when swiftly registering/unregistering from events.
Implemented a mutually exclusive approach for server message - Boss HPbar.
Fixed item-making Kage requiring lv71~80 ETC instead of the expected 81~90.
Removed the possibility to buy cosmetic coupons with mesos through the NPCs.
Sleepywood JQ's no longer gives cash items when they finish the quest repeatedly.
Added Duey trucks in several maps lacking it. Added NPC Duey in New Leaf City.
Fixed scripted quests not calculating QUEST_RATE (if applied) when rewarding experience and meso.
This commit is contained in:
ronancpl
2018-08-07 23:37:24 -03:00
parent cc541f39d5
commit 4c25c07e28
173 changed files with 38064 additions and 37254 deletions

View File

@@ -60,6 +60,7 @@ import net.server.guild.MapleGuildCharacter;
import net.server.worker.CharacterDiseaseWorker;
import net.server.worker.CouponWorker;
import net.server.worker.RankingWorker;
import net.server.worker.ReleaseLockWorker;
import net.server.world.World;
import org.apache.mina.core.buffer.IoBuffer;
@@ -206,7 +207,11 @@ public class Server {
public World getWorld(int id) {
wldRLock.lock();
try {
return worlds.get(id);
try {
return worlds.get(id);
} catch (IndexOutOfBoundsException e) {
return null;
}
} finally {
wldRLock.unlock();
}
@@ -231,21 +236,33 @@ public class Server {
}
public Channel getChannel(int world, int channel) {
return this.getWorld(world).getChannel(channel);
try {
return this.getWorld(world).getChannel(channel);
} catch(NullPointerException npe) {
return null;
}
}
public List<Channel> getChannelsFromWorld(int world) {
return this.getWorld(world).getChannels();
try {
return this.getWorld(world).getChannels();
} catch(NullPointerException npe) {
return new ArrayList<>(0);
}
}
public List<Channel> getAllChannels() {
List<Channel> channelz = new ArrayList<>();
for (World world : this.getWorlds()) {
for (Channel ch : world.getChannels()) {
channelz.add(ch);
try {
List<Channel> channelz = new ArrayList<>();
for (World world : this.getWorlds()) {
for (Channel ch : world.getChannels()) {
channelz.add(ch);
}
}
return channelz;
} catch(NullPointerException npe) {
return new ArrayList<>(0);
}
return channelz;
}
public Set<Integer> getOpenChannels(int world) {
@@ -257,7 +274,7 @@ public class Server {
}
}
public String getIP(int world, int channel) {
private String getIP(int world, int channel) {
wldRLock.lock();
try {
return channels.get(world).get(channel);
@@ -266,6 +283,15 @@ public class Server {
}
}
public String[] getInetSocket(int world, int channel) {
try {
return getIP(world, channel).split(":");
} catch (Exception e) {
return null;
}
}
private void dumpData() {
wldWLock.lock();
try {
@@ -643,6 +669,7 @@ public class Server {
long timeLeft = getTimeLeftForNextHour();
tMan.register(new CharacterDiseaseWorker(), ServerConstants.UPDATE_INTERVAL, ServerConstants.UPDATE_INTERVAL);
tMan.register(new ReleaseLockWorker(), 2 * 60 * 1000, 2 * 60 * 1000);
tMan.register(new CouponWorker(), ServerConstants.COUPON_INTERVAL, timeLeft);
tMan.register(new RankingWorker(), ServerConstants.RANKING_INTERVAL, timeLeft);
@@ -1149,8 +1176,10 @@ public class Server {
}
*/
public Pair<Pair<Integer, List<MapleCharacter>>, List<Pair<Integer, List<MapleCharacter>>>> loadAccountCharlist(Integer accountId) {
public Pair<Pair<Integer, List<MapleCharacter>>, List<Pair<Integer, List<MapleCharacter>>>> loadAccountCharlist(Integer accountId, int visibleWorlds) {
List<World> wlist = this.getWorlds();
if(wlist.size() > visibleWorlds) wlist = wlist.subList(0, visibleWorlds);
List<Pair<Integer, List<MapleCharacter>>> accChars = new ArrayList<>(wlist.size() + 1);
int chrTotal = 0;
List<MapleCharacter> lastwchars = null;

View File

@@ -0,0 +1,75 @@
/*
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.audit;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author Ronan
*/
public class LockCollector {
private static final LockCollector instance = new LockCollector();
private Map<Runnable, Integer> disposableLocks = new HashMap<>(200);
private final Lock lock = new ReentrantLock(true);
public static LockCollector getInstance() {
return instance;
}
public void registerDisposeAction(Runnable r) {
lock.lock();
try {
disposableLocks.put(r, 0);
} finally {
lock.unlock();
}
}
public void runLockCollector() {
List<Runnable> toDispose = new ArrayList<>();
lock.lock();
try {
for(Entry<Runnable, Integer> e : disposableLocks.entrySet()) {
Integer eVal = e.getValue();
if(eVal > 5) { // updates each 2min
toDispose.add(e.getKey());
} else {
disposableLocks.put(e.getKey(), ++eVal);
}
}
} finally {
lock.unlock();
}
for(Runnable r : toDispose) {
r.run();
}
}
}

View File

@@ -57,7 +57,7 @@ public class ThreadTracker {
private final Map<Long, AtomicInteger> lockCount = new HashMap<>();
private final Map<Long, MonitoredLockType> lockIds = new HashMap<>();
private final Map<Long, Long> lockThreads = new HashMap<>();
private final Map<Long, Byte> lockUpdate = new HashMap<>();
private final Map<Long, Integer> lockUpdate = new HashMap<>();
private final Map<MonitoredLockType, Map<Long, Integer>> locks = new HashMap<>();
ScheduledFuture<?> threadTrackerSchedule;
@@ -169,8 +169,8 @@ public class ThreadTracker {
toRemove.clear();
for(Entry<Long, Byte> it : lockUpdate.entrySet()) {
byte val = (byte)(it.getValue() + 1);
for(Entry<Long, Integer> it : lockUpdate.entrySet()) {
int val = it.getValue() + 1;
if(val < 60) {
lockUpdate.put(it.getKey(), val);
@@ -202,7 +202,7 @@ public class ThreadTracker {
lockCount.put(lockOid, c);
lockIds.put(lockOid, lockId);
lockThreads.put(lockOid, tid);
lockUpdate.put(lockOid, (byte) 0);
lockUpdate.put(lockOid, 0);
}
c.incrementAndGet();
@@ -233,7 +233,7 @@ public class ThreadTracker {
else {
AtomicInteger c = lockCount.get(lockOid);
c.decrementAndGet();
lockUpdate.put(lockOid, (byte) 0);
lockUpdate.put(lockOid, 0);
List<MonitoredLockType> list = threadTracker.get(tid);
for(int i = list.size() - 1; i >= 0; i--) {

View File

@@ -30,6 +30,7 @@ public enum MonitoredLockType {
CHARACTER_EFF,
CHARACTER_PET,
CHARACTER_PRT,
CHARACTER_EVT,
CLIENT,
CLIENT_ENCODER,
CLIENT_LOGIN,
@@ -40,6 +41,7 @@ public enum MonitoredLockType {
SRVHANDLER_TEMP,
BUFF_STORAGE,
PLAYER_STORAGE,
PLAYER_DOOR,
SERVER,
SERVER_DISEASES,
SERVER_LOGIN,
@@ -59,7 +61,7 @@ public enum MonitoredLockType {
GUILD,
PARTY,
WORLD_PARTY,
WORLD_OWL,
WORLD_SRVMESSAGES,
WORLD_PETS,
WORLD_CHARS,
WORLD_CHANNELS,

View File

@@ -159,11 +159,13 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
reentrantCount.set(Integer.MAX_VALUE);
} finally {
state.unlock();
}
//unlock();
return new EmptyReadLock();
return new EmptyReadLock(id);
}
}

View File

@@ -46,7 +46,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public TrackerReentrantLock(MonitoredLockType id) {
super();
this.id = id;
@@ -161,11 +161,13 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
reentrantCount.set(Integer.MAX_VALUE);
} finally {
state.unlock();
}
//unlock();
return new EmptyReentrantLock();
return new EmptyReentrantLock(id);
}
}

View File

@@ -157,11 +157,13 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
reentrantCount.set(Integer.MAX_VALUE);
} finally {
state.unlock();
}
//unlock();
return new EmptyWriteLock();
return new EmptyWriteLock(id);
}
}

View File

@@ -19,26 +19,56 @@
*/
package net.server.audit.locks.empty;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class EmptyReadLock implements MonitoredReadLock {
private final MonitoredLockType id;
public EmptyReadLock(MonitoredLockType type) {
this.id = type;
}
private static String printThreadStack(StackTraceElement[] list) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
String df = dateFormat.format(new Date());
String s = "\r\n" + df + "\r\n";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
s += "----------------------------";
return s;
}
@Override
public void lock() {}
public void lock() {
FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace()));
}
@Override
public void unlock() {}
@Override
public boolean tryLock() {
FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured try-locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace()));
return false;
}
@Override
public MonitoredReadLock dispose() {
return null;
return this;
}
}

View File

@@ -19,26 +19,56 @@
*/
package net.server.audit.locks.empty;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class EmptyReentrantLock implements MonitoredReentrantLock {
private final MonitoredLockType id;
public EmptyReentrantLock(MonitoredLockType type) {
this.id = type;
}
private static String printThreadStack(StackTraceElement[] list) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
String df = dateFormat.format(new Date());
String s = "\r\n" + df + "\r\n";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
s += "----------------------------";
return s;
}
@Override
public void lock() {}
public void lock() {
FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace()));
}
@Override
public void unlock() {}
@Override
public boolean tryLock() {
FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured try-locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace()));
return false;
}
@Override
public MonitoredReentrantLock dispose() {
return null;
return this;
}
}

View File

@@ -19,26 +19,56 @@
*/
package net.server.audit.locks.empty;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredWriteLock;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class EmptyWriteLock implements MonitoredWriteLock {
private final MonitoredLockType id;
public EmptyWriteLock(MonitoredLockType type) {
this.id = type;
}
private static String printThreadStack(StackTraceElement[] list) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
String df = dateFormat.format(new Date());
String s = "\r\n" + df + "\r\n";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
s += "----------------------------";
return s;
}
@Override
public void lock() {}
public void lock() {
FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace()));
}
@Override
public void unlock() {}
@Override
public boolean tryLock() {
FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured try-locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace()));
return false;
}
@Override
public MonitoredWriteLock dispose() {
return null;
return this;
}
}

View File

@@ -37,6 +37,7 @@ 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.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
@@ -266,7 +267,22 @@ public final class Channel {
channelSchedulers[i].dispose();
channelSchedulers[i] = null;
}
}
disposeLocks();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
for(int i = 0; i < 4; i++) {
faceLock[i] = faceLock[i].dispose();
}
@@ -300,6 +316,10 @@ public final class Channel {
players.addPlayer(chr);
chr.announce(MaplePacketCreator.serverMessage(serverMessage));
}
public String getServerMessage() {
return serverMessage;
}
public PlayerStorage getPlayerStorage() {
return players;
@@ -443,6 +463,7 @@ public final class Channel {
public void setServerMessage(String message) {
this.serverMessage = message;
broadcastPacket(MaplePacketCreator.serverMessage(message));
Server.getInstance().getWorld(world).resetDisabledServerMessages();
}
private static String [] getEvents(){
@@ -540,7 +561,7 @@ public final class Channel {
resetDojo(dojoMapId, 0);
}
private void resetDojo(int dojoMapId, int thisStg) {
public void resetDojo(int dojoMapId, int thisStg) {
int slot = getDojoSlot(dojoMapId);
this.dojoStage[slot] = thisStg;

View File

@@ -21,13 +21,11 @@
*/
package net.server.channel.handlers;
import java.util.Calendar;
import client.MapleCharacter;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import server.maps.MapleDoorObject;
import server.maps.MapleMapObject;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -43,10 +41,6 @@ public final class DoorHandler extends AbstractMaplePacketHandler {
MapleCharacter chr = c.getPlayer();
if (chr.isChangingMaps() || chr.isBanned()) {
if(chr.isChangingMaps()) {
FilePrinter.printError(FilePrinter.PORTAL_STUCK + chr.getName() + ".txt", "Player " + chr.getName() + " got stuck when changing maps (using Mystic Door). Timestamp: " + Calendar.getInstance().getTime().toString() + " Last visited mapids: " + chr.getLastVisitedMapids());
}
c.announce(MaplePacketCreator.enableActions());
return;
}

View File

@@ -45,7 +45,13 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler {
}
if(MapleMiniDungeonInfo.isDungeonMap(c.getPlayer().getMapId())) {
if(mc.getEventInstance() != null) {
c.announce(MaplePacketCreator.serverNotice(5, "Entering Cash Shop or MTS are disabled when registered on an event."));
c.announce(MaplePacketCreator.enableActions());
return;
}
if(MapleMiniDungeonInfo.isDungeonMap(mc.getMapId())) {
c.announce(MaplePacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon."));
c.announce(MaplePacketCreator.enableActions());
return;

View File

@@ -58,7 +58,13 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
return;
}
if(MapleMiniDungeonInfo.isDungeonMap(c.getPlayer().getMapId())) {
if(chr.getEventInstance() != null) {
c.announce(MaplePacketCreator.serverNotice(5, "Entering Cash Shop or MTS are disabled when registered on an event."));
c.announce(MaplePacketCreator.enableActions());
return;
}
if(MapleMiniDungeonInfo.isDungeonMap(chr.getMapId())) {
c.announce(MaplePacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon."));
c.announce(MaplePacketCreator.enableActions());
return;

View File

@@ -82,9 +82,9 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
player.setMPC(partyplayer);
player.getMap().addPartyMember(player);
player.silentPartyUpdate();
c.announce(MaplePacketCreator.partyCreated(partyplayer, party.getId()));
player.updateMapDropsUponPartyOperation(null);
player.partyOperationUpdate(party, null);
c.announce(MaplePacketCreator.partyCreated(party, partyplayer.getId()));
} else {
c.announce(MaplePacketCreator.serverNotice(5, "You can't create a party as you are already in one."));
}
@@ -94,7 +94,7 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
List<MapleCharacter> partymembers = player.getPartyMembers();
leaveParty(party, partyplayer, c);
player.updateMapDropsUponPartyOperation(partymembers);
player.partyOperationUpdate(party, partymembers);
break;
}
case 3: { // join
@@ -110,7 +110,7 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
player.receivePartyMemberHP();
player.updatePartyMemberHP();
player.updateMapDropsUponPartyOperation(null);
player.partyOperationUpdate(party, null);
} else {
c.announce(MaplePacketCreator.partyStatusMessage(17));
}
@@ -147,9 +147,9 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
player.setParty(party);
player.setMPC(partyplayer);
player.getMap().addPartyMember(player);
c.announce(MaplePacketCreator.partyCreated(partyplayer, party.getId()));
player.updateMapDropsUponPartyOperation(null);
player.partyOperationUpdate(party, null);
c.announce(MaplePacketCreator.partyCreated(party, partyplayer.getId()));
}
if (party.getMembers().size() < 6) {
invited.getClient().announce(MaplePacketCreator.partyInvite(player));
@@ -184,7 +184,7 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
emc.setParty(null);
world.updateParty(party.getId(), PartyOperation.EXPEL, expelled);
emc.updateMapDropsUponPartyOperation(partyMembers);
emc.partyOperationUpdate(party, partyMembers);
} else {
world.updateParty(party.getId(), PartyOperation.EXPEL, expelled);
}

View File

@@ -54,10 +54,8 @@ public final class PetCommandHandler extends AbstractMaplePacketHandler {
if (Randomizer.nextInt(101) <= petCommand.getProbability()) {
pet.gainClosenessFullness(chr, petCommand.getIncrease(), 0, command);
}
else {
} else {
chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, command, false));
if(chr.getMount() != null) chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), chr.getMount(), false));
}
}
}

View File

@@ -103,12 +103,18 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
int state = c.getLoginState();
boolean allowLogin = true;
Channel cserv = c.getChannelServer();
World world = server.getWorld(c.getWorld());
if(world == null) {
c.disconnect(true, false);
return;
}
Channel cserv = world.getChannel(c.getChannel());
if(cserv == null) {
c.setChannel(1);
cserv = c.getChannelServer();
cserv = world.getChannel(c.getChannel());
if(cserv == null) { // world server is out
if(cserv == null) {
c.disconnect(true, false);
return;
}
@@ -136,8 +142,6 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
return;
}
c.updateLoginState(MapleClient.LOGIN_LOGGEDIN);
World world = server.getWorld(c.getWorld());
cserv.addPlayer(player);
world.addPlayer(player);

View File

@@ -29,6 +29,7 @@ import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
import net.server.Server;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
@@ -196,6 +197,19 @@ public abstract class BaseScheduler {
externalLocks.clear();
}
disposeLocks();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
schedulerLock = schedulerLock.dispose();
}
}

View File

@@ -24,6 +24,7 @@ import net.server.audit.locks.MonitoredLockType;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
@@ -76,7 +77,20 @@ public class MobAnimationScheduler extends BaseScheduler {
@Override
public void dispose() {
animationLock = animationLock.dispose();
disposeLocks();
super.dispose();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
animationLock = animationLock.dispose();
}
}

View File

@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
@@ -111,7 +112,20 @@ public class MobStatusScheduler extends BaseScheduler {
@Override
public void dispose() {
overtimeStatusLock = overtimeStatusLock.dispose();
disposeLocks();
super.dispose();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
overtimeStatusLock = overtimeStatusLock.dispose();
}
}

View File

@@ -27,6 +27,7 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -52,7 +53,14 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
}
c.setWorld(server.getCharacterWorld(charId));
if(c.getWorldServer().isWorldCapacityFull()) {
World wserv = c.getWorldServer();
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
String[] socket = server.getInetSocket(c.getWorld(), c.getChannel());
if(socket == null) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
@@ -61,7 +69,6 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
String[] socket = server.getIP(c.getWorld(), c.getChannel()).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
} catch (UnknownHostException | NumberFormatException e) {

View File

@@ -6,6 +6,7 @@ import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
@@ -34,7 +35,14 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
if (c.checkPic(pic)) {
c.setWorld(server.getCharacterWorld(charId));
if(c.getWorldServer().isWorldCapacityFull()) {
World wserv = c.getWorldServer();
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
String[] socket = server.getInetSocket(c.getWorld(), c.getChannel());
if(socket == null) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
@@ -42,8 +50,7 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
server.unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
String[] socket = server.getIP(c.getWorld(), c.getChannel()).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
} catch (UnknownHostException | NumberFormatException e) {

View File

@@ -24,6 +24,7 @@ package net.server.handlers.login;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -33,14 +34,21 @@ public final class CharlistRequestHandler extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readByte();
int world = slea.readByte();
c.setWorld(world);
if(Server.getInstance().getWorld(world).isWorldCapacityFull()) {
World wserv = Server.getInstance().getWorld(world);
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getServerStatus(2));
return;
}
c.setChannel(slea.readByte() + 1);
int channel = slea.readByte() + 1;
if(wserv.getChannel(channel) == null) {
c.announce(MaplePacketCreator.getServerStatus(2));
return;
}
c.setWorld(world);
c.setChannel(channel);
c.sendCharList(world);
}
}

View File

@@ -5,6 +5,7 @@ import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
@@ -37,7 +38,14 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
c.setPic(pic);
c.setWorld(server.getCharacterWorld(charId));
if(c.getWorldServer().isWorldCapacityFull()) {
World wserv = c.getWorldServer();
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
String[] socket = server.getInetSocket(c.getWorld(), c.getChannel());
if(socket == null) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
@@ -46,7 +54,6 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
String[] socket = server.getIP(c.getWorld(), c.getChannel()).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
} catch (UnknownHostException e) {

View File

@@ -23,6 +23,7 @@ package net.server.handlers.login;
import client.MapleClient;
import constants.GameConstants;
import java.util.List;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
@@ -34,10 +35,10 @@ public final class ServerlistRequestHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
Server server = Server.getInstance();
server.loadAccountCharacters(c); // locks the login session until data is recovered from the cache or the DB.
c.setClickedNPC();
List<World> worlds = server.getWorlds();
c.requestedServerlist(worlds.size());
for (World world : server.getWorlds()) {
for (World world : worlds) {
c.announce(MaplePacketCreator.getServerList(world.getId(), GameConstants.WORLD_NAMES[world.getId()], world.getFlag(), world.getEventMessage(), world.getChannels()));
}
c.announce(MaplePacketCreator.getEndOfServerList());

View File

@@ -41,7 +41,7 @@ public final class ViewAllCharHandler extends AbstractMaplePacketHandler {
}
int accountId = c.getAccID();
Pair<Pair<Integer, List<MapleCharacter>>, List<Pair<Integer, List<MapleCharacter>>>> loginBlob = Server.getInstance().loadAccountCharlist(accountId);
Pair<Pair<Integer, List<MapleCharacter>>, List<Pair<Integer, List<MapleCharacter>>>> loginBlob = Server.getInstance().loadAccountCharlist(accountId, c.getVisibleWorlds());
List<Pair<Integer, List<MapleCharacter>>> worldChars = loginBlob.getRight();
int chrTotal = loginBlob.getLeft().getLeft();

View File

@@ -6,12 +6,12 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHandler { //Gey class name lol
public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
@@ -26,7 +26,8 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
}
c.setWorld(server.getCharacterWorld(charId));
if(c.getWorldServer().isWorldCapacityFull()) {
World wserv = c.getWorldServer();
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
@@ -45,11 +46,16 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
String pic = slea.readMapleAsciiString();
c.setPic(pic);
String[] socket = server.getInetSocket(c.getWorld(), channel);
if (socket == null) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
server.unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
String[] socket = server.getIP(c.getWorld(), channel).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
} catch (UnknownHostException e) {

View File

@@ -27,6 +27,7 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -45,7 +46,9 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
}
c.setWorld(server.getCharacterWorld(charId));
if(c.getWorldServer().isWorldCapacityFull()) {
World wserv = c.getWorldServer();
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
@@ -58,18 +61,23 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
}
try {
int channel = Randomizer.rand(1, c.getWorldServer().getChannelsSize());
int channel = Randomizer.rand(1, wserv.getChannelsSize());
c.setChannel(channel);
} catch (Exception e) {
e.printStackTrace();
c.setChannel(1);
}
String[] socket = server.getInetSocket(c.getWorld(), c.getChannel());
if(socket == null) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
server.unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
String[] socket = server.getIP(c.getWorld(), c.getChannel()).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
} catch (UnknownHostException e) {

View File

@@ -5,6 +5,7 @@ import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -27,7 +28,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
}
c.setWorld(server.getCharacterWorld(charId));
int channel = Randomizer.rand(1, c.getWorldServer().getChannelsSize());
World wserv = c.getWorldServer();
if(wserv == null || wserv.isWorldCapacityFull()) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
int channel = Randomizer.rand(1, wserv.getChannelsSize());
c.setChannel(channel);
String macs = slea.readMapleAsciiString();
@@ -38,7 +45,8 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
}
if (c.checkPic(pic)) {
if(c.getWorldServer().isWorldCapacityFull()) {
String[] socket = server.getInetSocket(c.getWorld(), c.getChannel());
if(socket == null) {
c.announce(MaplePacketCreator.getAfterLoginError(10));
return;
}
@@ -46,8 +54,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
server.unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
String[] socket = server.getIP(c.getWorld(), c.getChannel()).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
} catch (UnknownHostException e) {

View File

@@ -0,0 +1,33 @@
/*
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.worker;
import net.server.audit.LockCollector;
/**
* @author Ronan
* @info Thread responsible for expiring locks signalized for dispose.
*/
public class ReleaseLockWorker implements Runnable {
@Override
public void run() {
LockCollector.getInstance().runLockCollector();
}
}

View File

@@ -0,0 +1,40 @@
/*
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.worker;
import net.server.world.World;
/**
* @author Ronan
*/
public class ServerMessageWorker extends BaseWorker implements Runnable {
@Override
public void run() {
// It's purpose is for tracking whether the player client currently displays a boss HPBar and, if so,
// temporarily disable the server message for that player.
wserv.runDisabledServerMessagesSchedule();
}
public ServerMessageWorker(World world) {
super(world);
}
}

View File

@@ -30,9 +30,11 @@ import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Comparator;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import server.maps.MapleDoor;
public class MapleParty {
private int id;
@@ -44,11 +46,12 @@ public class MapleParty {
private Map<Integer, Integer> histMembers = new HashMap<>();
private int nextEntry = 0;
private Map<Integer, MapleDoor> doors = new HashMap<>();
private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.PARTY, true);
public MapleParty(int id, MaplePartyCharacter chrfor) {
this.leaderId = chrfor.getId();
this.members.add(chrfor);
this.id = id;
}
@@ -169,7 +172,7 @@ public class MapleParty {
}
}
public byte getPartyDoor(int cid) {
public List<Integer> getMembersSortedByHistory() {
List<Entry<Integer, Integer>> histList;
lock.lock();
@@ -187,16 +190,53 @@ public class MapleParty {
return ( o1.getValue() ).compareTo( o2.getValue() );
}
});
List<Integer> histSort = new LinkedList<>();
for(Entry<Integer, Integer> e : histList) {
histSort.add(e.getKey());
}
return histSort;
}
public byte getPartyDoor(int cid) {
List<Integer> histList = getMembersSortedByHistory();
byte slot = 0;
for(Entry<Integer, Integer> e: histList) {
if(e.getKey() == cid) break;
for(Integer e: histList) {
if(e == cid) break;
slot++;
}
return slot;
}
public void addDoor(Integer owner, MapleDoor door) {
lock.lock();
try {
this.doors.put(owner, door);
} finally {
lock.unlock();
}
}
public void removeDoor(Integer owner) {
lock.lock();
try {
this.doors.remove(owner);
} finally {
lock.unlock();
}
}
public Map<Integer, MapleDoor> getDoors() {
lock.lock();
try {
return Collections.unmodifiableMap(doors);
} finally {
lock.unlock();
}
}
public void assignNewLeader(MapleClient c) {
World world = c.getWorldServer();
MaplePartyCharacter newLeadr = null;
@@ -216,6 +256,15 @@ public class MapleParty {
}
public void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
lock = lock.dispose();
}

View File

@@ -21,15 +21,8 @@
*/
package net.server.world;
import java.util.Map;
import java.util.Map.Entry;
import java.util.LinkedHashMap;
import java.util.Collection;
import server.maps.MapleDoor;
import client.MapleCharacter;
import client.MapleJob;
import java.util.Collections;
public class MaplePartyCharacter {
private String name;
@@ -38,7 +31,6 @@ public class MaplePartyCharacter {
private int channel, world;
private int jobid;
private int mapid;
private Map<Integer, MapleDoor> doors = new LinkedHashMap<>();
private boolean online;
private MapleJob job;
private MapleCharacter character;
@@ -54,9 +46,6 @@ public class MaplePartyCharacter {
this.mapid = maplechar.getMapId();
this.online = true;
this.job = maplechar.getJob();
for (Entry<Integer, MapleDoor> entry : maplechar.getDoors().entrySet()) {
doors.put(entry.getKey(), entry.getValue());
}
}
public MaplePartyCharacter() {
@@ -118,18 +107,6 @@ public class MaplePartyCharacter {
public int getGuildId() {
return character.getGuildId();
}
public void addDoor(Integer owner, MapleDoor door) {
this.doors.put(owner, door);
}
public void removeDoor(Integer owner) {
this.doors.remove(owner);
}
public Collection<MapleDoor> getDoors() {
return Collections.unmodifiableCollection(doors.values());
}
@Override
public int hashCode() {

View File

@@ -63,12 +63,15 @@ import server.maps.MaplePlayerShop;
import server.maps.MaplePlayerShopItem;
import server.maps.AbstractMapleMapObject;
import net.server.worker.CharacterAutosaverWorker;
import net.server.worker.HiredMerchantWorker;
import net.server.worker.MountTirednessWorker;
import net.server.worker.PetFullnessWorker;
import net.server.worker.ServerMessageWorker;
import net.server.worker.TimedMapObjectWorker;
import net.server.worker.WeddingReservationWorker;
import net.server.PlayerStorage;
import net.server.Server;
import net.server.audit.LockCollector;
import net.server.channel.Channel;
import net.server.channel.CharacterIdChannelPair;
import net.server.guild.MapleGuild;
@@ -119,15 +122,17 @@ public class World {
private MonitoredReentrantLock partyLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PARTY, true);
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
private MonitoredReentrantLock owlLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_OWL);
private Map<Integer, Integer> disabledServerMessages = new HashMap<>(); // reuse owl lock
private MonitoredReentrantLock srvMessagesLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_SRVMESSAGES);
private ScheduledFuture<?> srvMessagesSchedule;
private MonitoredReentrantLock activePetsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PETS, true);
private Map<Integer, Byte> activePets = new LinkedHashMap<>();
private Map<Integer, Integer> activePets = new LinkedHashMap<>();
private ScheduledFuture<?> petsSchedule;
private long petUpdate;
private MonitoredReentrantLock activeMountsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_MOUNTS, true);
private Map<Integer, Byte> activeMounts = new LinkedHashMap<>();
private Map<Integer, Integer> activeMounts = new LinkedHashMap<>();
private ScheduledFuture<?> mountsSchedule;
private long mountUpdate;
@@ -135,7 +140,8 @@ public class World {
private Map<Integer, MaplePlayerShop> activePlayerShops = new LinkedHashMap<>();
private MonitoredReentrantLock activeMerchantsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_MERCHS, true);
private Map<Integer, Pair<MapleHiredMerchant, Byte>> activeMerchants = new LinkedHashMap<>();
private Map<Integer, Pair<MapleHiredMerchant, Integer>> activeMerchants = new LinkedHashMap<>();
private ScheduledFuture<?> merchantSchedule;
private long merchantUpdate;
private Map<Runnable, Long> registeredTimedMapObjects = new LinkedHashMap<>();
@@ -161,7 +167,9 @@ public class World {
TimerManager tman = TimerManager.getInstance();
petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000);
srvMessagesSchedule = tman.register(new ServerMessageWorker(this), 10 * 1000, 10 * 1000);
mountsSchedule = tman.register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000);
merchantSchedule = tman.register(new HiredMerchantWorker(this), 10 * 60 * 1000, 10 * 60 * 1000);
timedMapObjectsSchedule = tman.register(new TimedMapObjectWorker(this), 60 * 1000, 60 * 1000);
charactersSchedule = tman.register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000);
marriagesSchedule = tman.register(new WeddingReservationWorker(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000);
@@ -189,7 +197,11 @@ public class World {
public Channel getChannel(int channel) {
chnRLock.lock();
try {
return channels.get(channel - 1);
try {
return channels.get(channel - 1);
} catch (IndexOutOfBoundsException e) {
return null;
}
} finally {
chnRLock.unlock();
}
@@ -731,6 +743,7 @@ public class World {
partyLock.unlock();
}
party.addMember(chrfor);
return party;
}
@@ -784,7 +797,7 @@ public class World {
updateCharacterParty(party, operation, target, partyMembers);
for (MaplePartyCharacter partychar : partyMembers) {
MapleCharacter chr = getPlayerStorage().getCharacterByName(partychar.getName());
MapleCharacter chr = getPlayerStorage().getCharacterById(partychar.getId());
if (chr != null) {
if (operation == PartyOperation.DISBAND) {
chr.setParty(null);
@@ -799,7 +812,7 @@ public class World {
switch (operation) {
case LEAVE:
case EXPEL:
MapleCharacter chr = getPlayerStorage().getCharacterByName(target.getName());
MapleCharacter chr = getPlayerStorage().getCharacterById(target.getId());
if (chr != null) {
chr.getClient().announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target));
chr.setParty(null);
@@ -1126,7 +1139,7 @@ public class World {
}
public void addOwlItemSearch(Integer itemid) {
owlLock.lock();
srvMessagesLock.lock();
try {
Integer cur = owlSearched.get(itemid);
if(cur != null) {
@@ -1135,7 +1148,7 @@ public class World {
owlSearched.put(itemid, 1);
}
} finally {
owlLock.unlock();
srvMessagesLock.unlock();
}
}
@@ -1144,7 +1157,7 @@ public class World {
return new ArrayList<>(0);
}
owlLock.lock();
srvMessagesLock.lock();
try {
List<Pair<Integer, Integer>> searchCounts = new ArrayList<>(owlSearched.size());
@@ -1154,7 +1167,7 @@ public class World {
return searchCounts;
} finally {
owlLock.unlock();
srvMessagesLock.unlock();
}
}
@@ -1167,7 +1180,7 @@ public class World {
activePetsLock.lock();
try {
byte initProc;
int initProc;
if(System.currentTimeMillis() - petUpdate > 55000) initProc = ServerConstants.PET_EXHAUST_COUNT - 2;
else initProc = ServerConstants.PET_EXHAUST_COUNT - 1;
@@ -1189,7 +1202,7 @@ public class World {
}
public void runPetSchedule() {
Map<Integer, Byte> deployedPets;
Map<Integer, Integer> deployedPets;
activePetsLock.lock();
try {
@@ -1199,11 +1212,11 @@ public class World {
activePetsLock.unlock();
}
for(Map.Entry<Integer, Byte> dp: deployedPets.entrySet()) {
for(Map.Entry<Integer, Integer> dp: deployedPets.entrySet()) {
MapleCharacter chr = this.getPlayerStorage().getCharacterById(dp.getKey() / 4);
if(chr == null || !chr.isLoggedinWorld()) continue;
Byte dpVal = (byte)(dp.getValue() + 1);
Integer dpVal = dp.getValue() + 1;
if(dpVal == ServerConstants.PET_EXHAUST_COUNT) {
chr.runFullnessSchedule(dp.getKey() % 4);
dpVal = 0;
@@ -1226,7 +1239,7 @@ public class World {
Integer key = chr.getId();
activeMountsLock.lock();
try {
byte initProc;
int initProc;
if(System.currentTimeMillis() - mountUpdate > 45000) initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 2;
else initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 1;
@@ -1248,7 +1261,7 @@ public class World {
}
public void runMountSchedule() {
Map<Integer, Byte> deployedMounts;
Map<Integer, Integer> deployedMounts;
activeMountsLock.lock();
try {
mountUpdate = System.currentTimeMillis();
@@ -1257,11 +1270,11 @@ public class World {
activeMountsLock.unlock();
}
for(Map.Entry<Integer, Byte> dp: deployedMounts.entrySet()) {
for(Map.Entry<Integer, Integer> dp: deployedMounts.entrySet()) {
MapleCharacter chr = this.getPlayerStorage().getCharacterById(dp.getKey());
if(chr == null || !chr.isLoggedinWorld()) continue;
Byte dpVal = (byte)(dp.getValue() + 1);
int dpVal = dp.getValue() + 1;
if(dpVal == ServerConstants.MOUNT_EXHAUST_COUNT) {
chr.runTirednessSchedule();
dpVal = 0;
@@ -1320,8 +1333,8 @@ public class World {
public void registerHiredMerchant(MapleHiredMerchant hm) {
activeMerchantsLock.lock();
try {
byte initProc;
if(System.currentTimeMillis() - merchantUpdate > 5 * 60 * 1000) initProc = 1;
int initProc;
if(Server.getInstance().getCurrentTime() - merchantUpdate > 5 * 60 * 1000) initProc = 1;
else initProc = 0;
activeMerchants.put(hm.getOwnerId(), new Pair<>(hm, initProc));
@@ -1340,18 +1353,18 @@ public class World {
}
public void runHiredMerchantSchedule() {
Map<Integer, Pair<MapleHiredMerchant, Byte>> deployedMerchants;
Map<Integer, Pair<MapleHiredMerchant, Integer>> deployedMerchants;
activeMerchantsLock.lock();
try {
merchantUpdate = System.currentTimeMillis();
merchantUpdate = Server.getInstance().getCurrentTime();
deployedMerchants = new LinkedHashMap<>(activeMerchants);
for(Map.Entry<Integer, Pair<MapleHiredMerchant, Byte>> dm: deployedMerchants.entrySet()) {
byte timeOn = dm.getValue().getRight();
for(Map.Entry<Integer, Pair<MapleHiredMerchant, Integer>> dm: deployedMerchants.entrySet()) {
int timeOn = dm.getValue().getRight();
MapleHiredMerchant hm = dm.getValue().getLeft();
if(timeOn <= 144) { // 1440 minutes == 24hrs
activeMerchants.put(hm.getOwnerId(), new Pair<>(dm.getValue().getLeft(), (byte)(timeOn + 1)));
activeMerchants.put(hm.getOwnerId(), new Pair<>(dm.getValue().getLeft(), timeOn + 1));
} else {
hm.forceClose();
this.getChannel(hm.getChannel()).removeHiredMerchant(hm.getOwnerId());
@@ -1368,7 +1381,7 @@ public class World {
List<MapleHiredMerchant> hmList = new ArrayList<>();
activeMerchantsLock.lock();
try {
for(Pair<MapleHiredMerchant, Byte> hmp : activeMerchants.values()) {
for(Pair<MapleHiredMerchant, Integer> hmp : activeMerchants.values()) {
MapleHiredMerchant hm = hmp.getLeft();
if(hm.isOpen()) {
hmList.add(hm);
@@ -1397,7 +1410,7 @@ public class World {
public void registerTimedMapObject(Runnable r, long duration) {
timedMapObjectLock.lock();
try {
long expirationTime = System.currentTimeMillis() + duration;
long expirationTime = Server.getInstance().getCurrentTime() + duration;
registeredTimedMapObjects.put(r, expirationTime);
} finally {
timedMapObjectLock.unlock();
@@ -1409,7 +1422,7 @@ public class World {
timedMapObjectLock.lock();
try {
long timeNow = System.currentTimeMillis();
long timeNow = Server.getInstance().getCurrentTime();
for(Entry<Runnable, Long> rtmo : registeredTimedMapObjects.entrySet()) {
if(rtmo.getValue() <= timeNow) {
@@ -1429,6 +1442,68 @@ public class World {
}
}
public void resetDisabledServerMessages() {
srvMessagesLock.lock();
try {
disabledServerMessages.clear();
} finally {
srvMessagesLock.unlock();
}
}
public boolean registerDisabledServerMessage(int chrid) {
srvMessagesLock.lock();
try {
boolean alreadyDisabled = disabledServerMessages.containsKey(chrid);
disabledServerMessages.put(chrid, 0);
return alreadyDisabled;
} finally {
srvMessagesLock.unlock();
}
}
public boolean unregisterDisabledServerMessage(int chrid) {
srvMessagesLock.lock();
try {
return disabledServerMessages.remove(chrid) != null;
} finally {
srvMessagesLock.unlock();
}
}
public void runDisabledServerMessagesSchedule() {
List<Integer> toRemove = new LinkedList<>();
srvMessagesLock.lock();
try {
for(Entry<Integer, Integer> dsm : disabledServerMessages.entrySet()) {
int b = dsm.getValue();
if(b >= 4) { // ~35sec duration, 10sec update
toRemove.add(dsm.getKey());
} else {
disabledServerMessages.put(dsm.getKey(), ++b);
}
}
for(Integer chrid : toRemove) {
disabledServerMessages.remove(chrid);
}
} finally {
srvMessagesLock.unlock();
}
if(!toRemove.isEmpty()) {
for(Integer chrid : toRemove) {
MapleCharacter chr = players.getCharacterById(chrid);
if(chr != null && chr.isLoggedinWorld()) {
chr.announce(MaplePacketCreator.serverMessage(chr.getClient().getChannelServer().getServerMessage()));
}
}
}
}
public void setPlayerNpcMapStep(int mapid, int step) {
setPlayerNpcMapData(mapid, step, -1, false);
}
@@ -1670,7 +1745,7 @@ public class World {
}
}
private void disposeLocks() {
private void clearWorldData() {
List<MapleParty> pList;
partyLock.lock();
try {
@@ -1683,9 +1758,22 @@ public class World {
p.disposeLocks();
}
disposeLocks();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
accountCharsLock = accountCharsLock.dispose();
partyLock = partyLock.dispose();
owlLock = owlLock.dispose();
srvMessagesLock = srvMessagesLock.dispose();
activePetsLock = activePetsLock.dispose();
activeMountsLock = activeMountsLock.dispose();
activePlayerShopsLock = activePlayerShopsLock.dispose();
@@ -1703,11 +1791,21 @@ public class World {
petsSchedule = null;
}
if(srvMessagesSchedule != null) {
srvMessagesSchedule.cancel(false);
srvMessagesSchedule = null;
}
if(mountsSchedule != null) {
mountsSchedule.cancel(false);
mountsSchedule = null;
}
if(merchantSchedule != null) {
merchantSchedule.cancel(false);
merchantSchedule = null;
}
if(timedMapObjectsSchedule != null) {
timedMapObjectsSchedule.cancel(false);
timedMapObjectsSchedule = null;
@@ -1726,6 +1824,6 @@ public class World {
players.disconnectAll();
players = null;
disposeLocks();
clearWorldData();
}
}