Storybook announce + AOE Mobskills & Autopot & Inventory check Patch

Implemented storybook detection for the skillbook announcer.
Fixed an issue with AOE mob skills not locating players properly in certain situations.
Reviewed interaction of players within same remote network on multiclient system.
Patched messages when leaving the expedition area showing up for leaving players.
Patched some skill questline item chances unexpectedly too low.
Reviewed Rush questline-exclusive area not allowing multiple players in the room.
Fixed meso capacity check on shop owner happening after item transaction.
Patched server-side autopot settings, now using threshold cooling method to determine a player's updated settings.
Reviewed locking flow regarding world/channel deployment on main class.
Fixed a scenario where items collectable by party members would not show up in case those were the only items to be dropped and none needed by the last-hitter.
Patched an issue in the inventory space checking algorithm that would allow a transaction for multiple of the same equipments.
Refactored Dojo entry/exit modules.
Patched a check on Maker skill.
Fixed Cygnus FA buff getting reapplied each time the skill procs.
This commit is contained in:
ronancpl
2019-11-26 01:13:15 -03:00
parent 661dcf0a96
commit bb586a7c0b
49 changed files with 658 additions and 372 deletions

View File

@@ -320,7 +320,7 @@ public class Server {
private void dumpData() {
wldWLock.lock();
wldRLock.lock();
try {
System.out.println(worlds);
System.out.println(channels);
@@ -328,34 +328,44 @@ public class Server {
System.out.println();
System.out.println("---------------------");
} finally {
wldWLock.unlock();
wldRLock.unlock();
}
}
public int addChannel(int worldid) {
wldWLock.lock();
World world;
Map<Integer, String> channelInfo;
int channelid;
wldRLock.lock();
try {
if(worldid >= worlds.size()) return -3;
Map<Integer, String> worldChannels = channels.get(worldid);
if(worldChannels == null) return -3;
channelInfo = channels.get(worldid);
if(channelInfo == null) return -3;
int channelid = worldChannels.size();
channelid = channelInfo.size();
if(channelid >= YamlConfig.config.server.CHANNEL_SIZE) return -2;
channelid++;
World world = this.getWorld(worldid);
Channel channel = new Channel(worldid, channelid, getCurrentTime());
channel.setServerMessage(YamlConfig.config.worlds.get(worldid).why_am_i_recommended);
world.addChannel(channel);
worldChannels.put(channelid, channel.getIP());
return channelid;
world = this.getWorld(worldid);
} finally {
wldWLock.unlock();
wldRLock.unlock();
}
Channel channel = new Channel(worldid, channelid, getCurrentTime());
channel.setServerMessage(YamlConfig.config.worlds.get(worldid).why_am_i_recommended);
if (world.addChannel(channel)) {
wldWLock.lock();
try {
channelInfo.put(channelid, channel.getIP());
} finally {
wldWLock.unlock();
}
}
return channelid;
}
public int addWorld() {
@@ -380,73 +390,96 @@ public class Server {
}
private int initWorld() {
wldWLock.lock();
int i;
wldRLock.lock();
try {
int i = worlds.size();
i = worlds.size();
if(i >= YamlConfig.config.server.WLDLIST_SIZE) {
return -1;
}
System.out.println("Starting world " + i);
} finally {
wldRLock.unlock();
}
System.out.println("Starting world " + i);
int exprate = YamlConfig.config.worlds.get(i).exp_rate;
int mesorate = YamlConfig.config.worlds.get(i).meso_rate;
int droprate = YamlConfig.config.worlds.get(i).drop_rate;
int bossdroprate = YamlConfig.config.worlds.get(i).boss_drop_rate;
int questrate = YamlConfig.config.worlds.get(i).quest_rate;
int travelrate = YamlConfig.config.worlds.get(i).travel_rate;
int fishingrate = YamlConfig.config.worlds.get(i).fishing_rate;
int exprate = YamlConfig.config.worlds.get(i).exp_rate;
int mesorate = YamlConfig.config.worlds.get(i).meso_rate;
int droprate = YamlConfig.config.worlds.get(i).drop_rate;
int bossdroprate = YamlConfig.config.worlds.get(i).boss_drop_rate;
int questrate = YamlConfig.config.worlds.get(i).quest_rate;
int travelrate = YamlConfig.config.worlds.get(i).travel_rate;
int fishingrate = YamlConfig.config.worlds.get(i).fishing_rate;
int flag = YamlConfig.config.worlds.get(i).flag;
String event_message = YamlConfig.config.worlds.get(i).event_message;
String why_am_i_recommended = YamlConfig.config.worlds.get(i).why_am_i_recommended;
World world = new World(i,
flag,
event_message,
exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate);
int flag = YamlConfig.config.worlds.get(i).flag;
String event_message = YamlConfig.config.worlds.get(i).event_message;
String why_am_i_recommended = YamlConfig.config.worlds.get(i).why_am_i_recommended;
worldRecommendedList.add(new Pair<>(i, why_am_i_recommended));
worlds.add(world);
World world = new World(i,
flag,
event_message,
exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate);
Map<Integer, String> channelInfo = new HashMap<>();
long bootTime = getCurrentTime();
for (int j = 1; j <= YamlConfig.config.worlds.get(i).channels; j++) {
int channelid = j;
Channel channel = new Channel(i, channelid, bootTime);
Map<Integer, String> channelInfo = new HashMap<>();
long bootTime = getCurrentTime();
for (int j = 1; j <= YamlConfig.config.worlds.get(i).channels; j++) {
int channelid = j;
Channel channel = new Channel(i, channelid, bootTime);
world.addChannel(channel);
channelInfo.put(channelid, channel.getIP());
world.addChannel(channel);
channelInfo.put(channelid, channel.getIP());
}
boolean canDeploy;
wldWLock.lock(); // thanks Ashen for noticing a deadlock issue when trying to deploy a channel
try {
canDeploy = world.getId() == worlds.size();
if (canDeploy) {
worldRecommendedList.add(new Pair<>(i, why_am_i_recommended));
worlds.add(world);
channels.add(i, channelInfo);
}
channels.add(i, channelInfo);
world.setServerMessage(YamlConfig.config.worlds.get(i).server_message);
System.out.println("Finished loading world " + i + "\r\n");
return i;
} finally {
wldWLock.unlock();
}
if (canDeploy) {
world.setServerMessage(YamlConfig.config.worlds.get(i).server_message);
System.out.println("Finished loading world " + i + "\r\n");
return i;
} else {
System.out.println("Could not load world " + i + "...\r\n");
world.shutdown();
return -2;
}
}
public boolean removeChannel(int worldid) { //lol don't!
wldWLock.lock();
World world;
wldRLock.lock();
try {
if(worldid >= worlds.size()) return false;
World world = worlds.get(worldid);
if (world != null) {
int channel = world.removeChannel();
world = worlds.get(worldid);
} finally {
wldRLock.unlock();
}
if (world != null) {
int channel = world.removeChannel();
wldWLock.lock();
try {
Map<Integer, String> m = channels.get(worldid);
if(m != null) m.remove(channel);
return channel > -1;
} finally {
wldWLock.unlock();
}
} finally {
wldWLock.unlock();
return channel > -1;
}
return false;
@@ -472,17 +505,15 @@ public class Server {
return false;
}
removeWorldPlayerRanking();
w.shutdown();
wldWLock.lock();
try {
if(worldid == worlds.size() - 1) {
removeWorldPlayerRanking();
w.shutdown();
if (worldid == worlds.size() - 1) {
worlds.remove(worldid);
channels.remove(worldid);
worldRecommendedList.remove(worldid);
} else {
return false;
}
} finally {
wldWLock.unlock();
@@ -714,7 +745,7 @@ public class Server {
if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) {
wldWLock.lock();
try {
if(playerRanking.size() < this.getWorldsSize()) {
if(playerRanking.size() < worlds.size()) {
return;
}

View File

@@ -67,6 +67,7 @@ import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import client.MapleCharacter;
import constants.game.GameConstants;
import net.server.services.ServicesManager;
import net.server.services.BaseService;
import net.server.services.type.ChannelServices;
@@ -97,18 +98,18 @@ public final class Channel {
private final Map<Integer, Integer> storedVars = new HashMap<>();
private Set<Integer> playersAway = new HashSet<>();
private Map<MapleExpeditionType, MapleExpedition> expeditions = new HashMap<>();
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
private List<MapleExpeditionType> expedType = new ArrayList<>();
private Set<MapleMap> ownedMaps = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<MapleMap, Boolean>()));
private MapleEvent event;
private boolean finishedShutdown = false;
private int usedDojo = 0;
private Set<Integer> usedMC = new HashSet<>();
private int usedDojo = 0;
private int[] dojoStage;
private long[] dojoFinishTime;
private ScheduledFuture<?>[] dojoTask;
private Map<Integer, Integer> dojoParty = new HashMap<>();
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
private List<Integer> chapelReservationQueue = new LinkedList<>();
private List<Integer> cathedralReservationQueue = new LinkedList<>();
@@ -226,11 +227,16 @@ public final class Channel {
}
private void closeChannelSchedules() {
for(int i = 0; i < 20; i++) {
if(dojoTask[i] != null) {
dojoTask[i].cancel(false);
dojoTask[i] = null;
lock.lock();
try {
for(int i = 0; i < dojoTask.length; i++) {
if(dojoTask[i] != null) {
dojoTask[i].cancel(false);
dojoTask[i] = null;
}
}
} finally {
lock.unlock();
}
closeChannelServices();
@@ -493,43 +499,54 @@ public final class Channel {
this.storedVars.put(key, val);
}
public synchronized int lookupPartyDojo(MapleParty party) {
public int lookupPartyDojo(MapleParty party) {
if(party == null) return -1;
Integer i = dojoParty.get(party.hashCode());
return (i != null) ? i : -1;
}
public int getAvailableDojo(boolean isPartyDojo) {
return getAvailableDojo(isPartyDojo, null);
public int ingressDojo(boolean isPartyDojo, int fromStage) {
return ingressDojo(isPartyDojo, null, fromStage);
}
public synchronized int getAvailableDojo(boolean isPartyDojo, MapleParty party) {
int dojoList = this.usedDojo;
int range, slot = 0;
if(!isPartyDojo) {
dojoList = dojoList >> 5;
range = 15;
} else {
range = 5;
}
while((dojoList & 1) != 0) {
dojoList = (dojoList >> 1);
slot++;
}
if(slot < range) {
if(party != null) {
if(dojoParty.containsKey(party.hashCode())) return -2;
dojoParty.put(party.hashCode(), slot);
public int ingressDojo(boolean isPartyDojo, MapleParty party, int fromStage) {
lock.lock();
try {
int dojoList = this.usedDojo;
int range, slot = 0;
if(!isPartyDojo) {
dojoList = dojoList >> 5;
range = 15;
} else {
range = 5;
}
while((dojoList & 1) != 0) {
dojoList = (dojoList >> 1);
slot++;
}
if(slot < range) {
int slotMapid = (isPartyDojo ? 925030000 : 925020000) + (100 * (fromStage + 1)) + slot;
int dojoSlot = getDojoSlot(slotMapid);
this.usedDojo |= (1 << ((!isPartyDojo ? 5 : 0) + slot));
return slot;
} else {
return -1;
if(party != null) {
if(dojoParty.containsKey(party.hashCode())) return -2;
dojoParty.put(party.hashCode(), dojoSlot);
}
this.usedDojo |= (1 << dojoSlot);
this.resetDojo(slotMapid);
this.startDojoSchedule(slotMapid);
return slot;
} else {
return -1;
}
} finally {
lock.unlock();
}
}
@@ -537,13 +554,19 @@ public final class Channel {
int mask = 0b11111111111111111111;
mask ^= (1 << slot);
usedDojo &= mask;
lock.lock();
try {
usedDojo &= mask;
} finally {
lock.unlock();
}
if(party != null) {
if(dojoParty.remove(party.hashCode()) != null) return;
}
if(dojoParty.containsValue(slot)) { // strange case, no party there!
Set<Entry<Integer, Integer>> es = Collections.unmodifiableSet(dojoParty.entrySet());
Set<Entry<Integer, Integer>> es = new HashSet<>(dojoParty.entrySet());
for(Entry<Integer, Integer> e: es) {
if(e.getValue() == slot) {
@@ -565,17 +588,12 @@ public final class Channel {
}
public void resetDojo(int dojoMapId) {
resetDojo(dojoMapId, 0);
resetDojo(dojoMapId, -1);
}
public void resetDojo(int dojoMapId, int thisStg) {
private void resetDojo(int dojoMapId, int thisStg) {
int slot = getDojoSlot(dojoMapId);
this.dojoStage[slot] = thisStg;
if(this.dojoTask[slot] != null) {
this.dojoTask[slot].cancel(false);
this.dojoTask[slot] = null;
}
}
public void freeDojoSectionIfEmpty(int dojoMapId) {
@@ -595,34 +613,45 @@ public final class Channel {
freeDojoSlot(slot, null);
}
public void startDojoSchedule(final int dojoMapId) {
private void startDojoSchedule(final int dojoMapId) {
final int slot = getDojoSlot(dojoMapId);
final int stage = (dojoMapId / 100) % 100;
if(stage <= dojoStage[slot]) return;
long clockTime = (stage > 36 ? 15 : (stage / 6) + 5) * 60000;
this.dojoTask[slot] = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
final int delta = (dojoMapId) % 100;
final int dojoBaseMap = (slot < 5) ? 925030000 : 925020000;
MapleParty party = null;
for (int i = 0; i < 5; i++) { //only 32 stages, but 38 maps
if (stage + i > 38) {
break;
}
for(MapleCharacter chr: getMapFactory().getMap(dojoBaseMap + (100 * (stage + i)) + delta).getAllPlayers()) {
if(chr.getMap().isDojoMap()) {
chr.timeoutFromDojo();
}
party = chr.getParty();
}
}
freeDojoSlot(slot, party);
lock.lock();
try {
if (this.dojoTask[slot] != null) {
this.dojoTask[slot].cancel(false);
}
}, clockTime + 3000); // let the TIMES UP display for 3 seconds, then warp
this.dojoTask[slot] = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
final int delta = (dojoMapId) % 100;
final int dojoBaseMap = (slot < 5) ? 925030000 : 925020000;
MapleParty party = null;
for (int i = 0; i < 5; i++) { //only 32 stages, but 38 maps
if (stage + i > 38) {
break;
}
MapleMap dojoExit = getMapFactory().getMap(925020002);
for(MapleCharacter chr: getMapFactory().getMap(dojoBaseMap + (100 * (stage + i)) + delta).getAllPlayers()) {
if(GameConstants.isDojo(chr.getMap().getId())) {
chr.changeMap(dojoExit);
}
party = chr.getParty();
}
}
freeDojoSlot(slot, party);
}
}, clockTime + 3000); // let the TIMES UP display for 3 seconds, then warp
} finally {
lock.unlock();
}
dojoFinishTime[slot] = Server.getInstance().getCurrentTime() + clockTime;
}
@@ -632,9 +661,14 @@ public final class Channel {
int stage = (dojoMapId / 100) % 100;
if(stage <= dojoStage[slot]) return;
if(this.dojoTask[slot] != null) {
this.dojoTask[slot].cancel(false);
this.dojoTask[slot] = null;
lock.lock();
try {
if(this.dojoTask[slot] != null) {
this.dojoTask[slot].cancel(false);
this.dojoTask[slot] = null;
}
} finally {
lock.unlock();
}
freeDojoSlot(slot, party);

View File

@@ -56,7 +56,6 @@ import client.autoban.AutobanFactory;
import client.status.MonsterStatus;
import client.status.MonsterStatusEffect;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import constants.skills.Aran;
import constants.skills.Assassin;
import constants.skills.Bandit;
@@ -102,6 +101,7 @@ import constants.skills.SuperGM;
import constants.skills.ThunderBreaker;
import constants.skills.WhiteKnight;
import constants.skills.WindArcher;
import net.server.PlayerBuffValueHolder;
import scripting.AbstractPlayerInteraction;
public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandler {
@@ -166,14 +166,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
if (player.isAlive()) {
if(attack.skill == Aran.BODY_PRESSURE || attack.skill == Marauder.ENERGY_CHARGE || attack.skill == ThunderBreaker.ENERGY_CHARGE) { // thanks IxianMace for noticing Energy Charge skills refreshing on touch, leading to misleading buff applies
// prevent touch dmg skills refreshing
} else if(attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == WindArcher.FINAL_ATTACK) {
// prevent cygnus FA refreshing
mobCount = 15;
} else if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb
attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y));
} else {
attackEffect.applyTo(player);
if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
|| attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK
|| attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) {
if (attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
|| attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM
|| attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) {
mobCount = 15;//:(
} else if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) {
@@ -753,6 +756,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
calcDmgMax *= (100 + ceffect.getDamage()) / 100;
}
int bonusDmgBuff = 100;
for (PlayerBuffValueHolder pbvh : chr.getAllBuffs()) {
int bonusDmg = pbvh.effect.getDamage() - 100;
bonusDmgBuff += bonusDmg;
}
if (bonusDmgBuff != 100) {
float dmgBuff = bonusDmgBuff / 100.0f;
calcDmgMax = (long) Math.ceil(calcDmgMax * dmgBuff);
}
if(chr.getMapId() >= 914000000 && chr.getMapId() <= 914000500) {
calcDmgMax += 80000; // Aran Tutorial.
}
@@ -789,7 +803,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
if(chr.getBuffEffect(MapleBuffStat.WK_CHARGE) != null) {
// Charge, so now we need to check elemental effectiveness
int sourceID = chr.getBuffSource(MapleBuffStat.WK_CHARGE);
int level = chr.getBuffedValue(MapleBuffStat.WK_CHARGE);
int level = chr.getBuffedValue(MapleBuffStat.WK_CHARGE);
if(monster != null) {
if(sourceID == WhiteKnight.BW_FIRE_CHARGE || sourceID == WhiteKnight.SWORD_FIRE_CHARGE) {
if(monster.getStats().getEffectiveness(Element.FIRE) == ElementalEffectiveness.WEAK) {
@@ -880,7 +894,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
long maxWithCrit = hitDmgMax;
if(canCrit) // They can crit, so up the max.
maxWithCrit *= 2;
// Warn if the damage is over 1.5x what we calculated above.
if(damage > maxWithCrit * 1.5) {
AutobanFactory.DAMAGE_HACK.alert(chr, "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");

View File

@@ -68,7 +68,7 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
if (chr.getDojoEnergy() < 10000 && (attack.skill == 1009 || attack.skill == 10001009 || attack.skill == 20001009)) // PE hacking or maybe just lagging
return;
if (chr.getMap().isDojoMap() && attack.numAttacked > 0) {
if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) {
chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK);
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
}

View File

@@ -27,6 +27,7 @@ import client.MapleClient;
import client.Skill;
import client.SkillFactory;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.skills.Bishop;
import constants.skills.Evan;
import constants.skills.FPArchMage;
@@ -56,7 +57,7 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
}
}
if (chr.getMap().isDojoMap() && attack.numAttacked > 0) {
if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) {
chr.setDojoEnergy(chr.getDojoEnergy() + + YamlConfig.config.server.DOJO_ENERGY_ATK);
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
}

View File

@@ -130,7 +130,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
String remoteHwid;
if (player == null) {
remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(session);
if (remoteHwid == null) {
c.disconnect(true, false);
return;

View File

@@ -29,6 +29,6 @@ public class QuickslotKeyMappedModifiedHandler extends AbstractMaplePacketHandle
aQuickslotKeyMapped[i] = (byte) slea.readInt();
}
c.getPlayer().m_pQuickslotKeyMapped = new MapleQuickslotBinding(aQuickslotKeyMapped);
c.getPlayer().changeQuickslotKeybinding(aQuickslotKeyMapped);
}
}

View File

@@ -32,6 +32,7 @@ import client.inventory.MapleInventoryType;
import client.inventory.MapleWeaponType;
import client.inventory.manipulator.MapleInventoryManipulator;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.inventory.ItemConstants;
import constants.skills.Aran;
import constants.skills.Buccaneer;
@@ -69,7 +70,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
}
}
if (chr.getMap().isDojoMap() && attack.numAttacked > 0) {
if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) {
chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK);
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
}
@@ -104,7 +105,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
}
short slot = -1;
int projectile = 0;
byte bulletCount = 1;
short bulletCount = 1;
MapleStatEffect effect = null;
if (attack.skill != 0) {
effect = attack.getAttackEffect(chr, null);
@@ -168,7 +169,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
boolean shadowClaw = chr.getBuffedValue(MapleBuffStat.SHADOW_CLAW) != null;
if (projectile != 0) {
if (!soulArrow && !shadowClaw && attack.skill != 11101004 && attack.skill != 15111007 && attack.skill != 14101006) {
byte bulletConsume = bulletCount;
short bulletConsume = bulletCount;
if (effect != null && effect.getBulletConsume() != 0) {
bulletConsume = (byte) (effect.getBulletConsume() * (hasShadowPartner ? 2 : 1));

View File

@@ -197,7 +197,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
}
//in dojo player cannot use pot, so deadly attacks should be turned off as well
if(is_deadly && chr.getMap().isDojoMap() && !YamlConfig.config.server.USE_DEADLY_DOJO) {
if(is_deadly && GameConstants.isDojo(chr.getMap().getId()) && !YamlConfig.config.server.USE_DEADLY_DOJO) {
damage = 0;
mpattack = 0;
}

View File

@@ -319,10 +319,14 @@ public class MapleMatchCheckerCoordinator {
}
}
private void acceptMatchElement(MapleMatchCheckingElement mmce, int cid) {
private boolean acceptMatchElement(MapleMatchCheckingElement mmce, int cid) {
if (mmce.acceptEntry(cid)) {
unpoolMatchPlayer(cid);
disposeMatchElement(mmce);
return true;
} else {
return false;
}
}
@@ -355,7 +359,11 @@ public class MapleMatchCheckerCoordinator {
mmce = null;
} else {
if (accept) {
acceptMatchElement(mmce, cid);
if (!acceptMatchElement(mmce, cid)) {
mmce = null;
}
break; // thanks Rohenn for noticing loop scenario here
} else {
denyMatchElement(mmce, cid);
matchEntries.remove(cid);

View File

@@ -547,6 +547,11 @@ public class MapleSessionCoordinator {
// session.removeAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); No real need for removing String property on closed sessions
}
public String pickLoginSessionHwid(IoSession session) {
String remoteHost = getSessionRemoteAddress(session);
return cachedHostHwids.remove(remoteHost); // thanks BHB, resinate for noticing players from same network not being able to login
}
public String getGameSessionHwid(IoSession session) {
String remoteHost = getSessionRemoteHost(session);
return cachedHostHwids.get(remoteHost);

View File

@@ -88,8 +88,9 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
slea.skip(6); // localhost masked the initial part with zeroes...
byte[] hwidNibbles = slea.read(4);
int loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
String nibbleHwid = HexTool.toCompressedString(hwidNibbles);
int loginok = c.login(login, pwd, nibbleHwid);
Connection con = null;
PreparedStatement ps = null;
@@ -112,7 +113,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
e.printStackTrace();
} finally {
disposeSql(con, ps);
loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
loginok = c.login(login, pwd, nibbleHwid);
}
}

View File

@@ -260,10 +260,15 @@ public class World {
}
}
public void addChannel(Channel channel) {
public boolean addChannel(Channel channel) {
chnWLock.lock();
try {
channels.add(channel);
if (channel.getId() == channels.size() + 1) {
channels.add(channel);
return true;
} else {
return false;
}
} finally {
chnWLock.unlock();
}