Return map & MaplePacketEncoder & Quest status patch

Fixed null pointer issue when trying to use return scroll on maps such as Mu Lung.
Fixed a critical deadlock issue with MaplePacketEncoder.
Fixed a critical DB leak regarding player's quest status.
This commit is contained in:
ronancpl
2018-06-08 13:21:03 -03:00
parent cdac59326a
commit 0b8d3a0b2b
13 changed files with 218 additions and 35 deletions

View File

@@ -185,7 +185,7 @@ Exploits patched:
* Player being given free access to any character of any account once they have authenticated their account on login phase. * Player being given free access to any character of any account once they have authenticated their account on login phase.
* Player being given permission to delete any character of any account once they have authenticated their account on login phase. * Player being given permission to delete any character of any account once they have authenticated their account on login phase.
* Player being able to start/complete any quest freely. * Player being able to start/complete any quest freely.
* Several assynchronous-oriented explots patched, highlights on those involving Fredrick & Duey. * Several assynchronous-oriented exploits patched, highlights on those involving Fredrick & Duey.
Localhost: Localhost:

View File

@@ -1019,4 +1019,10 @@ Corrigido hired merchants agora removendo visitantes e dono quando expira.
Item maker agora puxa itemid de catalisadores do WZ. Item maker agora puxa itemid de catalisadores do WZ.
Corrigido sistema de evolução de pets passando valor de expiração com overflow pro novo pet, que resultava em pet inativo. Corrigido sistema de evolução de pets passando valor de expiração com overflow pro novo pet, que resultava em pet inativo.
Melhorado proteção contra acesso concorrente em mais algumas seções de código de player shop e hired merchants. Melhorado proteção contra acesso concorrente em mais algumas seções de código de player shop e hired merchants.
Adicionado comportamento de substituição de itens ao expirar, cortesia do GabrielSin. Adicionado comportamento de substituição de itens ao expirar, cortesia do GabrielSin.
07 Junho 2018,
Corrigido ponteiro nulo ao tentar usar return scroll em mapas como Mu Lung.
Corrigido um crítico problema de deadlock com o MaplePacketEncoder.
Corrigido um crítico problema de vazamento de dados na DB referente ao quest status.
4th job Aran agora usa script de evento, evitando múltiplos jogadores lutando contra múltiplas Mahas.

155
scripts/event/MahaBattle.js Normal file
View File

