Fixed Horntail command + minor EXP gain issue fix

Now Horntail can be spawned properly by a command. Fixed a rase case
where an extremely high value of EXP gain would cause integer overflow,
potentially crashing one's client.
This commit is contained in:
ronancpl
2017-05-19 19:11:03 -03:00
parent de22d2b3eb
commit 36c3b7dea8
8 changed files with 118 additions and 54 deletions
+5 -1
View File
@@ -225,4 +225,8 @@ Implementa
17 Maio 2017, 17 Maio 2017,
Correção de bug com o item "Pet Name Tag", que não retirava o item após ser usado. Correção de bug com o item "Pet Name Tag", que não retirava o item após ser usado.
Correção em bug de uma quest (id 3927) de Ariant que envolve uma "parede". Correção em bug de uma quest (id 3927) de Ariant que envolve uma "parede".
18 - 19 Maio 2017,
Implementação das mecânicas do Horntail (spawn e kill).
Correção de bugs em casos onde valores de EXP ganhos excedem o limite máximo de um inteiro de 32bits.
+16 -10
View File
@@ -3,18 +3,24 @@
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/> <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2"> <open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
<group> <group>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/UseCashItemHandler.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/inventory/MaplePet.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/SpecialMoveHandler.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/partyquest/MonsterCarnival.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/CashOperationHandler.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/command/Commands.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleStatEffect.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/scripts/npc/world0/2013002.js</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/skills/Priest.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/constants/ServerConstants.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/PacketProcessor.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/life/MapleMonster.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/tools/MaplePacketCreator.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/scripts/event/OrbisPQ.js</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/MapleServerHandler.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/scripts/event/LudiPQ.js</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/ScrollHandler.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/ItemRewardHandler.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/GeneralChatHandler.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/GeneralChatHandler.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/scripting/AbstractPlayerInteraction.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleInventoryManipulator.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MapleBuffStat.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PetFoodHandler.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MapleCharacter.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MapleCharacter.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/CashShop.java</file> <file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/maps/MapleMap.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PetLootHandler.java</file>
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/ItemPickupHandler.java</file>
</group> </group>
</open-files> </open-files>
</project-private> </project-private>
+7 -6
View File
@@ -1730,17 +1730,18 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
gain *= 0.5; gain *= 0.5;
party *= 0.5; party *= 0.5;
} }
int equip = (gain / 10) * pendantExp; if(gain < 0) gain = Integer.MAX_VALUE; // integer overflow, heh.
int total = gain + equip + party; int equip = (int)Math.min((long)((gain / 10) * pendantExp), Integer.MAX_VALUE);
long total = gain + equip + party;
if (level < getMaxLevel()) { if (level < getMaxLevel()) {
if ((long) this.exp.get() + (long) total > (long) Integer.MAX_VALUE) { if ((long) this.exp.get() + total > (long) Integer.MAX_VALUE) {
int gainFirst = ExpTable.getExpNeededForLevel(level) - this.exp.get(); int gainFirst = ExpTable.getExpNeededForLevel(level) - this.exp.get();
total -= gainFirst + 1; total -= gainFirst + 1;
this.gainExp(gainFirst + 1, party, false, inChat, white); this.gainExp(gainFirst + 1, party, false, inChat, white);
} }
updateSingleStat(MapleStat.EXP, this.exp.addAndGet(total)); updateSingleStat(MapleStat.EXP, this.exp.addAndGet((int)Math.min(total, Integer.MAX_VALUE)));
if (show && gain != 0) { if (show && gain != 0) {
client.announce(MaplePacketCreator.getShowExpGain(gain, equip, party, inChat, white)); client.announce(MaplePacketCreator.getShowExpGain(gain, equip, party, inChat, white));
} }
+24 -15
View File
@@ -101,8 +101,7 @@ import java.util.ArrayList;
import server.maps.FieldLimit; import server.maps.FieldLimit;
public class Commands { public class Commands {
private static HashMap<String, Integer> gotomaps = new HashMap<String, Integer>();
private static HashMap<String, Integer> gotomaps = new HashMap<String, Integer>();
private static String[] tips = { private static String[] tips = {
"Please only use @gm in emergencies or to report somebody.", "Please only use @gm in emergencies or to report somebody.",
@@ -612,7 +611,7 @@ public class Commands {
bar += i < percent ? "|" : "."; bar += i < percent ? "|" : ".";
} }
bar += "]"; bar += "]";
player.yellowMessage(monster.getName() + " has " + percent + "% HP left."); player.yellowMessage(monster.getName() + " (" + monster.getId() + ") has " + percent + "% HP left.");
player.yellowMessage("HP: " + bar); player.yellowMessage("HP: " + bar);
} }
} }
@@ -755,7 +754,7 @@ public class Commands {
player.getMap().clearDrops(player); player.getMap().clearDrops(player);
} else if (sub[0].equals("go")) { } else if (sub[0].equals("go")) {
if (sub.length < 2){ if (sub.length < 2){
player.yellowMessage("Syntax: !spawn <mobid>"); player.yellowMessage("Syntax: !go <mapid>");
return false; return false;
} }
@@ -1132,7 +1131,7 @@ public class Commands {
e.printStackTrace(); e.printStackTrace();
} }
} }
} else if (sub[0].equals("killall")) { } else if (sub[0].equals("killall")) { // will need to be used again in case of horntail or multiple state baddies
List<MapleMapObject> monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER)); List<MapleMapObject> monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
MapleMap map = player.getMap(); MapleMap map = player.getMap();
for (MapleMapObject monstermo : monsters) { for (MapleMapObject monstermo : monsters) {
@@ -1147,7 +1146,7 @@ public class Commands {
List<MapleMapObject> monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER)); List<MapleMapObject> monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
for (MapleMapObject monstermo : monsters) { for (MapleMapObject monstermo : monsters) {
MapleMonster monster = (MapleMonster) monstermo; MapleMonster monster = (MapleMonster) monstermo;
player.message("Monster ID: " + monster.getId()); player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((monster.getController() != null) ? monster.getController().getName() : "<none>"));
} }
} else if (sub[0].equals("unbug")) { } else if (sub[0].equals("unbug")) {
c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.enableActions()); c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.enableActions());
@@ -1428,6 +1427,24 @@ public class Commands {
public static void executeAdminCommand(MapleClient c, String[] sub, char heading) { public static void executeAdminCommand(MapleClient c, String[] sub, char heading) {
MapleCharacter player = c.getPlayer(); MapleCharacter player = c.getPlayer();
switch (sub[0]) { switch (sub[0]) {
case "zakum":
player.getMap().spawnFakeMonsterOnGroundBelow(MapleLifeFactory.getMonster(8800000), player.getPosition());
for (int x = 8800003; x < 8800011; x++) {
player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(x), player.getPosition());
}
break;
case "horntail":
final Point targetPoint = player.getPosition();
final MapleMap targetMap = player.getMap();
targetMap.spawnHorntailOnGroundBelow(targetPoint);
break;
case "pinkbean":
player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8820001), player.getPosition());
break;
case "sp": //Changed to support giving sp /a case "sp": //Changed to support giving sp /a
if (sub.length < 2){ if (sub.length < 2){
player.yellowMessage("Syntax: !sp <newsp>"); player.yellowMessage("Syntax: !sp <newsp>");
@@ -1443,9 +1460,7 @@ public class Commands {
victim.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSp()); victim.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSp());
} }
break; break;
case "horntail":
player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8810026), player.getPosition());
break;
case "packet": case "packet":
player.getMap().broadcastMessage(MaplePacketCreator.customPacket(joinStringFrom(sub, 1))); player.getMap().broadcastMessage(MaplePacketCreator.customPacket(joinStringFrom(sub, 1)));
break; break;
@@ -1613,12 +1628,6 @@ public class Commands {
player.getMap().removeMapObject(item); player.getMap().removeMapObject(item);
} }
break; break;
case "zakum":
player.getMap().spawnFakeMonsterOnGroundBelow(MapleLifeFactory.getMonster(8800000), player.getPosition());
for (int x = 8800003; x < 8800011; x++) {
player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(x), player.getPosition());
}
break;
case "clearquestcache": case "clearquestcache":
MapleQuest.clearCache(); MapleQuest.clearCache();
player.dropMessage(5, "Quest Cache Cleared."); player.dropMessage(5, "Quest Cache Cleared.");
+16 -6
View File
@@ -210,6 +210,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
int trueDamage = Math.min(hp, damage); // since magic happens otherwise B^) int trueDamage = Math.min(hp, damage); // since magic happens otherwise B^)
if(ServerConstants.USE_DEBUG == true && from != null) from.dropMessage(5, "Hitted MOB " + this.getId()); if(ServerConstants.USE_DEBUG == true && from != null) from.dropMessage(5, "Hitted MOB " + this.getId());
dispatchMonsterDamaged(from, trueDamage);
hp -= damage; hp -= damage;
if (takenDamage.containsKey(from.getId())) { if (takenDamage.containsKey(from.getId())) {
@@ -368,16 +369,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
personalExp *= (stati.get(MonsterStatus.SHOWDOWN).getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0); personalExp *= (stati.get(MonsterStatus.SHOWDOWN).getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0);
} }
} }
if (exp < 0) {//O.O ><
personalExp = Integer.MAX_VALUE;
}
attacker.gainExp(personalExp, partyExp, true, false, isKiller); attacker.gainExp(personalExp, partyExp, true, false, isKiller);
attacker.mobKilled(getId()); attacker.mobKilled(getId());
attacker.increaseEquipExp(personalExp);//better place attacker.increaseEquipExp(personalExp);//better place
} }
} }
public MapleCharacter killBy(MapleCharacter killer) { public MapleCharacter killBy(final MapleCharacter killer) {
distributeExperience(killer != null ? killer.getId() : 0); distributeExperience(killer != null ? killer.getId() : 0);
if (getController() != null) { // this can/should only happen when a hidden gm attacks the monster if (getController() != null) { // this can/should only happen when a hidden gm attacks the monster
@@ -424,11 +423,16 @@ public class MapleMonster extends AbstractLoadedMapleLife {
mob.disableDrops(); mob.disableDrops();
} }
reviveMap.spawnMonster(mob); reviveMap.spawnMonster(mob);
if(mob.getId() >= 8810010 && mob.getId() <= 8810017 && reviveMap.isHorntailDefeated()) {
for(int i = 8810018; i >= 8810010; i--)
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
}
} }
} }
}, getAnimationTime("die1")); }, getAnimationTime("die1"));
} }
else { else { // is this even necessary?
System.out.println("[CRITICAL LOSS] toSpawn is null for " + this.getName()); System.out.println("[CRITICAL LOSS] toSpawn is null for " + this.getName());
} }
@@ -447,6 +451,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
listener.monsterKilled(getAnimationTime("die1")); listener.monsterKilled(getAnimationTime("die1"));
} }
} }
public void dispatchMonsterDamaged(MapleCharacter from, int trueDmg) {
for (MonsterListener listener : listeners.toArray(new MonsterListener[listeners.size()])) {
listener.monsterDamaged(from, trueDmg);
}
}
// should only really be used to determine drop owner // should only really be used to determine drop owner
private int getHighestDamagerId() { private int getHighestDamagerId() {
@@ -992,7 +1002,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return(1.0f); return(1.0f);
} }
private final void changeLevelByDifficulty(final int difficulty, boolean pqMob) { private void changeLevelByDifficulty(final int difficulty, boolean pqMob) {
changeLevel((int)(this.getLevel() * getDifficultyRate(difficulty)), pqMob); changeLevel((int)(this.getLevel() * getDifficultyRate(difficulty)), pqMob);
} }
+2
View File
@@ -1,6 +1,8 @@
package server.life; package server.life;
import client.MapleCharacter;
public interface MonsterListener { public interface MonsterListener {
public void monsterKilled(int aniTime); public void monsterKilled(int aniTime);
public void monsterDamaged(MapleCharacter from, int trueDmg);
} }
+3
View File
@@ -91,6 +91,9 @@ public class SpawnPoint {
} }
spawnedMonsters.decrementAndGet(); spawnedMonsters.decrementAndGet();
} }
@Override
public void monsterDamaged(MapleCharacter from, int trueDmg) {}
}); });
if (mobTime == 0) { if (mobTime == 0) {
nextPossibleSpawn = System.currentTimeMillis() + mobInterval; nextPossibleSpawn = System.currentTimeMillis() + mobInterval;
+45 -16
View File
@@ -77,6 +77,7 @@ import server.partyquest.MonsterCarnival;
import server.partyquest.MonsterCarnivalParty; import server.partyquest.MonsterCarnivalParty;
import server.partyquest.Pyramid; import server.partyquest.Pyramid;
import scripting.event.EventInstanceManager; import scripting.event.EventInstanceManager;
import server.life.MonsterListener;
import tools.FilePrinter; import tools.FilePrinter;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.Pair; import tools.Pair;
@@ -611,7 +612,7 @@ public class MapleMap {
} }
if (monster.getStats().selfDestruction() != null && monster.getStats().selfDestruction().getHp() > -1) {// should work ;p if (monster.getStats().selfDestruction() != null && monster.getStats().selfDestruction().getHp() > -1) {// should work ;p
if (monster.getHp() <= monster.getStats().selfDestruction().getHp()) { if (monster.getHp() <= monster.getStats().selfDestruction().getHp()) {
killMonster(monster, chr, true, false, monster.getStats().selfDestruction().getAction()); killMonster(monster, chr, true, monster.getStats().selfDestruction().getAction());
return true; return true;
} }
} }
@@ -632,20 +633,12 @@ public class MapleMap {
} }
public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops) { public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops) {
killMonster(monster, chr, withDrops, false, 1); killMonster(monster, chr, withDrops, 1);
} }
public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops, final boolean secondTime, int animation) { public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops, int animation) {
if (monster.getId() == 8810018 && !secondTime) { if(monster == null) return;
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
killMonster(monster, chr, withDrops, true, 1);
killAllMonsters();
}
}, 3000);
return;
}
if (chr == null) { if (chr == null) {
spawnedMonstersOnMap.decrementAndGet(); spawnedMonstersOnMap.decrementAndGet();
monster.setHp(0); monster.setHp(0);
@@ -780,7 +773,7 @@ public class MapleMap {
continue; continue;
} }
killMonster(monster, null, false, false, 1); killMonster(monster, null, false, 1);
} }
} }
@@ -792,7 +785,7 @@ public class MapleMap {
for (MapleMapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER))) { for (MapleMapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER))) {
MapleMonster monster = (MapleMonster) monstermo; MapleMonster monster = (MapleMonster) monstermo;
killMonster(monster, null, false, false, 1); killMonster(monster, null, false, 1);
} }
} }
@@ -1174,7 +1167,7 @@ public class MapleMap {
TimerManager.getInstance().schedule(new Runnable() { TimerManager.getInstance().schedule(new Runnable() {
@Override @Override
public void run() { public void run() {
killMonster(monster, null, false, false, selfDestruction.getAction()); killMonster(monster, null, false, selfDestruction.getAction());
} }
}, selfDestruction.removeAfter() * 1000); }, selfDestruction.removeAfter() * 1000);
} }
@@ -2666,4 +2659,40 @@ public class MapleMap {
public boolean isDojoMap() { public boolean isDojoMap() {
return mapid >= 925020000 && mapid < 925040000; return mapid >= 925020000 && mapid < 925040000;
} }
public boolean isHorntailDefeated() { // all parts of dead horntail can be found here?
for(int i = 8810010; i <= 8810017; i++) {
if(getMonsterById(i) == null) return false;
}
return true;
}
public void spawnHorntailOnGroundBelow(final Point targetPoint) { // ayy lmao
spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8810026), targetPoint);
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
for (int x = 8810002; x <= 8810009; x++) {
spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(x), targetPoint);
}
spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8810018), targetPoint);
final MapleMonster ht = getMonsterById(8810018);
for(int mobId = 8810002; mobId <= 8810009; mobId++) {
getMonsterById(mobId).addListener(new MonsterListener() {
@Override
public void monsterKilled(int aniTime) {}
@Override
public void monsterDamaged(MapleCharacter from, int trueDmg) {
ht.damage(from, trueDmg);
}
});
}
}
}, 5 * 1000);
}
} }