Heal GMS + Improved chnl workers & Pshop tooltip + Equips on party HP

Slightly improved channel and disease announce workers performance.
Completion of repeatable quests no longer generates fame to players.
Equipment drop rates of Leprechaun were slightly decreased.
Fixed Pet Item Ignore not checking certain exploit cases correctly.
Optimized Pet Item Ignore server handler performance.
Fixed some exploits and improved performance on PetLootHandler.
Improved concurrency protection on MapleInventoryManipulator.
Heal skill effect on players now works GMS-intended, as description says. Also removed the delayed Heal cast effect to others.
Fixed party player HPBar not accounting the player's HP stat gained on equips towards the effective MaxHP.
The duration of mists generated by mobs has been rescaled to 10x longer than what has been displayed until now (wz duration property is supposed to actually be in 100ms).
Optimized timer management for mob skill cooldown and elemental effectiveness.
Implemented an additional inventory check system, to be used in cases where it's expected to remove a set group for items (with quantity) to then add a new group of items.
Fixed Player Shop/Hired Merchant "vacancy" tooltip, now properly showing whether the store has a visitor room or is already full at that time.
Fixed Player Shops only using the standard stand type.
Fixed cash pet food ignoring certain pet itemids when reading data from WZ.
This commit is contained in:
ronancpl
2018-07-21 14:40:46 -03:00
parent 3752ebbae5
commit bee8b5259b
78 changed files with 1194 additions and 628 deletions

View File

@@ -2182,6 +2182,55 @@ public class MaplePacketCreator {
mplew.write(joinable);
}
private static void updateHiredMerchantBoxInfo(MaplePacketLittleEndianWriter mplew, MapleHiredMerchant hm) {
byte[] roomInfo = hm.getShopRoomInfo();
mplew.write(5);
mplew.writeInt(hm.getObjectId());
mplew.writeMapleAsciiString(hm.getDescription());
mplew.write(hm.getItemId() % 100);
mplew.write(roomInfo);
}
public static byte[] updateHiredMerchantBox(MapleHiredMerchant hm) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_HIRED_MERCHANT.getValue());
mplew.writeInt(hm.getOwnerId());
updateHiredMerchantBoxInfo(mplew, hm);
return mplew.getPacket();
}
private static void updatePlayerShopBoxInfo(final MaplePacketLittleEndianWriter mplew, MaplePlayerShop shop) {
byte[] roomInfo = shop.getShopRoomInfo();
mplew.write(4);
mplew.writeInt(shop.getObjectId());
mplew.writeMapleAsciiString(shop.getDescription());
mplew.write(0); // pw
mplew.write(shop.getItemId() % 100);
mplew.write(roomInfo[0]); // curPlayers
mplew.write(roomInfo[1]); // maxPlayers
mplew.write(0);
}
public static byte[] updatePlayerShopBox(MaplePlayerShop shop) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(shop.getOwner().getId());
updatePlayerShopBoxInfo(mplew, shop);
return mplew.getPacket();
}
public static byte[] removePlayerShopBox(MaplePlayerShop shop) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(7);
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(shop.getOwner().getId());
mplew.write(0);
return mplew.getPacket();
}
public static byte[] facialExpression(MapleCharacter from, int expression) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(10);
mplew.writeShort(SendOpcode.FACIAL_EXPRESSION.getValue());
@@ -3241,23 +3290,7 @@ public class MaplePacketCreator {
mplew.write(2);
return mplew.getPacket();
}
public static byte[] addCharBox(MapleCharacter c, int type) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
addAnnounceBox(mplew, c.getPlayerShop(), type);
return mplew.getPacket();
}
public static byte[] removeCharBox(MapleCharacter c) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(7);
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
mplew.write(0);
return mplew.getPacket();
}
/**
* Possible values for <code>speaker</code>:<br> 0: Npc talking (left)<br>
* 1: Npc talking (right)<br> 2: Player talking (left)<br> 3: Player talking
@@ -5291,15 +5324,7 @@ public class MaplePacketCreator {
addAnnounceBox(mplew, c.getMiniGame(), 0, ammount, type);
return mplew.getPacket();
}
public static byte[] removeOmokBox(MapleCharacter c) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(7);
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
mplew.write(0);
return mplew.getPacket();
}
public static byte[] addMatchCardBox(MapleCharacter c, int ammount, int type) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
@@ -5307,11 +5332,11 @@ public class MaplePacketCreator {
addAnnounceBox(mplew, c.getMiniGame(), 0, ammount, type);
return mplew.getPacket();
}
public static byte[] removeMatchCardBox(MapleCharacter c) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
public static byte[] removeMinigameBox(MapleCharacter chr) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(7);
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
mplew.writeInt(chr.getId());
mplew.write(0);
return mplew.getPacket();
}
@@ -5576,7 +5601,7 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] spawnHiredMerchant(MapleHiredMerchant hm) {
public static byte[] spawnHiredMerchantBox(MapleHiredMerchant hm) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SPAWN_HIRED_MERCHANT.getValue());
mplew.writeInt(hm.getOwnerId());
@@ -5588,12 +5613,12 @@ public class MaplePacketCreator {
mplew.write(0x05);
mplew.writeInt(hm.getObjectId());
mplew.writeMapleAsciiString(hm.getDescription());
mplew.write(hm.getItemId() % 10);
mplew.write(hm.getItemId() % 100);
mplew.write(new byte[]{1, 4});
return mplew.getPacket();
}
public static byte[] destroyHiredMerchant(int id) {
public static byte[] removeHiredMerchantBox(int id) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.DESTROY_HIRED_MERCHANT.getValue());
mplew.writeInt(id);

View File

@@ -1,88 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
/**
*
* @author RonanLana
*/
public enum MonitoredLockType {
UNDEFINED,
CHARACTER_CHR,
CHARACTER_EFF,
CHARACTER_PET,
CHARACTER_PRT,
CLIENT,
CLIENT_ENCODER,
CLIENT_LOGIN,
BOOK,
ITEM,
INVENTORY,
SRVHANDLER_IDLE,
SRVHANDLER_TEMP,
BUFF_STORAGE,
PLAYER_STORAGE,
SERVER,
SERVER_LOGIN,
SERVER_DISEASES,
MERCHANT,
CHANNEL,
CHANNEL_FACEEXPRS,
CHANNEL_FACESCHDL,
CHANNEL_MOBACTION,
CHANNEL_MOBANIMAT,
CHANNEL_MOBSTATUS,
CHANNEL_OVTSTATUS,
CHANNEL_OVERALL,
GUILD,
PARTY,
WORLD_PARTY,
WORLD_OWL,
WORLD_PETS,
WORLD_CHARS,
WORLD_MOUNTS,
WORLD_PSHOPS,
WORLD_MERCHS,
WORLD_MAPOBJS,
EIM,
EIM_PARTY,
EIM_SCRIPT,
EM_LOBBY,
EM_QUEUE,
CASHSHOP,
VISITOR_PSHOP,
STORAGE,
MOB,
MOB_ANI,
MOB_EXT,
MOB_STATI,
MOBSKILL_FACTORY,
PORTAL,
VISITOR_MERCH,
MAP_CHRS,
MAP_OBJS,
MAP_FACTORY,
MAP_ITEM,
MAP_BOUNDS,
MINIDUNGEON,
REACTOR,
REACTOR_HIT;
}

