diff --git a/README.md b/README.md index 7ba64be0d3..c82836594b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Besides myself for maintaining this repository, credits are to be given to Wizet Regarding distributability and usage of the code presented here: like it was before, this MapleStory server is open-source. By that, it is meant that anyone is **free to install, use, modify and redistribute the contents**, as long as there is **no kind of commercial trading involved** and the **credits to the original creators are maintained** within the codes. -This is a NetBeans 8.2 Project, that should be built and run on Java 8 in order to run properly (used to be ran in Java 7, thanks @kolakcc for the Java 8 support!). +This is a NetBeans 8.2 Project, that should be built and run on Java 8 in order to run properly. -- Used to be ran in Java 7, thanks kolakcc (Familiar) for the Java 8 support! Being a NetBeans 8.2 Project, this means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application. @@ -33,6 +33,8 @@ Java 8 SDK & NetBeans bundle: https://www.oracle.com/technetwork/pt/java/javase/ **Change log:** + * Fixed Monster Magnet crashing the caster when trying to pull fixed mobs. https://gofile.io/?c=BW7dVM + * Cleared need for administrator privileges (OS) to play the game, credits to Ubaware. * Set a higher cap for AP assigning with AP Reset, credits to Ubaware. @@ -75,7 +77,7 @@ HeavenClient Github: https://github.com/ryantpayton/HeavenClient --- ### Development information -Status: __In development (4th round)__. +Status: __Released (4 rounds)__. #### Mission @@ -112,15 +114,11 @@ Our Discord channel is still available on: https://discord.gg/Q7wKxHX -### Donation - -If you REALLY liked what you have seen on the project, please feel free to donate a little something as a helping hand for my contributions towards Maple development. Also remember to **support Nexon**! - -Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3K8KVTWRLFBQ4 +[//]: <> (If you REALLY liked what you have seen on the project, please feel free to donate a little something as a helping hand for my contributions towards Maple development. Also remember to **support Nexon**!) ### Disclaimer -* HeavenMS development is decisively __ONLY accepting donations__ from the Paypal link aforementioned, in the __ronancpl/HeavenMS__ repository readme (no patreons or other revenue resources). +[//]: <> (* HeavenMS development is decisively __ONLY accepting donations__ from the Paypal link aforementioned, in the __ronancpl/HeavenMS__ repository readme (no patreons or other revenue resources).) * HeavenMS staff has __no current intention__ to publicly open a server with this source, if that ever comes to happen this note will be lifted. __Don't be scammed!__ diff --git a/config.yaml b/config.yaml index 182227bda5..15ec53a7a6 100644 --- a/config.yaml +++ b/config.yaml @@ -335,6 +335,7 @@ server: MOB_STATUS_MONITOR_LIFE: 84 #Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity. MOB_STATUS_AGGRO_PERSISTENCE: 2 #Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer. MOB_STATUS_AGGRO_INTERVAL: 5000 #Interval in milliseconds between aggro logistics update. + USE_AUTOAGGRO_NEARBY: false #Mobs start following the player when approached. #Some Gameplay Enhancing Configurations #Scroll Configuration diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 9a63d3ff28..7714778d3e 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -2330,3 +2330,29 @@ 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). + +07 Dezembro 2019, +Corrigido caso de exceção ao tentar atribuir pontos de stats ao jogador, que passou a ocorrer após mudança recente no sistema de stats. + +09 Dezembro 2019, +Implementado aplicação de buffs ao se realizar a chamada/requisição de controle de um mob. + +11 Dezembro 2019, +Incrementado suporte para aplicação de buffs de mobs de dano refletido, completando-se a ronda. + +14 Dezembro 2019, +Melhorado NPC de geração de scrolls, agora testando outras possibilidades caso nenhum scroll encontrado em conjunto de scrolls. +Corrigido método de ganho de slots atualizado recentemente levando a bug visual, onde somente os 4 primeiros itens do inventário estariam aparecendo na tela. +Revisado método de busca por nomes, não mais tentando gerar um objeto de mob somente para checar o nome. +Adicionado limpa de debuffs ao se utilizar buyback. +Revisado anunciador de skillbooks não informando devidamente sobre os campos de reatores e scripts. + +16 Dezembro 2019, +Revisado possível caso de dupe com itens ao fechar Hired Merchant. + +20 Dezembro 2019, +Corrigido autoaggro não atuando devidamente em mobs assim que jogador entra no mapa e adquire controladores sobre os mesmos. +Corrigido anunciador de skillbooks não reportando etc de quest para começar, quando lidando com questlines onde se obtém a skill diretamente. + +21 Dezembro 2019, +Corrigido mobs em estado "fake" desaparecendo da tela assim que muda-se o controlador do mesmo. \ No newline at end of file diff --git a/scripts/event/HorntailBattle.js b/scripts/event/HorntailBattle.js index 90c69d27ea..a49801f525 100644 --- a/scripts/event/HorntailBattle.js +++ b/scripts/event/HorntailBattle.js @@ -88,7 +88,7 @@ function setEventRewards(eim) { function afterSetup(eim) {} function setup(channel) { - var eim = em.newInstance("Horntail" + channel); // thanks Thora for reporting an issue with misleading event name here + var eim = em.newInstance("Horntail" + channel); // thanks Thora (Arufonsu) for reporting an issue with misleading event name here eim.setProperty("canJoin", 1); eim.setProperty("defeatedBoss", 0); eim.setProperty("defeatedHead", 0); diff --git a/scripts/npc/9209000.js b/scripts/npc/9209000.js index 3c149e6df6..8d222a5790 100644 --- a/scripts/npc/9209000.js +++ b/scripts/npc/9209000.js @@ -125,7 +125,7 @@ function action(mode, type, selection) { sendStr += " #L" + i + "# " + mobList[i] + "#l\r\n"; } - sendStr += "\r\n"; + sendStr += "\r\n\r\n"; } } else { sendStr = "\r\n\r\n"; diff --git a/scripts/npc/scroll_generator.js b/scripts/npc/scroll_generator.js index b3b3f038c7..1fa746d067 100644 --- a/scripts/npc/scroll_generator.js +++ b/scripts/npc/scroll_generator.js @@ -353,7 +353,7 @@ function calculateScrollTiers() { return tiers; } -function getRandomScroll(tiers) { +function getRandomScrollFromTiers(tiers) { var typeTier = tiers[0], subtypeTier = tiers[1], successTier = tiers[2]; var scrollTypePool = getScrollTypePool(typeTier); var scrollPool = getAvailableScrollsPool(scrollTypePool, subtypeTier, successTier); @@ -365,6 +365,36 @@ function getRandomScroll(tiers) { } } +function getRandomScrollFromRightPermutations(tiers) { + for (var i = 2; i >= 0; i--) { + for (var j = i - 1; j >= 0; j--) { + if (tiers[i] >= 3) { + break; + } else if (tiers[j] > 1) { + tiers[i]++; + tiers[j]--; + + var itemid = getRandomScrollFromTiers(tiers); + if (itemid != -1) { + return itemid; + } + } + } + } + + return -1; +} + +function getRandomScroll(tiers) { + var itemid = getRandomScrollFromTiers(tiers); + if (itemid == -1) { + // worst case shift-right permutations... + itemid = getRandomScrollFromRightPermutations(tiers); + } + + return itemid; +} + function performExchange(sgItemid, sgCount) { if (cm.getMeso() < sgAppliedMeso) { return false; diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 3ff5915271..ccb050c21a 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -144,7 +144,6 @@ import client.processor.action.PetAutopotProcessor; import constants.game.ExpTable; import constants.game.GameConstants; import constants.inventory.ItemConstants; -import constants.net.ServerConstants; import constants.skills.Aran; import constants.skills.Beginner; import constants.skills.Bishop; @@ -2819,7 +2818,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { dispelDebuff(MapleDisease.WEAKEN); dispelDebuff(MapleDisease.SLOW); // thanks Conrad for noticing ZOMBIFY isn't dispellable } - + + public void purgeDebuffs() { + dispelDebuff(MapleDisease.SEDUCE); + dispelDebuff(MapleDisease.ZOMBIFY); + dispelDebuff(MapleDisease.CONFUSE); + dispelDebuffs(); + } + public void cancelAllDebuffs() { chrLock.lock(); try { @@ -9420,26 +9426,27 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean gainSlots(int type, int slots, boolean update) { - boolean ret = gainSlotsInternal(type, slots, update); - if (ret) { + int newLimit = gainSlotsInternal(type, slots); + if (newLimit != -1) { this.saveCharToDB(); if (update) { - client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots)); + client.announce(MaplePacketCreator.updateInventorySlotLimit(type, newLimit)); } + return true; + } else { + return false; } - - return ret; } - private boolean gainSlotsInternal(int type, int slots, boolean update) { + private int gainSlotsInternal(int type, int slots) { inventory[type].lockInventory(); try { if (canGainSlots(type, slots)) { - slots += inventory[type].getSlotLimit(); - inventory[type].setSlotLimit(slots); - return true; + int newLimit = inventory[type].getSlotLimit() + slots; + inventory[type].setSlotLimit(newLimit); + return newLimit; } else { - return false; + return -1; } } finally { inventory[type].unlockInventory(); @@ -10652,6 +10659,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject { client = null; // clients still triggers handlers a few times after disconnecting map = null; setListener(null); + + // thanks Shavit for noticing a memory leak with inventories holding owner object + for (int i = 0; i < inventory.length; i++) { + inventory[i].dispose(); + } + inventory = null; } }, 5 * 60 * 1000); } diff --git a/src/client/command/commands/gm0/DisposeCommand.java b/src/client/command/commands/gm0/DisposeCommand.java index ffb5fa4414..50fb77086d 100644 --- a/src/client/command/commands/gm0/DisposeCommand.java +++ b/src/client/command/commands/gm0/DisposeCommand.java @@ -33,7 +33,7 @@ public class DisposeCommand extends Command { { setDescription(""); } - + @Override public void execute(MapleClient c, String[] params) { NPCScriptManager.getInstance().dispose(c); @@ -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/command/commands/gm0/TimeCommand.java b/src/client/command/commands/gm0/TimeCommand.java index d91a3b8a01..975d5b5717 100644 --- a/src/client/command/commands/gm0/TimeCommand.java +++ b/src/client/command/commands/gm0/TimeCommand.java @@ -25,7 +25,6 @@ package client.command.commands.gm0; import client.MapleClient; import client.command.Command; -import constants.net.ServerConstants; import java.text.DateFormat; import java.text.SimpleDateFormat; diff --git a/src/client/command/commands/gm1/WhatDropsFromCommand.java b/src/client/command/commands/gm1/WhatDropsFromCommand.java index 14747c0679..a35c092b2f 100644 --- a/src/client/command/commands/gm1/WhatDropsFromCommand.java +++ b/src/client/command/commands/gm1/WhatDropsFromCommand.java @@ -29,7 +29,6 @@ import client.MapleClient; import server.MapleItemInformationProvider; import server.life.MapleMonsterInformationProvider; import server.life.MonsterDropEntry; -import tools.MaplePacketCreator; import tools.Pair; import java.util.Iterator; diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java index a2ce3a7f16..705d281186 100644 --- a/src/client/inventory/MapleInventory.java +++ b/src/client/inventory/MapleInventory.java @@ -25,14 +25,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Map; -import java.util.Set; import java.util.concurrent.locks.Lock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; @@ -672,4 +670,8 @@ public class MapleInventory implements Iterable { public void unlockInventory() { lock.unlock(); } + + public void dispose() { + owner = null; + } } \ No newline at end of file diff --git a/src/client/processor/action/BuybackProcessor.java b/src/client/processor/action/BuybackProcessor.java index cacd0067be..4fd5a6e628 100644 --- a/src/client/processor/action/BuybackProcessor.java +++ b/src/client/processor/action/BuybackProcessor.java @@ -70,6 +70,7 @@ public class BuybackProcessor { } chr.healHpMp(); + chr.purgeDebuffs(); chr.broadcastStance(chr.isFacingLeft() ? 5 : 4); MapleMap map = chr.getMap(); diff --git a/src/config/ServerConfig.java b/src/config/ServerConfig.java index ea40b0b2ad..fbef750367 100644 --- a/src/config/ServerConfig.java +++ b/src/config/ServerConfig.java @@ -179,6 +179,7 @@ public class ServerConfig { public int MOB_STATUS_MONITOR_LIFE; public int MOB_STATUS_AGGRO_PERSISTENCE; public int MOB_STATUS_AGGRO_INTERVAL; + public boolean USE_AUTOAGGRO_NEARBY; //Some Gameplay Enhancing Configurations //Scroll Configuration diff --git a/src/constants/net/ServerConstants.java b/src/constants/net/ServerConstants.java index 869a135f5d..f341f45c4d 100644 --- a/src/constants/net/ServerConstants.java +++ b/src/constants/net/ServerConstants.java @@ -12,6 +12,7 @@ public class ServerConstants { public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes // https://github.com/openstreetmap/josm/blob/a3a6e8a6b657cf4c5b4c64ea14d6e87be6280d65/src/org/openstreetmap/josm/tools/Utils.java#L1566-L1585 + // Added by kolakcc (Familiar) /** * Returns the Java version as an int value. * @return the Java version as an int value (8, 9, etc.) diff --git a/src/net/server/channel/handlers/GuildOperationHandler.java b/src/net/server/channel/handlers/GuildOperationHandler.java index 92991f4269..c8c900177b 100644 --- a/src/net/server/channel/handlers/GuildOperationHandler.java +++ b/src/net/server/channel/handlers/GuildOperationHandler.java @@ -25,7 +25,6 @@ import config.YamlConfig; import net.server.guild.MapleGuildResponse; import net.server.guild.MapleGuild; import constants.game.GameConstants; -import constants.net.ServerConstants; import client.MapleClient; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/PartyOperationHandler.java b/src/net/server/channel/handlers/PartyOperationHandler.java index e73fe55966..4977ce0137 100644 --- a/src/net/server/channel/handlers/PartyOperationHandler.java +++ b/src/net/server/channel/handlers/PartyOperationHandler.java @@ -31,7 +31,6 @@ import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleCharacter; import client.MapleClient; -import constants.net.ServerConstants; import net.server.coordinator.world.MapleInviteCoordinator; import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; import net.server.coordinator.world.MapleInviteCoordinator.InviteType; diff --git a/src/net/server/channel/handlers/PlayerMapTransitionHandler.java b/src/net/server/channel/handlers/PlayerMapTransitionHandler.java index 00b94828dc..760cd41bc9 100644 --- a/src/net/server/channel/handlers/PlayerMapTransitionHandler.java +++ b/src/net/server/channel/handlers/PlayerMapTransitionHandler.java @@ -58,13 +58,13 @@ public final class PlayerMapTransitionHandler extends AbstractMaplePacketHandler if (m.getController() == chr) { c.announce(MaplePacketCreator.stopControllingMonster(m.getObjectId())); m.sendDestroyData(c); - m.aggroRedirectController(); + m.aggroRemoveController(); } else { m.sendDestroyData(c); } - m.aggroSwitchController(chr, false); m.sendSpawnData(c); + m.aggroSwitchController(chr, false); } } } diff --git a/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java b/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java index 29ba8611e0..0c6c0251cb 100644 --- a/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java +++ b/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java @@ -354,7 +354,7 @@ public class MapleMatchCheckerCoordinator { if (mmce != null) { synchronized (mmce) { - if (!mmce.isMatchActive()) { // thanks Alex (CanIGetaPR) for noticing that exploiters could stall on match checking + if (!mmce.isMatchActive()) { // thanks Alex (Alex-0000) for noticing that exploiters could stall on match checking matchEntries.remove(cid); mmce = null; } else { diff --git a/src/net/server/task/CharacterAutosaverTask.java b/src/net/server/task/CharacterAutosaverTask.java index 4c44ef2abb..122f6888a2 100644 --- a/src/net/server/task/CharacterAutosaverTask.java +++ b/src/net/server/task/CharacterAutosaverTask.java @@ -27,7 +27,7 @@ import net.server.PlayerStorage; /** * @author Ronan */ -public class CharacterAutosaverTask extends BaseTask implements Runnable { // thanks Alex (Alex09) for noticing these runnable classes are tasks, "workers" runs them +public class CharacterAutosaverTask extends BaseTask implements Runnable { // thanks Alex09 (Alex-0000) for noticing these runnable classes are tasks, "workers" runs them @Override public void run() { diff --git a/src/net/server/task/RankingLoginTask.java b/src/net/server/task/RankingLoginTask.java index c56a9ea4db..b5d0d74309 100644 --- a/src/net/server/task/RankingLoginTask.java +++ b/src/net/server/task/RankingLoginTask.java @@ -28,7 +28,6 @@ import java.sql.SQLException; import client.MapleJob; import config.YamlConfig; import tools.DatabaseConnection; -import constants.net.ServerConstants; import net.server.Server; /** diff --git a/src/scripting/npc/NPCConversationManager.java b/src/scripting/npc/NPCConversationManager.java index bb5f963d11..f4b22ed08a 100644 --- a/src/scripting/npc/NPCConversationManager.java +++ b/src/scripting/npc/NPCConversationManager.java @@ -60,7 +60,6 @@ import client.inventory.MaplePet; import constants.game.GameConstants; import constants.inventory.ItemConstants; import constants.string.LanguageConstants; -import net.server.PlayerStorage; import net.server.channel.Channel; import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType; import server.MapleMarriage; @@ -603,6 +602,12 @@ public class NPCConversationManager extends AbstractPlayerInteraction { switch (sbe) { case UNAVAILABLE: return ""; + + case REACTOR: + return " Obtainable through #rexploring#k (loot boxes)."; + + case SCRIPT: + return " Obtainable through #rexploring#k (field interaction)."; case QUEST_BOOK: return " Obtainable through #rquestline#k (collecting book)."; @@ -633,7 +638,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction { for (int i = 0; i < 6; i++) { if (fieldTaken(i)) { if (fieldLobbied(i)) { - msg += "#b#L" + i + "#Carnival Field " + (i + 1) + " (Level: " // "Carnival field" GMS-like improvement thanks to Jayd + msg += "#b#L" + i + "#Carnival Field " + (i + 1) + " (Level: " // "Carnival field" GMS-like improvement thanks to Jayd (jaydenseah) + cpqCalcAvgLvl(980000100 + i * 100) + " / " + getPlayerCount(980000100 + i * 100) + "x" + getPlayerCount(980000100 + i * 100) + ") #l\r\n"; diff --git a/src/server/CashShop.java b/src/server/CashShop.java index 19b99e9b03..1eb541e05d 100644 --- a/src/server/CashShop.java +++ b/src/server/CashShop.java @@ -49,7 +49,6 @@ import client.inventory.ItemFactory; import client.inventory.MapleInventoryType; import client.inventory.MaplePet; import constants.inventory.ItemConstants; -import constants.net.ServerConstants; import java.util.Collections; import net.server.audit.locks.MonitoredLockType; diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index a1ad6c58b2..e8b8dab9ff 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -60,7 +60,6 @@ import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.MapleWeaponType; -import constants.net.ServerConstants; import constants.inventory.EquipSlot; import constants.inventory.ItemConstants; import constants.skills.Assassin; @@ -2117,7 +2116,7 @@ public class MapleItemInformationProvider { ResultSet rs = ps.executeQuery(); while(rs.next()) { String resultName = MapleMonsterInformationProvider.getInstance().getMobNameFromId(rs.getInt("dropperid")); - if (resultName != null) { + if (!resultName.isEmpty()) { list.add(resultName); } } diff --git a/src/server/MapleSkillbookInformationProvider.java b/src/server/MapleSkillbookInformationProvider.java index 9d4c75055f..fdc2f4ab21 100644 --- a/src/server/MapleSkillbookInformationProvider.java +++ b/src/server/MapleSkillbookInformationProvider.java @@ -65,18 +65,11 @@ public class MapleSkillbookInformationProvider { SCRIPT } - private static String host = "jdbc:mysql://localhost:3306/heavenms"; - private static String driver = "com.mysql.jdbc.Driver"; - private static String username = "root"; - private static String password = ""; - private static String rootDirectory = "."; private static int skillbookMinItemid = 2280000; private static int skillbookMaxItemid = 2300000; // exclusively - private static Set questSkills = new HashSet<>(); - static { loadSkillbooks(); } @@ -156,7 +149,13 @@ public class MapleSkillbookInformationProvider { int skillid = MapleDataTool.getInt("id", questSkillData, 0); if (is4thJobSkill(skillid)) { // negative itemids are skill rewards - foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD); + + int questbook = fetchQuestbook(checkData, questData.getName()); + if (questbook < 0) { + foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD); + } else { + foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_BOOK); + } } } } diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index 7cde000dd9..d7821b1193 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -958,10 +958,7 @@ public class MapleStatEffect { if (isDispel() && makeChanceResult()) { applyto.dispelDebuffs(); } else if (isCureAllAbnormalStatus()) { - applyto.dispelDebuff(MapleDisease.SEDUCE); - applyto.dispelDebuff(MapleDisease.ZOMBIFY); - applyto.dispelDebuff(MapleDisease.CONFUSE); - applyto.dispelDebuffs(); + applyto.purgeDebuffs(); } else if (isComboReset()) { applyto.setCombo((short) 0); } diff --git a/src/server/MapleTrade.java b/src/server/MapleTrade.java index 5c2c00e3b8..7ad528c397 100644 --- a/src/server/MapleTrade.java +++ b/src/server/MapleTrade.java @@ -36,7 +36,6 @@ import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; import constants.game.GameConstants; -import constants.net.ServerConstants; import net.server.coordinator.world.MapleInviteCoordinator; import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; import net.server.coordinator.world.MapleInviteCoordinator.InviteType; diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index bef2d83a8d..b5f3c0c9ad 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -1040,23 +1040,6 @@ public class MapleMonster extends AbstractLoadedMapleLife { return isBoss() && getTagColor() > 0; } - public void broadcastMonsterStatus() { - Collection mseList = this.getStati().values(); - for (MapleCharacter chr : map.getAllPlayers()) { - announceMonsterStatusInternal(chr.getClient(), mseList); - } - } - - public void announceMonsterStatus(MapleClient client) { - announceMonsterStatusInternal(client, this.getStati().values()); - } - - public void announceMonsterStatusInternal(MapleClient client, Collection mseList) { - for (MonsterStatusEffect mse : mseList) { - client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null)); - } - } - @Override public void sendSpawnData(MapleClient client) { if (hp.get() <= 0) { // mustn't monsterLock this function @@ -1068,8 +1051,6 @@ public class MapleMonster extends AbstractLoadedMapleLife { client.announce(MaplePacketCreator.spawnMonster(this, false)); } - announceMonsterStatus(client); - if (hasBossHPBar()) { client.announceBossHpBar(this, this.hashCode(), makeBossHPBarPacket()); } @@ -1934,7 +1915,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { * Removes controllability status from the current controller of this mob. * */ - private Pair aggroRemoveController() { + public Pair aggroRemoveController() { MapleCharacter chrController; boolean hadAggro; @@ -1951,7 +1932,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster - chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId())); + if (!this.isFake()) chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId())); chrController.stopControllingMonster(this); } @@ -2123,9 +2104,12 @@ public class MapleMonster extends AbstractLoadedMapleLife { MapleCharacter chrController = this.getActiveController(); if (chrController == null) { - this.aggroSwitchController(player, true); + this.aggroSwitchController(player, true); } else if (chrController.getId() == player.getId()) { this.setControllerHasAggro(true); + if (!YamlConfig.config.server.USE_AUTOAGGRO_NEARBY) { // thanks Lichtmager for noticing autoaggro not updating the player properly + aggroMonsterControl(player.getClient(), this, true); + } } } @@ -2164,9 +2148,6 @@ public class MapleMonster extends AbstractLoadedMapleLife { private static void aggroMonsterControl(MapleClient c, MapleMonster mob, boolean immediateAggro) { c.announce(MaplePacketCreator.controlMonster(mob, false, immediateAggro)); - - // thanks BHB for noticing puppets disrupting mobstatuses for bowmans - mob.announceMonsterStatus(c); } private void aggroRefreshPuppetVisibility(MapleCharacter chrController, MapleSummon puppet) { @@ -2185,7 +2166,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { chrController.announce(MaplePacketCreator.removeSummon(puppet, false)); MapleClient c = chrController.getClient(); - for (MapleMonster mob : puppetControlled) { + for (MapleMonster mob : puppetControlled) { // thanks BHB for noticing puppets disrupting mobstatuses for bowmans aggroMonsterControl(c, mob, mob.isControllerKnowsAboutAggro()); } chrController.announce(MaplePacketCreator.spawnSummon(puppet, false)); diff --git a/src/server/life/MapleMonsterInformationProvider.java b/src/server/life/MapleMonsterInformationProvider.java index 66e0d385b6..565dc3b325 100644 --- a/src/server/life/MapleMonsterInformationProvider.java +++ b/src/server/life/MapleMonsterInformationProvider.java @@ -316,16 +316,10 @@ public class MapleMonsterInformationProvider { public String getMobNameFromId(int id) { String mobName = mobNameCache.get(id); if (mobName == null) { - try { - mobName = MapleLifeFactory.getMonster(id).getName(); - } catch (NullPointerException npe) { - mobName = ""; //nonexistant mob - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Nonexistant mob id " + id); - mobName = ""; //nonexistant mob - } - + MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz")); + MapleData mobData = dataProvider.getData("Mob.img"); + + mobName = MapleDataTool.getString(mobData.getChildByPath(id + "/name"), ""); mobNameCache.put(id, mobName); } diff --git a/src/server/life/MaplePlayerNPC.java b/src/server/life/MaplePlayerNPC.java index e49da183a2..86164d3a95 100644 --- a/src/server/life/MaplePlayerNPC.java +++ b/src/server/life/MaplePlayerNPC.java @@ -44,7 +44,6 @@ import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventoryType; import constants.game.GameConstants; -import constants.net.ServerConstants; import net.server.Server; import net.server.channel.Channel; import net.server.world.World; diff --git a/src/server/life/MaplePlayerNPCFactory.java b/src/server/life/MaplePlayerNPCFactory.java index bd32f773b6..ea8da0658f 100644 --- a/src/server/life/MaplePlayerNPCFactory.java +++ b/src/server/life/MaplePlayerNPCFactory.java @@ -19,7 +19,6 @@ */ package server.life; -import constants.net.ServerConstants; import java.io.File; import java.util.HashMap; import java.util.Map; diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java index a1870dd6ae..261238e411 100644 --- a/src/server/maps/MapleHiredMerchant.java +++ b/src/server/maps/MapleHiredMerchant.java @@ -413,18 +413,6 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { this.removeOwner(c.getPlayer()); try { - MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(ownerId); - if(player != null) { - player.setHasMerchant(false); - } else { - Connection con = DatabaseConnection.getConnection(); - try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) { - ps.setInt(1, ownerId); - ps.executeUpdate(); - } - con.close(); - } - List copyItems = getItems(); if (check(c.getPlayer(), copyItems) && !timeout) { for (MaplePlayerShopItem mpsi : copyItems) { @@ -448,6 +436,19 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { e.printStackTrace(); } + // thanks Rohenn for noticing a possible dupe scenario on closing shop + MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(ownerId); + if(player != null) { + player.setHasMerchant(false); + } else { + Connection con = DatabaseConnection.getConnection(); + try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) { + ps.setInt(1, ownerId); + ps.executeUpdate(); + } + con.close(); + } + if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) { c.getPlayer().saveCharToDB(false); } diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 2c820036c4..514f82a272 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -2017,7 +2017,7 @@ public class MapleMap { if (getEventInstance() != null) { getEventInstance().registerMonster(monster); } - + spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() { @Override public void sendPackets(MapleClient c) { @@ -2108,7 +2108,7 @@ public class MapleMap { c.announce(MaplePacketCreator.spawnFakeMonster(monster, 0)); } }); - + spawnedMonstersOnMap.incrementAndGet(); addSelfDestructive(monster); } @@ -2116,7 +2116,6 @@ public class MapleMap { public void makeMonsterReal(final MapleMonster monster) { monster.setFake(false); broadcastMessage(MaplePacketCreator.makeMonsterReal(monster)); - monster.broadcastMonsterStatus(); monster.aggroUpdateController(); updateBossSpawn(monster); } @@ -3081,8 +3080,6 @@ public class MapleMap { for (MapleMapObject o : objects) { if (isNonRangedType(o.getType())) { o.sendSpawnData(c); - } else if (o.getType() == MapleMapObjectType.MONSTER) { - ((MapleMonster) o).aggroUpdateController(); } else if (o.getType() == MapleMapObjectType.SUMMON) { MapleSummon summon = (MapleSummon) o; if (summon.getOwner() == chr) { @@ -3110,6 +3107,10 @@ public class MapleMap { } else { o.sendSpawnData(chr.getClient()); chr.addVisibleMapObject(o); + + if (o.getType() == MapleMapObjectType.MONSTER) { + ((MapleMonster) o).aggroUpdateController(); + } } } } diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index f26d8cdd80..893c76d44b 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -1486,6 +1486,43 @@ public class MaplePacketCreator { mplew.write(newSpawn ? -2 : -1); } + private static void encodeTemporary(MaplePacketLittleEndianWriter mplew, Map stati) { + int pCounter = -1, mCounter = -1; + + writeLongEncodeTemporaryMask(mplew, stati.keySet()); // packet structure mapped thanks to Eric + + for (Entry s : stati.entrySet()) { + MonsterStatusEffect mse = s.getValue(); + mplew.writeShort(mse.getStati().get(s.getKey())); + + MobSkill mobSkill = mse.getMobSkill(); + if (mobSkill != null) { + mplew.writeShort(mobSkill.getSkillId()); + mplew.writeShort(mobSkill.getSkillLevel()); + + switch(s.getKey()) { + case WEAPON_REFLECT: + pCounter = mobSkill.getX(); + break; + + case MAGIC_REFLECT: + mCounter = mobSkill.getY(); + break; + } + } else { + Skill skill = mse.getSkill(); + mplew.writeInt(skill != null ? skill.getId() : 0); + } + + mplew.writeShort(-1); // duration + } + + // reflect packet structure found thanks to Arnah (Vertisy) + if(pCounter != -1) mplew.writeInt(pCounter);// wPCounter_ + if(mCounter != -1) mplew.writeInt(mCounter);// wMCounter_ + if(pCounter != -1 || mCounter != -1) mplew.writeInt(100);// nCounterProb_ + } + /** * Internal function to handler monster spawning and controlling. * @@ -1513,9 +1550,13 @@ public class MaplePacketCreator { mplew.writeInt(life.getObjectId()); mplew.write(life.getController() == null ? 5 : 1); mplew.writeInt(life.getId()); - mplew.skip(15); - mplew.write(0x88); - mplew.skip(6); + + if (requestController) { + encodeTemporary(mplew, life.getStati()); // thanks shot for noticing encode temporary buffs missing + } else { + mplew.skip(16); + } + mplew.writePos(life.getPosition()); mplew.write(life.getStance()); mplew.writeShort(0); //Origin FH //life.getStartFh() @@ -1561,9 +1602,7 @@ public class MaplePacketCreator { mplew.writeInt(life.getObjectId()); mplew.write(5); mplew.writeInt(life.getId()); - mplew.skip(15); - mplew.write(0x88); - mplew.skip(6); + encodeTemporary(mplew, life.getStati()); mplew.writePos(life.getPosition()); mplew.write(life.getStance()); mplew.writeShort(0);//life.getStartFh() @@ -1591,9 +1630,7 @@ public class MaplePacketCreator { mplew.writeInt(life.getObjectId()); mplew.write(5); mplew.writeInt(life.getId()); - mplew.skip(15); - mplew.write(0x88); - mplew.skip(6); + encodeTemporary(mplew, life.getStati()); mplew.writePos(life.getPosition()); mplew.write(life.getStance()); mplew.writeShort(0);//life.getStartFh() @@ -3151,6 +3188,21 @@ public class MaplePacketCreator { mplew.writeLong(firstmask); mplew.writeLong(secondmask); } + + private static void writeLongEncodeTemporaryMask(final MaplePacketLittleEndianWriter mplew, Collection stati) { + int masks[] = new int[4]; + + for (MonsterStatus statup : stati) { + int pos = statup.isFirst() ? 0 : 2; + for (int i = 0; i < 2; i++) { + masks[pos + i] |= statup.getValue() >> 32 * i; + } + } + + for (int i = 0; i < masks.length; i++) { + mplew.writeInt(masks[i]); + } + } public static byte[] cancelDebuff(long mask) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(19); @@ -4137,30 +4189,6 @@ public class MaplePacketCreator { mplew.writeInt(secondmask); } - public static byte[] applyMonsterStatus(int oid, Map stats, int skill, boolean monsterSkill, int delay, MobSkill mobskill) { - MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.APPLY_MONSTER_STATUS.getValue()); - mplew.writeInt(oid); - int mask = 0; - for (MonsterStatus stat : stats.keySet()) { - mask |= stat.getValue(); - } - mplew.writeInt(mask); - for (Integer val : stats.values()) { - mplew.writeShort(val); - if (monsterSkill) { - mplew.writeShort(mobskill.getSkillId()); - mplew.writeShort(mobskill.getSkillLevel()); - } else { - mplew.writeInt(skill); - } - mplew.writeShort(0); // as this looks similar to giveBuff this - } - mplew.writeShort(delay); // delay in ms - mplew.write(1); // ? - return mplew.getPacket(); - } - public static byte[] applyMonsterStatus(final int oid, final MonsterStatusEffect mse, final List reflection) { Map stati = mse.getStati(); final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); diff --git a/src/tools/packets/Fishing.java b/src/tools/packets/Fishing.java index 469e56e6c6..8ea1a0fc49 100644 --- a/src/tools/packets/Fishing.java +++ b/src/tools/packets/Fishing.java @@ -23,7 +23,6 @@ import client.MapleCharacter; import config.YamlConfig; import constants.game.GameConstants; import constants.inventory.ItemConstants; -import constants.net.ServerConstants; import server.MapleItemInformationProvider; import tools.MaplePacketCreator;