From 8afbff9db94505cc0583a2544ed7147dc337cdd3 Mon Sep 17 00:00:00 2001 From: ronancpl Date: Sat, 7 Dec 2019 03:05:26 -0300 Subject: [PATCH] Stat pool & Skills on change field Patch + Elemental Charge broadcast Patched account storages not getting cached properly at login time. Reviewed item acquisition at the Cash Shop happening before point transaction. EXP toggle flag now also works on equipment gains. Factored several skills (Energy Charge, Wind Walk, Dash) not updating properly other players when changing maps. Refactored stat pool system, which wasn't working properly on limit scenarios. Fixed "untradeable at wear"equipments losing flags upon equipping. Reviewed Inventory Sort, now sorting projectiles at descending order on damage. Implemented support for visibility of effects on weapons imbued with Charge skill (e.g. Paladin's Holy Charge) for other players. --- docs/mychanges_ptbr.txt | 25 +++++- scripts/npc/9977777.js | 5 +- src/client/AbstractMapleCharacterObject.java | 83 +++++++++++-------- src/client/MapleCharacter.java | 70 +++++++++++----- src/client/MapleClient.java | 20 ++--- .../command/commands/gm0/DisposeCommand.java | 2 +- src/client/inventory/MapleInventory.java | 4 +- .../MapleInventoryManipulator.java | 5 +- src/constants/inventory/ItemConstants.java | 1 - src/net/server/Server.java | 57 +++++++------ .../audit/locks/empty/AbstractEmptyLock.java | 24 ++++++ .../audit/locks/empty/EmptyReadLock.java | 21 +---- .../audit/locks/empty/EmptyReentrantLock.java | 21 +---- .../audit/locks/empty/EmptyWriteLock.java | 21 +---- .../handlers/CashOperationHandler.java | 72 +++++++++++----- .../handlers/InventorySortHandler.java | 14 ++-- .../channel/handlers/UseCashItemHandler.java | 2 +- src/net/server/world/World.java | 10 ++- src/server/MapleStatEffect.java | 19 ++++- src/server/MapleStorage.java | 10 ++- src/server/maps/MapleMapFactory.java | 20 ++--- src/tools/MaplePacketCreator.java | 67 +++++++++++---- wz/String.wz/Consume.img.xml | 20 ++--- 23 files changed, 356 insertions(+), 237 deletions(-) create mode 100644 src/net/server/audit/locks/empty/AbstractEmptyLock.java diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index a4e71c40a2..3b787ea089 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -2305,4 +2305,27 @@ Corrigido buff Final Attack de Cygnus sendo reaplicado a todo acerto de skill. 24 - 25 Novembro 2019, Corrigido caso não sendo checado devidamente com Maker. Corrigido contagem de projéteis nos stats de skill usando tipo de dados de tamanho insuficiente. -Refatorado acesso a membros relativos a Dojo em canais de forma a buscar melhorar efetividade dos ingressos e liberações de lobby. \ No newline at end of file +Refatorado acesso a membros relativos a Dojo em canais de forma a buscar melhorar efetividade dos ingressos e liberações de lobby. +Revisado exceção inutilizável na classe geradora de áreas do jogo. + +27 Novembro 2019, +Revisado carregamento de storage da DB ocorrendo a cada login realizado. +Revisado aquisição de itens no CS ocorrendo antes de utilizar os pontos disponíveis. + +28 Novembro 2019, +Revisado interação de flag de permissão de ganho de EXP em equipamentos. + +29 - 30 Novembro 2019, +Fatorado diversas habilidades (Energy Charge, Wind Walk, Dash) não transcorrendo como esperado na visão de outros jogadores ao trocar de mapas. + +02 - 03 Dezembro 2019, +Revisado uso de locks compartilhados em MapleClient. +Refatorado criação de conjunto durante checagem de slots, que seria de fato efetivo em cenários muito raros (melhor deixar inserção de itens limitados dar fail-fast nas réplicas). +Refatorado sistema de pool de stats, que estava atuando erroneamente em casos-limite. +Corrigido Item Guard inconsistentemente levando a NPE ao utilizar o mesmo. +Corrigido itens perdendo flags ao equipar aqueles tidos como "untradeable após equipar". +Revisado Inventory Sort, agora ordenando projéteis por bônus de dano. + +06 Dezembro 2019, +Implementado pacote para visão de buffs de efeito imbuído em armas para outros jogadores. +Corrigido casos de exceção devido a portais nulos na função que troca jogador de mapas interferindo com próximas trocas de mapa (jogador fica preso até relogar). \ No newline at end of file diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index e5d2adfb4c..fcf7833e05 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -1,6 +1,6 @@ /* This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2018 RonanLana + Copyleft (L) 2016 - 2019 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 @@ -61,8 +61,9 @@ function writeFeatureTab_Skills() { addFeature("Maker skill features developed - pckts thanks Arnah."); addFeature("Chair Mastery - map chair boosts HP/MP rec."); addFeature("Mu Lung Dojo skills functional."); - addFeature("Monster Magnet skill no longer crashes players."); + addFeature("Monster Magnet skill on bosses no longer crash."); addFeature("HP/MP consumption from skills triggers pet autopot."); + addFeature("Elemental weapon imbue visibility for other players."); } function writeFeatureTab_Quests() { diff --git a/src/client/AbstractMapleCharacterObject.java b/src/client/AbstractMapleCharacterObject.java index 5b92ce2ec5..205d0535e1 100644 --- a/src/client/AbstractMapleCharacterObject.java +++ b/src/client/AbstractMapleCharacterObject.java @@ -277,17 +277,22 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple this.clientmaxmp = Math.min(30000, mp_); } - private static long calcStatPoolNode(long v, int displacement) { - if (v > Short.MAX_VALUE) { - v = Short.MAX_VALUE; - } else if (v < Short.MIN_VALUE) { - v = Short.MIN_VALUE; - } - - return ((v & 0x0FFFF) << displacement); + private static long clampStat(int v, int min, int max) { + return (v < min) ? min : ((v > max) ? max : v); } - private static long calcStatPoolLong(int v1, int v2, int v3, int v4) { + private static long calcStatPoolNode(Integer v, int displacement) { + long r; + if (v == null) { + r = -32768; + } else { + r = clampStat(v, -32767, 32767); + } + + return ((r & 0x0FFFF) << displacement); + } + + private static long calcStatPoolLong(Integer v1, Integer v2, Integer v3, Integer v4) { long ret = 0; ret |= calcStatPoolNode(v1, 48); @@ -419,40 +424,48 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple } protected void changeHpMp(int newhp, int newmp, boolean silent) { - changeHpMpPool(newhp, newmp, Short.MIN_VALUE, Short.MIN_VALUE, silent); + changeHpMpPool(newhp, newmp, null, null, silent); } - private void changeHpMpPool(int hp, int mp, int maxhp, int maxmp, boolean silent) { + private void changeHpMpPool(Integer hp, Integer mp, Integer maxhp, Integer maxmp, boolean silent) { long hpMpPool = calcStatPoolLong(hp, mp, maxhp, maxmp); changeStatPool(hpMpPool, null, null, -1, silent); } public void updateHp(int hp) { - updateHpMaxHp(hp, Short.MIN_VALUE); + updateHpMaxHp(hp, null); } public void updateMaxHp(int maxhp) { - updateHpMaxHp(Short.MIN_VALUE, maxhp); + updateHpMaxHp(null, maxhp); } public void updateHpMaxHp(int hp, int maxhp) { - changeHpMpPool(hp, Short.MIN_VALUE, maxhp, Short.MIN_VALUE, false); + updateHpMaxHp(Integer.valueOf(hp), Integer.valueOf(maxhp)); + } + + private void updateHpMaxHp(Integer hp, Integer maxhp) { + changeHpMpPool(hp, null, maxhp, null, false); } public void updateMp(int mp) { - updateMpMaxMp(mp, Short.MIN_VALUE); + updateMpMaxMp(mp, null); } public void updateMaxMp(int maxmp) { - updateMpMaxMp(Short.MIN_VALUE, maxmp); + updateMpMaxMp(null, maxmp); } public void updateMpMaxMp(int mp, int maxmp) { - changeHpMpPool(Short.MIN_VALUE, mp, Short.MIN_VALUE, maxmp, false); + updateMpMaxMp(Integer.valueOf(mp), Integer.valueOf(maxmp)); + } + + private void updateMpMaxMp(Integer mp, Integer maxmp) { + changeHpMpPool(null, mp, null, maxmp, false); } public void updateMaxHpMaxMp(int maxhp, int maxmp) { - changeHpMpPool(Short.MIN_VALUE, Short.MIN_VALUE, maxhp, maxmp, false); + changeHpMpPool(null, null, maxhp, maxmp, false); } protected void enforceMaxHpMp() { @@ -521,7 +534,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple effLock.lock(); statWlock.lock(); try { - changeHpMpPool(Short.MIN_VALUE, Short.MIN_VALUE, maxhp + hpdelta, maxmp + mpdelta, silent); + changeHpMpPool(null, null, maxhp + hpdelta, maxmp + mpdelta, silent); } finally { statWlock.unlock(); effLock.unlock(); @@ -567,19 +580,19 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple } public boolean assignStr(int x) { - return assignStrDexIntLuk(x, Short.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE); + return assignStrDexIntLuk(x, null, null, null); } public boolean assignDex(int x) { - return assignStrDexIntLuk(Short.MIN_VALUE, x, Short.MIN_VALUE, Short.MIN_VALUE); + return assignStrDexIntLuk(null, x, null, null); } public boolean assignInt(int x) { - return assignStrDexIntLuk(Short.MIN_VALUE, Short.MIN_VALUE, x, Short.MIN_VALUE); + return assignStrDexIntLuk(null, null, x, null); } public boolean assignLuk(int x) { - return assignStrDexIntLuk(Short.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE, x); + return assignStrDexIntLuk(null, null, null, x); } public boolean assignHP(int deltaHP, int deltaAp) { @@ -590,7 +603,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple return false; } - long hpMpPool = calcStatPoolLong(Short.MIN_VALUE, Short.MIN_VALUE, maxhp + deltaHP, maxmp); + long hpMpPool = calcStatPoolLong(null, null, maxhp + deltaHP, maxmp); long strDexIntLuk = calcStatPoolLong(str, dex, int_, luk); changeStatPool(hpMpPool, strDexIntLuk, null, remainingAp - deltaAp, false); @@ -610,7 +623,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple return false; } - long hpMpPool = calcStatPoolLong(Short.MIN_VALUE, Short.MIN_VALUE, maxhp, maxmp + deltaMP); + long hpMpPool = calcStatPoolLong(null, null, maxhp, maxmp + deltaMP); long strDexIntLuk = calcStatPoolLong(str, dex, int_, luk); changeStatPool(hpMpPool, strDexIntLuk, null, remainingAp - deltaAp, false); @@ -622,11 +635,15 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple } } - private static int apAssigned(int x) { - return x != Short.MIN_VALUE ? x : 0; + private static int apAssigned(Integer x) { + return x != null ? x : 0; } public boolean assignStrDexIntLuk(int deltaStr, int deltaDex, int deltaInt, int deltaLuk) { + return assignStrDexIntLuk(Integer.valueOf(deltaStr), Integer.valueOf(deltaDex), Integer.valueOf(deltaInt), Integer.valueOf(deltaLuk)); + } + + private boolean assignStrDexIntLuk(Integer deltaStr, Integer deltaDex, Integer deltaInt, Integer deltaLuk) { effLock.lock(); statWlock.lock(); try { @@ -636,19 +653,19 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple } int newStr = str + deltaStr, newDex = dex + deltaDex, newInt = int_ + deltaInt, newLuk = luk + deltaLuk; - if (newStr < 4 && deltaStr != Short.MIN_VALUE || newStr > YamlConfig.config.server.MAX_AP) { + if (newStr < 4 && deltaStr != null || newStr > YamlConfig.config.server.MAX_AP) { return false; } - if (newDex < 4 && deltaDex != Short.MIN_VALUE || newDex > YamlConfig.config.server.MAX_AP) { + if (newDex < 4 && deltaDex != null || newDex > YamlConfig.config.server.MAX_AP) { return false; } - if (newInt < 4 && deltaInt != Short.MIN_VALUE || newInt > YamlConfig.config.server.MAX_AP) { + if (newInt < 4 && deltaInt != null || newInt > YamlConfig.config.server.MAX_AP) { return false; } - if (newLuk < 4 && deltaLuk != Short.MIN_VALUE || newLuk > YamlConfig.config.server.MAX_AP) { + if (newLuk < 4 && deltaLuk != null || newLuk > YamlConfig.config.server.MAX_AP) { return false; } @@ -691,12 +708,12 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple changeStrDexIntLuk(str, dex, int_, luk, remainingAp, false); } - private void changeStrDexIntLuk(int str, int dex, int int_, int luk, int remainingAp, boolean silent) { + private void changeStrDexIntLuk(Integer str, Integer dex, Integer int_, Integer luk, int remainingAp, boolean silent) { long strDexIntLuk = calcStatPoolLong(str, dex, int_, luk); changeStatPool(null, strDexIntLuk, null, remainingAp, silent); } - private void changeStrDexIntLukSp(int str, int dex, int int_, int luk, int remainingAp, int remainingSp, int skillbook, boolean silent) { + private void changeStrDexIntLukSp(Integer str, Integer dex, Integer int_, Integer luk, int remainingAp, int remainingSp, int skillbook, boolean silent) { long strDexIntLuk = calcStatPoolLong(str, dex, int_, luk); long sp = calcStatPoolLong(0, 0, remainingSp, skillbook); changeStatPool(null, strDexIntLuk, sp, remainingAp, silent); diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index f23d08e6c0..7284964a6b 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -1472,11 +1472,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { changeMap(to, to.getPortal(portal)); } - public void changeMap(final MapleMap target, final MaplePortal pto) { + public void changeMap(final MapleMap target, MaplePortal pto) { canWarpCounter++; eventChangedMap(target.getId()); // player can be dropped from an event here, hence the new warping target. MapleMap to = getWarpMap(target.getId()); + if (pto == null) { + pto = to.getPortal(0); + } changeMapInternal(to, pto.getPosition(), MaplePacketCreator.getWarpToMap(to, pto.getId(), this)); canWarpMap = false; @@ -1504,7 +1507,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { eventAfterChangedMap(this.getMapId()); } - public void forceChangeMap(final MapleMap target, final MaplePortal pto) { + public void forceChangeMap(final MapleMap target, MaplePortal pto) { // will actually enter the map given as parameter, regardless of being an eventmap or whatnot canWarpCounter++; @@ -1525,6 +1528,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } MapleMap to = target; // warps directly to the target intead of the target's map id, this allows GMs to patrol players inside instances. + if (pto == null) { + pto = to.getPortal(0); + } changeMapInternal(to, pto.getPosition(), MaplePacketCreator.getWarpToMap(to, pto.getId(), this)); canWarpMap = false; @@ -6102,7 +6108,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { List> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ENERGY_CHARGE, energybar)); setBuffedValue(MapleBuffStat.ENERGY_CHARGE, energybar); client.announce(MaplePacketCreator.giveBuff(energybar, 0, stat)); - getMap().broadcastMessage(chr, MaplePacketCreator.giveForeignBuff(energybar, stat)); + getMap().broadcastMessage(chr, MaplePacketCreator.cancelForeignFirstDebuff(id, ((long) 1) << 50)); } }, ceffect.getDuration()); } @@ -9403,25 +9409,41 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public byte getSlots(int type) { return type == MapleInventoryType.CASH.getType() ? 96 : inventory[type].getSlotLimit(); } - + + public boolean canGainSlots(int type, int slots) { + slots += inventory[type].getSlotLimit(); + return slots <= 96; + } + public boolean gainSlots(int type, int slots) { return gainSlots(type, slots, true); } public boolean gainSlots(int type, int slots, boolean update) { - slots += inventory[type].getSlotLimit(); - if (slots <= 96) { - inventory[type].setSlotLimit(slots); - + boolean ret = gainSlotsInternal(type, slots, update); + if (ret) { this.saveCharToDB(); if (update) { client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots)); } - - return true; } - - return false; + + return ret; + } + + private boolean gainSlotsInternal(int type, int slots, boolean update) { + inventory[type].lockInventory(); + try { + if (canGainSlots(type, slots)) { + slots += inventory[type].getSlotLimit(); + inventory[type].setSlotLimit(slots); + return true; + } else { + return false; + } + } finally { + inventory[type].unlockInventory(); + } } public int sellAllItemsFromName(byte invTypeId, String name) { @@ -10487,18 +10509,20 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void increaseEquipExp(int expGain) { - if(expGain < 0) { - expGain = Integer.MAX_VALUE; - } - - for (Item item : getUpgradeableEquipList()) { - Equip nEquip = (Equip) item; - String itemName = ii.getName(nEquip.getItemId()); - if (itemName == null) { - continue; + if (allowExpGain) { // thanks Vcoc for suggesting equip EXP gain conditionally + if(expGain < 0) { + expGain = Integer.MAX_VALUE; + } + + for (Item item : getUpgradeableEquipList()) { + Equip nEquip = (Equip) item; + String itemName = ii.getName(nEquip.getItemId()); + if (itemName == null) { + continue; + } + + nEquip.gainItemExp(client, expGain); } - - nEquip.gainItemExp(client, expGain); } } diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index aad0849b22..bd8b34e773 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -118,8 +118,7 @@ public class MapleClient { private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true); private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true); private final Lock announcerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ANNOUNCER, true); - private static final int lockCount = 200; - private static final Lock loginLocks[] = new Lock[lockCount]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here + // thanks Masterrulax & try2hack for pointing out a bottleneck issue with shared locks, shavit for noticing an opportunity for improvement private Calendar tempBanCalendar; private int votePoints; private int voteTime = -1; @@ -129,12 +128,6 @@ public class MapleClient { private long lastPacket = System.currentTimeMillis(); private int lang = 0; - static { - for (int i = 0; i < lockCount; i++) { - loginLocks[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true); - } - } - public void updateLastPacket() { lastPacket = System.currentTimeMillis(); } @@ -453,8 +446,7 @@ public class MapleClient { } public int finishLogin() { - Lock loginLock = loginLocks[this.getAccID() % lockCount]; - loginLock.lock(); + encoderLock.lock(); try { if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN loggedIn = false; @@ -462,7 +454,7 @@ public class MapleClient { } updateLoginState(MapleClient.LOGIN_LOGGEDIN); } finally { - loginLock.unlock(); + encoderLock.unlock(); } return 0; @@ -1379,8 +1371,12 @@ public class MapleClient { characterSlots = slots; } + public boolean canGainCharacterSlot() { + return characterSlots < 15; + } + public synchronized boolean gainCharacterSlot() { - if (characterSlots < 15) { + if (canGainCharacterSlot()) { Connection con = null; try { con = DatabaseConnection.getConnection(); diff --git a/src/client/command/commands/gm0/DisposeCommand.java b/src/client/command/commands/gm0/DisposeCommand.java index eed816cde2..b37a17c8a3 100644 --- a/src/client/command/commands/gm0/DisposeCommand.java +++ b/src/client/command/commands/gm0/DisposeCommand.java @@ -41,5 +41,5 @@ public class DisposeCommand extends Command { c.announce(MaplePacketCreator.enableActions()); c.removeClickedNPC(); c.getPlayer().message("You've been disposed."); + } } -} diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java index f910a4d903..a2ce3a7f16 100644 --- a/src/client/inventory/MapleInventory.java +++ b/src/client/inventory/MapleInventory.java @@ -453,10 +453,10 @@ public class MapleInventory implements Iterable { private static boolean checkItemRestricted(List> items) { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - Set itemids = new HashSet<>(); + // thanks Shavit for noticing set creation that would be only effective in rare situations for (Pair p : items) { int itemid = p.getLeft().getItemId(); - if (ii.isPickupRestricted(itemid) && (p.getLeft().getQuantity() > 1 || !itemids.add(itemid))) { + if (ii.isPickupRestricted(itemid) && p.getLeft().getQuantity() > 1) { return false; } } diff --git a/src/client/inventory/manipulator/MapleInventoryManipulator.java b/src/client/inventory/manipulator/MapleInventoryManipulator.java index 14428fd4b0..682affe285 100644 --- a/src/client/inventory/manipulator/MapleInventoryManipulator.java +++ b/src/client/inventory/manipulator/MapleInventoryManipulator.java @@ -523,7 +523,10 @@ public class MapleInventoryManipulator { } boolean itemChanged = false; if (ii.isUntradeableOnEquip(source.getItemId())) { - source.setFlag((byte) ItemConstants.UNTRADEABLE); + short flag = source.getFlag(); // thanks BHB for noticing flags missing after equipping these + flag |= ItemConstants.UNTRADEABLE; + source.setFlag(flag); + itemChanged = true; } if (dst == -6) { // unequip the overall diff --git a/src/constants/inventory/ItemConstants.java b/src/constants/inventory/ItemConstants.java index dae6df6b89..c7b73f781e 100644 --- a/src/constants/inventory/ItemConstants.java +++ b/src/constants/inventory/ItemConstants.java @@ -21,7 +21,6 @@ */ package constants.inventory; -import constants.net.ServerConstants; import client.inventory.MapleInventoryType; import config.YamlConfig; diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 108ad0491c..00472b3262 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -894,33 +894,8 @@ public class Server { //MaplePet.clearMissingPetsFromDb(); // thanks Optimist for noticing this taking too long to run MapleCashidGenerator.loadExistentCashIdsFromDb(); - IoBuffer.setUseDirectBuffer(false); - IoBuffer.setAllocator(new SimpleBufferAllocator()); - acceptor = new NioSocketAcceptor(); - acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory())); - ThreadManager.getInstance().start(); - TimerManager tMan = TimerManager.getInstance(); - tMan.start(); - tMan.register(tMan.purge(), YamlConfig.config.server.PURGING_INTERVAL);//Purging ftw... - disconnectIdlesOnLoginTask(); - - long timeLeft = getTimeLeftForNextHour(); - tMan.register(new CharacterDiseaseTask(), YamlConfig.config.server.UPDATE_INTERVAL, YamlConfig.config.server.UPDATE_INTERVAL); - tMan.register(new ReleaseLockTask(), 2 * 60 * 1000, 2 * 60 * 1000); - tMan.register(new CouponTask(), YamlConfig.config.server.COUPON_INTERVAL, timeLeft); - tMan.register(new RankingCommandTask(), 5 * 60 * 1000, 5 * 60 * 1000); - tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft); - tMan.register(new LoginCoordinatorTask(), 60 * 60 * 1000, timeLeft); - tMan.register(new EventRecallCoordinatorTask(), 60 * 60 * 1000, timeLeft); - tMan.register(new LoginStorageTask(), 2 * 60 * 1000, 2 * 60 * 1000); - tMan.register(new DueyFredrickTask(), 60 * 60 * 1000, timeLeft); - tMan.register(new InvitationTask(), 30 * 1000, 30 * 1000); - tMan.register(new RespawnTask(), YamlConfig.config.server.RESPAWN_INTERVAL, YamlConfig.config.server.RESPAWN_INTERVAL); - - timeLeft = getTimeLeftForNextDay(); - MapleExpeditionBossLog.resetBossLogTable(); - tMan.register(new BossLogTask(), 24 * 60 * 60 * 1000, timeLeft); + initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan long timeToTake = System.currentTimeMillis(); SkillFactory.loadAllSkills(); @@ -965,6 +940,10 @@ public class Server { System.out.println(); + IoBuffer.setUseDirectBuffer(false); // join IO operations performed by lxconan + IoBuffer.setAllocator(new SimpleBufferAllocator()); + acceptor = new NioSocketAcceptor(); + acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory())); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); acceptor.setHandler(new MapleServerHandler()); try { @@ -986,6 +965,30 @@ public class Server { ch.reloadEventScriptManager(); } } + + private void initializeTimelyTasks() { + TimerManager tMan = TimerManager.getInstance(); + tMan.start(); + tMan.register(tMan.purge(), YamlConfig.config.server.PURGING_INTERVAL);//Purging ftw... + disconnectIdlesOnLoginTask(); + + long timeLeft = getTimeLeftForNextHour(); + tMan.register(new CharacterDiseaseTask(), YamlConfig.config.server.UPDATE_INTERVAL, YamlConfig.config.server.UPDATE_INTERVAL); + tMan.register(new ReleaseLockTask(), 2 * 60 * 1000, 2 * 60 * 1000); + tMan.register(new CouponTask(), YamlConfig.config.server.COUPON_INTERVAL, timeLeft); + tMan.register(new RankingCommandTask(), 5 * 60 * 1000, 5 * 60 * 1000); + tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft); + tMan.register(new LoginCoordinatorTask(), 60 * 60 * 1000, timeLeft); + tMan.register(new EventRecallCoordinatorTask(), 60 * 60 * 1000, timeLeft); + tMan.register(new LoginStorageTask(), 2 * 60 * 1000, 2 * 60 * 1000); + tMan.register(new DueyFredrickTask(), 60 * 60 * 1000, timeLeft); + tMan.register(new InvitationTask(), 30 * 1000, 30 * 1000); + tMan.register(new RespawnTask(), YamlConfig.config.server.RESPAWN_INTERVAL, YamlConfig.config.server.RESPAWN_INTERVAL); + + timeLeft = getTimeLeftForNextDay(); + MapleExpeditionBossLog.resetBossLogTable(); + tMan.register(new BossLogTask(), 24 * 60 * 60 * 1000, timeLeft); + } public static void main(String args[]) { System.setProperty("wzpath", "wz"); @@ -1755,7 +1758,7 @@ public class Server { for (Integer worldid : accWorlds) { if (worldid < worldList.size()) { World wserv = worldList.get(worldid); - wserv.registerAccountStorage(accountId); + wserv.loadAccountStorage(accountId); } } } diff --git a/src/net/server/audit/locks/empty/AbstractEmptyLock.java b/src/net/server/audit/locks/empty/AbstractEmptyLock.java new file mode 100644 index 0000000000..ca81b5af19 --- /dev/null +++ b/src/net/server/audit/locks/empty/AbstractEmptyLock.java @@ -0,0 +1,24 @@ +package net.server.audit.locks.empty; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public abstract class AbstractEmptyLock { + + protected static String printThreadStack(StackTraceElement[] list) { + DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); // DRY-code opportunity performed by jtumidanski + dateFormat.setTimeZone(TimeZone.getDefault()); + String df = dateFormat.format(new Date()); + + String s = "\r\n" + df + "\r\n"; + for(int i = 0; i < list.length; i++) { + s += (" " + list[i].toString() + "\r\n"); + } + s += "----------------------------\r\n\r\n"; + + return s; + } + +} diff --git a/src/net/server/audit/locks/empty/EmptyReadLock.java b/src/net/server/audit/locks/empty/EmptyReadLock.java index 18d3d66c5d..9010dc5ec8 100644 --- a/src/net/server/audit/locks/empty/EmptyReadLock.java +++ b/src/net/server/audit/locks/empty/EmptyReadLock.java @@ -19,11 +19,6 @@ */ package net.server.audit.locks.empty; -import constants.net.ServerConstants; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReadLock; import tools.FilePrinter; @@ -32,27 +27,13 @@ import tools.FilePrinter; * * @author RonanLana */ -public class EmptyReadLock implements MonitoredReadLock { +public class EmptyReadLock extends AbstractEmptyLock implements MonitoredReadLock { private final MonitoredLockType id; public EmptyReadLock(MonitoredLockType type) { this.id = type; } - private static String printThreadStack(StackTraceElement[] list) { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getDefault()); - String df = dateFormat.format(new Date()); - - String s = "\r\n" + df + "\r\n"; - for(int i = 0; i < list.length; i++) { - s += (" " + list[i].toString() + "\r\n"); - } - s += "----------------------------\r\n\r\n"; - - return s; - } - @Override public void lock() { FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace())); diff --git a/src/net/server/audit/locks/empty/EmptyReentrantLock.java b/src/net/server/audit/locks/empty/EmptyReentrantLock.java index be1889e7da..354b9528b8 100644 --- a/src/net/server/audit/locks/empty/EmptyReentrantLock.java +++ b/src/net/server/audit/locks/empty/EmptyReentrantLock.java @@ -19,11 +19,6 @@ */ package net.server.audit.locks.empty; -import constants.net.ServerConstants; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import tools.FilePrinter; @@ -32,27 +27,13 @@ import tools.FilePrinter; * * @author RonanLana */ -public class EmptyReentrantLock implements MonitoredReentrantLock { +public class EmptyReentrantLock extends AbstractEmptyLock implements MonitoredReentrantLock { private final MonitoredLockType id; public EmptyReentrantLock(MonitoredLockType type) { this.id = type; } - private static String printThreadStack(StackTraceElement[] list) { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getDefault()); - String df = dateFormat.format(new Date()); - - String s = "\r\n" + df + "\r\n"; - for(int i = 0; i < list.length; i++) { - s += (" " + list[i].toString() + "\r\n"); - } - s += "----------------------------\r\n\r\n"; - - return s; - } - @Override public void lock() { FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace())); diff --git a/src/net/server/audit/locks/empty/EmptyWriteLock.java b/src/net/server/audit/locks/empty/EmptyWriteLock.java index cbfa163f74..c900689950 100644 --- a/src/net/server/audit/locks/empty/EmptyWriteLock.java +++ b/src/net/server/audit/locks/empty/EmptyWriteLock.java @@ -19,11 +19,6 @@ */ package net.server.audit.locks.empty; -import constants.net.ServerConstants; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredWriteLock; import tools.FilePrinter; @@ -32,27 +27,13 @@ import tools.FilePrinter; * * @author RonanLana */ -public class EmptyWriteLock implements MonitoredWriteLock { +public class EmptyWriteLock extends AbstractEmptyLock implements MonitoredWriteLock { private final MonitoredLockType id; public EmptyWriteLock(MonitoredLockType type) { this.id = type; } - private static String printThreadStack(StackTraceElement[] list) { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getDefault()); - String df = dateFormat.format(new Date()); - - String s = "\r\n" + df + "\r\n"; - for(int i = 0; i < list.length; i++) { - s += (" " + list[i].toString() + "\r\n"); - } - s += "----------------------------\r\n\r\n"; - - return s; - } - @Override public void lock() { FilePrinter.printError(FilePrinter.DISPOSED_LOCKS, "Captured locking tentative on disposed lock " + id + ":" + printThreadStack(Thread.currentThread().getStackTrace())); diff --git a/src/net/server/channel/handlers/CashOperationHandler.java b/src/net/server/channel/handlers/CashOperationHandler.java index ea776e240e..39a56c61f0 100644 --- a/src/net/server/channel/handlers/CashOperationHandler.java +++ b/src/net/server/channel/handlers/CashOperationHandler.java @@ -86,16 +86,18 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { } Item item = cItem.toItem(); + cs.gainCash(useNX, cItem, chr.getWorld()); // thanks Rohenn for noticing cash operations after item acquisition cs.addToInventory(item); c.announce(MaplePacketCreator.showBoughtCashItem(item, c.getAccID())); } else { // Package + cs.gainCash(useNX, cItem, chr.getWorld()); + List cashPackage = CashItemFactory.getPackage(cItem.getItemId()); for (Item item : cashPackage) { cs.addToInventory(item); } c.announce(MaplePacketCreator.showBoughtCashPackage(cashPackage, c.getAccID())); } - cs.gainCash(useNX, cItem, chr.getWorld()); c.announce(MaplePacketCreator.showCash(chr)); } else if (action == 0x04) {//TODO check for gender int birthday = slea.readInt(); @@ -116,9 +118,9 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA8)); return; } + cs.gainCash(4, cItem, chr.getWorld()); cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN()); c.announce(MaplePacketCreator.showGiftSucceed(recipient.get("name"), cItem)); - cs.gainCash(4, cItem, chr.getWorld()); c.announce(MaplePacketCreator.showCash(chr)); try { chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not @@ -147,10 +149,17 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { c.enableCSActions(); return; } - if (chr.gainSlots(type, 4, false)) { + int qty = 4; + if (!chr.canGainSlots(type, qty)) { + c.enableCSActions(); + return; + } + cs.gainCash(cash, -4000); + if (chr.gainSlots(type, qty, false)) { c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type))); - cs.gainCash(cash, -4000); c.announce(MaplePacketCreator.showCash(chr)); + } else { + FilePrinter.printError(FilePrinter.CASHITEM_BOUGHT, "Could not add " + qty + " slots of type " + type + " for player " + MapleCharacter.makeMapleReadable(chr.getName())); } } else { CashItem cItem = CashItemFactory.getItem(slea.readInt()); @@ -159,10 +168,17 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { c.enableCSActions(); return; } - if (chr.gainSlots(type, 8, false)) { + int qty = 8; + if (!chr.canGainSlots(type, qty)) { + c.enableCSActions(); + return; + } + cs.gainCash(cash, cItem, chr.getWorld()); + if (chr.gainSlots(type, qty, false)) { c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type))); - cs.gainCash(cash, cItem, chr.getWorld()); c.announce(MaplePacketCreator.showCash(chr)); + } else { + FilePrinter.printError(FilePrinter.CASHITEM_BOUGHT, "Could not add " + qty + " slots of type " + type + " for player " + MapleCharacter.makeMapleReadable(chr.getName())); } } } else if (action == 0x07) { // Increase Storage Slots @@ -174,13 +190,20 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { c.enableCSActions(); return; } - if (chr.getStorage().gainSlots(4)) { - FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought 4 slots to their account storage."); + int qty = 4; + if (!chr.getStorage().canGainSlots(qty)) { + c.enableCSActions(); + return; + } + cs.gainCash(cash, -4000); + if (chr.getStorage().gainSlots(qty)) { + FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought " + qty + " slots to their account storage."); chr.setUsedStorage(); c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots())); - cs.gainCash(cash, -4000); c.announce(MaplePacketCreator.showCash(chr)); + } else { + FilePrinter.printError(FilePrinter.CASHITEM_BOUGHT, "Could not add " + qty + " slots to " + MapleCharacter.makeMapleReadable(chr.getName()) + "'s account."); } } else { CashItem cItem = CashItemFactory.getItem(slea.readInt()); @@ -189,13 +212,20 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { c.enableCSActions(); return; } - if (chr.getStorage().gainSlots(8)) { // thanks ABaldParrot & Thora for detecting storage issues here - FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought 8 slots to their account storage."); + int qty = 8; + if (!chr.getStorage().canGainSlots(qty)) { + c.enableCSActions(); + return; + } + cs.gainCash(cash, cItem, chr.getWorld()); + if (chr.getStorage().gainSlots(qty)) { // thanks ABaldParrot & Thora for detecting storage issues here + FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought " + qty + " slots to their account storage."); chr.setUsedStorage(); c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots())); - cs.gainCash(cash, cItem, chr.getWorld()); c.announce(MaplePacketCreator.showCash(chr)); + } else { + FilePrinter.printError(FilePrinter.CASHITEM_BOUGHT, "Could not add " + qty + " slots to " + MapleCharacter.makeMapleReadable(chr.getName()) + "'s account."); } } } else if (action == 0x08) { // Increase Character Slots @@ -207,13 +237,17 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { c.enableCSActions(); return; } - + if (!c.canGainCharacterSlot()) { + chr.dropMessage(1, "You have already used up all 12 extra character slots."); + c.enableCSActions(); + return; + } + cs.gainCash(cash, cItem, chr.getWorld()); if (c.gainCharacterSlot()) { c.announce(MaplePacketCreator.showBoughtCharacterSlot(c.getCharacterSlots())); - cs.gainCash(cash, cItem, chr.getWorld()); c.announce(MaplePacketCreator.showCash(chr)); } else { - chr.dropMessage(1, "You have already used up all 12 extra character slots."); + FilePrinter.printError(FilePrinter.CASHITEM_BOUGHT, "Could not add a character slot to " + MapleCharacter.makeMapleReadable(chr.getName()) + "'s account."); c.enableCSActions(); return; } @@ -287,8 +321,8 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { eqp.setRingId(rings.getLeft()); cs.addToInventory(eqp); c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID())); - cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight()); cs.gainCash(toCharge, itemRing, chr.getWorld()); + cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight()); chr.addCrushRing(MapleRing.loadFromDb(rings.getLeft())); try { chr.sendNote(partner.getName(), text, (byte) 1); @@ -353,8 +387,8 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { eqp.setRingId(rings.getLeft()); cs.addToInventory(eqp); c.announce(MaplePacketCreator.showBoughtCashRing(eqp, partner.getName(), c.getAccID())); - cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight()); cs.gainCash(payment, -itemRing.getPrice()); + cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight()); chr.addFriendshipRing(MapleRing.loadFromDb(rings.getLeft())); try { chr.sendNote(partner.getName(), text, (byte) 1); @@ -391,8 +425,8 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { if(chr.registerNameChange(newName)) { //success Item item = cItem.toItem(); c.announce(MaplePacketCreator.showNameChangeSuccess(item, c.getAccID())); - cs.addToInventory(item); cs.gainCash(4, cItem, chr.getWorld()); + cs.addToInventory(item); } else { c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); } @@ -421,8 +455,8 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { } else if(chr.registerWorldTransfer(newWorldSelection)) { Item item = cItem.toItem(); c.announce(MaplePacketCreator.showWorldTransferSuccess(item, c.getAccID())); - cs.addToInventory(item); cs.gainCash(4, cItem, chr.getWorld()); + cs.addToInventory(item); } else { c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); } diff --git a/src/net/server/channel/handlers/InventorySortHandler.java b/src/net/server/channel/handlers/InventorySortHandler.java index d7510405f8..63b00ba038 100644 --- a/src/net/server/channel/handlers/InventorySortHandler.java +++ b/src/net/server/channel/handlers/InventorySortHandler.java @@ -35,7 +35,6 @@ import client.inventory.Equip; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.ModifyInventory; -import constants.net.ServerConstants; import server.MapleItemInformationProvider; import net.server.Server; @@ -73,7 +72,11 @@ class PairedQuicksort { } while (i <= j); } - private void PartitionByItemIdReverse(int Esq, int Dir, ArrayList A) { + private int getWatkForProjectile(Item item) { + return ii.getWatkForProjectile(item.getItemId()); + } + + private void PartitionByProjectileAtk(int Esq, int Dir, ArrayList A) { Item x, w; i = Esq; @@ -81,8 +84,9 @@ class PairedQuicksort { x = A.get((i + j) / 2); do { - while (x.getItemId() < A.get(i).getItemId()) i++; - while (x.getItemId() > A.get(j).getItemId()) j--; + int watk = getWatkForProjectile(x); + while (watk < getWatkForProjectile(A.get(i))) i++; + while (watk > getWatkForProjectile(A.get(j))) j--; if (i <= j) { w = A.get(i); @@ -228,7 +232,7 @@ class PairedQuicksort { public void reverseSortSublist(ArrayList A, int[] range) { if (range != null) { - PartitionByItemIdReverse(range[0], range[1], A); + PartitionByProjectileAtk(range[0], range[1], A); } } diff --git a/src/net/server/channel/handlers/UseCashItemHandler.java b/src/net/server/channel/handlers/UseCashItemHandler.java index 7146eba05d..5c8a207a79 100644 --- a/src/net/server/channel/handlers/UseCashItemHandler.java +++ b/src/net/server/channel/handlers/UseCashItemHandler.java @@ -244,7 +244,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { eq.setExpiration(currentServerTime() + (period * 60 * 60 * 24 * 1000)); } - remove(c, position, itemId); + // double-remove found thanks to BHB } else if (itemId == 5060002) { // Incubator byte inventory2 = (byte) slea.readInt(); short slot2 = (short) slea.readInt(); diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 6d5181edd9..f1ebaf43d1 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -467,7 +467,13 @@ public class World { } } - public void registerAccountStorage(Integer accountId) { + public void loadAccountStorage(Integer accountId) { + if (getAccountStorage(accountId) == null) { + registerAccountStorage(accountId); + } + } + + private void registerAccountStorage(Integer accountId) { MapleStorage storage = MapleStorage.loadOrCreateFromDB(accountId, this.id); accountCharsLock.lock(); try { @@ -572,7 +578,7 @@ public class World { if(cserv != null) { if(!cserv.removePlayer(chr)) { - // oy the player is not where it should be, find this mf + // oy the player is not where they should be, find this mf for(Channel ch : getChannels()) { if(ch.removePlayer(chr)) { diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index 5fa69ad6c9..08b1b02916 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.HashMap; import java.util.Map; import config.YamlConfig; @@ -640,7 +639,7 @@ public class MapleStatEffect { break; case WindArcher.WIND_WALK: statups.add(new Pair<>(MapleBuffStat.WIND_WALK, Integer.valueOf(x))); - break; + //break; thanks Vcoc for noticing WW not showing for other players when changing maps case Rogue.DARK_SIGHT: case NightWalker.DARK_SIGHT: statups.add(new Pair<>(MapleBuffStat.DARKSIGHT, Integer.valueOf(x))); @@ -1343,6 +1342,8 @@ public class MapleStatEffect { if (isDash()) { buff = MaplePacketCreator.givePirateBuff(statups, sourceid, seconds); mbuff = MaplePacketCreator.giveForeignPirateBuff(applyto.getId(), sourceid, seconds, localstatups); + } else if (isWkCharge()) { + mbuff = MaplePacketCreator.giveForeignWKChargeEffect(applyto.getId(), sourceid, localstatups); } else if (isInfusion()) { buff = MaplePacketCreator.givePirateBuff(localstatups, sourceid, seconds); mbuff = MaplePacketCreator.giveForeignPirateBuff(applyto.getId(), sourceid, seconds, localstatups); @@ -1746,6 +1747,20 @@ public class MapleStatEffect { return false; } } + + private boolean isWkCharge() { + if (!skill) { + return false; + } + + for (Pair p : statups) { + if (p.getLeft().equals(MapleBuffStat.WK_CHARGE)) { + return true; + } + } + + return false; + } private boolean isDash() { return skill && (sourceid == Pirate.DASH || sourceid == ThunderBreaker.DASH || sourceid == Beginner.SPACE_DASH || sourceid == Noblesse.SPACE_DASH); diff --git a/src/server/MapleStorage.java b/src/server/MapleStorage.java index af551e435c..f1dd374fb8 100644 --- a/src/server/MapleStorage.java +++ b/src/server/MapleStorage.java @@ -114,12 +114,16 @@ public class MapleStorage { return slots; } + public boolean canGainSlots(int slots) { + slots += this.slots; + return slots <= 48; + } + public boolean gainSlots(int slots) { lock.lock(); try { - slots += this.slots; - - if (slots <= 48) { + if (canGainSlots(slots)) { + slots += this.slots; this.slots = (byte) slots; return true; } diff --git a/src/server/maps/MapleMapFactory.java b/src/server/maps/MapleMapFactory.java index dd483131d2..3b7ab48db3 100644 --- a/src/server/maps/MapleMapFactory.java +++ b/src/server/maps/MapleMapFactory.java @@ -310,19 +310,9 @@ public class MapleMapFactory { } } - try { - map.setMapName(loadPlaceName(mapid)); - map.setStreetName(loadStreetName(mapid)); - } catch (Exception e) { - if (mapid / 1000 != 1020) { // explorer job introduction scenes - e.printStackTrace(); - System.err.println("Not found mapid " + mapid); - } - - map.setMapName(""); - map.setStreetName(""); - } - + map.setMapName(loadPlaceName(mapid)); + map.setStreetName(loadStreetName(mapid)); + map.setClock(mapData.getChildByPath("clock") != null); map.setEverlast(MapleDataTool.getIntConvert("everlast", infoData, 0) != 0); // thanks davidlafriniere for noticing value 0 accounting as true map.setTown(MapleDataTool.getIntConvert("town", infoData, 0) != 0); @@ -435,7 +425,7 @@ public class MapleMapFactory { return builder.toString(); } - public static String loadPlaceName(int mapid) throws Exception { + public static String loadPlaceName(int mapid) { try { return MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), ""); } catch (Exception e) { @@ -443,7 +433,7 @@ public class MapleMapFactory { } } - public static String loadStreetName(int mapid) throws Exception { + public static String loadStreetName(int mapid) { try { return MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), ""); } catch (Exception e) { diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index ee0e25d53b..0b180c19cd 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -1890,7 +1890,7 @@ public class MaplePacketCreator { } long buffmask = 0; Integer buffvalue = null; - if (chr.getBuffedValue(MapleBuffStat.DARKSIGHT) != null && !chr.isHidden()) { + if ((chr.getBuffedValue(MapleBuffStat.DARKSIGHT) != null || chr.getBuffedValue(MapleBuffStat.WIND_WALK) != null) && !chr.isHidden()) { buffmask |= MapleBuffStat.DARKSIGHT.getValue(); } if (chr.getBuffedValue(MapleBuffStat.COMBO) != null) { @@ -1906,10 +1906,6 @@ public class MaplePacketCreator { if (chr.getBuffedValue(MapleBuffStat.MORPH) != null) { buffvalue = Integer.valueOf(chr.getBuffedValue(MapleBuffStat.MORPH).intValue()); } - if (chr.getBuffedValue(MapleBuffStat.ENERGY_CHARGE) != null) { - buffmask |= MapleBuffStat.ENERGY_CHARGE.getValue(); - buffvalue = Integer.valueOf(chr.getBuffedValue(MapleBuffStat.ENERGY_CHARGE).intValue()); - }//AREN'T THESE mplew.writeInt((int) ((buffmask >> 32) & 0xffffffffL)); if (buffvalue != null) { if (chr.getBuffedValue(MapleBuffStat.MORPH) != null) { //TEST @@ -1919,16 +1915,24 @@ public class MaplePacketCreator { } } mplew.writeInt((int) (buffmask & 0xffffffffL)); - int CHAR_MAGIC_SPAWN = Randomizer.nextInt(); - mplew.skip(6); - mplew.writeInt(CHAR_MAGIC_SPAWN); + + // Energy Charge + mplew.writeInt(chr.getEnergyBar() == 15000 ? 1 : 0); + mplew.writeShort(0); + mplew.skip(4); + + boolean dashBuff = chr.getBuffedValue(MapleBuffStat.DASH) != null; + // Dash Speed + mplew.writeInt(dashBuff ? 1 << 24 : 0); mplew.skip(11); - mplew.writeInt(CHAR_MAGIC_SPAWN);//v74 - mplew.skip(11); - mplew.writeInt(CHAR_MAGIC_SPAWN); + mplew.writeShort(0); + // Dash Jump + mplew.skip(9); + mplew.writeInt(dashBuff ? 1 << 24 : 0); mplew.writeShort(0); mplew.write(0); + // Monster Riding Integer bv = chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING); if (bv != null) { MapleMount mount = chr.getMount(); @@ -1942,17 +1946,23 @@ public class MaplePacketCreator { mplew.writeLong(0); } + int CHAR_MAGIC_SPAWN = Randomizer.nextInt(); // skill references found thanks to Rien dev team mplew.writeInt(CHAR_MAGIC_SPAWN); + // Speed Infusion + mplew.skip(8); + mplew.writeInt(CHAR_MAGIC_SPAWN); + mplew.write(0); + mplew.writeInt(CHAR_MAGIC_SPAWN); + mplew.writeShort(0); + // Homing Beacon + mplew.skip(9); + mplew.writeInt(CHAR_MAGIC_SPAWN); + mplew.writeInt(0); + // Zombify mplew.skip(9); mplew.writeInt(CHAR_MAGIC_SPAWN); mplew.writeShort(0); - mplew.writeInt(0); // actually not 0, why is it 0 then? - mplew.skip(10); - mplew.writeInt(CHAR_MAGIC_SPAWN); - mplew.skip(13); - mplew.writeInt(CHAR_MAGIC_SPAWN); mplew.writeShort(0); - mplew.write(0); } /** @@ -3067,6 +3077,15 @@ public class MaplePacketCreator { return mplew.getPacket(); } + public static byte[] cancelForeignFirstDebuff(int cid, long mask) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CANCEL_FOREIGN_BUFF.getValue()); + mplew.writeInt(cid); + mplew.writeLong(mask); + mplew.writeLong(0); + return mplew.getPacket(); + } + public static byte[] cancelForeignDebuff(int cid, long mask) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CANCEL_FOREIGN_BUFF.getValue()); @@ -3198,6 +3217,20 @@ public class MaplePacketCreator { return mplew.getPacket(); } + // packet found thanks to Ronan + public static byte[] giveForeignWKChargeEffect(int cid, int buffid, List> statups) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(19); + mplew.writeShort(SendOpcode.GIVE_FOREIGN_BUFF.getValue()); + mplew.writeInt(cid); + writeLongMask(mplew, statups); + mplew.writeInt(buffid); + mplew.writeShort(600); + mplew.writeShort(1000);//Delay + mplew.write(1); + + return mplew.getPacket(); + } + public static byte[] cancelForeignChairSkillEffect(int cid) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(19); mplew.writeShort(SendOpcode.CANCEL_FOREIGN_BUFF.getValue()); diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index e98d748844..3a4a74fc31 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -230,7 +230,7 @@ - + @@ -1445,31 +1445,31 @@ - + - + - + - + - + - + - + @@ -3689,7 +3689,7 @@ - + @@ -8305,7 +8305,7 @@ - +