Stat update & Mob skill animation & Yellow EXP patch + Packet logging
Solved a deadlock case within character stat locks that would sometimes tangle up during stat update dispatch operations. Fixed skill animation being unproperly casted when a mob tries to use a skill even though it couldn't possibly use from its current skillset. Fixed a bug with EXP gain (on where the solo player is on a party) where the EXP would appear in yellow even after soloing a mob. Added packet logging. Happy Easter, folks!
This commit is contained in:
@@ -32,12 +32,14 @@ import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import server.maps.AbstractAnimatedMapleMapObject;
|
||||
import server.maps.MapleMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMapleMapObject {
|
||||
protected MapleMap map;
|
||||
protected int str, dex, luk, int_, hp, maxhp, mp, maxmp;
|
||||
protected int hpMpApUsed, remainingAp;
|
||||
protected int[] remainingSp = new int[10];
|
||||
@@ -65,6 +67,14 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setMap(MapleMap map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public MapleMap getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public int getStr() {
|
||||
statRlock.lock();
|
||||
try {
|
||||
@@ -202,16 +212,37 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
|
||||
this.hpMpApUsed = mpApUsed;
|
||||
}
|
||||
|
||||
private void dispatchHpChanged(int oldHp) {
|
||||
listener.onHpChanged(oldHp);
|
||||
private void dispatchHpChanged(final int oldHp) {
|
||||
Runnable r = new Runnable() { // thanks BHB (BHB88) for detecting a deadlock case within player stats.
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onHpChanged(oldHp);
|
||||
}
|
||||
};
|
||||
|
||||
map.registerCharacterStatUpdate(r);
|
||||
}
|
||||
|
||||
private void dispatchHpmpPoolUpdated() {
|
||||
listener.onHpmpPoolUpdate();
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onHpmpPoolUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
map.registerCharacterStatUpdate(r);
|
||||
}
|
||||
|
||||
private void dispatchStatPoolUpdateAnnounced() {
|
||||
listener.onAnnounceStatPoolUpdate();
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onAnnounceStatPoolUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
map.registerCharacterStatUpdate(r);
|
||||
}
|
||||
|
||||
protected void setHp(int newHp) {
|
||||
|
||||
@@ -229,7 +229,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private MaplePartyCharacter mpc = null;
|
||||
private MapleInventory[] inventory;
|
||||
private MapleJob job = MapleJob.BEGINNER;
|
||||
private MapleMap map;
|
||||
private MapleMessenger messenger = null;
|
||||
private MapleMiniGame miniGame;
|
||||
private MapleMount maplemount;
|
||||
@@ -4847,10 +4846,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public MapleMap getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public int getMapId() {
|
||||
if (map != null) {
|
||||
return map.getId();
|
||||
@@ -8713,10 +8708,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public void setMap(int PmapId) {
|
||||
this.mapid = PmapId;
|
||||
}
|
||||
|
||||
public void setMap(MapleMap newmap) {
|
||||
this.map = newmap;
|
||||
}
|
||||
|
||||
public void setMessenger(MapleMessenger messenger) {
|
||||
this.messenger = messenger;
|
||||
|
||||
@@ -15,7 +15,7 @@ package constants;
|
||||
|
||||
public class CharsetConstants {
|
||||
|
||||
public static MapleLanguageType MAPLE_TYPE = MapleLanguageType.LANGUAGE_PT_BR;
|
||||
public static MapleLanguageType MAPLE_TYPE = MapleLanguageType.LANGUAGE_US;
|
||||
|
||||
public enum MapleLanguageType {
|
||||
LANGUAGE_PT_BR(1, "ISO-8859-1"),
|
||||
|
||||
45
src/constants/OpcodeConstants.java
Normal file
45
src/constants/OpcodeConstants.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
|
||||
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 constants;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import net.opcodes.RecvOpcode;
|
||||
import net.opcodes.SendOpcode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class OpcodeConstants {
|
||||
public static Map<Integer, String> sendOpcodeNames = new HashMap<>();
|
||||
public static Map<Integer, String> recvOpcodeNames = new HashMap<>();
|
||||
|
||||
public static void generateOpcodeNames() {
|
||||
for (SendOpcode op : SendOpcode.values()) {
|
||||
sendOpcodeNames.put(op.getValue(), op.name());
|
||||
}
|
||||
|
||||
for (RecvOpcode op : RecvOpcode.values()) {
|
||||
recvOpcodeNames.put(op.getValue(), op.name());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,6 +62,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info.
|
||||
public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids.
|
||||
public static boolean USE_DEBUG_SHOW_RCVD_MVLIFE = false; //Prints on the cmd all received move life content.
|
||||
public static final boolean USE_DEBUG_SHOW_PACKET = false;
|
||||
public static boolean USE_SUPPLY_RATE_COUPONS = true; //Allows rate coupons to be sold through the Cash Shop.
|
||||
|
||||
public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range.
|
||||
|
||||
@@ -21,13 +21,20 @@
|
||||
*/
|
||||
package net.mina;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleClient;
|
||||
import constants.OpcodeConstants;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import tools.HexTool;
|
||||
import tools.MapleAESOFB;
|
||||
import tools.data.input.ByteArrayByteStream;
|
||||
import tools.data.input.GenericLittleEndianAccessor;
|
||||
import net.opcodes.RecvOpcode;
|
||||
import tools.FilePrinter;
|
||||
|
||||
public class MaplePacketDecoder extends CumulativeProtocolDecoder {
|
||||
private static final String DECODER_STATE_KEY = MaplePacketDecoder.class.getName() + ".STATE";
|
||||
@@ -68,8 +75,32 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder {
|
||||
rcvdCrypto.crypt(decryptedPacket);
|
||||
MapleCustomEncryption.decryptData(decryptedPacket);
|
||||
out.write(decryptedPacket);
|
||||
if (ServerConstants.USE_DEBUG_SHOW_PACKET){ // packet traffic log: Atoot's idea, applied using auto-identation thanks to lrenex
|
||||
int packetLen = decryptedPacket.length;
|
||||
int pHeader = readFirstShort(decryptedPacket);
|
||||
String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();
|
||||
String op = lookupSend(pHeader);
|
||||
String Send = "ClientSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n";
|
||||
if (packetLen <= 3000) {
|
||||
String SendTo = Send + HexTool.toString(decryptedPacket) + "\r\n" + HexTool.toStringFromAscii(decryptedPacket);
|
||||
System.out.println(SendTo);
|
||||
if (op == null) {
|
||||
System.out.println("UnknownPacket:" + SendTo);
|
||||
}
|
||||
} else {
|
||||
FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{decryptedPacket[0], decryptedPacket[1]}) + "...");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String lookupSend(int val) {
|
||||
return OpcodeConstants.recvOpcodeNames.get(val);
|
||||
}
|
||||
|
||||
private int readFirstShort(byte[] arr) {
|
||||
return new GenericLittleEndianAccessor(new ByteArrayByteStream(arr)).readShort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.mina;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleClient;
|
||||
import constants.OpcodeConstants;
|
||||
import net.opcodes.SendOpcode;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoder;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import tools.MapleAESOFB;
|
||||
import tools.HexTool;
|
||||
import tools.data.input.ByteArrayByteStream;
|
||||
import tools.data.input.GenericLittleEndianAccessor;
|
||||
import tools.FilePrinter;
|
||||
|
||||
public class MaplePacketEncoder implements ProtocolEncoder {
|
||||
|
||||
@@ -39,6 +47,23 @@ public class MaplePacketEncoder implements ProtocolEncoder {
|
||||
try {
|
||||
final MapleAESOFB send_crypto = client.getSendCrypto();
|
||||
final byte[] input = (byte[]) message;
|
||||
if (ServerConstants.USE_DEBUG_SHOW_PACKET) {
|
||||
int packetLen = input.length;
|
||||
int pHeader = readFirstShort(input);
|
||||
String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();
|
||||
String op = lookupRecv(pHeader);
|
||||
String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n";
|
||||
if (packetLen <= 50000) {
|
||||
String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input);
|
||||
System.out.println(RecvTo);
|
||||
if (op == null) {
|
||||
System.out.println("UnknownPacket:" + RecvTo);
|
||||
}
|
||||
} else {
|
||||
FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ...");
|
||||
}
|
||||
}
|
||||
|
||||
final byte[] unencrypted = new byte[input.length];
|
||||
System.arraycopy(input, 0, unencrypted, 0, input.length);
|
||||
final byte[] ret = new byte[unencrypted.length + 4];
|
||||
@@ -59,6 +84,14 @@ public class MaplePacketEncoder implements ProtocolEncoder {
|
||||
out.write(IoBuffer.wrap(((byte[]) message)));
|
||||
}
|
||||
}
|
||||
|
||||
private String lookupRecv(int val) {
|
||||
return OpcodeConstants.sendOpcodeNames.get(val);
|
||||
}
|
||||
|
||||
private int readFirstShort(byte[] arr) {
|
||||
return new GenericLittleEndianAccessor(new ByteArrayByteStream(arr)).readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(IoSession session) throws Exception {}
|
||||
|
||||
@@ -86,6 +86,7 @@ import client.inventory.manipulator.MapleCashidGenerator;
|
||||
import client.newyear.NewYearCardRecord;
|
||||
import constants.ItemConstants;
|
||||
import constants.GameConstants;
|
||||
import constants.OpcodeConstants;
|
||||
import constants.ServerConstants;
|
||||
import java.util.TimeZone;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
@@ -969,6 +970,7 @@ public class Server {
|
||||
online = true;
|
||||
|
||||
MapleSkillbookInformationProvider.getInstance();
|
||||
OpcodeConstants.generateOpcodeNames();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
|
||||
@@ -126,7 +126,9 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
nextSkillLevel = skillToUse.getRight();
|
||||
nextUse = MobSkillFactory.getMobSkill(nextSkillId, nextSkillLevel);
|
||||
|
||||
if (!(nextUse != null && nextUse.getHP() >= (int) (((float) monster.getHp() / monster.getMaxHp()) * 100) && mobMp >= nextUse.getMpCon())) {
|
||||
if (!(nextUse != null && monster.canUseSkill(nextUse) && nextUse.getHP() >= (int) (((float) monster.getHp() / monster.getMaxHp()) * 100) && mobMp >= nextUse.getMpCon())) {
|
||||
// thanks OishiiKawaiiDesu for noticing mobs trying to cast skills they are not supposed to be able
|
||||
|
||||
nextSkillId = 0;
|
||||
nextSkillLevel = 0;
|
||||
nextUse = null;
|
||||
|
||||
@@ -541,12 +541,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
avgExpReward += exp;
|
||||
}
|
||||
|
||||
// thanks Simon for finding an issue with solo party player gaining yellow EXP when soloing mobs
|
||||
float realAvgExpReward = avgExpReward;
|
||||
avgExpReward -= exp2; // clear out the 20% raw exp from last hitting
|
||||
avgExpReward /= personalExpReward.size();
|
||||
|
||||
float varExpReward = 0.0f;
|
||||
for (Float exp : personalExpReward.values()) {
|
||||
varExpReward += Math.pow(exp - avgExpReward, 2);
|
||||
varExpReward += Math.pow(exp - realAvgExpReward, 2);
|
||||
}
|
||||
varExpReward /= personalExpReward.size();
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ public class FilePrinter {
|
||||
EXPLOITS = "game/exploits/",
|
||||
STORAGE = "game/storage/",
|
||||
PACKET_LOGS = "game/packetlogs/",
|
||||
PACKET_STREAM = "game/packetstream/",
|
||||
FREDRICK = "game/npcs/fredrick/",
|
||||
NPC_UNCODED = "game/npcs/UncodedNPCs.txt",
|
||||
QUEST_UNCODED = "game/quests/UncodedQuests.txt",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
package tools;
|
||||
|
||||
import constants.CharsetConstants;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class HexTool {
|
||||
@@ -84,4 +85,23 @@ public class HexTool {
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public static final String toStringFromAscii(final byte[] bytes) {
|
||||
byte[] ret = new byte[bytes.length];
|
||||
for (int x = 0; x < bytes.length; x++) {
|
||||
if (bytes[x] < 32 && bytes[x] >= 0) {
|
||||
ret[x] = '.';
|
||||
} else {
|
||||
int chr = ((short) bytes[x]) & 0xFF;
|
||||
ret[x] = (byte) chr;
|
||||
}
|
||||
}
|
||||
String encode = CharsetConstants.MAPLE_TYPE.getAscii();
|
||||
try {
|
||||
String str = new String(ret, encode);
|
||||
return str;
|
||||
} catch (Exception e) {}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user