View File

@@ -1,149 +0,0 @@
/*
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 tools.locks;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredReadLock extends ReentrantReadWriteLock.ReadLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredLockType id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredReadLock(MonitoredReentrantReadWriteLock lock) {
super(lock);
this.id = lock.id;
hashcode = this.hashCode();
}
@Override
public void lock() {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
super.lock();
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}

View File

@@ -1,153 +0,0 @@
/*
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 tools.locks;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredReentrantLock extends ReentrantLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredLockType id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredReentrantLock(MonitoredLockType id) {
super();
this.id = id;
hashcode = this.hashCode();
}
public MonitoredReentrantLock(MonitoredLockType id, boolean fair) {
super(fair);
this.id = id;
hashcode = this.hashCode();
}
@Override
public void lock() {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
super.lock();
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}

View File

@@ -1,50 +0,0 @@
/*
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 tools.locks;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
*
* @author RonanLana
*/
public class MonitoredReentrantReadWriteLock extends ReentrantReadWriteLock {
public final MonitoredLockType id;
public MonitoredReentrantReadWriteLock(MonitoredLockType id) {
super();
this.id = id;
}
public MonitoredReentrantReadWriteLock(MonitoredLockType id, boolean fair) {
super(fair);
this.id = id;
}
@Override
public ReadLock readLock() {
return super.readLock();
}
@Override
public WriteLock writeLock() {
return super.writeLock();
}
}

View File

@@ -1,148 +0,0 @@
/*
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 tools.locks;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredWriteLock extends ReentrantReadWriteLock.WriteLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredLockType id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredWriteLock(MonitoredReentrantReadWriteLock lock) {
super(lock);
this.id = lock.id;
hashcode = this.hashCode();
}
@Override
public void lock() {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
super.lock();
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}