@@ -0,0 +1,155 @@
/*
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/>.
*/
/**
* @author: Ronan
* @event: Vs Uncontrollable Maha
*/
var entryMap = 914020000;
var exitMap = 140000000;
var recruitMap = 140000000;
var clearMap = 140000000;
var minMapId = 914020000;
var maxMapId = 914020000;
var eventTime = 10; // 10 minutes
var lobbyRange = [0, 0];
function init() {}
function setup(level, lobbyid) {
var eim = em.newInstance("Maha" + lobbyid);
eim.setProperty("level", level);
eim.setProperty("boss", "0");
var mapObj = eim.getInstanceMap(entryMap);
mapObj.resetPQ(level);
mapObj.instanceMapForceRespawn();
respawnStages(eim);
eim.startEventTimer(eventTime * 60000);
return eim;
}
function afterSetup(eim) {}
function respawnStages(eim) {}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
player.changeMap(map, map.getPortal(0));
}
function scheduledTimeout(eim) {
end(eim);
}
function playerUnregistered(eim, player) {}
function playerExit(eim, player) {
eim.unregisterPlayer(player);
player.changeMap(exitMap, 0);
}
function playerLeft(eim, player) {
if(!eim.isEventCleared()) {
playerExit(eim, player);
}
}
function changedMap(eim, player, mapid) {
if (mapid < minMapId || mapid > maxMapId) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
}
function changedLeader(eim, leader) {}
function playerDead(eim, player) {}
function playerRevive(eim, player) { // player presses ok on the death pop up.
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function playerDisconnected(eim, player) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function leftParty(eim, player) {}
function disbandParty(eim) {}
function monsterValue(eim, mobId) {
return 1;
}
function end(eim) {
var party = eim.getPlayers();
for (var i = 0; i < party.size(); i++) {
playerExit(eim, party.get(i));
}
eim.dispose();
}
function giveRandomEventReward(eim, player) {
eim.giveEventReward(player);
}
function clearPQ(eim) {
eim.stopEventTimer();
eim.setEventCleared();
}
function isMaha(mob) {
var mobid = mob.getId();
return mobid == 9001014;
}
function monsterKilled(mob, eim) {
if(isMaha(mob)) {
eim.clearPQ();
}
}
function allMonstersDead(eim) {}
function cancelSchedule() {}
function dispose(eim) {}

View File

@@ -22,11 +22,15 @@
//carta //carta
function start(){ function start(){
if(cm.isQuestStarted(6301)) { if(cm.isQuestStarted(6301)) {
if (cm.haveItem(4000175)) if (cm.haveItem(4000175)) {
cm.gainItem(4000175, -1);
cm.warp(923000000, 0); cm.warp(923000000, 0);
else } else {
cm.sendOk("In order to open the crack of dimension you will have to posess one piece of Miniature Pianus. Those could be gained by defeating a Pianus."); cm.sendOk("In order to open the crack of dimension you will have to posess one piece of Miniature Pianus. Those could be gained by defeating a Pianus.");
} else }
} else {
cm.sendOk("I'm #bCarta the sea-witch.#k Don't fool around with me, as I'm known for my habit of turning people into worms."); cm.sendOk("I'm #bCarta the sea-witch.#k Don't fool around with me, as I'm known for my habit of turning people into worms.");
}
cm.dispose(); cm.dispose();
} }

View File

@@ -24,9 +24,11 @@ function start(mode, type, selection) {
} else if (status == 4) { } else if (status == 4) {
qm.startQuest(); qm.startQuest();
var map = qm.getClient().getChannelServer().getMapFactory().getMap(914020000); var mb = qm.getEventManager("MahaBattle");
spawnMob(-365, 86, 9001014, map); mb.newInstance("MahaBattle");
qm.warp(914020000, 0); mb.setProperty("player", qm.getPlayer().getName());
mb.startInstance(qm.getPlayer());
qm.dispose(); qm.dispose();
} }
} }

View File

