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:
75
src/net/server/audit/LockCollector.java
Normal file
75
src/net/server/audit/LockCollector.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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--) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user