@@ -2032,6 +2032,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
ps.setInt(1, cid); ps.setInt(1, cid);
ps.executeUpdate(); ps.executeUpdate();
} }
try (PreparedStatement ps = con.prepareStatement("DELETE FROM queststatus WHERE characterid = ?")) {
ps.setInt(1, cid);
ps.executeUpdate();
}
} }
private void deleteWhereCharacterId(Connection con, String sql) throws SQLException { private void deleteWhereCharacterId(Connection con, String sql) throws SQLException {
@@ -3717,7 +3722,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey()); MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey());
MapleBuffStatValueHolder statMbsvh = statup.getValue(); MapleBuffStatValueHolder statMbsvh = statup.getValue();
if(mbsvh == null || mbsvh.value < statMbsvh.value || (mbsvh.value == statMbsvh.value && mbsvh.effect.getStatups().size() < statMbsvh.effect.getStatups().size())) { if(mbsvh == null || mbsvh.value < statMbsvh.value || (mbsvh.value == statMbsvh.value && mbsvh.effect.getStatups().size() <= statMbsvh.effect.getStatups().size())) {
toDeploy.put(statup.getKey(), statMbsvh); toDeploy.put(statup.getKey(), statMbsvh);
} else { } else {
if(!isSingletonStatup(statup.getKey())) { if(!isSingletonStatup(statup.getKey())) {
@@ -5969,7 +5974,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<Integer, MapleQuestStatus> loadedQuestStatus = new LinkedHashMap<>(); Map<Integer, MapleQuestStatus> loadedQuestStatus = new LinkedHashMap<>();
while (rs.next()) { while (rs.next()) {
MapleQuest q = MapleQuest.getInstance(rs.getShort("quest")); MapleQuest q = MapleQuest.getInstance(rs.getShort("quest"));
MapleQuestStatus status = new MapleQuestStatus(q, MapleQuestStatus.Status.getById(rs.getInt("status"))); MapleQuestStatus status = new MapleQuestStatus(q, MapleQuestStatus.Status.getById(rs.getInt("status")));
@@ -6011,9 +6015,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} }
} }
/*
for(MapleQuestStatus mqs : loadedQuestStatus.values()) { for(MapleQuestStatus mqs : loadedQuestStatus.values()) {
mqs.resetUpdated(); mqs.resetUpdated();
} }
*/
loadedQuestStatus.clear(); loadedQuestStatus.clear();
@@ -7071,8 +7077,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
synchronized (quests) { synchronized (quests) {
for (MapleQuestStatus q : quests.values()) { for (MapleQuestStatus q : quests.values()) {
if(!q.wasUpdated()) continue;
ps.setInt(2, q.getQuest().getId()); ps.setInt(2, q.getQuest().getId());
ps.setInt(3, q.getStatus().getId()); ps.setInt(3, q.getStatus().getId());
ps.setInt(4, (int) (q.getCompletionTime() / 1000)); ps.setInt(4, (int) (q.getCompletionTime() / 1000));

View File

@@ -106,6 +106,7 @@ public class MapleClient {
private byte gender = -1; private byte gender = -1;
private boolean disconnecting = false; private boolean disconnecting = false;
private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true); private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true); private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true);
private int votePoints; private int votePoints;
private int voteTime = -1; private int voteTime = -1;
@@ -1142,6 +1143,14 @@ public class MapleClient {
public void unlockClient() { public void unlockClient() {
lock.unlock(); lock.unlock();
} }
public void lockEncoder() {
encoderLock.lock();
}
public void unlockEncoder() {
encoderLock.unlock();
}
private static class CharNameAndId { private static class CharNameAndId {

View File

@@ -60,7 +60,7 @@ public class MapleQuestStatus {
} }
private short questID; private short questID;
private Status status; private Status status;
private boolean updated; //private boolean updated; //maybe this can be of use for someone?
private final Map<Integer, String> progress = new LinkedHashMap<Integer, String>(); private final Map<Integer, String> progress = new LinkedHashMap<Integer, String>();
private final List<Integer> medalProgress = new LinkedList<Integer>(); private final List<Integer> medalProgress = new LinkedList<Integer>();
private int npc; private int npc;
@@ -73,7 +73,7 @@ public class MapleQuestStatus {
this.setStatus(status); this.setStatus(status);
this.completionTime = System.currentTimeMillis(); this.completionTime = System.currentTimeMillis();
this.expirationTime = 0; this.expirationTime = 0;
this.updated = true; //this.updated = true;
if (status == Status.STARTED) if (status == Status.STARTED)
registerMobs(); registerMobs();
} }
@@ -84,7 +84,7 @@ public class MapleQuestStatus {
this.setNpc(npc); this.setNpc(npc);
this.completionTime = System.currentTimeMillis(); this.completionTime = System.currentTimeMillis();
this.expirationTime = 0; this.expirationTime = 0;
this.updated = true; //this.updated = true;
if (status == Status.STARTED) { if (status == Status.STARTED) {
registerMobs(); registerMobs();
} }
@@ -106,17 +106,19 @@ public class MapleQuestStatus {
this.status = status; this.status = status;
} }
/*
public boolean wasUpdated() { public boolean wasUpdated() {
return updated; return updated;
} }
public void setUpdated() { private void setUpdated() {
this.updated = true; this.updated = true;
} }
public void resetUpdated() { public void resetUpdated() {
this.updated = false; this.updated = false;
} }
*/
public int getNpc() { public int getNpc() {
return npc; return npc;
@@ -130,13 +132,13 @@ public class MapleQuestStatus {
for (int i : MapleQuest.getInstance(questID).getRelevantMobs()) { for (int i : MapleQuest.getInstance(questID).getRelevantMobs()) {
progress.put(i, "000"); progress.put(i, "000");
} }
this.setUpdated(); //this.setUpdated();
} }
public boolean addMedalMap(int mapid) { public boolean addMedalMap(int mapid) {
if (medalProgress.contains(mapid)) return false; if (medalProgress.contains(mapid)) return false;
medalProgress.add(mapid); medalProgress.add(mapid);
this.setUpdated(); //this.setUpdated();
return true; return true;
} }
@@ -153,7 +155,7 @@ public class MapleQuestStatus {
int current = Integer.parseInt(progress.get(id)); int current = Integer.parseInt(progress.get(id));
String str = StringUtil.getLeftPaddedStr(Integer.toString(current + 1), '0', 3); String str = StringUtil.getLeftPaddedStr(Integer.toString(current + 1), '0', 3);
progress.put(id, str); progress.put(id, str);
this.setUpdated(); //this.setUpdated();
return true; return true;
} }
return false; return false;
@@ -161,7 +163,7 @@ public class MapleQuestStatus {
public void setProgress(int id, String pr) { public void setProgress(int id, String pr) {
progress.put(id, pr); progress.put(id, pr);
this.setUpdated(); //this.setUpdated();
} }
public boolean madeProgress() { public boolean madeProgress() {
@@ -221,7 +223,7 @@ public class MapleQuestStatus {
public void setInfo(String newInfo) { public void setInfo(String newInfo) {
progress.put(0, newInfo); progress.put(0, newInfo);
this.setUpdated(); //this.setUpdated();
} }
public void setForfeited(int forfeited) { public void setForfeited(int forfeited) {

View File

@@ -110,7 +110,7 @@ public class ServerConstants {
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system. public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC. public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC.
public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up. public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up.
public static final int TOT_MOB_QUEST_REQUIREMENT = 0; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default. public static final int TOT_MOB_QUEST_REQUIREMENT = 77; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default.
public static final int MOB_REACTOR_REFRESH_TIME = 30 * 1000; //Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default. public static final int MOB_REACTOR_REFRESH_TIME = 30 * 1000; //Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default.
//Dangling Items/Locks Configuration //Dangling Items/Locks Configuration

View File

@@ -22,7 +22,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package net.mina; package net.mina;
import client.MapleClient; import client.MapleClient;
import java.util.concurrent.locks.Lock;
import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession; import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder; import org.apache.mina.filter.codec.ProtocolEncoder;
@@ -36,7 +35,7 @@ public class MaplePacketEncoder implements ProtocolEncoder {
final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
try { try {
client.lockClient(); client.lockEncoder();
try { try {
final MapleAESOFB send_crypto = client.getSendCrypto(); final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message; final byte[] input = (byte[]) message;
@@ -52,7 +51,7 @@ public class MaplePacketEncoder implements ProtocolEncoder {
out.write(IoBuffer.wrap(ret)); out.write(IoBuffer.wrap(ret));
} finally { } finally {
client.unlockClient(); client.unlockEncoder();
} }
// System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length); // System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
// out.write(ByteBuffer.wrap(ret)); // out.write(ByteBuffer.wrap(ret));

View File

@@ -44,6 +44,7 @@ public final class ItemPickupHandler extends AbstractMaplePacketHandler {
int oid = slea.readInt(); int oid = slea.readInt();
MapleCharacter chr = c.getPlayer(); MapleCharacter chr = c.getPlayer();
MapleMapObject ob = chr.getMap().getMapObject(oid); MapleMapObject ob = chr.getMap().getMapObject(oid);
if(ob == null) return;
Point charPos = chr.getPosition(); Point charPos = chr.getPosition();
Point obPos = ob.getPosition(); Point obPos = ob.getPosition();

View File

@@ -1037,26 +1037,26 @@ public class MapleStatEffect {
int seconds = localDuration / 1000; int seconds = localDuration / 1000;
MapleMount givemount = null; MapleMount givemount = null;
if (isMonsterRiding()) { if (isMonsterRiding()) {
int ridingLevel = 0; int ridingMountId = 0;
Item mount = applyfrom.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18); Item mount = applyfrom.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18);
if (mount != null) { if (mount != null) {
ridingLevel = mount.getItemId(); ridingMountId = mount.getItemId();
} }
if (sourceid == Corsair.BATTLE_SHIP) { if (sourceid == Corsair.BATTLE_SHIP) {
ridingLevel = 1932000; ridingMountId = 1932000;
} else if (sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP) { } else if (sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP) {
ridingLevel = 1932000 + applyto.getSkillLevel(sourceid); ridingMountId = 1932000 + applyto.getSkillLevel(sourceid);
} else if (sourceid == Beginner.YETI_MOUNT1 || sourceid == Noblesse.YETI_MOUNT1 || sourceid == Legend.YETI_MOUNT1) { } else if (sourceid == Beginner.YETI_MOUNT1 || sourceid == Noblesse.YETI_MOUNT1 || sourceid == Legend.YETI_MOUNT1) {
ridingLevel = 1932003; ridingMountId = 1932003;
} else if (sourceid == Beginner.YETI_MOUNT2 || sourceid == Noblesse.YETI_MOUNT2 || sourceid == Legend.YETI_MOUNT2) { } else if (sourceid == Beginner.YETI_MOUNT2 || sourceid == Noblesse.YETI_MOUNT2 || sourceid == Legend.YETI_MOUNT2) {
ridingLevel = 1932004; ridingMountId = 1932004;
} else if (sourceid == Beginner.WITCH_BROOMSTICK || sourceid == Noblesse.WITCH_BROOMSTICK || sourceid == Legend.WITCH_BROOMSTICK) { } else if (sourceid == Beginner.WITCH_BROOMSTICK || sourceid == Noblesse.WITCH_BROOMSTICK || sourceid == Legend.WITCH_BROOMSTICK) {
ridingLevel = 1932005; ridingMountId = 1932005;
} else if (sourceid == Beginner.BALROG_MOUNT || sourceid == Noblesse.BALROG_MOUNT || sourceid == Legend.BALROG_MOUNT) { } else if (sourceid == Beginner.BALROG_MOUNT || sourceid == Noblesse.BALROG_MOUNT || sourceid == Legend.BALROG_MOUNT) {
ridingLevel = 1932010; ridingMountId = 1932010;
} else { } else {
if (applyto.getMount() == null) { if (applyto.getMount() == null) {
applyto.mount(ridingLevel, sourceid); applyto.mount(ridingMountId, sourceid);
} }
applyto.getClient().getWorldServer().registerMountHunger(applyto); applyto.getClient().getWorldServer().registerMountHunger(applyto);
@@ -1077,7 +1077,7 @@ public class MapleStatEffect {
givemount = applyto.getMount(); givemount = applyto.getMount();
} }
localDuration = sourceid; localDuration = sourceid;
localsourceid = ridingLevel; localsourceid = ridingMountId;
localstatups = Collections.singletonList(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0)); localstatups = Collections.singletonList(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0));
} else if (isSkillMorph()) { } else if (isSkillMorph()) {
localstatups = Collections.singletonList(new Pair<>(MapleBuffStat.MORPH, getMorph(applyto))); localstatups = Collections.singletonList(new Pair<>(MapleBuffStat.MORPH, getMorph(applyto)));

View File

@@ -257,6 +257,7 @@ public class MapleMap {
} }
public MapleMap getReturnMap() { public MapleMap getReturnMap() {
if(returnMapId == 999999999) return this;
return Server.getInstance().getWorld(world).getChannel(channel).getMapFactory().getMap(returnMapId); return Server.getInstance().getWorld(world).getChannel(channel).getMapFactory().getMap(returnMapId);
} }