From 851b57e8ef01989eee128427c53c03fd687e0544 Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 5 Feb 2024 07:45:31 +0100 Subject: [PATCH 01/55] Fix able to gain infinite slots with clean slate scroll And remove a GM config option. Thanks Crabo for the suggested solution. --- config.yaml | 1 - src/main/java/config/ServerConfig.java | 1 - .../channel/handlers/ScrollHandler.java | 37 +++++++-------- .../java/server/ItemInformationProvider.java | 47 +++++++++++++++---- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/config.yaml b/config.yaml index 0f350fd551..27d0706ab2 100644 --- a/config.yaml +++ b/config.yaml @@ -347,7 +347,6 @@ server: USE_PERFECT_SCROLLING: false #Scrolls doesn't use slots upon failure. USE_ENHANCED_CHSCROLL: false #Equips even more powerful with chaos upgrade. USE_ENHANCED_CRAFTING: false #Apply chaos scroll on every equip crafted. - USE_ENHANCED_CLNSLATE: false #Clean slates can be applied to recover successfully used slots as well. SCROLL_CHANCE_ROLLS: 1 #Number of rolls for success on a scroll, set 1 for default. CHSCROLL_STAT_RATE: 1 #Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default. CHSCROLL_STAT_RANGE: 6 #Stat upgrade range (-N, N) on chaos scrolls. diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index d7050da6a6..91e42533e3 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -195,7 +195,6 @@ public class ServerConfig { public boolean USE_PERFECT_SCROLLING; public boolean USE_ENHANCED_CHSCROLL; public boolean USE_ENHANCED_CRAFTING; - public boolean USE_ENHANCED_CLNSLATE; public int SCROLL_CHANCE_ROLLS; public int CHSCROLL_STAT_RATE; public int CHSCROLL_STAT_RANGE; diff --git a/src/main/java/net/server/channel/handlers/ScrollHandler.java b/src/main/java/net/server/channel/handlers/ScrollHandler.java index 2fc9bc3307..4d6f3b12c9 100644 --- a/src/main/java/net/server/channel/handlers/ScrollHandler.java +++ b/src/main/java/net/server/channel/handlers/ScrollHandler.java @@ -25,8 +25,12 @@ import client.Character; import client.Client; import client.Skill; import client.SkillFactory; -import client.inventory.*; +import client.inventory.Equip; import client.inventory.Equip.ScrollResult; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ModifyInventory; import client.inventory.manipulator.InventoryManipulator; import constants.id.ItemId; import constants.inventory.ItemConstants; @@ -37,7 +41,6 @@ import tools.PacketCreator; import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * @author Matze @@ -50,8 +53,8 @@ public final class ScrollHandler extends AbstractPacketHandler { if (c.tryacquireClient()) { try { p.readInt(); // whatever... - short slot = p.readShort(); - short dst = p.readShort(); + short scrollSlot = p.readShort(); + short equipSlot = p.readShort(); byte ws = (byte) p.readShort(); boolean whiteScroll = false; // white scroll being used? boolean legendarySpirit = false; // legendary spirit skill @@ -61,24 +64,21 @@ public final class ScrollHandler extends AbstractPacketHandler { ItemInformationProvider ii = ItemInformationProvider.getInstance(); Character chr = c.getPlayer(); - Equip toScroll = (Equip) chr.getInventory(InventoryType.EQUIPPED).getItem(dst); + Equip toScroll = (Equip) chr.getInventory(InventoryType.EQUIPPED).getItem(equipSlot); Skill LegendarySpirit = SkillFactory.getSkill(1003); - if (chr.getSkillLevel(LegendarySpirit) > 0 && dst >= 0) { + if (chr.getSkillLevel(LegendarySpirit) > 0 && equipSlot >= 0) { legendarySpirit = true; - toScroll = (Equip) chr.getInventory(InventoryType.EQUIP).getItem(dst); + toScroll = (Equip) chr.getInventory(InventoryType.EQUIP).getItem(equipSlot); } byte oldLevel = toScroll.getLevel(); byte oldSlots = toScroll.getUpgradeSlots(); Inventory useInventory = chr.getInventory(InventoryType.USE); - Item scroll = useInventory.getItem(slot); + Item scroll = useInventory.getItem(scrollSlot); Item wscroll = null; - if (ItemConstants.isCleanSlate(scroll.getItemId())) { - Map eqStats = ii.getEquipStats(toScroll.getItemId()); // clean slate issue found thanks to Masterrulax - if (eqStats == null || eqStats.get("tuc") == 0) { - announceCannotScroll(c, legendarySpirit); - return; - } + if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) { + announceCannotScroll(c, legendarySpirit); + return; } else if (!ItemConstants.isModifierScroll(scroll.getItemId()) && toScroll.getUpgradeSlots() < 1) { announceCannotScroll(c, legendarySpirit); // thanks onechord for noticing zero upgrade slots freezing Legendary Scroll UI return; @@ -103,11 +103,6 @@ public final class ScrollHandler extends AbstractPacketHandler { } } - if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) { - announceCannotScroll(c, legendarySpirit); - return; - } - Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, scroll.getItemId(), whiteScroll, 0, chr.isGM()); ScrollResult scrollSuccess = Equip.ScrollResult.FAIL; // fail if (scrolled == null) { @@ -141,7 +136,7 @@ public final class ScrollHandler extends AbstractPacketHandler { if (scrollSuccess == Equip.ScrollResult.CURSE) { if (!ItemId.isWeddingRing(toScroll.getItemId())) { mods.add(new ModifyInventory(3, toScroll)); - if (dst < 0) { + if (equipSlot < 0) { Inventory inv = chr.getInventory(InventoryType.EQUIPPED); inv.lockInventory(); @@ -174,7 +169,7 @@ public final class ScrollHandler extends AbstractPacketHandler { } c.sendPacket(PacketCreator.modifyInventory(true, mods)); chr.getMap().broadcastMessage(PacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit, whiteScroll)); - if (dst < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) { + if (equipSlot < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) { chr.equipChanged(); } } finally { diff --git a/src/main/java/server/ItemInformationProvider.java b/src/main/java/server/ItemInformationProvider.java index a06dab11cb..c12759719f 100644 --- a/src/main/java/server/ItemInformationProvider.java +++ b/src/main/java/server/ItemInformationProvider.java @@ -22,9 +22,16 @@ package server; import client.Character; -import client.*; +import client.Client; +import client.Job; +import client.Skill; +import client.SkillFactory; import client.autoban.AutobanFactory; -import client.inventory.*; +import client.inventory.Equip; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.WeaponType; import config.YamlConfig; import constants.id.ItemId; import constants.inventory.EquipSlot; @@ -35,19 +42,36 @@ import constants.skills.NightWalker; import net.server.Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import provider.*; +import provider.Data; +import provider.DataDirectoryEntry; +import provider.DataFileEntry; +import provider.DataProvider; +import provider.DataProviderFactory; +import provider.DataTool; import provider.wz.WZFiles; import server.MakerItemFactory.MakerItemCreateEntry; import server.life.LifeFactory; import server.life.MonsterInformationProvider; -import tools.*; +import tools.DatabaseConnection; +import tools.PacketCreator; +import tools.Pair; +import tools.Randomizer; +import tools.StringUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; /** * @author Matze @@ -1025,9 +1049,16 @@ public class ItemInformationProvider { Issue with clean slate found thanks to Masterrulax Vicious added in the clean slate check thanks to Crypter (CrypterDEV) */ - public boolean canUseCleanSlate(Equip nEquip) { - Map eqstats = this.getEquipStats(nEquip.getItemId()); - return YamlConfig.config.server.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < (byte) (eqstats.get("tuc") + nEquip.getVicious()); + public boolean canUseCleanSlate(Equip equip) { + Map eqStats = getEquipStats(equip.getItemId()); + if (eqStats == null || eqStats.get("tuc") == 0 ) { + return false; + } + int totalUpgradeCount = eqStats.get("tuc"); + int freeUpgradeCount = equip.getUpgradeSlots(); + int viciousCount = equip.getVicious(); + int appliedScrollCount = equip.getLevel(); + return freeUpgradeCount + appliedScrollCount < totalUpgradeCount + viciousCount; } public Item scrollEquipWithId(Item equip, int scrollId, boolean usingWhiteScroll, int vegaItemId, boolean isGM) { From a7931c3e4d2f4120d3fd04eff90d791542c3901b Mon Sep 17 00:00:00 2001 From: Silwhoon Date: Mon, 5 Feb 2024 16:03:41 +0000 Subject: [PATCH 02/55] New characters now utilise the MakeCharInfo.img data in the WZ files --- .../java/client/creator/CharacterFactory.java | 33 +++-- .../java/client/creator/MakeCharInfo.java | 140 ++++++++++++++++++ .../client/creator/MakeCharInfoHandler.java | 41 +++++ .../creator/novice/BeginnerCreator.java | 3 +- .../client/creator/novice/LegendCreator.java | 3 +- .../creator/novice/NoblesseCreator.java | 3 +- .../handlers/login/CreateCharHandler.java | 40 +---- 7 files changed, 204 insertions(+), 59 deletions(-) create mode 100644 src/main/java/client/creator/MakeCharInfo.java create mode 100644 src/main/java/client/creator/MakeCharInfoHandler.java diff --git a/src/main/java/client/creator/CharacterFactory.java b/src/main/java/client/creator/CharacterFactory.java index 1b26d1dc48..197d0a9e51 100644 --- a/src/main/java/client/creator/CharacterFactory.java +++ b/src/main/java/client/creator/CharacterFactory.java @@ -47,19 +47,19 @@ public abstract class CharacterFactory { return -1; } - Character newchar = Character.getDefault(c); - newchar.setWorld(c.getWorld()); - newchar.setSkinColor(SkinColor.getById(skin)); - newchar.setGender(gender); - newchar.setName(name); - newchar.setHair(hair); - newchar.setFace(face); + Character newCharacter = Character.getDefault(c); + newCharacter.setWorld(c.getWorld()); + newCharacter.setSkinColor(SkinColor.getById(skin)); + newCharacter.setGender(gender); + newCharacter.setName(name); + newCharacter.setHair(hair); + newCharacter.setFace(face); - newchar.setLevel(recipe.getLevel()); - newchar.setJob(recipe.getJob()); - newchar.setMapId(recipe.getMap()); + newCharacter.setLevel(recipe.getLevel()); + newCharacter.setJob(recipe.getJob()); + newCharacter.setMapId(recipe.getMap()); - Inventory equipped = newchar.getInventory(InventoryType.EQUIPPED); + Inventory equipped = newCharacter.getInventory(InventoryType.EQUIPPED); ItemInformationProvider ii = ItemInformationProvider.getInstance(); int top = recipe.getTop(), bottom = recipe.getBottom(), shoes = recipe.getShoes(), weapon = recipe.getWeapon(); @@ -88,12 +88,17 @@ public abstract class CharacterFactory { equipped.addItemFromDB(eq_weapon.copy()); } - if (!newchar.insertNewChar(recipe)) { + if (!MakeCharInfoHandler.isNewCharacterValid(newCharacter)) { + log.warn("Owner from account {} tried to packet edit in character creation", c.getAccountName()); return -2; } - c.sendPacket(PacketCreator.addNewCharEntry(newchar)); - Server.getInstance().createCharacterEntry(newchar); + if (!newCharacter.insertNewChar(recipe)) { + return -2; + } + c.sendPacket(PacketCreator.addNewCharEntry(newCharacter)); + + Server.getInstance().createCharacterEntry(newCharacter); Server.getInstance().broadcastGMMessage(c.getWorld(), PacketCreator.sendYellowTip("[New Char]: " + c.getAccountName() + " has created a new character with IGN " + name)); log.info("Account {} created chr with name {}", c.getAccountName(), name); diff --git a/src/main/java/client/creator/MakeCharInfo.java b/src/main/java/client/creator/MakeCharInfo.java new file mode 100644 index 0000000000..40956103f1 --- /dev/null +++ b/src/main/java/client/creator/MakeCharInfo.java @@ -0,0 +1,140 @@ +package client.creator; + +import client.Character; +import client.Job; +import client.inventory.InventoryType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import provider.Data; +import provider.DataTool; + +import java.util.ArrayList; +import java.util.List; + +public class MakeCharInfo { + private static final Logger log = LoggerFactory.getLogger(MakeCharInfo.class); + private static final String FACE_ID = "0"; + private static final String HAIR_ID = "1"; + private static final String HAIR_COLOR_ID = "2"; + private static final String SKIN_ID = "3"; + private static final String TOP_ID = "4"; + private static final String BOTTOM_ID = "5"; + private static final String SHOE_ID = "6"; + private static final String WEAPON_ID = "7"; + + private final List charFaces = new ArrayList<>(); + private final List charHairs = new ArrayList<>(); + private final List charHairColors = new ArrayList<>(); + private final List charSkins = new ArrayList<>(); + private final List charTops = new ArrayList<>(); + private final List charBottoms = new ArrayList<>(); + private final List charShoes = new ArrayList<>(); + private final List charWeapons = new ArrayList<>(); + + public MakeCharInfo(Data charInfoData) { + for (Data data : charInfoData.getChildren()) { + switch (data.getName()) { + case FACE_ID -> { + for (Data faceData : data) { + charFaces.add(DataTool.getInt(faceData)); + } + } + case HAIR_ID -> { + for (Data hairData : data) { + charHairs.add(DataTool.getInt(hairData)); + } + } + case HAIR_COLOR_ID -> { + for (Data hairColorData : data) { + charHairColors.add(DataTool.getInt(hairColorData)); + } + } + case SKIN_ID -> { + for (Data skinData : data) { + charSkins.add(DataTool.getInt(skinData)); + } + } + case TOP_ID -> { + for (Data topData : data) { + charTops.add(DataTool.getInt(topData)); + } + } + case BOTTOM_ID -> { + for (Data bottomData : data) { + charBottoms.add(DataTool.getInt(bottomData)); + } + } + case SHOE_ID -> { + for (Data shoeData : data) { + charShoes.add(DataTool.getInt(shoeData)); + } + } + case WEAPON_ID -> { + for (Data weaponData : data) { + charWeapons.add(DataTool.getInt(weaponData)); + } + } + default -> log.error("Unhandled node inside MakeCharInfo.img.xml: '" + data.getName() + "'"); + } + } + } + + public boolean verifyFaceId(int id) { + return this.charFaces.contains(id); + } + + public boolean verifyHairId(int id) { + if (id % 10 != 0) { + return this.charHairs.contains(id - (id % 10)); + } + return this.charHairs.contains(id); + } + + public boolean verifyHairColorId(int id) { + return this.charHairColors.contains(id % 10); + } + + public boolean verifySkinId(int id) { + return this.charSkins.contains(id); + } + + public boolean verifyTopId(int id) { + return this.charTops.contains(id); + } + + public boolean verifyBottomId(int id) { + return this.charBottoms.contains(id); + } + + public boolean verifyShoeId(int id) { + return this.charShoes.contains(id); + } + + public boolean verifyWeaponId(int id) { + return this.charWeapons.contains(id); + } + + public boolean verifyCharacter(Character character) { + if (!verifyFaceId(character.getFace())) return false; + if (!verifyHairId(character.getHair())) return false; + if (!verifyHairColorId(character.getHair())) return false; + if (!verifySkinId(character.getSkinColor().getId())) return false; + + // Here we only verify the equipment if the character that's being created is of type 'Beginner' + // This is because when the Maple Life A or Maple Life B items are used, the client does not send any data + // regarding what equipment the character should be wearing (as it's all handled server-side) + Job characterJob = character.getJob(); + if (characterJob == Job.BEGINNER || characterJob == Job.NOBLESSE || characterJob == Job.LEGEND) { + if (!verifyTopId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -5).getItemId())) + return false; + if (!verifyBottomId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -6).getItemId())) + return false; + if (!verifyShoeId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -7).getItemId())) + return false; + if (!verifyWeaponId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -11).getItemId())) + return false; + } + + return true; + } +} diff --git a/src/main/java/client/creator/MakeCharInfoHandler.java b/src/main/java/client/creator/MakeCharInfoHandler.java new file mode 100644 index 0000000000..1393018299 --- /dev/null +++ b/src/main/java/client/creator/MakeCharInfoHandler.java @@ -0,0 +1,41 @@ +package client.creator; + +import client.Character; +import provider.Data; +import provider.DataProviderFactory; +import provider.wz.WZFiles; + +public class MakeCharInfoHandler { + private static final MakeCharInfo charFemale; + private static final MakeCharInfo charMale; + private static final MakeCharInfo orientCharFemale; + private static final MakeCharInfo orientCharMale; + private static final MakeCharInfo premiumCharFemale; + private static final MakeCharInfo premiumCharMale; + + static { + Data data = DataProviderFactory.getDataProvider(WZFiles.ETC).getData("MakeCharInfo.img"); + charFemale = new MakeCharInfo(data.getChildByPath("Info/CharFemale")); + charMale = new MakeCharInfo(data.getChildByPath("Info/CharMale")); + orientCharFemale = new MakeCharInfo(data.getChildByPath("OrientCharFemale")); + orientCharMale = new MakeCharInfo(data.getChildByPath("OrientCharMale")); + premiumCharFemale = new MakeCharInfo(data.getChildByPath("PremiumCharFemale")); + premiumCharMale = new MakeCharInfo(data.getChildByPath("PremiumCharMale")); + } + + private static MakeCharInfo getMakeCharInfo(Character character) { + return switch (character.getJob()) { + case BEGINNER, WARRIOR, MAGICIAN, BOWMAN, THIEF, PIRATE -> character.isMale() ? charMale : charFemale; + case NOBLESSE -> character.isMale() ? premiumCharMale : premiumCharFemale; + case LEGEND -> character.isMale() ? orientCharMale : orientCharFemale; + default -> null; + }; + } + + public static boolean isNewCharacterValid(Character character) { + MakeCharInfo makeCharInfo = getMakeCharInfo(character); + if (makeCharInfo == null) return false; + + return makeCharInfo.verifyCharacter(character); + } +} diff --git a/src/main/java/client/creator/novice/BeginnerCreator.java b/src/main/java/client/creator/novice/BeginnerCreator.java index 5271025bbf..986eb66fda 100644 --- a/src/main/java/client/creator/novice/BeginnerCreator.java +++ b/src/main/java/client/creator/novice/BeginnerCreator.java @@ -43,7 +43,6 @@ public class BeginnerCreator extends CharacterFactory { } public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) { - int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon)); - return status; + return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon)); } } diff --git a/src/main/java/client/creator/novice/LegendCreator.java b/src/main/java/client/creator/novice/LegendCreator.java index 75c5226c5a..638bc5a8cd 100644 --- a/src/main/java/client/creator/novice/LegendCreator.java +++ b/src/main/java/client/creator/novice/LegendCreator.java @@ -43,7 +43,6 @@ public class LegendCreator extends CharacterFactory { } public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) { - int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon)); - return status; + return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon)); } } diff --git a/src/main/java/client/creator/novice/NoblesseCreator.java b/src/main/java/client/creator/novice/NoblesseCreator.java index 2c51e7c898..cb04459622 100644 --- a/src/main/java/client/creator/novice/NoblesseCreator.java +++ b/src/main/java/client/creator/novice/NoblesseCreator.java @@ -43,7 +43,6 @@ public class NoblesseCreator extends CharacterFactory { } public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) { - int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon)); - return status; + return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon)); } } diff --git a/src/main/java/net/server/handlers/login/CreateCharHandler.java b/src/main/java/net/server/handlers/login/CreateCharHandler.java index 9a62af04ed..853e08e67a 100644 --- a/src/main/java/net/server/handlers/login/CreateCharHandler.java +++ b/src/main/java/net/server/handlers/login/CreateCharHandler.java @@ -25,43 +25,14 @@ import client.Client; import client.creator.novice.BeginnerCreator; import client.creator.novice.LegendCreator; import client.creator.novice.NoblesseCreator; -import constants.id.ItemId; import net.AbstractPacketHandler; import net.packet.InPacket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import tools.PacketCreator; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - public final class CreateCharHandler extends AbstractPacketHandler { - private static final Logger log = LoggerFactory.getLogger(CreateCharHandler.class); - - private final static Set IDs = new HashSet<>(Arrays.asList( - ItemId.SWORD, ItemId.HAND_AXE, ItemId.WOODEN_CLUB, ItemId.BASIC_POLEARM,// weapons - ItemId.WHITE_UNDERSHIRT, ItemId.UNDERSHIRT, ItemId.GREY_TSHIRT, ItemId.WHITE_TUBETOP, ItemId.YELLOW_TSHIRT, - ItemId.GREEN_TSHIRT, ItemId.RED_STRIPED_TOP, ItemId.SIMPLE_WARRIOR_TOP,// bottom - ItemId.BLUE_JEAN_SHORTS, ItemId.BROWN_COTTON_SHORTS, ItemId.RED_MINISKIRT, ItemId.INDIGO_MINISKIRT, - ItemId.SIMPLE_WARRIOR_PANTS, // top - ItemId.RED_RUBBER_BOOTS, ItemId.LEATHER_SANDALS, ItemId.YELLOW_RUBBER_BOOTS, ItemId.BLUE_RUBBER_BOOTS, - ItemId.AVERAGE_MUSASHI_SHOES,// shoes - ItemId.BLACK_TOBEN, ItemId.ZETA, ItemId.BLACK_REBEL, ItemId.BLACK_BUZZ, ItemId.BLACK_SAMMY, - ItemId.BLACK_EDGY, ItemId.BLACK_CONNIE,// hair - ItemId.MOTIVATED_LOOK_M, ItemId.PERPLEXED_STARE, ItemId.LEISURE_LOOK_M, ItemId.MOTIVATED_LOOK_F, - ItemId.FEARFUL_STARE_M, ItemId.LEISURE_LOOK_F, ItemId.FEARFUL_STARE_F, ItemId.PERPLEXED_STARE_HAZEL, - ItemId.LEISURE_LOOK_HAZEL, ItemId.MOTIVATED_LOOK_AMETHYST, ItemId.MOTIVATED_LOOK_BLUE //face - //#NeverTrustStevenCode - )); - - private static boolean isLegal(Integer toCompare) { - return IDs.contains(toCompare); - } - @Override - public final void handlePacket(InPacket p, Client c) { + public void handlePacket(InPacket p, Client c) { String name = p.readString(); int job = p.readInt(); int face = p.readInt(); @@ -76,15 +47,6 @@ public final class CreateCharHandler extends AbstractPacketHandler { int weapon = p.readInt(); int gender = p.readByte(); - int[] items = new int[]{weapon, top, bottom, shoes, hair, face}; - for (int item : items) { - if (!isLegal(item)) { - log.warn("Owner from account {} tried to packet edit in chr creation", c.getAccountName()); - c.disconnect(true, false); - return; - } - } - int status; switch (job) { case 0: // Knights of Cygnus From 6f68f4edfd5438e73f6e4ecce9dfd678eb2f0cd1 Mon Sep 17 00:00:00 2001 From: Silwhoon Date: Mon, 5 Feb 2024 17:00:18 +0000 Subject: [PATCH 03/55] Addressed nitpicks and removed unused item ID constants --- .../java/client/creator/CharacterFactory.java | 2 +- .../java/client/creator/MakeCharInfo.java | 20 +++++----- ...andler.java => MakeCharInfoValidator.java} | 2 +- src/main/java/constants/id/ItemId.java | 40 ------------------- 4 files changed, 12 insertions(+), 52 deletions(-) rename src/main/java/client/creator/{MakeCharInfoHandler.java => MakeCharInfoValidator.java} (97%) diff --git a/src/main/java/client/creator/CharacterFactory.java b/src/main/java/client/creator/CharacterFactory.java index 197d0a9e51..920fc891a9 100644 --- a/src/main/java/client/creator/CharacterFactory.java +++ b/src/main/java/client/creator/CharacterFactory.java @@ -88,7 +88,7 @@ public abstract class CharacterFactory { equipped.addItemFromDB(eq_weapon.copy()); } - if (!MakeCharInfoHandler.isNewCharacterValid(newCharacter)) { + if (!MakeCharInfoValidator.isNewCharacterValid(newCharacter)) { log.warn("Owner from account {} tried to packet edit in character creation", c.getAccountName()); return -2; } diff --git a/src/main/java/client/creator/MakeCharInfo.java b/src/main/java/client/creator/MakeCharInfo.java index 40956103f1..5447c958ea 100644 --- a/src/main/java/client/creator/MakeCharInfo.java +++ b/src/main/java/client/creator/MakeCharInfo.java @@ -8,8 +8,8 @@ import org.slf4j.LoggerFactory; import provider.Data; import provider.DataTool; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public class MakeCharInfo { private static final Logger log = LoggerFactory.getLogger(MakeCharInfo.class); @@ -22,14 +22,14 @@ public class MakeCharInfo { private static final String SHOE_ID = "6"; private static final String WEAPON_ID = "7"; - private final List charFaces = new ArrayList<>(); - private final List charHairs = new ArrayList<>(); - private final List charHairColors = new ArrayList<>(); - private final List charSkins = new ArrayList<>(); - private final List charTops = new ArrayList<>(); - private final List charBottoms = new ArrayList<>(); - private final List charShoes = new ArrayList<>(); - private final List charWeapons = new ArrayList<>(); + private final Set charFaces = new HashSet<>(); + private final Set charHairs = new HashSet<>(); + private final Set charHairColors = new HashSet<>(); + private final Set charSkins = new HashSet<>(); + private final Set charTops = new HashSet<>(); + private final Set charBottoms = new HashSet<>(); + private final Set charShoes = new HashSet<>(); + private final Set charWeapons = new HashSet<>(); public MakeCharInfo(Data charInfoData) { for (Data data : charInfoData.getChildren()) { diff --git a/src/main/java/client/creator/MakeCharInfoHandler.java b/src/main/java/client/creator/MakeCharInfoValidator.java similarity index 97% rename from src/main/java/client/creator/MakeCharInfoHandler.java rename to src/main/java/client/creator/MakeCharInfoValidator.java index 1393018299..4ae333e8a3 100644 --- a/src/main/java/client/creator/MakeCharInfoHandler.java +++ b/src/main/java/client/creator/MakeCharInfoValidator.java @@ -5,7 +5,7 @@ import provider.Data; import provider.DataProviderFactory; import provider.wz.WZFiles; -public class MakeCharInfoHandler { +public class MakeCharInfoValidator { private static final MakeCharInfo charFemale; private static final MakeCharInfo charMale; private static final MakeCharInfo orientCharFemale; diff --git a/src/main/java/constants/id/ItemId.java b/src/main/java/constants/id/ItemId.java index 95ad8f2ec7..2624966f0b 100644 --- a/src/main/java/constants/id/ItemId.java +++ b/src/main/java/constants/id/ItemId.java @@ -95,46 +95,6 @@ public class ItemId { public static final int BEGINNERS_GUIDE = 4161001; public static final int LEGENDS_GUIDE = 4161048; public static final int NOBLESSE_GUIDE = 4161047; - public static final int SWORD = 1302000; // Weapon - public static final int HAND_AXE = 1312004; - public static final int WOODEN_CLUB = 1322005; - public static final int BASIC_POLEARM = 1442079; - public static final int WHITE_UNDERSHIRT = 1040002; // Top - public static final int UNDERSHIRT = 1040006; - public static final int GREY_TSHIRT = 1040010; - public static final int WHITE_TUBETOP = 1041002; - public static final int YELLOW_TSHIRT = 1041006; - public static final int GREEN_TSHIRT = 1041010; - public static final int RED_STRIPED_TOP = 1041011; - public static final int SIMPLE_WARRIOR_TOP = 1042167; - public static final int BLUE_JEAN_SHORTS = 1060002; // Bottom - public static final int BROWN_COTTON_SHORTS = 1060006; - public static final int RED_MINISKIRT = 1061002; - public static final int INDIGO_MINISKIRT = 1061008; - public static final int SIMPLE_WARRIOR_PANTS = 1062115; - public static final int RED_RUBBER_BOOTS = 1072001; - public static final int LEATHER_SANDALS = 1072005; - public static final int YELLOW_RUBBER_BOOTS = 1072037; - public static final int BLUE_RUBBER_BOOTS = 1072038; - public static final int AVERAGE_MUSASHI_SHOES = 1072383; - public static final int BLACK_TOBEN = 30000; // Hair - public static final int ZETA = 30010; - public static final int BLACK_REBEL = 30020; - public static final int BLACK_BUZZ = 30030; - public static final int BLACK_SAMMY = 31000; - public static final int BLACK_EDGY = 31040; - public static final int BLACK_CONNIE = 31050; - public static final int MOTIVATED_LOOK_M = 20000; // Face - public static final int PERPLEXED_STARE = 20001; - public static final int LEISURE_LOOK_M = 20002; - public static final int MOTIVATED_LOOK_F = 21000; - public static final int FEARFUL_STARE_M = 21001; - public static final int LEISURE_LOOK_F = 21002; - public static final int FEARFUL_STARE_F = 21201; - public static final int PERPLEXED_STARE_HAZEL = 20401; - public static final int LEISURE_LOOK_HAZEL = 20402; - public static final int MOTIVATED_LOOK_AMETHYST = 21700; - public static final int MOTIVATED_LOOK_BLUE = 20100; // Warrior public static final int RED_HWARANG_SHIRT = 1040021; From f9b328b432c9ed3defdd176f2eae994e0afff76f Mon Sep 17 00:00:00 2001 From: Silwhoon Date: Mon, 5 Feb 2024 17:58:43 +0000 Subject: [PATCH 04/55] Mob Skills now have a success rate This was only used for MAGIC_IMMUNITY and WEAPON_IMMUNITY for some reason? A lot of other MobSkills also have a 'prop' node in the WZ files such as; Darkness and Seduce --- src/main/java/server/life/MobSkill.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/server/life/MobSkill.java b/src/main/java/server/life/MobSkill.java index 6f222a5446..4b47f1ad74 100644 --- a/src/main/java/server/life/MobSkill.java +++ b/src/main/java/server/life/MobSkill.java @@ -190,6 +190,11 @@ public class MobSkill { // TODO: avoid output argument banishPlayersOutput public void applyEffect(Character player, Monster monster, boolean skill, List banishPlayersOutput) { + // See if the MobSkill is successful before doing anything + if (!makeChanceResult()) { + return; + } + Disease disease = null; Map stats = new EnumMap<>(MonsterStatus.class); List reflection = new ArrayList<>(); @@ -213,12 +218,12 @@ public class MobSkill { case REVERSE_INPUT -> disease = Disease.CONFUSE; case UNDEAD -> disease = Disease.ZOMBIFY; case PHYSICAL_IMMUNE -> { - if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) { + if (!monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) { stats.put(MonsterStatus.WEAPON_IMMUNITY, x); } } case MAGIC_IMMUNE -> { - if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) { + if (!monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) { stats.put(MonsterStatus.MAGIC_IMMUNITY, x); } } From 0d684c1400a90b1ce261d0309d7cd0bdaf758f18 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Sat, 2 Mar 2024 02:19:59 +0800 Subject: [PATCH 05/55] NPC: Fix type casting error for `gainMeso()` method Number type values might be passed into the `gainMeso()` method in js scripts, and thus it expects a `gainMeso(Double gain)` method in the `NPCConversationManager` class. --- src/main/java/scripting/npc/NPCConversationManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/scripting/npc/NPCConversationManager.java b/src/main/java/scripting/npc/NPCConversationManager.java index 0fc4f94efe..bec34cb939 100644 --- a/src/main/java/scripting/npc/NPCConversationManager.java +++ b/src/main/java/scripting/npc/NPCConversationManager.java @@ -278,6 +278,10 @@ public class NPCConversationManager extends AbstractPlayerInteraction { getPlayer().gainMeso(gain); } + public void gainMeso(Double gain) { + getPlayer().gainMeso(gain.intValue()); + } + public void gainExp(int gain) { getPlayer().gainExp(gain, true, true); } From f61fee829aa119311fb765f9ae46c2a8af5d6f76 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 11:38:40 +0200 Subject: [PATCH 06/55] Upgrade Java version and dependencies --- .github/workflows/run-build.yml | 6 +++--- pom.xml | 35 +++++++++++++++++---------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/workflows/run-build.yml b/.github/workflows/run-build.yml index 3d8a42ac71..28d370fb03 100644 --- a/.github/workflows/run-build.yml +++ b/.github/workflows/run-build.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' - distribution: 'temurin' + java-version: '21' + distribution: 'corretto' - name: Build with Maven (compile -> test -> package) run: mvn -B package --file pom.xml diff --git a/pom.xml b/pom.xml index 93823a00c5..27c4980487 100644 --- a/pom.xml +++ b/pom.xml @@ -13,28 +13,29 @@ UTF-8 - 17 + 21 ${java.version} ${java.version} net.server.Server - 3.0.0-M9 - 3.3.0 - 3.5.0 + 3.2.5 + 3.4.1 + 3.7.1 - 1.7.36 - 2.20.0 - 22.3.1 - 4.1.89.Final - 1.15 + 2.0.13 + 2.23.1 + 23.0.4 + 24.0.1 + 4.1.109.Final + 1.17 1.0 - 5.0.1 - 8.0.32 - 3.37.1 - 5.9.2 - 5.1.1 + 5.1.0 + 8.4.0 + 3.45.1 + 5.10.2 + 5.11.0 @@ -107,7 +108,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl ${log4j.version} @@ -115,12 +116,12 @@ org.graalvm.js js - ${graalvm.version} + ${graalvm-js.version} org.graalvm.js js-scriptengine - ${graalvm.version} + ${graalvm-js-scriptengine.version} From d5be9130aa8af60333351f35fba72c3fc638b39e Mon Sep 17 00:00:00 2001 From: Ponk <81079018+P0nk@users.noreply.github.com> Date: Thu, 9 May 2024 11:53:54 +0200 Subject: [PATCH 07/55] Add bug report issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..8ca2301e30 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description (in English) of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. From 21ecfeef17fd4bc9f6f1b4b2f9af49ebd2d8aa35 Mon Sep 17 00:00:00 2001 From: Ponk <81079018+P0nk@users.noreply.github.com> Date: Thu, 9 May 2024 12:20:08 +0200 Subject: [PATCH 08/55] Add pull request template --- .github/pull_request_template.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..1ec6218694 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +## Description + + +## Checklist before requesting a review +- [ ] I have performed a self-review of my code +- [ ] I have tested my changes +- [ ] I have added unit tests that prove my changes work + +## Screenshots (if appropriate) + From 9c1406f75d59cb265e71155f9e7da007f11d65f5 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 12:33:50 +0200 Subject: [PATCH 09/55] Update pull request template --- .github/pull_request_template.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1ec6218694..55a2ed9d3e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,9 +2,10 @@ ## Checklist before requesting a review + - [ ] I have performed a self-review of my code - [ ] I have tested my changes - [ ] I have added unit tests that prove my changes work -## Screenshots (if appropriate) - +## Screenshots + From c5089881b36b775eb2c7b93d56bc9b1916c82656 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 14:23:47 +0200 Subject: [PATCH 10/55] Tidy pom mvn tidy:pom --- pom.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 27c4980487..4d4de5cb6d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,13 +1,10 @@ - + 4.0.0 cosmic-maplestory Cosmic 1.0-SNAPSHOT - jar From c7b9e0eee3b34e12803922a7211999c9ab4b977c Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 21:11:53 +0200 Subject: [PATCH 11/55] Major rewrite of README --- README.md | 358 ++++++++++++++++-------------------------------------- 1 file changed, 108 insertions(+), 250 deletions(-) diff --git a/README.md b/README.md index e1f5ce03bf..ba76cbce64 100644 --- a/README.md +++ b/README.md @@ -1,303 +1,161 @@ -# Cosmic - MapleStory v83 server +# Cosmic +Cosmic is a server emulator for Global MapleStory (GMS) version 83. ## Introduction -Cosmic launched as a successor to HeavenMS on March 21st 2021. -HeavenMS is archived, ie. it receives no further updates. This project aims to continue its development; mainly by improving code quality and make getting into PS development as easy as possible. +Cosmic launched on March 2021. It is based on code from a long line of server sources spanning over a decade - starting with OdinMS (2008) and ending with HeavenMS (2019). -This is an open source project. Anyone may contribute by opening a pull request. +This is mainly a Java based project, but there are also a bunch of scripts written in JavaScript. -Only the server side is maintained. The client is directly copied from HeavenMS. +Head developer and maintainer: __Ponk__.\ +Contributors: a lot of people over the years, and hopefully more to come. Big thanks to everyone who has contributed so far! +Join the Discord server where most of the discussions take place: https://discord.gg/JU5aQapVZK -Beware - ***This server emulator is not production ready.*** -It can be useful for testing things locally or for trying out ideas, but launching a new private server based on this and opening it up to the public -without knowing what you're doing is not recommended. +### Goals +What we are working towards. +* __Vanilla gameplay__ - stay as close to the original game as possible (within reason). +* __Ease of use__ - getting started should be frictionless and contributing to the project straightforward. +* __Reduce technical debt__ - making changes should be easy without causing unintended side effects. +* __Modern tools & technologies__ - stay appealing by continuously improving the code and the project as a whole. +### Non-goals +Explicitly excluded from the scope of the project. +* __Custom gameplay features__ - existing custom features will be removed over time and new ones are unlikely to be added. +* __Client development__ - this project is focused on the server. Please go elsewhere for client related questions. +* __Public server__ - there will not be an official Cosmic server open to the public. Feel free to launch your own server __at your own risk__. No support will be provided. -## Development information +## Project setup -### Status (updated 2022-10-16) +### Contribute +You may contribute to the project in various ways, mainly through GitHub: +* Providing improvements to the code through a [Pull Request](https://github.com/P0nk/Cosmic/pulls) from your own fork. +* Reporting a bug by creating an [Issue](https://github.com/P0nk/Cosmic/issues). +* Providing information to existing issues or reviewing pull requests that others have made. -Development is currently **sporadic**. +### Continuous integration +A GitHub Actions pipeline is set up to run the build automatically when a new pull request is opened or commits are pushed to an existing one. This ensures that the code compiles and all the tests pass. -My time is very limited nowadays, but I try to keep up with the submitted pull requests. I may submit some stuff of my own, once in a while. +Once a pull request is merged, a tag with the new version is automatically created. -### Ways to contribute - -* Submit a Pull Request (fork -> commit -> PR). If you don't know where to start, have a look at the issues on GitHub. -* Report a bug (preferably as an Issue on GitHub, as reports on Discord may be forgotten or lost) -* Spread the word about Cosmic - -### Working with GitHub - -Anyone with a GitHub account can contribute by making some changes in a branch and opening up a PR. - -All activity on the GitHub repo (opening PR, commenting, creating issue, etc.) is automatically pushed (via webhook) to a public Discord channel for visibility. - -Issues is the main place where bugs, issues or general improvements are tracked. Feel free to submit a new issue, but please keep it in English. By providing a good description, you increase the chance of a bug being fixed. - -Tasks (past, present and future) are kept in the Cosmic project, which you get to via the "Projects" tab. This gives you an idea of where the project is moving. +### Discord integration +Most GitHub activity is pushed to a Discord channel for visibility. This works by leveraging a webhook. The activity includes (but is not limited to): merged commits, created PRs, comments, and new tags. ### Versioning - -The project follows the [SemVer](https://semver.org/) versioning scheme using git tags. -As a pull request gets merged, a new version is automatically created. - -Bug fixes result in bumped patch version: 1.2.__3__ -> 1.2.__4__ - -General improvements result in bumped minor version: 1.__2__.3 -> 1.__3__.3 - -Major changes result in bumped major version: __1__.2.3 -> __2__.2.3 - -### Cosmic - -- GitHub: https://github.com/P0nk/Cosmic -- Discord: https://discord.gg/JU5aQapVZK - -### HeavenMS -- GitHub: https://github.com/ronancpl/HeavenMS -- Discord: https://discord.gg/Q7wKxHX - -## Tools / downloads -* **Java 17 SDK** - Needed to compile and run Java code. Install manually or through IntelliJ depending on how you prefer to launch the server. Not required for launching with Docker. - * Link: https://jdk.java.net/17/ - - -* **IntelliJ IDEA** - Java IDE and your main tool for working with the source code. Community edition is good enough. - * Link: https://www.jetbrains.com/idea/ - - -* **MySQL Community Server 8** - Database for game data. - * Link: https://dev.mysql.com/downloads/mysql/ - - -* **MySQL Workbench 8** - Client for interacting with the database. Other clients do exist. - * Link: https://dev.mysql.com/downloads/workbench/ - - -* **Docker Desktop** (optional) - For launching the game locally with less hassle. - * Link: https://www.docker.com/products/docker-desktop - - -* **Client files and general tools** - * Link: https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT?usp=sharing - * This is Ponk's own Google Drive, similar to how Ronan provides files for HeavenMS. - - -### MapleStory client - -- Latest localhost client: https://hostr.co/amuX5SLeeVZx - -**Important note about localhost clients**: these executables are red-flagged by antivirus tools as __potentially malicious software__, -this happens due to the reverse engineering methods that were applied onto these software artifacts. -Those depicted here have been put to use for years already and posed no harm so far, so they are soundly assumed to be safe. - +The project follows the [semantic versioning](https://semver.org/) scheme using git tags. +* *Bug fixes* are treated as PATCH: 1.2.__3__ -> 1.2.__4__ +* *General changes or improvements* are treated as MINOR: 1.__2__.3 -> 1.__3.0__ +* *Major changes* are treated as MAJOR: __1__.2.3 -> __2.0.0__ ## Getting started -The localhost MapleStory client needs to be installed, as well as the server that will host the game. +Follow along as I go through the steps to play the game on your local computer from start to finish. I won't go into extreme detail, so if you don't have prior experience with Java or git, you might struggle. -### Installing the client +### 1 - Database +The database contains game data used by the server such as accounts and inventory items. It is required for the server to start. -1. Install MapleStory with "MapleGlobal-v83-setup.exe" in your folder of choice (e.g. "C:\Nexon\MapleStory") and follow their instructions. -2. Once done, erase these files: "HShield" (folder), "ASPLnchr.exe", "MapleStory.exe" and "Patcher.exe". -3. Extract into the client folder the "HeavenMS-localhost-WINDOW.exe" (from now on referred to as "localhost.exe") from the provided link. -4. Overwrite the original WZ files with the ones provided on the Google Drive: "CosmicWZ-v1-2021.05.10.zip" - - This is currently identical to the latest HeavenMS WZ files (except for the file name): "commit397_wz-20210321T173600Z-001.zip" +You will start by installing the database server and client, and then run some scripts to prepare it for the server. -#### Editing localhost IP target +#### Steps -If you are not using "localhost" as the target IP on the server's config file, you will need to HEX-EDIT localhost.exe to fetch your IP. Track down all IP locations by searching for "Type: String" "127.0.0.1", and applying the changes wherever it fits. +1. Download and install [MySQL Community Server 8+](https://dev.mysql.com/downloads/mysql/). You will have to set a root password, make sure you don't lose it because you will need it later. +2. Download and install [HeidiSQL](https://www.heidisql.com/download.php). +3. Open HeidiSQL and connect to the database ("New" -> "Session in root folder" -> fill in password -> "Open"). +4. Run all four scripts located in database/sql in order. Starting with ``1-db_database.sql`` and ending with ``4-db-admin.sql``. In HeidiSQL: "File" -> "Run SQL File...". +5. The database is ready! -To hex-edit, install the Neo Hex Editor from "free-hex-editor-neo.exe" and follow their instructions. Once done, open localhost.exe for editing and overwrite the IP values under the 3 addresses. Save the changes and exit the editor. +### 2 - Server +You will start by cloning the repository, then configure the database properties and lastly start the server. -(TODO: find suitable alternative to Neo Hex Editor) +#### Prerequisites +* Java 21+ (I recommend [Amazon Corretto](https://aws.amazon.com/corretto)) +* IDE (I recommend [IntelliJ IDEA](https://www.jetbrains.com/idea/)) -#### Testing the localhost +#### Steps -Open the "localhost.exe" client. -If by any means the program did not open, and checking the server log your ping has been listened by the server -and you are using Windows 8, 10 or 11, it is probably some compatibility issue. +1. Clone Cosmic into a new project. In IntelliJ, you would create a new project from version control. +2. Open _config.yaml_. Find "DB_PASS" and set it to your database root user password. +3. Start the server. The main method is located in `net.server.Server`. +4. If you see "Cosmic is now online" in the console, it means the server is online and ready to serve traffic. Yay! -In some cases it helps to spam click the exe a few times (2-3 times usually works for me on W10). +Support for Docker is also provided out of the box, as an alternative to running straight in the IDE. If you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed it's as easy as running `docker compose up`. Making changes becomes a bit more tedious though as you have to rebuild the server image via `docker compose up --build`. -In that case, extract "lolwut.exe" from "lolwut-v0.01.rar" and place it on the MapleStory client folder ("C:\Nexon\MapleStory"). -Your "localhost.exe" property settings must follow these: +### 3 - Client +You will start by installing the game with the old installer, then overwrite some WZ files with our custom ones, and lastly get the localhost executable in place. -Note: "lolwut.exe" is currently not available in the Google Drive. +#### Steps -* Run in compatibility mode: Windows 7; -* Unchecked reduced color mode; -* 640 x 480 resolution; -* Unchecked disable display on high DPI settings; -* Run as an administrator; -* Opening "lolwut.exe", use Fraysa's method. +1. Download _MapleGlobal-v83-setup.exe_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). This is the official installer from back then. +2. Install it in a directory of your choice. +3. Delete the following files from the installation directory: _HShield_ (entire directory), _ASPLnchr.exe_, _MapleStory.exe_, and _Patcher.exe_. +4. Download _CosmicWZ-v1-2021.05.10.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). +5. Unzip it and copy all .wz-files into the installation directory. Replace the existing ones. +6. Download _HeavenMS-localhost-WINDOW.exe_ from [hostr.co](https://hostr.co/amuX5SLeeVZx). This is a client modified to connect to your localhost instead of Nexon's server (along with some fixes and custom changes). + - Your antivirus will likely detect the file as a trojan or similar and automatically delete it. To prevent this from happening, add your _Downloads_ directory and the installation directory as exclusions in your antivirus software. On W11, this is under "Virus & threat protection settings" -> "Add or remove exclusions". +7. Move _HeavenMS-localhost-WINDOW.exe_ into the installation directory. +8. Done! Double-click the exe and the game should start. + - The client may be a bit fiddly. Sometimes it won't start, but if you see "Client connected" in the server console it's a good indication. Try spam-clicking it like 10+ times, that usually works for me. -Important: should the client be refused a connection to the game server, it may be because of firewall issues. Head to the end of this file to proceed in allowing this connection through the computer's firewall. Alternatively, one can deactivate the firewall and try opening the client again. -You can also search the server logs (/logs/cosmic-log.log) if any connection attempts have been made to ease debugging. +**Important note about localhost clients**: these executables are red-flagged by antivirus tools as potentially malicious software. +This happens due to the reverse engineering methods that were applied onto these software artifacts. +The one provided here has been in use for years already and posed no harm so far, so it is assumed to be safe. ---- -### Installing the server -1. Configure the project -2. Set up the database -3. Launch the server +### 4 - Getting into the game +The client has started, and you're looking at the login screen. -If you are using Docker (quick start): -1. Configure the project -2. Launch the server - -#### Configuring the project - -The easiest way to set up your project is to clone the repository directly into a new IntelliJ project. - -1. Install IntelliJ -2. Create a new "Project from Version Control..." -3. Enter the URL to this GitHub repository: "https://github.com/P0nk/Cosmic.git" -4. Click on "Clone". A new project will now be created with all the files from the repository. - -#### Setting up the database - -1. Install MySQL Server 8 and MySQL Workbench 8. -2. Using Workbench, create a new user with username "cosmic_server" and password "snailshell". - This the default configuration in Cosmic. - * (Optional) Restrict the Schema Privileges for this new user for improved security. - Add a new entry with "Schemas matching pattern: cosmic" and only select "SELECT", "INSERT", "UPDATE", "DELETE" under "Object Rights" -3. Run the sql scripts in the "database/sql" directory of the project in the order indicated by their names. - * Make sure you are connected to the database with the "root" user to be able to run the scripts. - * Run scripts one by one through the menu: "File" -> "Run SQL Script" -> select the script file to run -> "Run" - * The 3rd script "3-db_shopupdate" is optional. It adds custom shop items for certain NPCs. - * The 4th script "4-db_admin" is also optional, but recommended if you are new. It adds an admin account to simplify the setup. - -Use this info when you connect to MySQL Server for the first time: -* Server Host: localhost -* Port: 3306 -* Username: root -* Password: - -At the end of the execution of these sql scripts, you should have installed a database schema named "cosmic". -REGISTER YOUR FIRST ACCOUNT to be used in-game by **manually creating** an entry in the table "accounts" in the database with a username and password. - - -### Running the server - -Configure the IP you want to use for your MapleStory server in "config.yaml" file, or set it as "localhost" if you want to run it only on your machine. -Alternatively, you can use the IP given by Hamachi to use on a Hamachi network, or you can use a non-Hamachi method of port-forwarding. Neither will be approached here. - - -To launch the server, you may either: -* Launch inside IntelliJ -* Launch a built jar file -* Launch with Docker - -#### Run inside IntelliJ -1. Open the file src/main/java/net/server/Server.java. -2. Click the green arrow to the left of the class definition "public class Server", and then "Run Cosmic". - * Alternatively (recommended), create a new Configuration that points to "net.server.Server". -3. The server launches in a terminal window inside IntelliJ. - -#### Run from a jar file -1. Create the jar file - * The jar file is created by the Maven assembly plugin in the package lifecycle. - * If you already have Maven installed, simply run the command "mvn clean install" to create the jar file. - * IntelliJ also comes with built-in Maven support. Open a new terminal window inside IntelliJ, type "mvn clean install" (your command should now be marked green), then Ctrl+Enter to build the jar file. -2. Launch the jar file - * Double click on "launch.bat" (need to have Java 17 installed) - -#### Run as containers with Docker -1. Start Docker -2. Run the command "docker compose up" at the root of the project. - * If you make any changes to the code, make sure you append the "--build" option at the end of the command to force rebuild the server image. - ---- -### Getting into the game - -If you ran the admin sql script, there already exists an account in the database with an admin character on it (GM level 6). - -Log in using these credentials: +#### Logging in +At this point, you can log in to the admin account using the following credentials: * Username: "admin" * Password: "admin" * Pin: "0000" * Pic: "000000" -Admin characters have "hide" mode enabled by default. This means your character will be translucent on your screen, and completely invisible to others. -It will also prevent you from controlling mobs (making them stand still). To toggle this mode on and off, type "@hide" in the in-game chat. +Or create a regular account by typing in your desired username & password and attempting to log in. This "automatic registration" feature lets you create new accounts to play around with. It is enabled by default (see _config.yaml_). -By default, the server source is set to allow AUTO-REGISTERING. This means that, by simply typing in a "Login ID" and a "Password", you're able to create a new account. +#### Entering the game +Create a new character as you normally would, and then select it to enter the game. Hooray, finally we're in! -After creating a character, experiment typing in all-chat "@commands". -This will display all available commands for the current GM level your character has. +If you log in to the "Admin" character, you'll notice that the character looks almost invisible. This is hide mode, which is enabled by default when you log in to a GM character. You won't be visible to normal players and no mobs will move if you're alone on the map. Toggle hide mode on or off by typing "@hide" in the in-game chat. -To change a character's GM level, make sure that character is not logged in, then: +Hide is one of many commands available to players, type "@commands" to see the full list. Higher ranked GMs have access to more powerful commands. -1. Open MySQL Workbench; -2. Expand "cosmic" schema; -3. Expand "Tables"; -4. Right-click "characters" and click "Select Rows" -5. Find your character in Result Grid. Scroll to the right and find the "gm" column. -6. Edit your character's gm value and click "Apply", and then "Apply" again in the window that appeared, then "Finish". - * 0 is what ordinary players start with, and 6 is the highest gm value. Higher level gms have access to more commands in game. +That's it, have fun playing around in game! ---- -### Some notes about WZ/WZ.XML EDITING -Brief introduction to WZ files: they are the asset/data files required by the client and server. The client can read the .wz files directly, but the server requires them in XML format. -The server also does not make use of any of the sprites, which is where different kinds of exporting comes into the picture. HaRepacker allows you to export to Private server XML, which is the .img files packaged in the .wz stripped of sprites and converted to XML. +## Advanced concepts +Some slightly more advanced concepts that might be useful once you're up and running. -Link to HaRepacker-resurrected, the standard tool for handling WZ files: https://github.com/lastbattle/Harepacker-resurrected +### Host on remote server +You don't have to host the server on your local machine to play. It's possible to host on a remote server such as a VPS or even a dedicated server. -NOTE: Be extremely wary when using server-side's XMLs data being reimported into the client's WZ, as some means of synchronization between the server and client modules, this action COULD generate some kind of bugs afterwards. Client-to-server data reimporting seems to be fine, though. +I leave it to you to figure out the server hosting part, but once you have that running you'll need to edit the client exe to point to your remote server ip. -#### Editing the v83 WZ's: +#### Edit client ip +1. Download and install a hex editor: [HxD](https://mh-nexus.de/en/hxd/) +2. Start HxD and open your client exe (I recommend making a copy of it first). At this point you should see a bunch of hex codes and a "Decoded text" column to the right of it. +3. Ctrl+f and search for Text-string "127.0.0.1". You should find three occurrences right above each other. +4. Place your cursor before the first "127" and start typing the desired ip, overwriting what is already there. Do the same on the other two and click on Save. +5. Done! Now the client will attempt to connect to that ip address instead when you launch it. +### WZ files +WZ files are the asset/data files required by the client and server. Typically, [HaRepacker-resurrected](https://github.com/lastbattle/Harepacker-resurrected) is used to handle (view, edit, export) the .wz files. -* Use the HaRepacker-resurrected 4.2.4 editor, encryption "GMS (old)". -* Open the desired WZ for editing and use the node hierarchy to make the desired changes (copy/pasting nodes may be unreliable in rare scenarios). -* Save the changed WZ, **overwriting the original content** at the client folder. -* Finally, **RE-EXPORT (using the "Private Server..." exporting option) the changed XMLs into the server's WZ.XML files**, overwriting the old contents. +The client can read the .wz files directly, but the server requires them in XML format. The server also does not make use of the sprites, which is the motivation for different kinds of exporting. +HaRepacker allows you to export to "Private server", which is the .img files packaged in the .wz stripped of sprites and converted to XML. This takes much less disk space. -**These steps are IMPORTANT, to maintain synchronization** between the server and client modules. +This server requires custom .wz files (unfortunately), as you may have noted during installation of the client. The intention is for these to be removed eventually and to solely run on vanilla .wz files. ---- -### Portforwarding the SERVER +#### WZ editing +* Use the HaRepacker-resurrected editor, encryption "GMS (old)". +* Open the desired .wz for editing and use the node hierarchy to make the desired changes (copy/pasting nodes may be unreliable in rare scenarios). +* Save the changed .wz, overwriting the original content at the client folder. +* Finally, re-export (using the "Private Server" exporting option) the changed XMLs into the server's .wz XML files (found in the "wz" directory), overwriting the old contents. -To use portforward, you will need to have permission to change things on the LAN router. Access your router using the Internet browser. URLs vary accordingly with the manufacturer. To discover it, open the command prompt and type "ipconfig" and search for the "default gateway" field. The IP shown there is the URL needed to access the router. Also, look for the IP given to your machine (aka "IPv4 address" field), which will be the server one. +Make sure to always export from the client .wz files to the server XML, and not the other way around. -The default login/password also varies, so use the link http://www.routerpasswords.com/ as reference. Usually, login as "admin" and password as "password" completes the task well. +Editing the client .wz without exporting to the server may lead to strange behavior. -Now you have logged in the router system, search for anything related to portforwarding. Should the system prompt you between portforwarding and portriggering, pick the first, it is what we will be using. +### Client features +For more information about the client and its features, see [HeavenMS on GitHub](https://github.com/ronancpl/HeavenMS#download-items). -Now, it is needed to enable the right ports for the Internet. For Cosmic, it is basically needed to open ports 7575 to 7575 + (number of channels) and port 8484. Create a new custom service which enables that range of ports for the server's channel and opt to use TCP/UDP protocols. Finally, create a custom service now for using port 8484. - -Optionally, if you want to host a webpage, portforward the port 80 (the HTTP port) as well. - -It is not done yet, sometimes the firewalls will block connections between the LAN and the Internet. To overcome this, it is needed to create some rules for the firewall to permit these connections. Search for the advanced options with firewalls on your computer and, with it open, create two rules (one outbound and one inbound). - -These rules must target "one application", "enable connections" and must target your MapleStory client (aka localhost). - -After all these steps, the portforwarding process should now be complete. - ---- - -### Client changelog -The following list, in bottom-up chronological order, -holds information regarding all changes that were applied from the starting localhost used in this development. -Some lines have a link attached, that will lead you to a snapshot of the localhost at that version of the artifact. -Naturally, later versions holds all previous changes along with the proposed changes. - -**Change log:** - -* Fixed Monster Magnet crashing the caster when trying to pull fixed mobs, credits to Shavit. https://gofile.io/?c=BW7dVM (dead link) -* 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. -* Fixed Monster Magnet crashing the caster when trying to pull bosses. Drawback: Dojo HPBar becomes unavailable. https://hostr.co/SvnSKrGzXhG0 -* Fixed some 'rn' problems with quest icons & removed "tab" from party leader changed message. https://hostr.co/tsYsQzzV6xT0 -* Removed block on applying attack-based strengthening gems on non-weapon equipments. https://hostr.co/m2bVtnizCtmD -* Set a higher cap for SPEED. -* Removed the AP assigning block for beginners below level 10. https://hostr.co/AHAHzneCti9B -* Removed block on party for beginners level 10 or below. https://hostr.co/JZq53mMtToCz -* Removed block on MTS entering in some maps, rendering the buyback option available. -* Removed "AP excess" popup and limited actions on Admin/MWLB, credits to kevintjuh93. -* Removed "You've gained a level!" popup, credits to PrinceReborn. -* Removed caps for WATK, WDEF, MDEF, ACC, AVOID. -* 'n' problem fixed. -* Fraysa's https://hostr.co/gJbLZITRVHmv -* Eric's MapleSilver starting on window-mode. \ No newline at end of file +Some notable features: +* Opens in window mode by default +* Uncapped max speed From 7d42870abd74a4e7397e01d728c497f1758c0fd5 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 21:26:09 +0200 Subject: [PATCH 12/55] Fix sql script not being runnable on newer versions of MySQL --- database/sql/1-db_database.sql | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/database/sql/1-db_database.sql b/database/sql/1-db_database.sql index 639ca90b80..3f88280ab5 100644 --- a/database/sql/1-db_database.sql +++ b/database/sql/1-db_database.sql @@ -1,9 +1,4 @@ -#EXECUTE THIS FIRST, THEN NEXT SQL: 'db_drops.sql' - -SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; -SET time_zone = "+00:00"; - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; @@ -21510,4 +21505,4 @@ ALTER TABLE `skills` /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; \ No newline at end of file +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; From 0eb78e2d9ebfe7c035e1b715ca35c8c98f4d12e7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 22:08:28 +0200 Subject: [PATCH 13/55] Use root db user by default --- Dockerfile | 14 ++++++++++---- config.yaml | 4 ++-- docker-compose.yml | 7 +++---- src/main/java/tools/DatabaseConnection.java | 3 ++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 261ce9e499..f291adf60d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,15 +4,21 @@ # # Cosmic JAR creation stage # -FROM maven:3.9.1-eclipse-temurin-17 AS jar +FROM maven:3.9.6-amazoncorretto-21 AS jar # Build in a separated location which won't have permissions issues. WORKDIR /opt/cosmic + # Any changes to the pom will affect the entire build, so it should be copied first. COPY pom.xml ./pom.xml + # Grab all the dependencies listed in the pom early, since it prevents changes to source code from requiring a complete re-download. -# Skip compiling tests since we don't want all the dependecies to be downloaded. -RUN mvn -f ./pom.xml clean dependency:go-offline -Dmaven.test.skip -T 1C +# Skip compiling tests since we don't want all the dependencies to be downloaded. +# RUN mvn -f ./pom.xml clean dependency:go-offline -Dmaven.test.skip -T 1C +# TODO: The above command stopped working as of Java 21 upgrade due to: +# Failed to execute goal org.apache.maven.plugins:maven-dependency-plugin:3.6.1:go-offline (default-cli) on project Cosmic: org.eclipse.aether.resolution.DependencyResolutionException: The following artifacts could +# not be resolved: io.netty:netty-tcnative:jar:${os.detected.classifier}:2.0.65.Final (absent): Could not find artifact io.netty:netty-tcnative:jar:${os.detected.classifier}:2.0.65.Final in central (https://repo.maven.apache.org/maven2) -> [Help 1] + # Source code changes may not change dependencies, so it can go last. # Skip compiling tests since we don't want all the dependecies to be downloaded for plugins. COPY src ./src @@ -21,7 +27,7 @@ RUN mvn -f ./pom.xml clean package -Dmaven.test.skip -T 1C # # Server creation stage # -FROM eclipse-temurin:17.0.6_10-jre +FROM amazoncorretto:21 # Host the server in a location that won't have permissions issues. WORKDIR /opt/server diff --git a/config.yaml b/config.yaml index 27d0706ab2..77ea225d1a 100644 --- a/config.yaml +++ b/config.yaml @@ -161,8 +161,8 @@ server: #Database Configuration DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic" # If the docker ENV for DB_HOST is anything but "db", this string format should be changed from 3306 to 3307 (or whichever port it was changed to in docker) DB_HOST: "localhost" - DB_USER: "cosmic_server" - DB_PASS: "snailshell" + DB_USER: "root" + DB_PASS: "" INIT_CONNECTION_POOL_TIMEOUT: 90 # Seconds #Login Configuration diff --git a/docker-compose.yml b/docker-compose.yml index 90bbb04fc0..8709cbe0e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,12 +21,11 @@ services: DB_HOST: "db" ## Remember if this is present it will OVERRIDE the host in the config.yaml, if you put here anything other than db, you'll need to change the config.yaml jdbc string to port 3307, and not port 3306 db: - image: mysql:8.0.23 + image: mysql:8.4.0 environment: - MYSQL_RANDOM_ROOT_PASSWORD: "true" MYSQL_DATABASE: "cosmic" - MYSQL_USER: "cosmic_server" - MYSQL_PASSWORD: "snailshell" + MYSQL_ROOT_PASSWORD: "" + MYSQL_ALLOW_EMPTY_PASSWORD: yes ports: - "3307:3306" volumes: diff --git a/src/main/java/tools/DatabaseConnection.java b/src/main/java/tools/DatabaseConnection.java index d7b51f0366..b0313b92fa 100644 --- a/src/main/java/tools/DatabaseConnection.java +++ b/src/main/java/tools/DatabaseConnection.java @@ -81,8 +81,9 @@ public class DatabaseConnection { return true; } - log.info("Initializing connection pool..."); final HikariConfig config = getConfig(); + log.info("Initializing database connection pool. Connecting to:'{}' with user:'{}'", config.getJdbcUrl(), + config.getUsername()); Instant initStart = Instant.now(); try { dataSource = new HikariDataSource(config); From 970fb3155fd8fee1fffc58ffcbb21271d41699d9 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 9 May 2024 22:29:15 +0200 Subject: [PATCH 14/55] Complement pom.xml --- pom.xml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pom.xml b/pom.xml index 4d4de5cb6d..1c7ac8da10 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,40 @@ 1.0-SNAPSHOT jar + Cosmic + Server emulator for Global MapleStory version 83 + https://github.com/P0nk/Cosmic + 2021 + + + + Ponk + ponkcode@gmail.com + https://github.com/P0nk + + maintainer + developer + + + ponkcode + + + + + + scm:git:https://github.com/P0nk/Cosmic.git + scm:git:https://github.com/P0nk/Cosmic.git + https://github.com/P0nk/Cosmic + + + GitHub Issues + https://github.com/P0nk/Cosmic/issues + + + GitHub Actions + https://github.com/P0nk/Cosmic/actions + + UTF-8 From 7a6d3e1b688c8a1c3739e3d3f034b6d9768993b7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Fri, 10 May 2024 21:13:09 +0200 Subject: [PATCH 15/55] Add feature request issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 5 ++--- .github/ISSUE_TEMPLATE/feature_request.md | 15 +++++++++++++++ README.md | 7 ++++++- 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8ca2301e30..7460504776 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,14 +8,13 @@ assignees: '' --- **Describe the bug** -A clear and concise description (in English) of what the bug is. +A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' -3. Scroll down to '....' -4. See error +3. See error **Expected behavior** A clear and concise description of what you expected to happen. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..0eaf22b473 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,15 @@ +--- +name: Feature request +about: Suggest an idea for this project, such as a missing gameplay feature +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + diff --git a/README.md b/README.md index ba76cbce64..6d63ab50af 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,12 @@ You will start by cloning the repository, then configure the database properties 3. Start the server. The main method is located in `net.server.Server`. 4. If you see "Cosmic is now online" in the console, it means the server is online and ready to serve traffic. Yay! -Support for Docker is also provided out of the box, as an alternative to running straight in the IDE. If you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed it's as easy as running `docker compose up`. Making changes becomes a bit more tedious though as you have to rebuild the server image via `docker compose up --build`. +#### Docker +Support for Docker is also provided out of the box, as an alternative to running straight in the IDE. If you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed it's as easy as running `docker compose up`. + +Making changes becomes a bit more tedious though as you have to rebuild the server image via `docker compose up --build`. + +On the first launch, the database container will run the scripts which may take so long that the server fails to start. In that case, just wait until the database is done running the scripts and then retry (Ctrl+C and re-run the command). ### 3 - Client You will start by installing the game with the old installer, then overwrite some WZ files with our custom ones, and lastly get the localhost executable in place. From dc1a712f52478a91d18c3703bfe558c155ae2a58 Mon Sep 17 00:00:00 2001 From: P0nk Date: Fri, 10 May 2024 22:06:38 +0200 Subject: [PATCH 16/55] Re-add section about running from jar --- README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6d63ab50af..0f27a0df06 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Cosmic is a server emulator for Global MapleStory (GMS) version 83. ## Introduction -Cosmic launched on March 2021. It is based on code from a long line of server sources spanning over a decade - starting with OdinMS (2008) and ending with HeavenMS (2019). +Cosmic launched on March 2021. It is based on code from a long line of server emulators spanning over a decade - starting with OdinMS (2008) and ending with HeavenMS (2019). This is mainly a Java based project, but there are also a bunch of scripts written in JavaScript. @@ -32,6 +32,7 @@ You may contribute to the project in various ways, mainly through GitHub: * Providing improvements to the code through a [Pull Request](https://github.com/P0nk/Cosmic/pulls) from your own fork. * Reporting a bug by creating an [Issue](https://github.com/P0nk/Cosmic/issues). * Providing information to existing issues or reviewing pull requests that others have made. +* ...and in other ways that I haven't thought of! ### Continuous integration A GitHub Actions pipeline is set up to run the build automatically when a new pull request is opened or commits are pushed to an existing one. This ensures that the code compiles and all the tests pass. @@ -50,9 +51,12 @@ The project follows the [semantic versioning](https://semver.org/) scheme using ## Getting started Follow along as I go through the steps to play the game on your local computer from start to finish. I won't go into extreme detail, so if you don't have prior experience with Java or git, you might struggle. -### 1 - Database -The database contains game data used by the server such as accounts and inventory items. It is required for the server to start. +We will set up the following: +- Database - the database is used by the server to store game data such as accounts, characters and inventory items. +- Server - the server is the "brain" and routes network traffic between the clients. +- Client - the client is the application used to _play the game_, i.e. MapleStory.exe. +### 1 - Database You will start by installing the database server and client, and then run some scripts to prepare it for the server. #### Steps @@ -77,12 +81,23 @@ You will start by cloning the repository, then configure the database properties 3. Start the server. The main method is located in `net.server.Server`. 4. If you see "Cosmic is now online" in the console, it means the server is online and ready to serve traffic. Yay! +Below, I list other ways of running the server which are completely optional. + #### Docker Support for Docker is also provided out of the box, as an alternative to running straight in the IDE. If you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed it's as easy as running `docker compose up`. Making changes becomes a bit more tedious though as you have to rebuild the server image via `docker compose up --build`. -On the first launch, the database container will run the scripts which may take so long that the server fails to start. In that case, just wait until the database is done running the scripts and then retry (Ctrl+C and re-run the command). +On the first launch, the database container will run the scripts which may take so long that the server fails to start. In that case, just wait until the database is done running the scripts and then retry (Ctrl+C and re-run the command). + +#### Jar +Another option is to start the server from a terminal by running a jar file. You first need to build the jar file from source which requires [Maven](https://maven.apache.org/). + +Building the jar file is as easy as running ``mvn clean package``. The project is configured to produce a "fat" jar which contains all dependencies (by utilizing the _maven-assembly-plugin_). Note that the WZ XML files are __not__ included in the jar. + +To run the jar, a ``launch.bat`` file is provided for convenience. Simply double-click it and the server will start in a new terminal window. + +Alternatively, run the jar file from the terminal. Just remember to provide the `wz-path` system property pointing to your wz directory. ### 3 - Client You will start by installing the game with the old installer, then overwrite some WZ files with our custom ones, and lastly get the localhost executable in place. From 04a92fe0c1f19140cf13863b601c7c8ebf33ef0d Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 21 May 2024 19:42:56 +0200 Subject: [PATCH 17/55] Remove buyback custom feature --- config.yaml | 9 - docs/feature_list.md | 4 +- scripts/npc/9977777.js | 5 +- src/main/java/client/Character.java | 213 +++++++-------- .../java/client/command/CommandsExecutor.java | 181 ++++++++++++- .../command/commands/gm0/BuyBackCommand.java | 48 ---- .../processor/action/BuybackProcessor.java | 83 ------ src/main/java/config/ServerConfig.java | 9 - .../channel/handlers/EnterMTSHandler.java | 249 +++++++++--------- src/main/java/tools/PacketCreator.java | 73 +++-- wz/Sound.wz/Field.img.xml | 23 -- 11 files changed, 459 insertions(+), 438 deletions(-) delete mode 100644 src/main/java/client/command/commands/gm0/BuyBackCommand.java delete mode 100644 src/main/java/client/processor/action/BuybackProcessor.java diff --git a/config.yaml b/config.yaml index 77ea225d1a..bcbb1c37db 100644 --- a/config.yaml +++ b/config.yaml @@ -220,7 +220,6 @@ server: USE_MTS: false USE_CPQ: true #Renders the CPQ available or not. USE_AUTOHIDE_GM: true #When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152). - USE_BUYBACK_SYSTEM: false #Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button. USE_FIXED_RATIO_HPMP_UPDATE: false #Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio. USE_FAMILY_SYSTEM: true USE_DUEY: true @@ -447,14 +446,6 @@ server: WEDDING_GIFT_LIMIT: 1 #Max number of gifts per person to same wishlist on marriage instances. WEDDING_BLESSER_SHOWFX: true #Pops bubble sprite effect on players blessing the couple. Setting this false shows the blessing effect on the couple instead. - #Buyback Configuration - USE_BUYBACK_WITH_MESOS: true #Enables usage of either mesos or NX for the buyback fee. - BUYBACK_FEE: 77.70 #Sets the base amount needed to buyback (level 30 or under will use the base value). - BUYBACK_LEVEL_STACK_FEE: 85.47 #Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak). - BUYBACK_MESO_MULTIPLIER: 1000 #Sets a multiplier for the fee when using meso as the charge unit. - BUYBACK_RETURN_MINUTES: 1 #Sets the maximum amount of time the player can wait before decide to buyback. - BUYBACK_COOLDOWN_MINUTES: 7 #Sets the time the player must wait before using buyback again. - # Login timeout by shavit TIMEOUT_DURATION: 3600000 # Kicks clients who don't send any packet to the game server in due time (in millisseconds). diff --git a/docs/feature_list.md b/docs/feature_list.md index 5e0b410217..235ff0e7f4 100644 --- a/docs/feature_list.md +++ b/docs/feature_list.md @@ -302,10 +302,10 @@ Localhost: * Removed caps for MATK, WDEF, MDEF, ACC and AVOID. * Removed "AP excess" popup and "Admin/MWLB" action block, original credits to kevintjuh93. * Removed "You've gained a level!" popup, original credits to PrinceReborn. -* Removed "Cannot enter MTS from this map." popup on maps that blocks transitions (such change channel, CS/MTS), rendering the buyback option now available for all maps. +* Removed "Cannot enter MTS from this map." popup on maps that blocks transitions (such change channel, CS/MTS). * Removed a check for players wishing to create/join a party being novices under level 10. * Set a new high cap for SPEED. * Removed the AP assign block for novices. * Removed a block that would show up when trying to apply an attack gem on equipments that aren't weapons. ---------------------------- \ No newline at end of file +--------------------------- diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index ce1087771b..7231068bd4 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -217,7 +217,6 @@ function writeFeatureTab_Serverpotentials() { addFeature("Poison damage value visible for other players."); addFeature("M. book announcer displays info based on demand."); addFeature("Custom jail system."); - addFeature("Custom buyback system, uses mesos / NX, via MTS."); addFeature("Custom fishing system having 'seasonal' catch times."); addFeature("Actual fishing handling w/ F. Net - thanks Dragohe4rt!"); addFeature("Custom map leasing system."); @@ -260,7 +259,7 @@ function writeFeatureTab_CustomNPCs() { function writeFeatureTab_Localhostedits() { addFeature("Removed the 'n' NPC dialog issue."); addFeature("Removed caps for MATK, WMDEF, ACC and AVOID."); - addFeature("Removed MTS block, buyback available anywhere."); + addFeature("Removed MTS block."); addFeature("Removed party blocks for novices under level 10."); addFeature("Set a much more higher cap for SPEED."); addFeature("Removed AP usage block for novices."); @@ -369,4 +368,4 @@ function generateSelectionMenu(array) { menu += "#L" + i + "#" + array[i] + "#l\r\n"; } return menu; -} \ No newline at end of file +} diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index e194af2d55..c90d31480c 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -24,8 +24,17 @@ package client; import client.autoban.AutobanManager; import client.creator.CharacterFactoryRecipe; -import client.inventory.*; +import client.inventory.Equip; import client.inventory.Equip.StatUpgrade; +import client.inventory.Inventory; +import client.inventory.InventoryProof; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ItemFactory; +import client.inventory.ModifyInventory; +import client.inventory.Pet; +import client.inventory.PetDataFactory; +import client.inventory.WeaponType; import client.inventory.manipulator.CashIdGenerator; import client.inventory.manipulator.InventoryManipulator; import client.keybind.KeyBinding; @@ -40,7 +49,35 @@ import constants.id.ItemId; import constants.id.MapId; import constants.id.MobId; import constants.inventory.ItemConstants; -import constants.skills.*; +import constants.skills.Aran; +import constants.skills.Beginner; +import constants.skills.Bishop; +import constants.skills.BlazeWizard; +import constants.skills.Bowmaster; +import constants.skills.Brawler; +import constants.skills.Buccaneer; +import constants.skills.Corsair; +import constants.skills.Crusader; +import constants.skills.DarkKnight; +import constants.skills.DawnWarrior; +import constants.skills.Evan; +import constants.skills.FPArchMage; +import constants.skills.Hermit; +import constants.skills.Hero; +import constants.skills.ILArchMage; +import constants.skills.Legend; +import constants.skills.Magician; +import constants.skills.Marauder; +import constants.skills.Marksman; +import constants.skills.NightLord; +import constants.skills.Noblesse; +import constants.skills.Paladin; +import constants.skills.Priest; +import constants.skills.Ranger; +import constants.skills.Shadower; +import constants.skills.Sniper; +import constants.skills.ThunderBreaker; +import constants.skills.Warrior; import net.packet.Packet; import net.server.PlayerBuffValueHolder; import net.server.PlayerCoolDownValueHolder; @@ -52,39 +89,98 @@ import net.server.guild.GuildCharacter; import net.server.guild.GuildPackets; import net.server.services.task.world.CharacterSaveService; import net.server.services.type.WorldServices; -import net.server.world.*; +import net.server.world.Messenger; +import net.server.world.MessengerCharacter; +import net.server.world.Party; +import net.server.world.PartyCharacter; +import net.server.world.PartyOperation; +import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; import scripting.item.ItemScriptManager; -import server.*; +import server.CashShop; +import server.ExpLogger; import server.ExpLogger.ExpLogRecord; +import server.ItemInformationProvider; import server.ItemInformationProvider.ScriptedItem; +import server.Marriage; +import server.Shop; +import server.StatEffect; +import server.Storage; +import server.ThreadManager; +import server.TimerManager; +import server.Trade; import server.events.Events; import server.events.RescueGaga; import server.events.gm.Fitness; import server.events.gm.Ola; -import server.life.*; -import server.maps.*; +import server.life.MobSkill; +import server.life.MobSkillFactory; +import server.life.MobSkillId; +import server.life.MobSkillType; +import server.life.Monster; +import server.life.PlayerNPC; +import server.maps.AbstractAnimatedMapObject; +import server.maps.Door; +import server.maps.DoorObject; +import server.maps.Dragon; +import server.maps.FieldLimit; +import server.maps.HiredMerchant; +import server.maps.MapEffect; +import server.maps.MapItem; +import server.maps.MapManager; +import server.maps.MapObject; +import server.maps.MapObjectType; +import server.maps.MapleMap; +import server.maps.MiniGame; import server.maps.MiniGame.MiniGameResult; +import server.maps.PlayerShop; +import server.maps.PlayerShopItem; +import server.maps.Portal; +import server.maps.SavedLocation; +import server.maps.SavedLocationType; +import server.maps.Summon; import server.minigame.RockPaperScissor; import server.partyquest.AriantColiseum; import server.partyquest.MonsterCarnival; import server.partyquest.MonsterCarnivalParty; import server.partyquest.PartyQuest; import server.quest.Quest; -import tools.*; +import tools.DatabaseConnection; +import tools.LongTool; +import tools.PacketCreator; +import tools.Pair; +import tools.Randomizer; import tools.exceptions.NotEnabledException; import tools.packets.WeddingPackets; import java.awt.*; import java.lang.ref.WeakReference; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; -import java.util.*; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.Stack; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; @@ -94,7 +190,9 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; public class Character extends AbstractCharacterObject { private static final Logger log = LoggerFactory.getLogger(Character.class); @@ -128,7 +226,7 @@ public class Character extends AbstractCharacterObject { private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1; private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses; private int owlSearch; - private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastBuyback = 0, lastDeathtime, jailExpiration = -1; + private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastDeathtime, jailExpiration = -1; private transient int localstr, localdex, localluk, localint_, localmagic, localwatk; private transient int equipmaxhp, equipmaxmp, equipstr, equipdex, equipluk, equipint_, equipmagic, equipwatk, localchairhp, localchairmp; private int localchairrate; @@ -6054,65 +6152,6 @@ public class Character extends AbstractCharacterObject { } } - private boolean canBuyback(int fee, boolean usingMesos) { - return (usingMesos ? this.getMeso() : cashshop.getCash(1)) >= fee; - } - - private void applyBuybackFee(int fee, boolean usingMesos) { - if (usingMesos) { - this.gainMeso(-fee); - } else { - cashshop.gainCash(1, -fee); - } - } - - private long getNextBuybackTime() { - return lastBuyback + MINUTES.toMillis(YamlConfig.config.server.BUYBACK_COOLDOWN_MINUTES); - } - - private boolean isBuybackInvincible() { - return Server.getInstance().getCurrentTime() - lastBuyback < 4200; - } - - private int getBuybackFee() { - float fee = YamlConfig.config.server.BUYBACK_FEE; - int grade = Math.min(Math.max(level, 30), 120) - 30; - - fee += (grade * YamlConfig.config.server.BUYBACK_LEVEL_STACK_FEE); - if (YamlConfig.config.server.USE_BUYBACK_WITH_MESOS) { - fee *= YamlConfig.config.server.BUYBACK_MESO_MULTIPLIER; - } - - return (int) Math.floor(fee); - } - - public void showBuybackInfo() { - String s = "#eBUYBACK STATUS#n\r\n\r\nCurrent buyback fee: #b" + getBuybackFee() + " " + (YamlConfig.config.server.USE_BUYBACK_WITH_MESOS ? "mesos" : "NX") + "#k\r\n\r\n"; - - long timeNow = Server.getInstance().getCurrentTime(); - boolean avail = true; - if (!isAlive()) { - long timeLapsed = timeNow - lastDeathtime; - long timeRemaining = MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES) - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow)); - if (timeRemaining < 1) { - s += "Buyback #e#rUNAVAILABLE#k#n"; - avail = false; - } else { - s += "Buyback countdown: #e#b" + getTimeRemaining(MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES) - timeLapsed) + "#k#n"; - } - s += "\r\n"; - } - - if (timeNow < getNextBuybackTime() && avail) { - s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k"; - s += "\r\n"; - } else { - s += "Buyback #bavailable#k"; - } - - this.showHint(s); - } - private static String getTimeRemaining(long timeLeft) { int seconds = (int) Math.floor(timeLeft / SECONDS.toMillis(1)) % 60; int minutes = (int) Math.floor(timeLeft / MINUTES.toMillis(1)) % 60; @@ -6120,34 +6159,6 @@ public class Character extends AbstractCharacterObject { return (minutes > 0 ? (String.format("%02d", minutes) + " minutes, ") : "") + String.format("%02d", seconds) + " seconds"; } - public boolean couldBuyback() { // Ronan's buyback system - long timeNow = Server.getInstance().getCurrentTime(); - - if (timeNow - lastDeathtime > MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES)) { - this.dropMessage(5, "The period of time to decide has expired, therefore you are unable to buyback."); - return false; - } - - long nextBuybacktime = getNextBuybackTime(); - if (timeNow < nextBuybacktime) { - long timeLeft = nextBuybacktime - timeNow; - this.dropMessage(5, "Next buyback available in " + getTimeRemaining(timeLeft) + "."); - return false; - } - - boolean usingMesos = YamlConfig.config.server.USE_BUYBACK_WITH_MESOS; - int fee = getBuybackFee(); - - if (!canBuyback(fee, usingMesos)) { - this.dropMessage(5, "You don't have " + fee + " " + (usingMesos ? "mesos" : "NX") + " to buyback."); - return false; - } - - lastBuyback = timeNow; - applyBuybackFee(fee, usingMesos); - return true; - } - public boolean isBuffFrom(BuffStat stat, Skill skill) { effLock.lock(); chrLock.lock(); @@ -8911,11 +8922,7 @@ public class Character extends AbstractCharacterObject { boolean playerDied = false; if (hp <= 0) { if (oldHp > hp) { - if (!isBuybackInvincible()) { - playerDied = true; - } else { - hp = 1; - } + playerDied = true; } } diff --git a/src/main/java/client/command/CommandsExecutor.java b/src/main/java/client/command/CommandsExecutor.java index cc35cce603..a95e5943e2 100644 --- a/src/main/java/client/command/CommandsExecutor.java +++ b/src/main/java/client/command/CommandsExecutor.java @@ -24,13 +24,179 @@ package client.command; import client.Client; -import client.command.commands.gm0.*; -import client.command.commands.gm1.*; -import client.command.commands.gm2.*; -import client.command.commands.gm3.*; -import client.command.commands.gm4.*; -import client.command.commands.gm5.*; -import client.command.commands.gm6.*; +import client.command.commands.gm0.ChangeLanguageCommand; +import client.command.commands.gm0.DisposeCommand; +import client.command.commands.gm0.DropLimitCommand; +import client.command.commands.gm0.EnableAuthCommand; +import client.command.commands.gm0.EquipLvCommand; +import client.command.commands.gm0.GachaCommand; +import client.command.commands.gm0.GmCommand; +import client.command.commands.gm0.HelpCommand; +import client.command.commands.gm0.JoinEventCommand; +import client.command.commands.gm0.LeaveEventCommand; +import client.command.commands.gm0.MapOwnerClaimCommand; +import client.command.commands.gm0.OnlineCommand; +import client.command.commands.gm0.RanksCommand; +import client.command.commands.gm0.RatesCommand; +import client.command.commands.gm0.ReadPointsCommand; +import client.command.commands.gm0.ReportBugCommand; +import client.command.commands.gm0.ShowRatesCommand; +import client.command.commands.gm0.StaffCommand; +import client.command.commands.gm0.StatDexCommand; +import client.command.commands.gm0.StatIntCommand; +import client.command.commands.gm0.StatLukCommand; +import client.command.commands.gm0.StatStrCommand; +import client.command.commands.gm0.TimeCommand; +import client.command.commands.gm0.ToggleExpCommand; +import client.command.commands.gm0.UptimeCommand; +import client.command.commands.gm1.BossHpCommand; +import client.command.commands.gm1.BuffMeCommand; +import client.command.commands.gm1.GotoCommand; +import client.command.commands.gm1.MobHpCommand; +import client.command.commands.gm1.WhatDropsFromCommand; +import client.command.commands.gm1.WhoDropsCommand; +import client.command.commands.gm2.ApCommand; +import client.command.commands.gm2.BombCommand; +import client.command.commands.gm2.BuffCommand; +import client.command.commands.gm2.BuffMapCommand; +import client.command.commands.gm2.ClearDropsCommand; +import client.command.commands.gm2.ClearSavedLocationsCommand; +import client.command.commands.gm2.ClearSlotCommand; +import client.command.commands.gm2.DcCommand; +import client.command.commands.gm2.EmpowerMeCommand; +import client.command.commands.gm2.GachaListCommand; +import client.command.commands.gm2.GmShopCommand; +import client.command.commands.gm2.HealCommand; +import client.command.commands.gm2.HideCommand; +import client.command.commands.gm2.IdCommand; +import client.command.commands.gm2.ItemCommand; +import client.command.commands.gm2.ItemDropCommand; +import client.command.commands.gm2.JailCommand; +import client.command.commands.gm2.JobCommand; +import client.command.commands.gm2.LevelCommand; +import client.command.commands.gm2.LevelProCommand; +import client.command.commands.gm2.LootCommand; +import client.command.commands.gm2.MaxSkillCommand; +import client.command.commands.gm2.MaxStatCommand; +import client.command.commands.gm2.MobSkillCommand; +import client.command.commands.gm2.ReachCommand; +import client.command.commands.gm2.RechargeCommand; +import client.command.commands.gm2.ResetSkillCommand; +import client.command.commands.gm2.SearchCommand; +import client.command.commands.gm2.SetSlotCommand; +import client.command.commands.gm2.SetStatCommand; +import client.command.commands.gm2.SpCommand; +import client.command.commands.gm2.SummonCommand; +import client.command.commands.gm2.UnBugCommand; +import client.command.commands.gm2.UnHideCommand; +import client.command.commands.gm2.UnJailCommand; +import client.command.commands.gm2.WarpAreaCommand; +import client.command.commands.gm2.WarpCommand; +import client.command.commands.gm2.WarpMapCommand; +import client.command.commands.gm2.WhereaMiCommand; +import client.command.commands.gm3.BanCommand; +import client.command.commands.gm3.ChatCommand; +import client.command.commands.gm3.CheckDmgCommand; +import client.command.commands.gm3.ClosePortalCommand; +import client.command.commands.gm3.DebuffCommand; +import client.command.commands.gm3.EndEventCommand; +import client.command.commands.gm3.ExpedsCommand; +import client.command.commands.gm3.FaceCommand; +import client.command.commands.gm3.FameCommand; +import client.command.commands.gm3.FlyCommand; +import client.command.commands.gm3.GiveMesosCommand; +import client.command.commands.gm3.GiveNxCommand; +import client.command.commands.gm3.GiveRpCommand; +import client.command.commands.gm3.GiveVpCommand; +import client.command.commands.gm3.HairCommand; +import client.command.commands.gm3.HealMapCommand; +import client.command.commands.gm3.HealPersonCommand; +import client.command.commands.gm3.HpMpCommand; +import client.command.commands.gm3.HurtCommand; +import client.command.commands.gm3.IgnoreCommand; +import client.command.commands.gm3.IgnoredCommand; +import client.command.commands.gm3.InMapCommand; +import client.command.commands.gm3.KillAllCommand; +import client.command.commands.gm3.KillCommand; +import client.command.commands.gm3.KillMapCommand; +import client.command.commands.gm3.MaxEnergyCommand; +import client.command.commands.gm3.MaxHpMpCommand; +import client.command.commands.gm3.MonitorCommand; +import client.command.commands.gm3.MonitorsCommand; +import client.command.commands.gm3.MusicCommand; +import client.command.commands.gm3.MuteMapCommand; +import client.command.commands.gm3.NightCommand; +import client.command.commands.gm3.NoticeCommand; +import client.command.commands.gm3.NpcCommand; +import client.command.commands.gm3.OnlineTwoCommand; +import client.command.commands.gm3.OpenPortalCommand; +import client.command.commands.gm3.PeCommand; +import client.command.commands.gm3.PosCommand; +import client.command.commands.gm3.QuestCompleteCommand; +import client.command.commands.gm3.QuestResetCommand; +import client.command.commands.gm3.QuestStartCommand; +import client.command.commands.gm3.ReloadDropsCommand; +import client.command.commands.gm3.ReloadEventsCommand; +import client.command.commands.gm3.ReloadMapCommand; +import client.command.commands.gm3.ReloadPortalsCommand; +import client.command.commands.gm3.ReloadShopsCommand; +import client.command.commands.gm3.RipCommand; +import client.command.commands.gm3.SeedCommand; +import client.command.commands.gm3.SpawnCommand; +import client.command.commands.gm3.StartEventCommand; +import client.command.commands.gm3.StartMapEventCommand; +import client.command.commands.gm3.StopMapEventCommand; +import client.command.commands.gm3.TimerAllCommand; +import client.command.commands.gm3.TimerCommand; +import client.command.commands.gm3.TimerMapCommand; +import client.command.commands.gm3.ToggleCouponCommand; +import client.command.commands.gm3.UnBanCommand; +import client.command.commands.gm4.BossDropRateCommand; +import client.command.commands.gm4.CakeCommand; +import client.command.commands.gm4.DropRateCommand; +import client.command.commands.gm4.ExpRateCommand; +import client.command.commands.gm4.FishingRateCommand; +import client.command.commands.gm4.ForceVacCommand; +import client.command.commands.gm4.HorntailCommand; +import client.command.commands.gm4.ItemVacCommand; +import client.command.commands.gm4.MesoRateCommand; +import client.command.commands.gm4.PapCommand; +import client.command.commands.gm4.PianusCommand; +import client.command.commands.gm4.PinkbeanCommand; +import client.command.commands.gm4.PlayerNpcCommand; +import client.command.commands.gm4.PlayerNpcRemoveCommand; +import client.command.commands.gm4.PmobCommand; +import client.command.commands.gm4.PmobRemoveCommand; +import client.command.commands.gm4.PnpcCommand; +import client.command.commands.gm4.PnpcRemoveCommand; +import client.command.commands.gm4.ProItemCommand; +import client.command.commands.gm4.QuestRateCommand; +import client.command.commands.gm4.ServerMessageCommand; +import client.command.commands.gm4.SetEqStatCommand; +import client.command.commands.gm4.TravelRateCommand; +import client.command.commands.gm4.ZakumCommand; +import client.command.commands.gm5.DebugCommand; +import client.command.commands.gm5.IpListCommand; +import client.command.commands.gm5.SetCommand; +import client.command.commands.gm5.ShowMoveLifeCommand; +import client.command.commands.gm5.ShowPacketsCommand; +import client.command.commands.gm5.ShowSessionsCommand; +import client.command.commands.gm6.ClearQuestCacheCommand; +import client.command.commands.gm6.ClearQuestCommand; +import client.command.commands.gm6.DCAllCommand; +import client.command.commands.gm6.EraseAllPNpcsCommand; +import client.command.commands.gm6.GetAccCommand; +import client.command.commands.gm6.MapPlayersCommand; +import client.command.commands.gm6.SaveAllCommand; +import client.command.commands.gm6.ServerAddChannelCommand; +import client.command.commands.gm6.ServerAddWorldCommand; +import client.command.commands.gm6.ServerRemoveChannelCommand; +import client.command.commands.gm6.ServerRemoveWorldCommand; +import client.command.commands.gm6.SetGmLevelCommand; +import client.command.commands.gm6.ShutdownCommand; +import client.command.commands.gm6.SpawnAllPNpcsCommand; +import client.command.commands.gm6.SupplyRateCouponCommand; +import client.command.commands.gm6.WarpWorldCommand; import constants.id.MapId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -177,7 +343,6 @@ public class CommandsExecutor { addCommand("droplimit", DropLimitCommand.class); addCommand("time", TimeCommand.class); addCommand("credits", StaffCommand.class); - addCommand("buyback", BuyBackCommand.class); addCommand("uptime", UptimeCommand.class); addCommand("gacha", GachaCommand.class); addCommand("dispose", DisposeCommand.class); diff --git a/src/main/java/client/command/commands/gm0/BuyBackCommand.java b/src/main/java/client/command/commands/gm0/BuyBackCommand.java deleted file mode 100644 index 00d0a94521..0000000000 --- a/src/main/java/client/command/commands/gm0/BuyBackCommand.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server, commands OdinMS-based - 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 - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -/* - @Author: Arthur L - Refactored command content into modules -*/ -package client.command.commands.gm0; - -import client.Client; -import client.command.Command; -import client.processor.action.BuybackProcessor; - -public class BuyBackCommand extends Command { - { - setDescription("Revive yourself after a death."); - } - - @Override - public void execute(Client c, String[] params) { - if (params.length < 1) { - c.getPlayer().yellowMessage("Syntax: @buyback "); - return; - } - - if (params[0].contentEquals("now")) { - BuybackProcessor.processBuyback(c); - } else { - c.getPlayer().showBuybackInfo(); - } - } -} diff --git a/src/main/java/client/processor/action/BuybackProcessor.java b/src/main/java/client/processor/action/BuybackProcessor.java deleted file mode 100644 index f77e9e5803..0000000000 --- a/src/main/java/client/processor/action/BuybackProcessor.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - 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 - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package client.processor.action; // thanks Alex for pointing out some package structures containing broad modules - -import client.Character; -import client.Client; -import server.maps.MapleMap; -import tools.PacketCreator; - -/** - * @author RonanLana - */ -public class BuybackProcessor { - - public static void processBuyback(Client c) { - Character chr = c.getPlayer(); - boolean buyback; - - c.lockClient(); - try { - buyback = !chr.isAlive() && chr.couldBuyback(); - } finally { - c.unlockClient(); - } - - if (buyback) { - String jobString; - switch (chr.getJobStyle()) { - case WARRIOR: - jobString = "warrior"; - break; - - case MAGICIAN: - jobString = "magician"; - break; - - case BOWMAN: - jobString = "bowman"; - break; - - case THIEF: - jobString = "thief"; - break; - - case BRAWLER: - case GUNSLINGER: - jobString = "pirate"; - break; - - default: - jobString = "beginner"; - } - - chr.healHpMp(); - chr.purgeDebuffs(); - chr.broadcastStance(chr.isFacingLeft() ? 5 : 4); - - MapleMap map = chr.getMap(); - map.broadcastMessage(PacketCreator.playSound("Buyback/" + jobString)); - map.broadcastMessage(PacketCreator.earnTitleMessage(chr.getName() + " just bought back into the game!")); - - chr.sendPacket(PacketCreator.showBuybackEffect()); - map.broadcastMessage(chr, PacketCreator.showForeignBuybackEffect(chr.getId()), false); - } - } -} diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 91e42533e3..125675b68e 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -68,7 +68,6 @@ public class ServerConfig { public boolean USE_MTS; public boolean USE_CPQ; public boolean USE_AUTOHIDE_GM; - public boolean USE_BUYBACK_SYSTEM; public boolean USE_FIXED_RATIO_HPMP_UPDATE; public boolean USE_FAMILY_SYSTEM; public boolean USE_DUEY; @@ -295,14 +294,6 @@ public class ServerConfig { public int WEDDING_GIFT_LIMIT; public boolean WEDDING_BLESSER_SHOWFX; - //Buyback Configuration - public boolean USE_BUYBACK_WITH_MESOS; - public float BUYBACK_FEE; - public float BUYBACK_LEVEL_STACK_FEE; - public int BUYBACK_MESO_MULTIPLIER; - public int BUYBACK_RETURN_MINUTES; - public int BUYBACK_COOLDOWN_MINUTES; - // Login timeout by shavit public long TIMEOUT_DURATION; diff --git a/src/main/java/net/server/channel/handlers/EnterMTSHandler.java b/src/main/java/net/server/channel/handlers/EnterMTSHandler.java index 26e1cc5449..493916af78 100644 --- a/src/main/java/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/main/java/net/server/channel/handlers/EnterMTSHandler.java @@ -25,7 +25,6 @@ import client.Character; import client.Client; import client.inventory.Equip; import client.inventory.Item; -import client.processor.action.BuybackProcessor; import config.YamlConfig; import net.AbstractPacketHandler; import net.packet.InPacket; @@ -45,136 +44,132 @@ import java.util.List; public final class EnterMTSHandler extends AbstractPacketHandler { + @Override - public final void handlePacket(InPacket p, Client c) { + public void handlePacket(InPacket p, Client c) { Character chr = c.getPlayer(); - if (!chr.isAlive() && YamlConfig.config.server.USE_BUYBACK_SYSTEM) { - BuybackProcessor.processBuyback(c); + if (!YamlConfig.config.server.USE_MTS) { c.sendPacket(PacketCreator.enableActions()); - } else { - if (!YamlConfig.config.server.USE_MTS) { - c.sendPacket(PacketCreator.enableActions()); - return; - } - - if (chr.getEventInstance() != null) { - c.sendPacket(PacketCreator.serverNotice(5, "Entering Cash Shop or MTS are disabled when registered on an event.")); - c.sendPacket(PacketCreator.enableActions()); - return; - } - - if (MiniDungeonInfo.isDungeonMap(chr.getMapId())) { - c.sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon.")); - c.sendPacket(PacketCreator.enableActions()); - return; - } - - if (FieldLimit.CANNOTMIGRATE.check(chr.getMap().getFieldLimit())) { - chr.dropMessage(1, "You can't do it here in this map."); - c.sendPacket(PacketCreator.enableActions()); - return; - } - - if (!chr.isAlive()) { - c.sendPacket(PacketCreator.enableActions()); - return; - } - if (chr.getLevel() < 10) { - c.sendPacket(PacketCreator.blockedMessage2(5)); - c.sendPacket(PacketCreator.enableActions()); - return; - } - - chr.closePlayerInteractions(); - chr.closePartySearchInteractions(); - - chr.unregisterChairBuff(); - Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); - Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases()); - chr.setAwayFromChannelWorld(); - chr.notifyMapTransferToPartner(-1); - chr.removeIncomingInvites(); - chr.cancelAllBuffs(true); - chr.cancelAllDebuffs(); - chr.cancelBuffExpireTask(); - chr.cancelDiseaseExpireTask(); - chr.cancelSkillCooldownTask(); - chr.cancelExpirationTask(); - - chr.forfeitExpirableQuests(); - chr.cancelQuestExpirationTask(); - - chr.saveCharToDB(); - - c.getChannelServer().removePlayer(chr); - chr.getMap().removePlayer(c.getPlayer()); - try { - c.sendPacket(PacketCreator.openCashShop(c, true)); - } catch (Exception ex) { - ex.printStackTrace(); - } - chr.getCashShop().open(true);// xD - c.enableCSActions(); - c.sendPacket(PacketCreator.MTSWantedListingOver(0, 0)); - c.sendPacket(PacketCreator.showMTSCash(c.getPlayer())); - List items = new ArrayList<>(); - int pages = 0; - try (Connection con = DatabaseConnection.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT * FROM mts_items WHERE tab = 1 AND transfer = 0 ORDER BY id DESC LIMIT 16, 16"); - ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - if (rs.getInt("type") != 1) { - Item i = new Item(rs.getInt("itemid"), (short) 0, (short) rs.getInt("quantity")); - i.setOwner(rs.getString("owner")); - items.add(new MTSItemInfo(i, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); - } else { - Equip equip = new Equip(rs.getInt("itemid"), (byte) rs.getInt("position"), -1); - equip.setOwner(rs.getString("owner")); - equip.setQuantity((short) 1); - equip.setAcc((short) rs.getInt("acc")); - equip.setAvoid((short) rs.getInt("avoid")); - equip.setDex((short) rs.getInt("dex")); - equip.setHands((short) rs.getInt("hands")); - equip.setHp((short) rs.getInt("hp")); - equip.setInt((short) rs.getInt("int")); - equip.setJump((short) rs.getInt("jump")); - equip.setVicious((short) rs.getInt("vicious")); - equip.setFlag((short) rs.getInt("flag")); - equip.setLuk((short) rs.getInt("luk")); - equip.setMatk((short) rs.getInt("matk")); - equip.setMdef((short) rs.getInt("mdef")); - equip.setMp((short) rs.getInt("mp")); - equip.setSpeed((short) rs.getInt("speed")); - equip.setStr((short) rs.getInt("str")); - equip.setWatk((short) rs.getInt("watk")); - equip.setWdef((short) rs.getInt("wdef")); - equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); - equip.setLevel((byte) rs.getInt("level")); - equip.setItemLevel(rs.getByte("itemlevel")); - equip.setItemExp(rs.getInt("itemexp")); - equip.setRingId(rs.getInt("ringid")); - equip.setExpiration(rs.getLong("expiration")); - equip.setGiftFrom(rs.getString("giftFrom")); - - items.add(new MTSItemInfo(equip, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); - } - } - } - - try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items"); - ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - pages = (int) Math.ceil(rs.getInt(1) / 16); - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - c.sendPacket(PacketCreator.sendMTS(items, 1, 0, 0, pages)); - c.sendPacket(PacketCreator.transferInventory(getTransfer(chr.getId()))); - c.sendPacket(PacketCreator.notYetSoldInv(getNotYetSold(chr.getId()))); + return; } + + if (chr.getEventInstance() != null) { + c.sendPacket(PacketCreator.serverNotice(5, "Entering Cash Shop or MTS are disabled when registered on an event.")); + c.sendPacket(PacketCreator.enableActions()); + return; + } + + if (MiniDungeonInfo.isDungeonMap(chr.getMapId())) { + c.sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon.")); + c.sendPacket(PacketCreator.enableActions()); + return; + } + + if (FieldLimit.CANNOTMIGRATE.check(chr.getMap().getFieldLimit())) { + chr.dropMessage(1, "You can't do it here in this map."); + c.sendPacket(PacketCreator.enableActions()); + return; + } + + if (!chr.isAlive()) { + c.sendPacket(PacketCreator.enableActions()); + return; + } + if (chr.getLevel() < 10) { + c.sendPacket(PacketCreator.blockedMessage2(5)); + c.sendPacket(PacketCreator.enableActions()); + return; + } + + chr.closePlayerInteractions(); + chr.closePartySearchInteractions(); + + chr.unregisterChairBuff(); + Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); + Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases()); + chr.setAwayFromChannelWorld(); + chr.notifyMapTransferToPartner(-1); + chr.removeIncomingInvites(); + chr.cancelAllBuffs(true); + chr.cancelAllDebuffs(); + chr.cancelBuffExpireTask(); + chr.cancelDiseaseExpireTask(); + chr.cancelSkillCooldownTask(); + chr.cancelExpirationTask(); + + chr.forfeitExpirableQuests(); + chr.cancelQuestExpirationTask(); + + chr.saveCharToDB(); + + c.getChannelServer().removePlayer(chr); + chr.getMap().removePlayer(c.getPlayer()); + try { + c.sendPacket(PacketCreator.openCashShop(c, true)); + } catch (Exception ex) { + ex.printStackTrace(); + } + chr.getCashShop().open(true);// xD + c.enableCSActions(); + c.sendPacket(PacketCreator.MTSWantedListingOver(0, 0)); + c.sendPacket(PacketCreator.showMTSCash(c.getPlayer())); + List items = new ArrayList<>(); + int pages = 0; + try (Connection con = DatabaseConnection.getConnection()) { + try (PreparedStatement ps = con.prepareStatement("SELECT * FROM mts_items WHERE tab = 1 AND transfer = 0 ORDER BY id DESC LIMIT 16, 16"); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + if (rs.getInt("type") != 1) { + Item i = new Item(rs.getInt("itemid"), (short) 0, (short) rs.getInt("quantity")); + i.setOwner(rs.getString("owner")); + items.add(new MTSItemInfo(i, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); + } else { + Equip equip = new Equip(rs.getInt("itemid"), (byte) rs.getInt("position"), -1); + equip.setOwner(rs.getString("owner")); + equip.setQuantity((short) 1); + equip.setAcc((short) rs.getInt("acc")); + equip.setAvoid((short) rs.getInt("avoid")); + equip.setDex((short) rs.getInt("dex")); + equip.setHands((short) rs.getInt("hands")); + equip.setHp((short) rs.getInt("hp")); + equip.setInt((short) rs.getInt("int")); + equip.setJump((short) rs.getInt("jump")); + equip.setVicious((short) rs.getInt("vicious")); + equip.setFlag((short) rs.getInt("flag")); + equip.setLuk((short) rs.getInt("luk")); + equip.setMatk((short) rs.getInt("matk")); + equip.setMdef((short) rs.getInt("mdef")); + equip.setMp((short) rs.getInt("mp")); + equip.setSpeed((short) rs.getInt("speed")); + equip.setStr((short) rs.getInt("str")); + equip.setWatk((short) rs.getInt("watk")); + equip.setWdef((short) rs.getInt("wdef")); + equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); + equip.setLevel((byte) rs.getInt("level")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); + + items.add(new MTSItemInfo(equip, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); + } + } + } + + try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items"); + ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + pages = (int) Math.ceil(rs.getInt(1) / 16); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + c.sendPacket(PacketCreator.sendMTS(items, 1, 0, 0, pages)); + c.sendPacket(PacketCreator.transferInventory(getTransfer(chr.getId()))); + c.sendPacket(PacketCreator.notYetSoldInv(getNotYetSold(chr.getId()))); } private List getNotYetSold(int cid) { @@ -276,4 +271,4 @@ public final class EnterMTSHandler extends AbstractPacketHandler { } return items; } -} \ No newline at end of file +} diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index b7dfe1e1b8..7471e903b7 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -20,11 +20,29 @@ */ package tools; +import client.BuddylistEntry; +import client.BuffStat; import client.Character; -import client.*; import client.Character.SkillEntry; -import client.inventory.*; +import client.Client; +import client.Disease; +import client.FamilyEntitlement; +import client.FamilyEntry; +import client.MonsterBook; +import client.Mount; +import client.QuestStatus; +import client.Ring; +import client.Skill; +import client.SkillMacro; +import client.Stat; +import client.inventory.Equip; import client.inventory.Equip.ScrollResult; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ItemFactory; +import client.inventory.ModifyInventory; +import client.inventory.Pet; import client.keybind.KeyBinding; import client.keybind.QuickslotBinding; import client.newyear.NewYearCardRecord; @@ -62,19 +80,45 @@ import net.server.world.World; import server.CashShop.CashItem; import server.CashShop.CashItemFactory; import server.CashShop.SpecialCashItem; -import server.*; +import server.DueyPackage; +import server.ItemInformationProvider; +import server.MTSItemInfo; +import server.ShopItem; +import server.Trade; import server.events.gm.Snowball; -import server.life.*; -import server.maps.*; +import server.life.MobSkill; +import server.life.MobSkillId; +import server.life.Monster; +import server.life.NPC; +import server.life.PlayerNPC; +import server.maps.AbstractMapObject; +import server.maps.Door; +import server.maps.DoorObject; +import server.maps.Dragon; +import server.maps.HiredMerchant; +import server.maps.MapItem; +import server.maps.MapleMap; +import server.maps.MiniGame; import server.maps.MiniGame.MiniGameResult; +import server.maps.Mist; +import server.maps.PlayerShop; +import server.maps.PlayerShopItem; +import server.maps.Reactor; +import server.maps.Summon; import server.movement.LifeMovementFragment; import java.awt.*; import java.net.InetAddress; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; -import java.util.*; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.TimeZone; import java.util.stream.Collectors; /** @@ -6136,23 +6180,6 @@ public class PacketCreator { return showSpecialEffect(15); } - public static Packet showBuybackEffect() { - final OutPacket p = OutPacket.create(SendOpcode.SHOW_ITEM_GAIN_INCHAT); - p.writeByte(11); - p.writeInt(0); - - return p; - } - - public static Packet showForeignBuybackEffect(int cid) { - final OutPacket p = OutPacket.create(SendOpcode.SHOW_FOREIGN_EFFECT); - p.writeInt(cid); - p.writeByte(11); - p.writeInt(0); - - return p; - } - /** * 0 = Levelup 6 = Exp did not drop (Safety Charms) 7 = Enter portal sound * 8 = Job change 9 = Quest complete 10 = Recovery 11 = Buff effect diff --git a/wz/Sound.wz/Field.img.xml b/wz/Sound.wz/Field.img.xml index c946c981f3..2213c14dc4 100644 --- a/wz/Sound.wz/Field.img.xml +++ b/wz/Sound.wz/Field.img.xml @@ -85,27 +85,4 @@ - - - - - - - - - From 98fd8c13d48d203d2158b7f8aa4e530a45a92e02 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 21 May 2024 19:54:30 +0200 Subject: [PATCH 18/55] Remove brazil anthem --- scripts/npc/9977777.js | 3 --- wz/Sound.wz/Field.img.xml | 3 --- 2 files changed, 6 deletions(-) diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index 7231068bd4..45d7ab3198 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -24,7 +24,6 @@ var status; -var anthemSong = "Field/anthem/brazil"; // sound src: https://c7.rbxcdn.com/f91060652a6e9fbfbf92cb1418435448 var ambientSong = "Bgm04/Shinin'Harbor"; var feature_tree = []; @@ -307,8 +306,6 @@ function writeAllFeatures() { } function start() { - const PacketCreator = Java.type('tools.PacketCreator'); - cm.getPlayer().sendPacket(PacketCreator.musicChange(anthemSong)); status = -1; writeAllFeatures(); action(1, 0, 0); diff --git a/wz/Sound.wz/Field.img.xml b/wz/Sound.wz/Field.img.xml index 2213c14dc4..dfae853d1a 100644 --- a/wz/Sound.wz/Field.img.xml +++ b/wz/Sound.wz/Field.img.xml @@ -82,7 +82,4 @@ - - - From d55437ddf30994868b269eadb571cb21adfa9fdf Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 21 May 2024 19:56:09 +0200 Subject: [PATCH 19/55] Remove subway whistle --- scripts/event/Subway.js | 10 ---------- wz/Sound.wz/Field.img.xml | 3 --- 2 files changed, 13 deletions(-) diff --git a/scripts/event/Subway.js b/scripts/event/Subway.js index fd250d946a..34559ac878 100644 --- a/scripts/event/Subway.js +++ b/scripts/event/Subway.js @@ -36,12 +36,6 @@ function stopEntry() { } function takeoff() { - const PacketCreator = Java.type('tools.PacketCreator'); - - //sound src: https://www.soundjay.com/transportation/metro-door-close-01.mp3 - KC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle")); - NLC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle")); - em.setProperty("docked", "false"); KC_Waiting.warpEveryone(Subway_to_NLC.getId()); NLC_Waiting.warpEveryone(Subway_to_KC.getId()); @@ -52,10 +46,6 @@ function arrived() { Subway_to_KC.warpEveryone(KC_docked.getId(), 0); Subway_to_NLC.warpEveryone(NLC_docked.getId(), 0); scheduleNew(); - - const PacketCreator = Java.type('tools.PacketCreator'); - KC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle")); - NLC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle")); } function cancelSchedule() {} diff --git a/wz/Sound.wz/Field.img.xml b/wz/Sound.wz/Field.img.xml index dfae853d1a..ed8a2cdc37 100644 --- a/wz/Sound.wz/Field.img.xml +++ b/wz/Sound.wz/Field.img.xml @@ -79,7 +79,4 @@ - - - From 948a9de667d2aed19e365811315f773b9cfb2135 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 21 May 2024 20:23:34 +0200 Subject: [PATCH 20/55] Remove Spirit of Rock die sound --- scripts/quest/2293.js | 12 +++++++++--- wz/Sound.wz/Field.img.xml | 3 --- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/quest/2293.js b/scripts/quest/2293.js index 4dbb21b61b..a82f18c3b7 100644 --- a/scripts/quest/2293.js +++ b/scripts/quest/2293.js @@ -50,8 +50,14 @@ function end(mode, type, selection) { status++; } + // TODO: there are 10 different riffs; quest2288/0 through quest2288/9. + // One of the riffs should play randomly upon the death of Spirit of Rock, but there is currently no system in place to achieve that in a reasonable way. + // Spirit of Rock (4300013) spawns an invisible mob on death (Spirit of Rock's Soul, 4300017) which was likely used in some clever way in GMS. + // The map (103040430) has two scripts which could be useful: onFirstUserEnter=Depart_Boss_F_Enter and onUserEnter=Depart_BossEnter + // Currently, the best hypothesis is that one of the map scripts registers some form of "mob spawn" action/script that runs once the invisible mob spawns. + // The script would randomly pick one of the 10 riffs and then register it with all chrs on the map (to later be used by this quest 2293) and play it. if (status == 0) { - qm.sendSimple("Here, I'll give you some samples. Please listen to them and choose one. Please listen carefully before making your choide.\r\n\ + qm.sendSimple("Here, I'll give you some samples. Please listen to them and choose one. Please listen carefully before making your choice.\r\n\ \t#b#L1# Listen to song No. 1#l \r\n\ \t#L2# Listen to Song No. 2#l \r\n\ \t#L3# Listen to Song No. 3#l \r\n\ @@ -67,7 +73,7 @@ function end(mode, type, selection) { qm.sendOk("Was it this?"); status = -1; } else if (selection == 3) { - qm.playSound("quest2293/Die"); + qm.playSound("quest2288/6"); qm.sendOk("You heard that?"); status = -1; } else if (selection == 4) { @@ -88,4 +94,4 @@ function end(mode, type, selection) { } else if (status == 3) { qm.dispose(); } -} \ No newline at end of file +} diff --git a/wz/Sound.wz/Field.img.xml b/wz/Sound.wz/Field.img.xml index ed8a2cdc37..a9bea020ef 100644 --- a/wz/Sound.wz/Field.img.xml +++ b/wz/Sound.wz/Field.img.xml @@ -76,7 +76,4 @@ - - - From dee8651e6170fe34a80bfcce01886802c3d328d0 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 21 May 2024 21:36:54 +0200 Subject: [PATCH 21/55] Add DevtestCommand to easily test without restarting server --- scripts/devtest.js | 3 ++ .../java/client/command/CommandsExecutor.java | 2 + .../command/commands/gm6/DevtestCommand.java | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 scripts/devtest.js create mode 100644 src/main/java/client/command/commands/gm6/DevtestCommand.java diff --git a/scripts/devtest.js b/scripts/devtest.js new file mode 100644 index 0000000000..c2b5abe462 --- /dev/null +++ b/scripts/devtest.js @@ -0,0 +1,3 @@ +function run(chr) { + chr.message("devtest.js") +} diff --git a/src/main/java/client/command/CommandsExecutor.java b/src/main/java/client/command/CommandsExecutor.java index a95e5943e2..16de527825 100644 --- a/src/main/java/client/command/CommandsExecutor.java +++ b/src/main/java/client/command/CommandsExecutor.java @@ -184,6 +184,7 @@ import client.command.commands.gm5.ShowSessionsCommand; import client.command.commands.gm6.ClearQuestCacheCommand; import client.command.commands.gm6.ClearQuestCommand; import client.command.commands.gm6.DCAllCommand; +import client.command.commands.gm6.DevtestCommand; import client.command.commands.gm6.EraseAllPNpcsCommand; import client.command.commands.gm6.GetAccCommand; import client.command.commands.gm6.MapPlayersCommand; @@ -556,6 +557,7 @@ public class CommandsExecutor { addCommand("addworld", 6, ServerAddWorldCommand.class); addCommand("removechannel", 6, ServerRemoveChannelCommand.class); addCommand("removeworld", 6, ServerRemoveWorldCommand.class); + addCommand("devtest", 6, DevtestCommand.class); commandsNameDesc.add(levelCommandsCursor); } diff --git a/src/main/java/client/command/commands/gm6/DevtestCommand.java b/src/main/java/client/command/commands/gm6/DevtestCommand.java new file mode 100644 index 0000000000..c0fd30dd57 --- /dev/null +++ b/src/main/java/client/command/commands/gm6/DevtestCommand.java @@ -0,0 +1,40 @@ +package client.command.commands.gm6; + +import client.Client; +import client.command.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import scripting.AbstractScriptManager; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +public class DevtestCommand extends Command { + { + setDescription("Runs devtest.js. Developer utility - test stuff without restarting the server."); + } + + private static final Logger log = LoggerFactory.getLogger(DevtestCommand.class); + + private static class DevtestScriptManager extends AbstractScriptManager { + + @Override + public ScriptEngine getInvocableScriptEngine(String path) { + return super.getInvocableScriptEngine(path); + } + + } + + @Override + public void execute(Client client, String[] params) { + DevtestScriptManager scriptManager = new DevtestScriptManager(); + ScriptEngine scriptEngine = scriptManager.getInvocableScriptEngine("devtest.js"); + try { + Invocable invocable = (Invocable) scriptEngine; + invocable.invokeFunction("run", client.getPlayer()); + } catch (ScriptException | NoSuchMethodException e) { + log.info("devtest.js run() threw an exception", e); + } + } +} From 9fe6ba4483d4680351a9122cfd67b2606e260d2f Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 21 May 2024 22:47:28 +0200 Subject: [PATCH 22/55] Add new wz files zip (reduced size by ~400mb) Removed: Base.wz, Effect.wz, Morph.wz, Sound.wz, TamingMob.wz --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f27a0df06..61394d1710 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ You will start by installing the game with the old installer, then overwrite som 1. Download _MapleGlobal-v83-setup.exe_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). This is the official installer from back then. 2. Install it in a directory of your choice. 3. Delete the following files from the installation directory: _HShield_ (entire directory), _ASPLnchr.exe_, _MapleStory.exe_, and _Patcher.exe_. -4. Download _CosmicWZ-v1-2021.05.10.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). +4. Download _CosmicWZ-2024-05-21-v0.13.0.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). 5. Unzip it and copy all .wz-files into the installation directory. Replace the existing ones. 6. Download _HeavenMS-localhost-WINDOW.exe_ from [hostr.co](https://hostr.co/amuX5SLeeVZx). This is a client modified to connect to your localhost instead of Nexon's server (along with some fixes and custom changes). - Your antivirus will likely detect the file as a trojan or similar and automatically delete it. To prevent this from happening, add your _Downloads_ directory and the installation directory as exclusions in your antivirus software. On W11, this is under "Virus & threat protection settings" -> "Add or remove exclusions". From 2ed35db216e1bb31d8fcc5bf2f960b97779b3826 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 22 May 2024 08:06:46 +0200 Subject: [PATCH 23/55] Add IntelliJ code style settings (no wildcard imports) --- .gitignore | 7 ++++++- .idea/codeStyles/Project.xml | 8 ++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.gitignore b/.gitignore index 580bdf21a1..8543b81417 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /logs/** -.idea/ +.idea/* +!.idea/codeStyles/ *.iml /target @@ -16,3 +17,7 @@ .project # JDT-specific (Eclipse Java Development Tools) .classpath + +# Database +database/docker-db-data +database/docker-pg-db-data diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..a74a84d3a0 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..79ee123c2b --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file From 4e743128e9613390c8320c13dbd75f9caeaa388a Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 22 May 2024 08:33:23 +0200 Subject: [PATCH 24/55] Remove all wildcard imports --- src/main/java/client/BuddyList.java | 7 +- src/main/java/client/Client.java | 26 ++- src/main/java/client/MonsterBook.java | 6 +- src/main/java/client/QuestStatus.java | 6 +- src/main/java/client/SkillFactory.java | 59 +++++- .../command/commands/gm0/UptimeCommand.java | 5 +- .../command/commands/gm1/GotoCommand.java | 6 +- .../command/commands/gm2/IdCommand.java | 6 +- .../command/commands/gm2/MaxSkillCommand.java | 5 +- .../commands/gm2/ResetSkillCommand.java | 5 +- .../command/commands/gm6/ShutdownCommand.java | 5 +- src/main/java/client/inventory/Inventory.java | 11 +- .../java/client/inventory/ItemFactory.java | 8 +- .../manipulator/InventoryManipulator.java | 7 +- .../client/processor/npc/DueyProcessor.java | 7 +- .../processor/npc/FredrickProcessor.java | 6 +- .../processor/stat/AssignAPProcessor.java | 13 +- src/main/java/net/PacketProcessor.java | 169 +++++++++++++++++- src/main/java/net/server/PlayerStorage.java | 8 +- src/main/java/net/server/Server.java | 31 +++- src/main/java/net/server/channel/Channel.java | 23 ++- .../handlers/AbstractDealDamageHandler.java | 67 ++++++- .../AbstractMovementPacketHandler.java | 7 +- .../handlers/BuddylistModifyHandler.java | 7 +- .../channel/handlers/CancelBuffHandler.java | 11 +- .../handlers/CloseRangeDamageHandler.java | 16 +- .../channel/handlers/CouponCodeHandler.java | 6 +- .../handlers/InventorySortHandler.java | 6 +- .../server/channel/handlers/MTSHandler.java | 1 - .../channel/handlers/MagicDamageHandler.java | 5 +- .../channel/handlers/MoveLifeHandler.java | 9 +- .../handlers/PlayerInteractionHandler.java | 9 +- .../handlers/PlayerLoggedinHandler.java | 23 ++- .../channel/handlers/RangedAttackHandler.java | 15 +- .../channel/handlers/SkillEffectHandler.java | 20 ++- .../channel/handlers/SpecialMoveHandler.java | 10 +- .../channel/handlers/TakeDamageHandler.java | 12 +- .../channel/handlers/TransferNameHandler.java | 8 +- .../handlers/TransferWorldHandler.java | 8 +- .../channel/handlers/UseCashItemHandler.java | 25 ++- .../matchchecker/MatchCheckerCoordinator.java | 6 +- .../partysearch/PartySearchCoordinator.java | 8 +- .../partysearch/PartySearchStorage.java | 6 +- .../session/SessionCoordinator.java | 8 +- .../coordinator/session/SessionDAO.java | 6 +- .../world/MonsterAggroCoordinator.java | 8 +- src/main/java/net/server/guild/Guild.java | 8 +- .../handlers/login/LoginPasswordHandler.java | 8 +- .../handlers/login/ViewAllCharHandler.java | 6 +- .../net/server/services/BaseScheduler.java | 6 +- .../server/services/type/ChannelServices.java | 7 +- src/main/java/net/server/world/Party.java | 7 +- src/main/java/net/server/world/World.java | 49 ++++- src/main/java/server/CashShop.java | 12 +- src/main/java/server/ExpLogger.java | 10 +- src/main/java/server/StatEffect.java | 75 +++++++- src/main/java/server/Storage.java | 9 +- src/main/java/server/ThreadManager.java | 6 +- .../java/server/expeditions/Expedition.java | 8 +- .../server/expeditions/ExpeditionBossLog.java | 6 +- src/main/java/server/life/LifeFactory.java | 6 +- src/main/java/server/life/MobSkill.java | 5 +- .../java/server/life/MobSkillFactory.java | 5 +- src/main/java/server/life/Monster.java | 29 ++- .../life/MonsterInformationProvider.java | 8 +- src/main/java/server/life/MonsterStats.java | 8 +- src/main/java/server/life/PlayerNPC.java | 14 +- .../maps/AbstractAnimatedMapObject.java | 6 +- src/main/java/server/maps/HiredMerchant.java | 7 +- src/main/java/server/maps/MapleMap.java | 31 +++- src/main/java/server/maps/Mist.java | 6 +- src/main/java/server/maps/PlayerShop.java | 9 +- src/main/java/server/quest/Quest.java | 45 ++++- .../tools/mapletools/BossHpBarFetcher.java | 5 +- .../mapletools/CashCosmeticsChecker.java | 16 +- .../mapletools/CashCosmeticsFetcher.java | 6 +- .../tools/mapletools/CashDropFetcher.java | 10 +- .../tools/mapletools/CashVegaChecker.java | 7 +- .../tools/mapletools/CodeCouponGenerator.java | 9 +- .../tools/mapletools/CouponInstaller.java | 4 +- .../tools/mapletools/EmptyItemWzChecker.java | 16 +- .../mapletools/EquipmentOmniLeveller.java | 5 +- .../tools/mapletools/EventMethodFiller.java | 7 +- .../mapletools/GachaponItemIdRetriever.java | 11 +- .../java/tools/mapletools/IdRetriever.java | 7 +- .../mapletools/MapFieldLimitChecker.java | 3 +- .../java/tools/mapletools/MobBookIndexer.java | 4 +- .../java/tools/mapletools/MobBookUpdate.java | 5 +- .../tools/mapletools/MonsterStatFetcher.java | 20 ++- .../tools/mapletools/NoItemIdFetcher.java | 16 +- .../tools/mapletools/NoItemNameFetcher.java | 15 +- .../mapletools/QuestItemCountFetcher.java | 13 +- .../tools/mapletools/QuestItemFetcher.java | 9 +- .../tools/mapletools/QuestMesoFetcher.java | 12 +- .../tools/mapletools/QuestlineFetcher.java | 18 +- .../tools/mapletools/ReactorDropFetcher.java | 8 +- .../tools/mapletools/SkillMakerFetcher.java | 5 +- .../mapletools/SkillMakerReagentIndexer.java | 5 +- .../mapletools/SkillbookStackUpdate.java | 4 +- .../tools/mapletools/WorldmapChecker.java | 15 +- src/test/java/model/NoteTest.java | 5 +- .../java/server/life/MobSkillFactoryTest.java | 7 +- src/test/java/service/NoteServiceTest.java | 13 +- 103 files changed, 1224 insertions(+), 178 deletions(-) diff --git a/src/main/java/client/BuddyList.java b/src/main/java/client/BuddyList.java index fdc98f3cff..6d2bdb974a 100644 --- a/src/main/java/client/BuddyList.java +++ b/src/main/java/client/BuddyList.java @@ -30,7 +30,12 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; public class BuddyList { public enum BuddyOperation { diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index edeea33ba8..b3292fd04d 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -44,7 +44,11 @@ import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.guild.Guild; import net.server.guild.GuildCharacter; import net.server.guild.GuildPackets; -import net.server.world.*; +import net.server.world.MessengerCharacter; +import net.server.world.Party; +import net.server.world.PartyCharacter; +import net.server.world.PartyOperation; +import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scripting.AbstractPlayerInteraction; @@ -73,9 +77,23 @@ import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; import java.util.Date; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -1581,4 +1599,4 @@ public class Client extends ChannelInboundHandlerAdapter { public void setLanguage(int lingua) { this.lang = lingua; } -} \ No newline at end of file +} diff --git a/src/main/java/client/MonsterBook.java b/src/main/java/client/MonsterBook.java index cb3e882e76..ca75906e82 100644 --- a/src/main/java/client/MonsterBook.java +++ b/src/main/java/client/MonsterBook.java @@ -28,8 +28,12 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/src/main/java/client/QuestStatus.java b/src/main/java/client/QuestStatus.java index 4e681f23c0..48ad279435 100644 --- a/src/main/java/client/QuestStatus.java +++ b/src/main/java/client/QuestStatus.java @@ -24,7 +24,11 @@ package client; import server.quest.Quest; import tools.StringUtil; -import java.util.*; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; /** * @author Matze diff --git a/src/main/java/client/SkillFactory.java b/src/main/java/client/SkillFactory.java index d4386c58f9..1c7b6fde80 100644 --- a/src/main/java/client/SkillFactory.java +++ b/src/main/java/client/SkillFactory.java @@ -21,8 +21,63 @@ */ package client; -import constants.skills.*; -import provider.*; +import constants.skills.Aran; +import constants.skills.Archer; +import constants.skills.Assassin; +import constants.skills.Bandit; +import constants.skills.Beginner; +import constants.skills.Bishop; +import constants.skills.BlazeWizard; +import constants.skills.Bowmaster; +import constants.skills.Buccaneer; +import constants.skills.ChiefBandit; +import constants.skills.Cleric; +import constants.skills.Corsair; +import constants.skills.Crossbowman; +import constants.skills.Crusader; +import constants.skills.DarkKnight; +import constants.skills.DawnWarrior; +import constants.skills.DragonKnight; +import constants.skills.Evan; +import constants.skills.FPArchMage; +import constants.skills.FPMage; +import constants.skills.FPWizard; +import constants.skills.Fighter; +import constants.skills.GM; +import constants.skills.Gunslinger; +import constants.skills.Hermit; +import constants.skills.Hero; +import constants.skills.Hunter; +import constants.skills.ILArchMage; +import constants.skills.ILMage; +import constants.skills.ILWizard; +import constants.skills.Legend; +import constants.skills.Magician; +import constants.skills.Marauder; +import constants.skills.Marksman; +import constants.skills.NightLord; +import constants.skills.NightWalker; +import constants.skills.Noblesse; +import constants.skills.Page; +import constants.skills.Paladin; +import constants.skills.Pirate; +import constants.skills.Priest; +import constants.skills.Ranger; +import constants.skills.Rogue; +import constants.skills.Shadower; +import constants.skills.Sniper; +import constants.skills.Spearman; +import constants.skills.SuperGM; +import constants.skills.ThunderBreaker; +import constants.skills.Warrior; +import constants.skills.WhiteKnight; +import constants.skills.WindArcher; +import provider.Data; +import provider.DataDirectoryEntry; +import provider.DataFileEntry; +import provider.DataProvider; +import provider.DataProviderFactory; +import provider.DataTool; import provider.wz.WZFiles; import server.StatEffect; import server.life.Element; diff --git a/src/main/java/client/command/commands/gm0/UptimeCommand.java b/src/main/java/client/command/commands/gm0/UptimeCommand.java index fd4a234c5c..8080a4d41b 100644 --- a/src/main/java/client/command/commands/gm0/UptimeCommand.java +++ b/src/main/java/client/command/commands/gm0/UptimeCommand.java @@ -27,7 +27,10 @@ import client.Client; import client.command.Command; import net.server.Server; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; public class UptimeCommand extends Command { { diff --git a/src/main/java/client/command/commands/gm1/GotoCommand.java b/src/main/java/client/command/commands/gm1/GotoCommand.java index c6e32e2e3d..dfbf0c6105 100644 --- a/src/main/java/client/command/commands/gm1/GotoCommand.java +++ b/src/main/java/client/command/commands/gm1/GotoCommand.java @@ -28,7 +28,11 @@ import client.Client; import client.command.Command; import constants.game.GameConstants; import constants.id.NpcId; -import server.maps.*; +import server.maps.FieldLimit; +import server.maps.MapFactory; +import server.maps.MapleMap; +import server.maps.MiniDungeonInfo; +import server.maps.Portal; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/client/command/commands/gm2/IdCommand.java b/src/main/java/client/command/commands/gm2/IdCommand.java index c64466b68d..36d119b8a1 100644 --- a/src/main/java/client/command/commands/gm2/IdCommand.java +++ b/src/main/java/client/command/commands/gm2/IdCommand.java @@ -11,7 +11,11 @@ import tools.exceptions.IdTypeNotSupportedException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.stream.Collectors; diff --git a/src/main/java/client/command/commands/gm2/MaxSkillCommand.java b/src/main/java/client/command/commands/gm2/MaxSkillCommand.java index a91cd89097..8f1ffbd9a6 100644 --- a/src/main/java/client/command/commands/gm2/MaxSkillCommand.java +++ b/src/main/java/client/command/commands/gm2/MaxSkillCommand.java @@ -24,7 +24,10 @@ package client.command.commands.gm2; import client.Character; -import client.*; +import client.Client; +import client.Job; +import client.Skill; +import client.SkillFactory; import client.command.Command; import provider.Data; import provider.DataProviderFactory; diff --git a/src/main/java/client/command/commands/gm2/ResetSkillCommand.java b/src/main/java/client/command/commands/gm2/ResetSkillCommand.java index a1724f5d4a..db578291ac 100644 --- a/src/main/java/client/command/commands/gm2/ResetSkillCommand.java +++ b/src/main/java/client/command/commands/gm2/ResetSkillCommand.java @@ -24,7 +24,10 @@ package client.command.commands.gm2; import client.Character; -import client.*; +import client.Client; +import client.Job; +import client.Skill; +import client.SkillFactory; import client.command.Command; import provider.Data; import provider.DataProviderFactory; diff --git a/src/main/java/client/command/commands/gm6/ShutdownCommand.java b/src/main/java/client/command/commands/gm6/ShutdownCommand.java index ad575d29d4..a083c9b63f 100644 --- a/src/main/java/client/command/commands/gm6/ShutdownCommand.java +++ b/src/main/java/client/command/commands/gm6/ShutdownCommand.java @@ -30,7 +30,10 @@ import net.server.Server; import net.server.world.World; import server.TimerManager; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; public class ShutdownCommand extends Command { { diff --git a/src/main/java/client/inventory/Inventory.java b/src/main/java/client/inventory/Inventory.java index 6e1c9e433c..a0ddedda82 100644 --- a/src/main/java/client/inventory/Inventory.java +++ b/src/main/java/client/inventory/Inventory.java @@ -31,7 +31,14 @@ import server.ItemInformationProvider; import server.ThreadManager; import tools.Pair; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -652,4 +659,4 @@ public class Inventory implements Iterable { public void dispose() { owner = null; } -} \ No newline at end of file +} diff --git a/src/main/java/client/inventory/ItemFactory.java b/src/main/java/client/inventory/ItemFactory.java index 1c24f0108a..22538041a9 100644 --- a/src/main/java/client/inventory/ItemFactory.java +++ b/src/main/java/client/inventory/ItemFactory.java @@ -23,7 +23,11 @@ package client.inventory; import tools.DatabaseConnection; import tools.Pair; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; @@ -418,4 +422,4 @@ public enum ItemFactory { lock.unlock(); } } -} \ No newline at end of file +} diff --git a/src/main/java/client/inventory/manipulator/InventoryManipulator.java b/src/main/java/client/inventory/manipulator/InventoryManipulator.java index 331a7061b0..acf3607b64 100644 --- a/src/main/java/client/inventory/manipulator/InventoryManipulator.java +++ b/src/main/java/client/inventory/manipulator/InventoryManipulator.java @@ -24,7 +24,12 @@ package client.inventory.manipulator; import client.BuffStat; import client.Character; import client.Client; -import client.inventory.*; +import client.inventory.Equip; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ModifyInventory; +import client.inventory.Pet; import client.newyear.NewYearCardRecord; import config.YamlConfig; import constants.id.ItemId; diff --git a/src/main/java/client/processor/npc/DueyProcessor.java b/src/main/java/client/processor/npc/DueyProcessor.java index 8a8615985b..7aa5cfa7b7 100644 --- a/src/main/java/client/processor/npc/DueyProcessor.java +++ b/src/main/java/client/processor/npc/DueyProcessor.java @@ -45,7 +45,12 @@ import tools.DatabaseConnection; import tools.PacketCreator; import tools.Pair; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; import java.util.Calendar; import java.util.Collections; import java.util.LinkedList; diff --git a/src/main/java/client/processor/npc/FredrickProcessor.java b/src/main/java/client/processor/npc/FredrickProcessor.java index 5e0bd24015..ed91e2d083 100644 --- a/src/main/java/client/processor/npc/FredrickProcessor.java +++ b/src/main/java/client/processor/npc/FredrickProcessor.java @@ -41,7 +41,11 @@ import tools.DatabaseConnection; import tools.PacketCreator; import tools.Pair; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; import java.util.LinkedList; import java.util.List; diff --git a/src/main/java/client/processor/stat/AssignAPProcessor.java b/src/main/java/client/processor/stat/AssignAPProcessor.java index 8f33ac3b55..bc42c06cc5 100644 --- a/src/main/java/client/processor/stat/AssignAPProcessor.java +++ b/src/main/java/client/processor/stat/AssignAPProcessor.java @@ -24,13 +24,22 @@ package client.processor.stat; import client.Character; -import client.*; +import client.Client; +import client.Job; +import client.Skill; +import client.SkillFactory; +import client.Stat; import client.autoban.AutobanFactory; import client.inventory.Equip; import client.inventory.InventoryType; import client.inventory.Item; import config.YamlConfig; -import constants.skills.*; +import constants.skills.BlazeWizard; +import constants.skills.Brawler; +import constants.skills.DawnWarrior; +import constants.skills.Magician; +import constants.skills.ThunderBreaker; +import constants.skills.Warrior; import net.packet.InPacket; import tools.PacketCreator; import tools.Randomizer; diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index 391143cb1a..6a27fb7085 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -23,11 +23,174 @@ package net; import net.netty.LoginServer; import net.opcodes.RecvOpcode; -import net.server.channel.handlers.*; +import net.server.channel.handlers.AcceptFamilyHandler; +import net.server.channel.handlers.AdminChatHandler; +import net.server.channel.handlers.AdminCommandHandler; +import net.server.channel.handlers.AdminLogHandler; +import net.server.channel.handlers.AllianceOperationHandler; +import net.server.channel.handlers.AranComboHandler; +import net.server.channel.handlers.AutoAggroHandler; +import net.server.channel.handlers.AutoAssignHandler; +import net.server.channel.handlers.BBSOperationHandler; +import net.server.channel.handlers.BeholderHandler; +import net.server.channel.handlers.BuddylistModifyHandler; +import net.server.channel.handlers.CancelBuffHandler; +import net.server.channel.handlers.CancelChairHandler; +import net.server.channel.handlers.CancelDebuffHandler; +import net.server.channel.handlers.CancelItemEffectHandler; +import net.server.channel.handlers.CashOperationHandler; +import net.server.channel.handlers.CashShopSurpriseHandler; +import net.server.channel.handlers.ChangeChannelHandler; +import net.server.channel.handlers.ChangeMapHandler; +import net.server.channel.handlers.ChangeMapSpecialHandler; +import net.server.channel.handlers.CharInfoRequestHandler; +import net.server.channel.handlers.ClickGuideHandler; +import net.server.channel.handlers.CloseChalkboardHandler; +import net.server.channel.handlers.CloseRangeDamageHandler; +import net.server.channel.handlers.CoconutHandler; +import net.server.channel.handlers.CouponCodeHandler; +import net.server.channel.handlers.DamageSummonHandler; +import net.server.channel.handlers.DenyAllianceRequestHandler; +import net.server.channel.handlers.DenyGuildRequestHandler; +import net.server.channel.handlers.DenyPartyRequestHandler; +import net.server.channel.handlers.DistributeAPHandler; +import net.server.channel.handlers.DistributeSPHandler; +import net.server.channel.handlers.DoorHandler; +import net.server.channel.handlers.DueyHandler; +import net.server.channel.handlers.EnterCashShopHandler; +import net.server.channel.handlers.EnterMTSHandler; +import net.server.channel.handlers.FaceExpressionHandler; +import net.server.channel.handlers.FamilyAddHandler; +import net.server.channel.handlers.FamilyPreceptsHandler; +import net.server.channel.handlers.FamilySeparateHandler; +import net.server.channel.handlers.FamilySummonResponseHandler; +import net.server.channel.handlers.FamilyUseHandler; +import net.server.channel.handlers.FieldDamageMobHandler; +import net.server.channel.handlers.FredrickHandler; +import net.server.channel.handlers.GeneralChatHandler; +import net.server.channel.handlers.GiveFameHandler; +import net.server.channel.handlers.GrenadeEffectHandler; +import net.server.channel.handlers.GuildOperationHandler; +import net.server.channel.handlers.HealOvertimeHandler; +import net.server.channel.handlers.HiredMerchantRequest; +import net.server.channel.handlers.InnerPortalHandler; +import net.server.channel.handlers.InventoryMergeHandler; +import net.server.channel.handlers.InventorySortHandler; +import net.server.channel.handlers.ItemMoveHandler; +import net.server.channel.handlers.ItemPickupHandler; +import net.server.channel.handlers.ItemRewardHandler; +import net.server.channel.handlers.KeymapChangeHandler; +import net.server.channel.handlers.LeftKnockbackHandler; +import net.server.channel.handlers.MTSHandler; +import net.server.channel.handlers.MagicDamageHandler; +import net.server.channel.handlers.MakerSkillHandler; +import net.server.channel.handlers.MesoDropHandler; +import net.server.channel.handlers.MessengerHandler; +import net.server.channel.handlers.MobBanishPlayerHandler; +import net.server.channel.handlers.MobDamageMobFriendlyHandler; +import net.server.channel.handlers.MobDamageMobHandler; +import net.server.channel.handlers.MonsterBombHandler; +import net.server.channel.handlers.MonsterBookCoverHandler; +import net.server.channel.handlers.MonsterCarnivalHandler; +import net.server.channel.handlers.MoveDragonHandler; +import net.server.channel.handlers.MoveLifeHandler; +import net.server.channel.handlers.MovePetHandler; +import net.server.channel.handlers.MovePlayerHandler; +import net.server.channel.handlers.MoveSummonHandler; +import net.server.channel.handlers.MultiChatHandler; +import net.server.channel.handlers.NPCAnimationHandler; +import net.server.channel.handlers.NPCMoreTalkHandler; +import net.server.channel.handlers.NPCShopHandler; +import net.server.channel.handlers.NPCTalkHandler; +import net.server.channel.handlers.NewYearCardHandler; +import net.server.channel.handlers.NoteActionHandler; +import net.server.channel.handlers.OpenFamilyHandler; +import net.server.channel.handlers.OpenFamilyPedigreeHandler; +import net.server.channel.handlers.OwlWarpHandler; +import net.server.channel.handlers.PartyOperationHandler; +import net.server.channel.handlers.PartySearchRegisterHandler; +import net.server.channel.handlers.PartySearchStartHandler; +import net.server.channel.handlers.PartySearchUpdateHandler; +import net.server.channel.handlers.PetAutoPotHandler; +import net.server.channel.handlers.PetChatHandler; +import net.server.channel.handlers.PetCommandHandler; +import net.server.channel.handlers.PetExcludeItemsHandler; +import net.server.channel.handlers.PetFoodHandler; +import net.server.channel.handlers.PetLootHandler; +import net.server.channel.handlers.PlayerInteractionHandler; +import net.server.channel.handlers.PlayerLoggedinHandler; +import net.server.channel.handlers.PlayerMapTransitionHandler; +import net.server.channel.handlers.QuestActionHandler; +import net.server.channel.handlers.QuickslotKeyMappedModifiedHandler; +import net.server.channel.handlers.RPSActionHandler; +import net.server.channel.handlers.RaiseIncExpHandler; +import net.server.channel.handlers.RaiseUIStateHandler; +import net.server.channel.handlers.RangedAttackHandler; +import net.server.channel.handlers.ReactorHitHandler; +import net.server.channel.handlers.RemoteGachaponHandler; +import net.server.channel.handlers.RemoteStoreHandler; +import net.server.channel.handlers.ReportHandler; +import net.server.channel.handlers.RingActionHandler; +import net.server.channel.handlers.ScriptedItemHandler; +import net.server.channel.handlers.ScrollHandler; +import net.server.channel.handlers.SkillBookHandler; +import net.server.channel.handlers.SkillEffectHandler; +import net.server.channel.handlers.SkillMacroHandler; +import net.server.channel.handlers.SnowballHandler; +import net.server.channel.handlers.SpawnPetHandler; +import net.server.channel.handlers.SpecialMoveHandler; +import net.server.channel.handlers.SpouseChatHandler; +import net.server.channel.handlers.StorageHandler; +import net.server.channel.handlers.SummonDamageHandler; +import net.server.channel.handlers.TakeDamageHandler; +import net.server.channel.handlers.TouchMonsterDamageHandler; +import net.server.channel.handlers.TouchReactorHandler; +import net.server.channel.handlers.TouchingCashShopHandler; +import net.server.channel.handlers.TransferNameHandler; +import net.server.channel.handlers.TransferNameResultHandler; +import net.server.channel.handlers.TransferWorldHandler; +import net.server.channel.handlers.TrockAddMapHandler; +import net.server.channel.handlers.UseCashItemHandler; +import net.server.channel.handlers.UseCatchItemHandler; +import net.server.channel.handlers.UseChairHandler; +import net.server.channel.handlers.UseDeathItemHandler; +import net.server.channel.handlers.UseGachaExpHandler; +import net.server.channel.handlers.UseHammerHandler; +import net.server.channel.handlers.UseItemEffectHandler; +import net.server.channel.handlers.UseItemHandler; +import net.server.channel.handlers.UseMapleLifeHandler; +import net.server.channel.handlers.UseMountFoodHandler; +import net.server.channel.handlers.UseOwlOfMinervaHandler; +import net.server.channel.handlers.UseSolomonHandler; +import net.server.channel.handlers.UseSummonBagHandler; +import net.server.channel.handlers.UseWaterOfLifeHandler; +import net.server.channel.handlers.WeddingHandler; +import net.server.channel.handlers.WeddingTalkHandler; +import net.server.channel.handlers.WeddingTalkMoreHandler; +import net.server.channel.handlers.WhisperHandler; import net.server.handlers.CustomPacketHandler; import net.server.handlers.KeepAliveHandler; import net.server.handlers.LoginRequiringNoOpHandler; -import net.server.handlers.login.*; +import net.server.handlers.login.AcceptToSHandler; +import net.server.handlers.login.AfterLoginHandler; +import net.server.handlers.login.CharSelectedHandler; +import net.server.handlers.login.CharSelectedWithPicHandler; +import net.server.handlers.login.CharlistRequestHandler; +import net.server.handlers.login.CheckCharNameHandler; +import net.server.handlers.login.CreateCharHandler; +import net.server.handlers.login.DeleteCharHandler; +import net.server.handlers.login.GuestLoginHandler; +import net.server.handlers.login.LoginPasswordHandler; +import net.server.handlers.login.RegisterPicHandler; +import net.server.handlers.login.RegisterPinHandler; +import net.server.handlers.login.RelogRequestHandler; +import net.server.handlers.login.ServerStatusRequestHandler; +import net.server.handlers.login.ServerlistRequestHandler; +import net.server.handlers.login.SetGenderHandler; +import net.server.handlers.login.ViewAllCharHandler; +import net.server.handlers.login.ViewAllCharRegisterPicHandler; +import net.server.handlers.login.ViewAllCharSelectedHandler; +import net.server.handlers.login.ViewAllCharSelectedWithPicHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -286,4 +449,4 @@ public final class PacketProcessor { registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler()); registerHandler(RecvOpcode.CHANGE_QUICKSLOT, new QuickslotKeyMappedModifiedHandler()); } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/PlayerStorage.java b/src/main/java/net/server/PlayerStorage.java index fd406cadbb..c964d357f8 100644 --- a/src/main/java/net/server/PlayerStorage.java +++ b/src/main/java/net/server/PlayerStorage.java @@ -24,7 +24,11 @@ package net.server; import client.Character; import client.Client; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -124,4 +128,4 @@ public class PlayerStorage { rlock.unlock(); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 679d136643..1b0159d2cc 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -47,7 +47,17 @@ import net.server.coordinator.session.SessionCoordinator; import net.server.guild.Alliance; import net.server.guild.Guild; import net.server.guild.GuildCharacter; -import net.server.task.*; +import net.server.task.BossLogTask; +import net.server.task.CharacterDiseaseTask; +import net.server.task.CouponTask; +import net.server.task.DueyFredrickTask; +import net.server.task.EventRecallCoordinatorTask; +import net.server.task.InvitationTask; +import net.server.task.LoginCoordinatorTask; +import net.server.task.LoginStorageTask; +import net.server.task.RankingCommandTask; +import net.server.task.RankingLoginTask; +import net.server.task.RespawnTask; import net.server.world.World; import org.apache.logging.log4j.LogManager; import org.slf4j.Logger; @@ -69,8 +79,20 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.SortedMap; +import java.util.TimeZone; +import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -80,7 +102,10 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; public class Server { private static final Logger log = LoggerFactory.getLogger(Server.class); diff --git a/src/main/java/net/server/channel/Channel.java b/src/main/java/net/server/channel/Channel.java index 98c3151953..13a0d22a95 100644 --- a/src/main/java/net/server/channel/Channel.java +++ b/src/main/java/net/server/channel/Channel.java @@ -41,7 +41,11 @@ import server.TimerManager; import server.events.gm.Event; import server.expeditions.Expedition; import server.expeditions.ExpeditionType; -import server.maps.*; +import server.maps.HiredMerchant; +import server.maps.MapManager; +import server.maps.MapleMap; +import server.maps.MiniDungeon; +import server.maps.MiniDungeonInfo; import tools.PacketCreator; import tools.Pair; @@ -49,14 +53,25 @@ import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; public final class Channel { private static final Logger log = LoggerFactory.getLogger(Channel.class); @@ -1035,4 +1050,4 @@ public final class Channel { log.debug("Guest list: {}", ongoingChapelGuests); log.debug("Starttime: {}", ongoingStartTime); } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index 62dac683ce..8a882e2a0c 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -21,8 +21,11 @@ */ package net.server.channel.handlers; +import client.BuffStat; import client.Character; -import client.*; +import client.Job; +import client.Skill; +import client.SkillFactory; import client.autoban.AutobanFactory; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; @@ -31,14 +34,66 @@ import constants.game.GameConstants; import constants.id.ItemId; import constants.id.MapId; import constants.id.MobId; -import constants.skills.*; +import constants.skills.Aran; +import constants.skills.Assassin; +import constants.skills.Bandit; +import constants.skills.Beginner; +import constants.skills.Bishop; +import constants.skills.BlazeWizard; +import constants.skills.Bowmaster; +import constants.skills.Brawler; +import constants.skills.Buccaneer; +import constants.skills.ChiefBandit; +import constants.skills.Cleric; +import constants.skills.Corsair; +import constants.skills.Crossbowman; +import constants.skills.Crusader; +import constants.skills.DawnWarrior; +import constants.skills.DragonKnight; +import constants.skills.Evan; +import constants.skills.FPArchMage; +import constants.skills.FPMage; +import constants.skills.FPWizard; +import constants.skills.Fighter; +import constants.skills.Gunslinger; +import constants.skills.Hermit; +import constants.skills.Hero; +import constants.skills.Hunter; +import constants.skills.ILArchMage; +import constants.skills.ILMage; +import constants.skills.Legend; +import constants.skills.Marauder; +import constants.skills.Marksman; +import constants.skills.NightLord; +import constants.skills.NightWalker; +import constants.skills.Noblesse; +import constants.skills.Outlaw; +import constants.skills.Page; +import constants.skills.Paladin; +import constants.skills.Ranger; +import constants.skills.Rogue; +import constants.skills.Shadower; +import constants.skills.Sniper; +import constants.skills.Spearman; +import constants.skills.SuperGM; +import constants.skills.ThunderBreaker; +import constants.skills.WhiteKnight; +import constants.skills.WindArcher; import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.PlayerBuffValueHolder; import scripting.AbstractPlayerInteraction; import server.StatEffect; import server.TimerManager; -import server.life.*; +import server.life.Element; +import server.life.ElementalEffectiveness; +import server.life.MobSkill; +import server.life.MobSkillFactory; +import server.life.MobSkillId; +import server.life.MobSkillType; +import server.life.Monster; +import server.life.MonsterDropEntry; +import server.life.MonsterInformationProvider; import server.maps.MapItem; import server.maps.MapObject; import server.maps.MapObjectType; @@ -47,8 +102,12 @@ import tools.PacketCreator; import tools.Randomizer; import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; -import java.util.*; +import java.util.Map; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; diff --git a/src/main/java/net/server/channel/handlers/AbstractMovementPacketHandler.java b/src/main/java/net/server/channel/handlers/AbstractMovementPacketHandler.java index b6dea059aa..e15d75e736 100644 --- a/src/main/java/net/server/channel/handlers/AbstractMovementPacketHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractMovementPacketHandler.java @@ -26,7 +26,12 @@ import net.packet.InPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import server.maps.AnimatedMapObject; -import server.movement.*; +import server.movement.AbsoluteLifeMovement; +import server.movement.ChangeEquip; +import server.movement.JumpDownMovement; +import server.movement.LifeMovementFragment; +import server.movement.RelativeLifeMovement; +import server.movement.TeleportMovement; import tools.exceptions.EmptyMovementException; import java.awt.*; diff --git a/src/main/java/net/server/channel/handlers/BuddylistModifyHandler.java b/src/main/java/net/server/channel/handlers/BuddylistModifyHandler.java index 88b4453a99..a6d9bb66d0 100644 --- a/src/main/java/net/server/channel/handlers/BuddylistModifyHandler.java +++ b/src/main/java/net/server/channel/handlers/BuddylistModifyHandler.java @@ -21,10 +21,13 @@ */ package net.server.channel.handlers; -import client.*; +import client.BuddyList; import client.BuddyList.BuddyAddResult; -import client.Character; import client.BuddyList.BuddyOperation; +import client.BuddylistEntry; +import client.Character; +import client.CharacterNameAndId; +import client.Client; import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.world.World; diff --git a/src/main/java/net/server/channel/handlers/CancelBuffHandler.java b/src/main/java/net/server/channel/handlers/CancelBuffHandler.java index a9788250b2..584f6ecc73 100644 --- a/src/main/java/net/server/channel/handlers/CancelBuffHandler.java +++ b/src/main/java/net/server/channel/handlers/CancelBuffHandler.java @@ -23,7 +23,14 @@ package net.server.channel.handlers; import client.Client; import client.SkillFactory; -import constants.skills.*; +import constants.skills.Bishop; +import constants.skills.Bowmaster; +import constants.skills.Corsair; +import constants.skills.Evan; +import constants.skills.FPArchMage; +import constants.skills.ILArchMage; +import constants.skills.Marksman; +import constants.skills.WindArcher; import net.AbstractPacketHandler; import net.PacketHandler; import net.packet.InPacket; @@ -53,4 +60,4 @@ public final class CancelBuffHandler extends AbstractPacketHandler implements Pa break; } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java index 756675623b..5a8a9bef61 100644 --- a/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java @@ -21,12 +21,22 @@ */ package net.server.channel.handlers; +import client.BuffStat; import client.Character; -import client.*; +import client.Client; +import client.Job; +import client.Skill; +import client.SkillFactory; import config.YamlConfig; import constants.game.GameConstants; import constants.id.MapId; -import constants.skills.*; +import constants.skills.Crusader; +import constants.skills.DawnWarrior; +import constants.skills.DragonKnight; +import constants.skills.Hero; +import constants.skills.NightWalker; +import constants.skills.Rogue; +import constants.skills.WindArcher; import net.packet.InPacket; import server.StatEffect; import tools.PacketCreator; @@ -183,4 +193,4 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { applyAttack(attack, chr, attackCount); } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/CouponCodeHandler.java b/src/main/java/net/server/channel/handlers/CouponCodeHandler.java index 7f5085990c..199d0d376f 100644 --- a/src/main/java/net/server/channel/handlers/CouponCodeHandler.java +++ b/src/main/java/net/server/channel/handlers/CouponCodeHandler.java @@ -42,7 +42,11 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; /** diff --git a/src/main/java/net/server/channel/handlers/InventorySortHandler.java b/src/main/java/net/server/channel/handlers/InventorySortHandler.java index e6721569bf..f60824475f 100644 --- a/src/main/java/net/server/channel/handlers/InventorySortHandler.java +++ b/src/main/java/net/server/channel/handlers/InventorySortHandler.java @@ -23,7 +23,11 @@ package net.server.channel.handlers; import client.Character; import client.Client; -import client.inventory.*; +import client.inventory.Equip; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ModifyInventory; import config.YamlConfig; import net.AbstractPacketHandler; import net.packet.InPacket; diff --git a/src/main/java/net/server/channel/handlers/MTSHandler.java b/src/main/java/net/server/channel/handlers/MTSHandler.java index abffbe9ad1..bc5d3dac74 100644 --- a/src/main/java/net/server/channel/handlers/MTSHandler.java +++ b/src/main/java/net/server/channel/handlers/MTSHandler.java @@ -48,7 +48,6 @@ import java.sql.SQLException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; public final class MTSHandler extends AbstractPacketHandler { diff --git a/src/main/java/net/server/channel/handlers/MagicDamageHandler.java b/src/main/java/net/server/channel/handlers/MagicDamageHandler.java index 8b8201e863..7350e55c55 100644 --- a/src/main/java/net/server/channel/handlers/MagicDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/MagicDamageHandler.java @@ -21,8 +21,11 @@ */ package net.server.channel.handlers; +import client.BuffStat; import client.Character; -import client.*; +import client.Client; +import client.Skill; +import client.SkillFactory; import config.YamlConfig; import constants.id.MapId; import constants.skills.Bishop; diff --git a/src/main/java/net/server/channel/handlers/MoveLifeHandler.java b/src/main/java/net/server/channel/handlers/MoveLifeHandler.java index 0f6ea855a5..34ec1daad7 100644 --- a/src/main/java/net/server/channel/handlers/MoveLifeHandler.java +++ b/src/main/java/net/server/channel/handlers/MoveLifeHandler.java @@ -27,7 +27,12 @@ import config.YamlConfig; import net.packet.InPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import server.life.*; +import server.life.MobSkill; +import server.life.MobSkillFactory; +import server.life.MobSkillId; +import server.life.MobSkillType; +import server.life.Monster; +import server.life.MonsterInformationProvider; import server.maps.MapObject; import server.maps.MapObjectType; import server.maps.MapleMap; @@ -176,4 +181,4 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { private static boolean inRangeInclusive(Byte pVal, Integer pMin, Integer pMax) { return !(pVal < pMin) || (pVal > pMax); } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java b/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java index a671b51690..f8680e757a 100644 --- a/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java @@ -39,8 +39,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import server.ItemInformationProvider; import server.Trade; -import server.maps.*; +import server.maps.FieldLimit; +import server.maps.HiredMerchant; +import server.maps.MapObject; +import server.maps.MapObjectType; +import server.maps.MiniGame; import server.maps.MiniGame.MiniGameType; +import server.maps.PlayerShop; +import server.maps.PlayerShopItem; +import server.maps.Portal; import tools.PacketCreator; import java.awt.*; diff --git a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java index 72a904006c..f3e2b9288c 100644 --- a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -21,9 +21,21 @@ */ package net.server.channel.handlers; +import client.BuddyList; +import client.BuddylistEntry; import client.Character; -import client.*; -import client.inventory.*; +import client.CharacterNameAndId; +import client.Client; +import client.Disease; +import client.Family; +import client.FamilyEntry; +import client.Mount; +import client.SkillFactory; +import client.inventory.Equip; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.Pet; import client.keybind.KeyBinding; import config.YamlConfig; import constants.game.GameConstants; @@ -56,8 +68,13 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.stream.Collectors; public final class PlayerLoggedinHandler extends AbstractPacketHandler { diff --git a/src/main/java/net/server/channel/handlers/RangedAttackHandler.java b/src/main/java/net/server/channel/handlers/RangedAttackHandler.java index a8ea010361..85d599b9fc 100644 --- a/src/main/java/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/main/java/net/server/channel/handlers/RangedAttackHandler.java @@ -21,8 +21,11 @@ along with this program. If not, see . */ package net.server.channel.handlers; +import client.BuffStat; import client.Character; -import client.*; +import client.Client; +import client.Skill; +import client.SkillFactory; import client.inventory.Inventory; import client.inventory.InventoryType; import client.inventory.Item; @@ -32,7 +35,13 @@ import config.YamlConfig; import constants.id.ItemId; import constants.id.MapId; import constants.inventory.ItemConstants; -import constants.skills.*; +import constants.skills.Aran; +import constants.skills.Buccaneer; +import constants.skills.NightLord; +import constants.skills.NightWalker; +import constants.skills.Shadower; +import constants.skills.ThunderBreaker; +import constants.skills.WindArcher; import net.packet.InPacket; import net.packet.Packet; import org.slf4j.Logger; @@ -237,4 +246,4 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/SkillEffectHandler.java b/src/main/java/net/server/channel/handlers/SkillEffectHandler.java index 6b5b251da0..e5443004c7 100644 --- a/src/main/java/net/server/channel/handlers/SkillEffectHandler.java +++ b/src/main/java/net/server/channel/handlers/SkillEffectHandler.java @@ -22,7 +22,23 @@ package net.server.channel.handlers; import client.Client; -import constants.skills.*; +import constants.skills.Bishop; +import constants.skills.Bowmaster; +import constants.skills.Brawler; +import constants.skills.ChiefBandit; +import constants.skills.Corsair; +import constants.skills.DarkKnight; +import constants.skills.Evan; +import constants.skills.FPArchMage; +import constants.skills.FPMage; +import constants.skills.Gunslinger; +import constants.skills.Hero; +import constants.skills.ILArchMage; +import constants.skills.Marksman; +import constants.skills.NightWalker; +import constants.skills.Paladin; +import constants.skills.ThunderBreaker; +import constants.skills.WindArcher; import net.AbstractPacketHandler; import net.packet.InPacket; import org.slf4j.Logger; @@ -65,4 +81,4 @@ public final class SkillEffectHandler extends AbstractPacketHandler { return; } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/SpecialMoveHandler.java b/src/main/java/net/server/channel/handlers/SpecialMoveHandler.java index 5e5d665d7e..6c2d1bf1eb 100644 --- a/src/main/java/net/server/channel/handlers/SpecialMoveHandler.java +++ b/src/main/java/net/server/channel/handlers/SpecialMoveHandler.java @@ -26,7 +26,13 @@ import client.Client; import client.Skill; import client.SkillFactory; import config.YamlConfig; -import constants.skills.*; +import constants.skills.Brawler; +import constants.skills.Corsair; +import constants.skills.DarkKnight; +import constants.skills.Hero; +import constants.skills.Paladin; +import constants.skills.Priest; +import constants.skills.SuperGM; import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.Server; @@ -153,4 +159,4 @@ public final class SpecialMoveHandler extends AbstractPacketHandler { c.sendPacket(PacketCreator.enableActions()); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java index ca21a3f257..234f56eb0c 100644 --- a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java @@ -21,8 +21,11 @@ along with this program. If not, see . */ package net.server.channel.handlers; +import client.BuffStat; import client.Character; -import client.*; +import client.Client; +import client.Skill; +import client.SkillFactory; import client.inventory.Inventory; import client.inventory.InventoryType; import client.inventory.Item; @@ -39,7 +42,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import server.StatEffect; import server.life.LifeFactory.loseItem; -import server.life.*; +import server.life.MobAttackInfo; +import server.life.MobAttackInfoFactory; +import server.life.MobSkill; +import server.life.MobSkillFactory; +import server.life.MobSkillType; +import server.life.Monster; import server.maps.MapObject; import server.maps.MapleMap; import tools.PacketCreator; diff --git a/src/main/java/net/server/channel/handlers/TransferNameHandler.java b/src/main/java/net/server/channel/handlers/TransferNameHandler.java index fd4f142dc1..6996e0bfa3 100644 --- a/src/main/java/net/server/channel/handlers/TransferNameHandler.java +++ b/src/main/java/net/server/channel/handlers/TransferNameHandler.java @@ -28,7 +28,11 @@ import net.packet.InPacket; import tools.DatabaseConnection; import tools.PacketCreator; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; import java.util.Calendar; import static java.util.concurrent.TimeUnit.DAYS; @@ -81,4 +85,4 @@ public final class TransferNameHandler extends AbstractPacketHandler { } c.sendPacket(PacketCreator.sendNameTransferRules(0)); } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/TransferWorldHandler.java b/src/main/java/net/server/channel/handlers/TransferWorldHandler.java index 17179ca903..baf3f99426 100644 --- a/src/main/java/net/server/channel/handlers/TransferWorldHandler.java +++ b/src/main/java/net/server/channel/handlers/TransferWorldHandler.java @@ -29,7 +29,11 @@ import net.server.Server; import tools.DatabaseConnection; import tools.PacketCreator; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; /** * @author Ronan @@ -76,4 +80,4 @@ public final class TransferWorldHandler extends AbstractPacketHandler { } c.sendPacket(PacketCreator.sendWorldTransferRules(0, c)); } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/UseCashItemHandler.java b/src/main/java/net/server/channel/handlers/UseCashItemHandler.java index 56b6b02a3c..d998e724e3 100644 --- a/src/main/java/net/server/channel/handlers/UseCashItemHandler.java +++ b/src/main/java/net/server/channel/handlers/UseCashItemHandler.java @@ -22,10 +22,22 @@ package net.server.channel.handlers; import client.Character; -import client.*; -import client.creator.veteran.*; -import client.inventory.*; +import client.Client; +import client.Skill; +import client.SkillFactory; +import client.SkillMacro; +import client.creator.veteran.BowmanCreator; +import client.creator.veteran.MagicianCreator; +import client.creator.veteran.PirateCreator; +import client.creator.veteran.ThiefCreator; +import client.creator.veteran.WarriorCreator; +import client.inventory.Equip; import client.inventory.Equip.ScrollResult; +import client.inventory.Inventory; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ModifyInventory; +import client.inventory.Pet; import client.inventory.manipulator.InventoryManipulator; import client.inventory.manipulator.KarmaManipulator; import client.processor.npc.DueyProcessor; @@ -46,7 +58,12 @@ import server.ItemInformationProvider; import server.Shop; import server.ShopFactory; import server.TimerManager; -import server.maps.*; +import server.maps.AbstractMapObject; +import server.maps.FieldLimit; +import server.maps.Kite; +import server.maps.MapleMap; +import server.maps.MapleTVEffect; +import server.maps.PlayerShopItem; import service.NoteService; import tools.PacketCreator; import tools.Pair; diff --git a/src/main/java/net/server/coordinator/matchchecker/MatchCheckerCoordinator.java b/src/main/java/net/server/coordinator/matchchecker/MatchCheckerCoordinator.java index 10ab35c236..de9851c175 100644 --- a/src/main/java/net/server/coordinator/matchchecker/MatchCheckerCoordinator.java +++ b/src/main/java/net/server/coordinator/matchchecker/MatchCheckerCoordinator.java @@ -25,8 +25,12 @@ import net.server.Server; import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType; import net.server.world.World; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.Semaphore; /** diff --git a/src/main/java/net/server/coordinator/partysearch/PartySearchCoordinator.java b/src/main/java/net/server/coordinator/partysearch/PartySearchCoordinator.java index fbaba5e7e9..6d56a94a5f 100644 --- a/src/main/java/net/server/coordinator/partysearch/PartySearchCoordinator.java +++ b/src/main/java/net/server/coordinator/partysearch/PartySearchCoordinator.java @@ -33,8 +33,14 @@ import provider.wz.WZFiles; import tools.PacketCreator; import tools.Pair; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; diff --git a/src/main/java/net/server/coordinator/partysearch/PartySearchStorage.java b/src/main/java/net/server/coordinator/partysearch/PartySearchStorage.java index 27a8407e69..854665a7b9 100644 --- a/src/main/java/net/server/coordinator/partysearch/PartySearchStorage.java +++ b/src/main/java/net/server/coordinator/partysearch/PartySearchStorage.java @@ -22,7 +22,11 @@ package net.server.coordinator.partysearch; import client.Character; import tools.IntervalBuilder; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; diff --git a/src/main/java/net/server/coordinator/session/SessionCoordinator.java b/src/main/java/net/server/coordinator/session/SessionCoordinator.java index 18c9f5559c..29f340ad61 100644 --- a/src/main/java/net/server/coordinator/session/SessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/SessionCoordinator.java @@ -32,8 +32,14 @@ import tools.DatabaseConnection; import java.sql.Connection; import java.sql.SQLException; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; diff --git a/src/main/java/net/server/coordinator/session/SessionDAO.java b/src/main/java/net/server/coordinator/session/SessionDAO.java index ca862632bf..c77d53c0b7 100644 --- a/src/main/java/net/server/coordinator/session/SessionDAO.java +++ b/src/main/java/net/server/coordinator/session/SessionDAO.java @@ -4,7 +4,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tools.DatabaseConnection; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; import java.time.Instant; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/net/server/coordinator/world/MonsterAggroCoordinator.java b/src/main/java/net/server/coordinator/world/MonsterAggroCoordinator.java index b5e8bf4113..e699b227fe 100644 --- a/src/main/java/net/server/coordinator/world/MonsterAggroCoordinator.java +++ b/src/main/java/net/server/coordinator/world/MonsterAggroCoordinator.java @@ -27,8 +27,14 @@ import server.life.Monster; import server.maps.MapleMap; import tools.Pair; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/src/main/java/net/server/guild/Guild.java b/src/main/java/net/server/guild/Guild.java index a68f00e89d..10c8783b15 100644 --- a/src/main/java/net/server/guild/Guild.java +++ b/src/main/java/net/server/guild/Guild.java @@ -42,7 +42,13 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java index 4dbbae373b..b9c6089c97 100644 --- a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java +++ b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java @@ -37,7 +37,13 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.sql.*; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; import java.util.Calendar; public final class LoginPasswordHandler implements PacketHandler { diff --git a/src/main/java/net/server/handlers/login/ViewAllCharHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharHandler.java index d48e7d6302..df81896b70 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharHandler.java @@ -29,7 +29,11 @@ import net.packet.InPacket; import net.server.Server; import tools.PacketCreator; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; public final class ViewAllCharHandler extends AbstractPacketHandler { private static final int CHARACTER_LIMIT = 60; // Client will crash if sending 61 or more characters diff --git a/src/main/java/net/server/services/BaseScheduler.java b/src/main/java/net/server/services/BaseScheduler.java index 5314813f8e..d8f9147a76 100644 --- a/src/main/java/net/server/services/BaseScheduler.java +++ b/src/main/java/net/server/services/BaseScheduler.java @@ -24,7 +24,11 @@ import net.server.Server; import server.TimerManager; import tools.Pair; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.locks.Lock; diff --git a/src/main/java/net/server/services/type/ChannelServices.java b/src/main/java/net/server/services/type/ChannelServices.java index bd3cadf9b2..f8fffb85e4 100644 --- a/src/main/java/net/server/services/type/ChannelServices.java +++ b/src/main/java/net/server/services/type/ChannelServices.java @@ -22,7 +22,12 @@ package net.server.services.type; import net.server.services.BaseService; import net.server.services.Service; import net.server.services.ServiceType; -import net.server.services.task.channel.*; +import net.server.services.task.channel.EventService; +import net.server.services.task.channel.MobAnimationService; +import net.server.services.task.channel.MobClearSkillService; +import net.server.services.task.channel.MobMistService; +import net.server.services.task.channel.MobStatusService; +import net.server.services.task.channel.OverallService; /** * @author Ronan diff --git a/src/main/java/net/server/world/Party.java b/src/main/java/net/server/world/Party.java index c466418fdf..69bc913c82 100644 --- a/src/main/java/net/server/world/Party.java +++ b/src/main/java/net/server/world/Party.java @@ -32,7 +32,12 @@ import server.maps.MapleMap; import server.partyquest.MonsterCarnival; import tools.PacketCreator; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/src/main/java/net/server/world/World.java b/src/main/java/net/server/world/World.java index 2bdf1b3ada..0abb0e570e 100644 --- a/src/main/java/net/server/world/World.java +++ b/src/main/java/net/server/world/World.java @@ -46,21 +46,57 @@ import net.server.guild.GuildSummary; import net.server.services.BaseService; import net.server.services.ServicesManager; import net.server.services.type.WorldServices; -import net.server.task.*; +import net.server.task.CharacterAutosaverTask; +import net.server.task.CharacterHpDecreaseTask; +import net.server.task.FamilyDailyResetTask; +import net.server.task.FishingTask; +import net.server.task.HiredMerchantTask; +import net.server.task.MapOwnershipTask; +import net.server.task.MountTirednessTask; +import net.server.task.PartySearchTask; +import net.server.task.PetFullnessTask; +import net.server.task.ServerMessageTask; +import net.server.task.TimedMapObjectTask; +import net.server.task.TimeoutTask; +import net.server.task.WeddingReservationTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scripting.event.EventInstanceManager; import server.Storage; import server.TimerManager; -import server.maps.*; +import server.maps.AbstractMapObject; +import server.maps.HiredMerchant; +import server.maps.MapleMap; +import server.maps.MiniDungeon; +import server.maps.MiniDungeonInfo; +import server.maps.PlayerShop; +import server.maps.PlayerShopItem; import tools.DatabaseConnection; import tools.PacketCreator; import tools.Pair; import tools.packets.Fishing; -import java.sql.*; -import java.util.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; @@ -69,7 +105,10 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; /** * @author kevintjuh93 diff --git a/src/main/java/server/CashShop.java b/src/main/java/server/CashShop.java index cb5e4f9db8..346a909391 100644 --- a/src/main/java/server/CashShop.java +++ b/src/main/java/server/CashShop.java @@ -21,7 +21,11 @@ along with this program. If not, see . */ package server; -import client.inventory.*; +import client.inventory.Equip; +import client.inventory.InventoryType; +import client.inventory.Item; +import client.inventory.ItemFactory; +import client.inventory.Pet; import config.YamlConfig; import constants.id.ItemId; import constants.inventory.ItemConstants; @@ -38,7 +42,11 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/src/main/java/server/ExpLogger.java b/src/main/java/server/ExpLogger.java index c9016bdf69..0093d7dd2f 100644 --- a/src/main/java/server/ExpLogger.java +++ b/src/main/java/server/ExpLogger.java @@ -1,19 +1,21 @@ package server; +import config.YamlConfig; +import tools.DatabaseConnection; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; -import java.sql.Timestamp; -import static java.util.concurrent.TimeUnit.*; -import config.YamlConfig; -import tools.DatabaseConnection; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; public class ExpLogger { private static final LinkedBlockingQueue expLoggerQueue = new LinkedBlockingQueue<>(); diff --git a/src/main/java/server/StatEffect.java b/src/main/java/server/StatEffect.java index 820d5df9b5..44cf094b63 100644 --- a/src/main/java/server/StatEffect.java +++ b/src/main/java/server/StatEffect.java @@ -21,8 +21,13 @@ */ package server; +import client.BuffStat; import client.Character; -import client.*; +import client.Disease; +import client.Job; +import client.Mount; +import client.Skill; +import client.SkillFactory; import client.inventory.Inventory; import client.inventory.InventoryType; import client.inventory.Item; @@ -33,7 +38,57 @@ import config.YamlConfig; import constants.id.ItemId; import constants.id.MapId; import constants.inventory.ItemConstants; -import constants.skills.*; +import constants.skills.Aran; +import constants.skills.Assassin; +import constants.skills.Bandit; +import constants.skills.Beginner; +import constants.skills.Bishop; +import constants.skills.BlazeWizard; +import constants.skills.Bowmaster; +import constants.skills.Brawler; +import constants.skills.Buccaneer; +import constants.skills.ChiefBandit; +import constants.skills.Cleric; +import constants.skills.Corsair; +import constants.skills.Crossbowman; +import constants.skills.Crusader; +import constants.skills.DarkKnight; +import constants.skills.DawnWarrior; +import constants.skills.DragonKnight; +import constants.skills.Evan; +import constants.skills.FPArchMage; +import constants.skills.FPMage; +import constants.skills.FPWizard; +import constants.skills.Fighter; +import constants.skills.GM; +import constants.skills.Gunslinger; +import constants.skills.Hermit; +import constants.skills.Hero; +import constants.skills.Hunter; +import constants.skills.ILArchMage; +import constants.skills.ILMage; +import constants.skills.ILWizard; +import constants.skills.Legend; +import constants.skills.Magician; +import constants.skills.Marauder; +import constants.skills.Marksman; +import constants.skills.NightLord; +import constants.skills.NightWalker; +import constants.skills.Noblesse; +import constants.skills.Outlaw; +import constants.skills.Page; +import constants.skills.Paladin; +import constants.skills.Pirate; +import constants.skills.Priest; +import constants.skills.Ranger; +import constants.skills.Rogue; +import constants.skills.Shadower; +import constants.skills.Sniper; +import constants.skills.Spearman; +import constants.skills.SuperGM; +import constants.skills.ThunderBreaker; +import constants.skills.WhiteKnight; +import constants.skills.WindArcher; import net.packet.Packet; import net.server.Server; import net.server.world.Party; @@ -44,15 +99,27 @@ import server.life.MobSkill; import server.life.MobSkillFactory; import server.life.MobSkillType; import server.life.Monster; -import server.maps.*; +import server.maps.Door; +import server.maps.FieldLimit; +import server.maps.MapObject; +import server.maps.MapObjectType; +import server.maps.MapleMap; +import server.maps.Mist; +import server.maps.Portal; +import server.maps.Summon; +import server.maps.SummonMovementType; import server.partyquest.CarnivalFactory; import server.partyquest.CarnivalFactory.MCSkill; import tools.PacketCreator; import tools.Pair; import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; import java.util.List; -import java.util.*; +import java.util.Map; /** * @author Matze diff --git a/src/main/java/server/Storage.java b/src/main/java/server/Storage.java index 67e64e75b9..0c50479138 100644 --- a/src/main/java/server/Storage.java +++ b/src/main/java/server/Storage.java @@ -38,7 +38,12 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -358,4 +363,4 @@ public class Storage { } } -} \ No newline at end of file +} diff --git a/src/main/java/server/ThreadManager.java b/src/main/java/server/ThreadManager.java index 683c419d7d..3d4bf099fe 100644 --- a/src/main/java/server/ThreadManager.java +++ b/src/main/java/server/ThreadManager.java @@ -19,7 +19,11 @@ */ package server; -import java.util.concurrent.*; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; diff --git a/src/main/java/server/expeditions/Expedition.java b/src/main/java/server/expeditions/Expedition.java index 2becc69565..ba96778515 100644 --- a/src/main/java/server/expeditions/Expedition.java +++ b/src/main/java/server/expeditions/Expedition.java @@ -37,8 +37,14 @@ import server.maps.MapleMap; import tools.PacketCreator; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; diff --git a/src/main/java/server/expeditions/ExpeditionBossLog.java b/src/main/java/server/expeditions/ExpeditionBossLog.java index 6f6355a70e..c7337d69d4 100644 --- a/src/main/java/server/expeditions/ExpeditionBossLog.java +++ b/src/main/java/server/expeditions/ExpeditionBossLog.java @@ -23,7 +23,11 @@ import config.YamlConfig; import tools.DatabaseConnection; import tools.Pair; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; import java.util.Calendar; import java.util.LinkedList; import java.util.List; diff --git a/src/main/java/server/life/LifeFactory.java b/src/main/java/server/life/LifeFactory.java index 6f2e9f8aed..04e6cabe2a 100644 --- a/src/main/java/server/life/LifeFactory.java +++ b/src/main/java/server/life/LifeFactory.java @@ -33,8 +33,12 @@ import tools.Pair; import tools.StringUtil; import java.awt.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; -import java.util.*; +import java.util.Map; +import java.util.Set; public class LifeFactory { private static final Logger log = LoggerFactory.getLogger(LifeFactory.class); diff --git a/src/main/java/server/life/MobSkill.java b/src/main/java/server/life/MobSkill.java index 4b47f1ad74..63f500ef09 100644 --- a/src/main/java/server/life/MobSkill.java +++ b/src/main/java/server/life/MobSkill.java @@ -38,8 +38,11 @@ import server.maps.Mist; import tools.Randomizer; import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; import java.util.List; -import java.util.*; +import java.util.Map; /** * @author Danny (Leifde) diff --git a/src/main/java/server/life/MobSkillFactory.java b/src/main/java/server/life/MobSkillFactory.java index 7b5ea96b9e..435f4c9e3f 100644 --- a/src/main/java/server/life/MobSkillFactory.java +++ b/src/main/java/server/life/MobSkillFactory.java @@ -28,8 +28,11 @@ import provider.DataTool; import provider.wz.WZFiles; import java.awt.*; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.*; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; diff --git a/src/main/java/server/life/Monster.java b/src/main/java/server/life/Monster.java index efd42f8b8d..71f9b66c45 100644 --- a/src/main/java/server/life/Monster.java +++ b/src/main/java/server/life/Monster.java @@ -21,13 +21,26 @@ */ package server.life; +import client.BuffStat; import client.Character; -import client.*; +import client.Client; +import client.FamilyEntry; +import client.Job; +import client.Skill; +import client.SkillFactory; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; import config.YamlConfig; import constants.id.MobId; -import constants.skills.*; +import constants.skills.Crusader; +import constants.skills.FPMage; +import constants.skills.Hermit; +import constants.skills.ILMage; +import constants.skills.NightLord; +import constants.skills.NightWalker; +import constants.skills.Priest; +import constants.skills.Shadower; +import constants.skills.WhiteKnight; import net.packet.Packet; import net.server.channel.Channel; import net.server.coordinator.world.MonsterAggroCoordinator; @@ -56,9 +69,17 @@ import tools.Randomizer; import java.awt.*; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; -import java.util.*; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -2177,4 +2198,4 @@ public class Monster extends AbstractLoadedLife { this.getMap().dismissRemoveAfter(this); } -} \ No newline at end of file +} diff --git a/src/main/java/server/life/MonsterInformationProvider.java b/src/main/java/server/life/MonsterInformationProvider.java index e610559007..6055a4fdb0 100644 --- a/src/main/java/server/life/MonsterInformationProvider.java +++ b/src/main/java/server/life/MonsterInformationProvider.java @@ -38,7 +38,13 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; public class MonsterInformationProvider { private static final Logger log = LoggerFactory.getLogger(MonsterInformationProvider.class); diff --git a/src/main/java/server/life/MonsterStats.java b/src/main/java/server/life/MonsterStats.java index 093adc9334..5c091fd76d 100644 --- a/src/main/java/server/life/MonsterStats.java +++ b/src/main/java/server/life/MonsterStats.java @@ -27,7 +27,13 @@ import server.life.LifeFactory.selfDestruction; import tools.Pair; import java.lang.reflect.Field; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author Frz diff --git a/src/main/java/server/life/PlayerNPC.java b/src/main/java/server/life/PlayerNPC.java index f5a592016d..c1fb312859 100644 --- a/src/main/java/server/life/PlayerNPC.java +++ b/src/main/java/server/life/PlayerNPC.java @@ -44,9 +44,19 @@ import tools.PacketCreator; import tools.Pair; import java.awt.*; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; -import java.util.*; +import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** diff --git a/src/main/java/server/maps/AbstractAnimatedMapObject.java b/src/main/java/server/maps/AbstractAnimatedMapObject.java index f0dba2ca91..5e05797d21 100644 --- a/src/main/java/server/maps/AbstractAnimatedMapObject.java +++ b/src/main/java/server/maps/AbstractAnimatedMapObject.java @@ -22,7 +22,11 @@ package server.maps; import io.netty.buffer.Unpooled; -import net.packet.*; +import net.packet.ByteBufInPacket; +import net.packet.ByteBufOutPacket; +import net.packet.InPacket; +import net.packet.OutPacket; +import net.packet.Packet; import java.util.Arrays; diff --git a/src/main/java/server/maps/HiredMerchant.java b/src/main/java/server/maps/HiredMerchant.java index c668e2cd24..bcc4fb636e 100644 --- a/src/main/java/server/maps/HiredMerchant.java +++ b/src/main/java/server/maps/HiredMerchant.java @@ -45,7 +45,12 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/src/main/java/server/maps/MapleMap.java b/src/main/java/server/maps/MapleMap.java index bf44ba733f..ceadc01aff 100644 --- a/src/main/java/server/maps/MapleMap.java +++ b/src/main/java/server/maps/MapleMap.java @@ -52,9 +52,21 @@ import scripting.map.MapScriptManager; import server.ItemInformationProvider; import server.StatEffect; import server.TimerManager; -import server.events.gm.*; -import server.life.*; +import server.events.gm.Coconut; +import server.events.gm.Fitness; +import server.events.gm.Ola; +import server.events.gm.OxQuiz; +import server.events.gm.Snowball; +import server.life.LifeFactory; import server.life.LifeFactory.selfDestruction; +import server.life.Monster; +import server.life.MonsterDropEntry; +import server.life.MonsterGlobalDropEntry; +import server.life.MonsterInformationProvider; +import server.life.MonsterListener; +import server.life.NPC; +import server.life.PlayerNPC; +import server.life.SpawnPoint; import server.partyquest.CarnivalFactory; import server.partyquest.CarnivalFactory.MCSkill; import server.partyquest.GuardianSpawnPoint; @@ -64,9 +76,22 @@ import tools.Randomizer; import java.awt.*; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; -import java.util.*; +import java.util.Map; import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; diff --git a/src/main/java/server/maps/Mist.java b/src/main/java/server/maps/Mist.java index 6aa41391d8..9f60884e32 100644 --- a/src/main/java/server/maps/Mist.java +++ b/src/main/java/server/maps/Mist.java @@ -25,7 +25,11 @@ import client.Character; import client.Client; import client.Skill; import client.SkillFactory; -import constants.skills.*; +import constants.skills.BlazeWizard; +import constants.skills.Evan; +import constants.skills.FPMage; +import constants.skills.NightWalker; +import constants.skills.Shadower; import net.packet.Packet; import server.StatEffect; import server.life.MobSkill; diff --git a/src/main/java/server/maps/PlayerShop.java b/src/main/java/server/maps/PlayerShop.java index ab110cc008..2918276974 100644 --- a/src/main/java/server/maps/PlayerShop.java +++ b/src/main/java/server/maps/PlayerShop.java @@ -33,7 +33,12 @@ import server.Trade; import tools.PacketCreator; import tools.Pair; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -617,4 +622,4 @@ public class PlayerShop extends AbstractMapObject { return mesos; } } -} \ No newline at end of file +} diff --git a/src/main/java/server/quest/Quest.java b/src/main/java/server/quest/Quest.java index bab66d9a60..b53eda0fc1 100644 --- a/src/main/java/server/quest/Quest.java +++ b/src/main/java/server/quest/Quest.java @@ -32,13 +32,52 @@ import provider.DataProvider; import provider.DataProviderFactory; import provider.DataTool; import provider.wz.WZFiles; -import server.quest.actions.*; -import server.quest.requirements.*; +import server.quest.actions.AbstractQuestAction; +import server.quest.actions.BuffAction; +import server.quest.actions.ExpAction; +import server.quest.actions.FameAction; +import server.quest.actions.InfoAction; +import server.quest.actions.ItemAction; +import server.quest.actions.MesoAction; +import server.quest.actions.NextQuestAction; +import server.quest.actions.PetSkillAction; +import server.quest.actions.PetSpeedAction; +import server.quest.actions.PetTamenessAction; +import server.quest.actions.QuestAction; +import server.quest.actions.SkillAction; +import server.quest.requirements.AbstractQuestRequirement; +import server.quest.requirements.BuffExceptRequirement; +import server.quest.requirements.BuffRequirement; +import server.quest.requirements.CompletedQuestRequirement; +import server.quest.requirements.EndDateRequirement; +import server.quest.requirements.FieldEnterRequirement; +import server.quest.requirements.InfoExRequirement; +import server.quest.requirements.InfoNumberRequirement; +import server.quest.requirements.IntervalRequirement; +import server.quest.requirements.ItemRequirement; +import server.quest.requirements.JobRequirement; +import server.quest.requirements.MaxLevelRequirement; +import server.quest.requirements.MesoRequirement; +import server.quest.requirements.MinLevelRequirement; +import server.quest.requirements.MinTamenessRequirement; +import server.quest.requirements.MobRequirement; +import server.quest.requirements.MonsterBookCountRequirement; +import server.quest.requirements.NpcRequirement; +import server.quest.requirements.PetRequirement; +import server.quest.requirements.QuestRequirement; +import server.quest.requirements.ScriptRequirement; import tools.PacketCreator; import tools.StringUtil; -import java.util.*; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.SECONDS; diff --git a/src/main/java/tools/mapletools/BossHpBarFetcher.java b/src/main/java/tools/mapletools/BossHpBarFetcher.java index e7634f09bb..7305c56cd1 100644 --- a/src/main/java/tools/mapletools/BossHpBarFetcher.java +++ b/src/main/java/tools/mapletools/BossHpBarFetcher.java @@ -2,7 +2,10 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/tools/mapletools/CashCosmeticsChecker.java b/src/main/java/tools/mapletools/CashCosmeticsChecker.java index 261cf555b1..5034721660 100644 --- a/src/main/java/tools/mapletools/CashCosmeticsChecker.java +++ b/src/main/java/tools/mapletools/CashCosmeticsChecker.java @@ -3,11 +3,23 @@ package tools.mapletools; import provider.wz.WZFiles; import tools.Pair; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/CashCosmeticsFetcher.java b/src/main/java/tools/mapletools/CashCosmeticsFetcher.java index ef290f40d8..c6f64adb07 100644 --- a/src/main/java/tools/mapletools/CashCosmeticsFetcher.java +++ b/src/main/java/tools/mapletools/CashCosmeticsFetcher.java @@ -8,7 +8,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/CashDropFetcher.java b/src/main/java/tools/mapletools/CashDropFetcher.java index 91282fa6d7..03b0b97abd 100644 --- a/src/main/java/tools/mapletools/CashDropFetcher.java +++ b/src/main/java/tools/mapletools/CashDropFetcher.java @@ -3,7 +3,9 @@ package tools.mapletools; import provider.wz.WZFiles; import tools.Pair; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -11,7 +13,11 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/CashVegaChecker.java b/src/main/java/tools/mapletools/CashVegaChecker.java index 1cbea1b5aa..929ec0a4d9 100644 --- a/src/main/java/tools/mapletools/CashVegaChecker.java +++ b/src/main/java/tools/mapletools/CashVegaChecker.java @@ -2,7 +2,12 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/tools/mapletools/CodeCouponGenerator.java b/src/main/java/tools/mapletools/CodeCouponGenerator.java index 62e35c6e09..93da8662fd 100644 --- a/src/main/java/tools/mapletools/CodeCouponGenerator.java +++ b/src/main/java/tools/mapletools/CodeCouponGenerator.java @@ -2,10 +2,15 @@ package tools.mapletools; import tools.Pair; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/tools/mapletools/CouponInstaller.java b/src/main/java/tools/mapletools/CouponInstaller.java index 2532458370..5aeb2960db 100644 --- a/src/main/java/tools/mapletools/CouponInstaller.java +++ b/src/main/java/tools/mapletools/CouponInstaller.java @@ -2,7 +2,9 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; diff --git a/src/main/java/tools/mapletools/EmptyItemWzChecker.java b/src/main/java/tools/mapletools/EmptyItemWzChecker.java index 4ac3face4f..f52b11db31 100644 --- a/src/main/java/tools/mapletools/EmptyItemWzChecker.java +++ b/src/main/java/tools/mapletools/EmptyItemWzChecker.java @@ -2,11 +2,23 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/EquipmentOmniLeveller.java b/src/main/java/tools/mapletools/EquipmentOmniLeveller.java index 71e3635b31..d2644f1eec 100644 --- a/src/main/java/tools/mapletools/EquipmentOmniLeveller.java +++ b/src/main/java/tools/mapletools/EquipmentOmniLeveller.java @@ -2,7 +2,10 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/tools/mapletools/EventMethodFiller.java b/src/main/java/tools/mapletools/EventMethodFiller.java index c8686ff0dc..584d7805b1 100644 --- a/src/main/java/tools/mapletools/EventMethodFiller.java +++ b/src/main/java/tools/mapletools/EventMethodFiller.java @@ -5,7 +5,12 @@ import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/tools/mapletools/GachaponItemIdRetriever.java b/src/main/java/tools/mapletools/GachaponItemIdRetriever.java index f39fb0f918..48b5e11acd 100644 --- a/src/main/java/tools/mapletools/GachaponItemIdRetriever.java +++ b/src/main/java/tools/mapletools/GachaponItemIdRetriever.java @@ -1,13 +1,20 @@ package tools.mapletools; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/tools/mapletools/IdRetriever.java b/src/main/java/tools/mapletools/IdRetriever.java index b511499f96..37ceff1712 100644 --- a/src/main/java/tools/mapletools/IdRetriever.java +++ b/src/main/java/tools/mapletools/IdRetriever.java @@ -1,6 +1,11 @@ package tools.mapletools; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/tools/mapletools/MapFieldLimitChecker.java b/src/main/java/tools/mapletools/MapFieldLimitChecker.java index 92909399bc..1aa2a127af 100644 --- a/src/main/java/tools/mapletools/MapFieldLimitChecker.java +++ b/src/main/java/tools/mapletools/MapFieldLimitChecker.java @@ -2,7 +2,8 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/tools/mapletools/MobBookIndexer.java b/src/main/java/tools/mapletools/MobBookIndexer.java index 91dbcaadbc..1686dacd4b 100644 --- a/src/main/java/tools/mapletools/MobBookIndexer.java +++ b/src/main/java/tools/mapletools/MobBookIndexer.java @@ -2,7 +2,9 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; diff --git a/src/main/java/tools/mapletools/MobBookUpdate.java b/src/main/java/tools/mapletools/MobBookUpdate.java index a83af1c0e0..b652802e0e 100644 --- a/src/main/java/tools/mapletools/MobBookUpdate.java +++ b/src/main/java/tools/mapletools/MobBookUpdate.java @@ -2,7 +2,10 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; diff --git a/src/main/java/tools/mapletools/MonsterStatFetcher.java b/src/main/java/tools/mapletools/MonsterStatFetcher.java index 6cbb136ca8..38033757b8 100644 --- a/src/main/java/tools/mapletools/MonsterStatFetcher.java +++ b/src/main/java/tools/mapletools/MonsterStatFetcher.java @@ -1,17 +1,31 @@ package tools.mapletools; -import provider.*; +import provider.Data; +import provider.DataDirectoryEntry; +import provider.DataFileEntry; +import provider.DataProvider; +import provider.DataProviderFactory; +import provider.DataTool; import provider.wz.DataType; import provider.wz.WZFiles; -import server.life.*; +import server.life.Element; +import server.life.ElementalEffectiveness; import server.life.LifeFactory.BanishInfo; import server.life.LifeFactory.loseItem; import server.life.LifeFactory.selfDestruction; +import server.life.MobSkillId; +import server.life.MobSkillType; +import server.life.MonsterStats; import tools.Pair; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; public class MonsterStatFetcher { private static final DataProvider data = DataProviderFactory.getDataProvider(WZFiles.MOB); diff --git a/src/main/java/tools/mapletools/NoItemIdFetcher.java b/src/main/java/tools/mapletools/NoItemIdFetcher.java index 7e55e11fff..4eed18f474 100644 --- a/src/main/java/tools/mapletools/NoItemIdFetcher.java +++ b/src/main/java/tools/mapletools/NoItemIdFetcher.java @@ -2,7 +2,13 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -10,7 +16,11 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author RonanLana @@ -211,4 +221,4 @@ public class NoItemIdFetcher { e.printStackTrace(); } } -} \ No newline at end of file +} diff --git a/src/main/java/tools/mapletools/NoItemNameFetcher.java b/src/main/java/tools/mapletools/NoItemNameFetcher.java index c5e463b769..fb35c80992 100644 --- a/src/main/java/tools/mapletools/NoItemNameFetcher.java +++ b/src/main/java/tools/mapletools/NoItemNameFetcher.java @@ -1,12 +1,23 @@ package tools.mapletools; -import provider.*; +import provider.Data; +import provider.DataDirectoryEntry; +import provider.DataFileEntry; +import provider.DataProvider; +import provider.DataProviderFactory; +import provider.DataTool; import provider.wz.WZFiles; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/QuestItemCountFetcher.java b/src/main/java/tools/mapletools/QuestItemCountFetcher.java index 1881438f48..5d8d379984 100644 --- a/src/main/java/tools/mapletools/QuestItemCountFetcher.java +++ b/src/main/java/tools/mapletools/QuestItemCountFetcher.java @@ -3,11 +3,20 @@ package tools.mapletools; import provider.wz.WZFiles; import tools.Pair; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/QuestItemFetcher.java b/src/main/java/tools/mapletools/QuestItemFetcher.java index 130ba51de9..ac6631a728 100644 --- a/src/main/java/tools/mapletools/QuestItemFetcher.java +++ b/src/main/java/tools/mapletools/QuestItemFetcher.java @@ -15,7 +15,14 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/QuestMesoFetcher.java b/src/main/java/tools/mapletools/QuestMesoFetcher.java index c2aae9e4d5..5c0157745e 100644 --- a/src/main/java/tools/mapletools/QuestMesoFetcher.java +++ b/src/main/java/tools/mapletools/QuestMesoFetcher.java @@ -2,10 +2,18 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/QuestlineFetcher.java b/src/main/java/tools/mapletools/QuestlineFetcher.java index 25c8e256a0..52fc56988c 100644 --- a/src/main/java/tools/mapletools/QuestlineFetcher.java +++ b/src/main/java/tools/mapletools/QuestlineFetcher.java @@ -2,13 +2,27 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; /** * @author RonanLana diff --git a/src/main/java/tools/mapletools/ReactorDropFetcher.java b/src/main/java/tools/mapletools/ReactorDropFetcher.java index 795d85c9b9..1b90a42b60 100644 --- a/src/main/java/tools/mapletools/ReactorDropFetcher.java +++ b/src/main/java/tools/mapletools/ReactorDropFetcher.java @@ -9,7 +9,11 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author RonanLana @@ -110,4 +114,4 @@ public class ReactorDropFetcher { public static void main(String[] args) { reportMissingReactors(); } -} \ No newline at end of file +} diff --git a/src/main/java/tools/mapletools/SkillMakerFetcher.java b/src/main/java/tools/mapletools/SkillMakerFetcher.java index 38a52ad07e..4b7b072315 100644 --- a/src/main/java/tools/mapletools/SkillMakerFetcher.java +++ b/src/main/java/tools/mapletools/SkillMakerFetcher.java @@ -4,7 +4,10 @@ import provider.wz.WZFiles; import server.ItemInformationProvider; import tools.DatabaseConnection; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; diff --git a/src/main/java/tools/mapletools/SkillMakerReagentIndexer.java b/src/main/java/tools/mapletools/SkillMakerReagentIndexer.java index 77708d225a..340148344d 100644 --- a/src/main/java/tools/mapletools/SkillMakerReagentIndexer.java +++ b/src/main/java/tools/mapletools/SkillMakerReagentIndexer.java @@ -3,7 +3,10 @@ package tools.mapletools; import provider.wz.WZFiles; import tools.Pair; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; diff --git a/src/main/java/tools/mapletools/SkillbookStackUpdate.java b/src/main/java/tools/mapletools/SkillbookStackUpdate.java index cedd89a2b9..7f5fc76fb4 100644 --- a/src/main/java/tools/mapletools/SkillbookStackUpdate.java +++ b/src/main/java/tools/mapletools/SkillbookStackUpdate.java @@ -2,7 +2,9 @@ package tools.mapletools; import provider.wz.WZFiles; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/tools/mapletools/WorldmapChecker.java b/src/main/java/tools/mapletools/WorldmapChecker.java index a05c2b97a5..e2902d69b3 100644 --- a/src/main/java/tools/mapletools/WorldmapChecker.java +++ b/src/main/java/tools/mapletools/WorldmapChecker.java @@ -3,11 +3,22 @@ package tools.mapletools; import provider.wz.WZFiles; import tools.Pair; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author RonanLana diff --git a/src/test/java/model/NoteTest.java b/src/test/java/model/NoteTest.java index 1779e3f258..87071e4dbe 100644 --- a/src/test/java/model/NoteTest.java +++ b/src/test/java/model/NoteTest.java @@ -2,7 +2,8 @@ package model; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; class NoteTest { @@ -25,4 +26,4 @@ class NoteTest { void createNew() { assertDoesNotThrow(() -> new Note(4, "message", "from", "to", System.currentTimeMillis(), 5)); } -} \ No newline at end of file +} diff --git a/src/test/java/server/life/MobSkillFactoryTest.java b/src/test/java/server/life/MobSkillFactoryTest.java index c4aebebec6..078a9c6035 100644 --- a/src/test/java/server/life/MobSkillFactoryTest.java +++ b/src/test/java/server/life/MobSkillFactoryTest.java @@ -10,7 +10,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class MobSkillFactoryTest { @@ -67,4 +70,4 @@ class MobSkillFactoryTest { assertThrows(IllegalArgumentException.class, () -> MobSkillFactory.getMobSkillOrThrow(MobSkillType.DEFENSE_UP, 1)); } -} \ No newline at end of file +} diff --git a/src/test/java/service/NoteServiceTest.java b/src/test/java/service/NoteServiceTest.java index 9ce7c3cad9..7256e31cf3 100644 --- a/src/test/java/service/NoteServiceTest.java +++ b/src/test/java/service/NoteServiceTest.java @@ -14,9 +14,16 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static testutil.AnyValues.daoException; import static testutil.AnyValues.string; @@ -139,4 +146,4 @@ class NoteServiceTest { assertTrue(deletedNote.isEmpty()); } -} \ No newline at end of file +} From eed47a9064fb9450bda495f4c8ba42a27dfacd9a Mon Sep 17 00:00:00 2001 From: remsus Date: Tue, 11 Jun 2024 18:44:47 +0200 Subject: [PATCH 25/55] Check if the amount of damage lines doesn't exceed the max (autoban) --- .../channel/handlers/AbstractDealDamageHandler.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index 8a882e2a0c..c661d03efc 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -689,9 +689,10 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { calcDmgMax = chr.calculateMaxBaseDamage(chr.getTotalWatk()); } + StatEffect effect = null; if (ret.skill != 0) { Skill skill = SkillFactory.getSkill(ret.skill); - StatEffect effect = skill.getEffect(ret.skilllevel); + effect = skill.getEffect(ret.skilllevel); if (magic) { // Since the skill is magic based, use the magic formula @@ -930,6 +931,16 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { damage = -Integer.MAX_VALUE + damage - 1; } + if(effect != null) { + int maxattack = Math.max(effect.getBulletCount(), effect.getAttackCount()); + if (shadowPartner) { + maxattack = maxattack * 2; + } + if (ret.numDamage > maxattack) { + AutobanFactory.DAMAGE_HACK.addPoint(chr.getAutobanManager(), "Too many lines: " + ret.numDamage + " Max lines: " + maxattack + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")"); + } + } + allDamageNumbers.add(damage); } if (ret.skill != Corsair.RAPID_FIRE || ret.skill != Aran.HIDDEN_FULL_DOUBLE || ret.skill != Aran.HIDDEN_FULL_TRIPLE || ret.skill != Aran.HIDDEN_OVER_DOUBLE || ret.skill != Aran.HIDDEN_OVER_TRIPLE) { From db82cbcfae6f4eca0dc8e21b7f0de4c182b75a14 Mon Sep 17 00:00:00 2001 From: Channarit Sittiparat Date: Thu, 13 Jun 2024 20:02:40 +0700 Subject: [PATCH 26/55] Surprise Box Implementation - Added showCashInventory after a successful gachapon opening in the CashShopSurpriseHandler. - Added getItemsSize() condition to check the inventory size before proceeding with the cash shop surprise opening --- .../handlers/CashShopSurpriseHandler.java | 1 + src/main/java/server/CashShop.java | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java index 0d56fd2b29..4e01451eb7 100644 --- a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java +++ b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java @@ -41,6 +41,7 @@ public class CashShopSurpriseHandler extends AbstractPacketHandler { if (cssResult != null) { Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight(); c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true)); + c.sendPacket(PacketCreator.showCashInventory(c)); } else { c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); } diff --git a/src/main/java/server/CashShop.java b/src/main/java/server/CashShop.java index 346a909391..d7e0ec1e28 100644 --- a/src/main/java/server/CashShop.java +++ b/src/main/java/server/CashShop.java @@ -36,6 +36,7 @@ import provider.DataProviderFactory; import provider.DataTool; import provider.wz.WZFiles; import tools.DatabaseConnection; +import tools.PacketCreator; import tools.Pair; import java.sql.Connection; @@ -407,6 +408,17 @@ public class CashShop { } } + public int getItemsSize() { + int size = 0; + lock.lock(); + try { + size = inventory.size(); + } finally { + lock.unlock(); + } + return size; + } + public List getWishList() { return wishList; } @@ -544,14 +556,14 @@ public class CashShop { Item css = getCashShopItemByItemid(ItemId.CASH_SHOP_SURPRISE); if (css != null) { + if (getItemsSize() >= 100) { + return null; + } + CashItem cItem = CashItemFactory.getRandomCashItem(); if (cItem != null) { if (css.getQuantity() > 1) { - /* if(NOT ENOUGH SPACE) { looks like we're not dealing with cash inventory limit whatsoever, k then - return null; - } */ - css.setQuantity((short) (css.getQuantity() - 1)); } else { removeFromInventory(css); From b67b29def5639d4c597354f7c49ab192e6bf9b8c Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 15 Jun 2024 08:30:23 +0200 Subject: [PATCH 27/55] Fix cash shop surprise count not properly updated on usage This reverts the hacky solution made in db82cbcfae6f4eca0dc8e21b7f0de4c182b75a14 --- .../net/server/channel/handlers/CashShopSurpriseHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java index 4e01451eb7..618c3f7d31 100644 --- a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java +++ b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java @@ -40,8 +40,7 @@ public class CashShopSurpriseHandler extends AbstractPacketHandler { if (cssResult != null) { Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight(); - c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true)); - c.sendPacket(PacketCreator.showCashInventory(c)); + c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getCashId(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true)); } else { c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); } From 01ae462b726f9d00f14f06d8c40cee3b373d6411 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 16 Jun 2024 12:19:16 +0200 Subject: [PATCH 28/55] Refactor CashShop - add constants for cash types --- src/main/java/client/Character.java | 4 +- .../handlers/CashOperationHandler.java | 6 +- .../server/channel/handlers/MTSHandler.java | 7 +- src/main/java/server/CashShop.java | 154 +++++++++--------- src/main/java/tools/PacketCreator.java | 11 +- 5 files changed, 89 insertions(+), 93 deletions(-) diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index c90d31480c..0bc5926a02 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -2068,7 +2068,7 @@ public class Character extends AbstractCharacterObject { this.getCashShop().gainCash(1, nxGain); if (YamlConfig.config.server.USE_ANNOUNCE_NX_COUPON_LOOT) { - showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300); + showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(CashShop.NX_CREDIT) + " NX)", 300); } this.getMap().pickItemDrop(pickupPacket, mapitem); @@ -2120,7 +2120,7 @@ public class Character extends AbstractCharacterObject { this.getCashShop().gainCash(1, nxGain); if (YamlConfig.config.server.USE_ANNOUNCE_NX_COUPON_LOOT) { - showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300); + showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(CashShop.NX_CREDIT) + " NX)", 300); } } else if (applyConsumeOnPickup(mItem.getItemId())) { } else if (InventoryManipulator.addFromDrop(client, mItem, true)) { diff --git a/src/main/java/net/server/channel/handlers/CashOperationHandler.java b/src/main/java/net/server/channel/handlers/CashOperationHandler.java index 077c654a5c..86efa554b0 100644 --- a/src/main/java/net/server/channel/handlers/CashOperationHandler.java +++ b/src/main/java/net/server/channel/handlers/CashOperationHandler.java @@ -116,7 +116,7 @@ public final class CashOperationHandler extends AbstractPacketHandler { CashItem cItem = CashItemFactory.getItem(p.readInt()); Map recipient = Character.getCharacterFromDatabase(p.readString()); String message = p.readString(); - if (!canBuy(chr, cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) { + if (!canBuy(chr, cItem, cs.getCash(CashShop.NX_PREPAID)) || message.isEmpty() || message.length() > 73) { c.enableCSActions(); return; } @@ -405,7 +405,7 @@ public final class CashOperationHandler extends AbstractPacketHandler { c.sendPacket(PacketCreator.showCash(c.getPlayer())); } else if (action == 0x2E) { //name change CashItem cItem = CashItemFactory.getItem(p.readInt()); - if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) { + if (cItem == null || !canBuy(chr, cItem, cs.getCash(CashShop.NX_PREPAID))) { c.sendPacket(PacketCreator.showCashShopMessage((byte) 0)); c.enableCSActions(); return; @@ -434,7 +434,7 @@ public final class CashOperationHandler extends AbstractPacketHandler { c.enableCSActions(); } else if (action == 0x31) { //world transfer CashItem cItem = CashItemFactory.getItem(p.readInt()); - if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) { + if (cItem == null || !canBuy(chr, cItem, cs.getCash(CashShop.NX_PREPAID))) { c.sendPacket(PacketCreator.showCashShopMessage((byte) 0)); c.enableCSActions(); return; diff --git a/src/main/java/net/server/channel/handlers/MTSHandler.java b/src/main/java/net/server/channel/handlers/MTSHandler.java index bc5d3dac74..4634c2bef3 100644 --- a/src/main/java/net/server/channel/handlers/MTSHandler.java +++ b/src/main/java/net/server/channel/handlers/MTSHandler.java @@ -35,6 +35,7 @@ import net.server.Server; import net.server.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import server.CashShop; import server.ItemInformationProvider; import server.MTSItemInfo; import tools.DatabaseConnection; @@ -402,7 +403,7 @@ public final class MTSHandler extends AbstractPacketHandler { ResultSet rs = ps.executeQuery(); if (rs.next()) { int price = rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1); // taxes - if (c.getPlayer().getCashShop().getCash(4) >= price) { // FIX + if (c.getPlayer().getCashShop().getCash(CashShop.NX_PREPAID) >= price) { // FIX boolean alwaysnull = true; for (Channel cserv : Server.getInstance().getAllChannels()) { Character victim = cserv.getPlayerStorage().getCharacterById(rs.getInt("seller")); @@ -459,11 +460,11 @@ public final class MTSHandler extends AbstractPacketHandler { ResultSet rs = ps.executeQuery(); if (rs.next()) { int price = rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1); - if (c.getPlayer().getCashShop().getCash(4) >= price) { + if (c.getPlayer().getCashShop().getCash(CashShop.NX_PREPAID) >= price) { for (Channel cserv : Server.getInstance().getAllChannels()) { Character victim = cserv.getPlayerStorage().getCharacterById(rs.getInt("seller")); if (victim != null) { - victim.getCashShop().gainCash(4, rs.getInt("price")); + victim.getCashShop().gainCash(CashShop.NX_PREPAID, rs.getInt("price")); } else { try (PreparedStatement pse = con.prepareStatement("SELECT accountid FROM characters WHERE id = ?")) { pse.setInt(1, rs.getInt("seller")); diff --git a/src/main/java/server/CashShop.java b/src/main/java/server/CashShop.java index d7e0ec1e28..6b93dea39d 100644 --- a/src/main/java/server/CashShop.java +++ b/src/main/java/server/CashShop.java @@ -36,7 +36,6 @@ import provider.DataProviderFactory; import provider.DataTool; import provider.wz.WZFiles; import tools.DatabaseConnection; -import tools.PacketCreator; import tools.Pair; import java.sql.Connection; @@ -58,6 +57,71 @@ import static java.util.concurrent.TimeUnit.HOURS; * @author Flav */ public class CashShop { + public static final int NX_CREDIT = 1; + public static final int MAPLE_POINT = 2; + public static final int NX_PREPAID = 4; + + private final int accountId; + private final int characterId; + private int nxCredit; + private int maplePoint; + private int nxPrepaid; + private boolean opened; + private ItemFactory factory; + private final List inventory = new ArrayList<>(); + private final List wishList = new ArrayList<>(); + private int notes = 0; + private final Lock lock = new ReentrantLock(); + + public CashShop(int accountId, int characterId, int jobType) throws SQLException { + this.accountId = accountId; + this.characterId = characterId; + + if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) { + switch (jobType) { + case 0: + factory = ItemFactory.CASH_EXPLORER; + break; + case 1: + factory = ItemFactory.CASH_CYGNUS; + break; + case 2: + factory = ItemFactory.CASH_ARAN; + break; + } + } else { + factory = ItemFactory.CASH_OVERALL; + } + + try (Connection con = DatabaseConnection.getConnection()) { + try (PreparedStatement ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?")) { + ps.setInt(1, accountId); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + this.nxCredit = rs.getInt("nxCredit"); + this.maplePoint = rs.getInt("maplePoint"); + this.nxPrepaid = rs.getInt("nxPrepaid"); + } + } + } + + for (Pair item : factory.loadItems(accountId, false)) { + inventory.add(item.getLeft()); + } + + try (PreparedStatement ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?")) { + ps.setInt(1, characterId); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + wishList.add(rs.getInt("sn")); + } + } + } + } + } + public static class CashItem { private final int sn; @@ -259,91 +323,21 @@ public class CashShop { } } - private final int accountId; - private final int characterId; - private int nxCredit; - private int maplePoint; - private int nxPrepaid; - private boolean opened; - private ItemFactory factory; - private final List inventory = new ArrayList<>(); - private final List wishList = new ArrayList<>(); - private int notes = 0; - private final Lock lock = new ReentrantLock(); - - public CashShop(int accountId, int characterId, int jobType) throws SQLException { - this.accountId = accountId; - this.characterId = characterId; - - if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) { - switch (jobType) { - case 0: - factory = ItemFactory.CASH_EXPLORER; - break; - case 1: - factory = ItemFactory.CASH_CYGNUS; - break; - case 2: - factory = ItemFactory.CASH_ARAN; - break; - } - } else { - factory = ItemFactory.CASH_OVERALL; - } - - try (Connection con = DatabaseConnection.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?")) { - ps.setInt(1, accountId); - - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - this.nxCredit = rs.getInt("nxCredit"); - this.maplePoint = rs.getInt("maplePoint"); - this.nxPrepaid = rs.getInt("nxPrepaid"); - } - } - } - - for (Pair item : factory.loadItems(accountId, false)) { - inventory.add(item.getLeft()); - } - - try (PreparedStatement ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?")) { - ps.setInt(1, characterId); - - try (ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - wishList.add(rs.getInt("sn")); - } - } - } - } - } - public int getCash(int type) { - switch (type) { - case 1: - return nxCredit; - case 2: - return maplePoint; - case 4: - return nxPrepaid; - } + return switch (type) { + case NX_CREDIT -> nxCredit; + case MAPLE_POINT -> maplePoint; + case NX_PREPAID -> nxPrepaid; + default -> 0; + }; - return 0; } public void gainCash(int type, int cash) { switch (type) { - case 1: - nxCredit += cash; - break; - case 2: - maplePoint += cash; - break; - case 4: - nxPrepaid += cash; - break; + case NX_CREDIT -> nxCredit += cash; + case MAPLE_POINT -> maplePoint += cash; + case NX_PREPAID -> nxPrepaid += cash; } } diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index 7471e903b7..29643ab662 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -77,6 +77,7 @@ import net.server.world.Party; import net.server.world.PartyCharacter; import net.server.world.PartyOperation; import net.server.world.World; +import server.CashShop; import server.CashShop.CashItem; import server.CashShop.CashItemFactory; import server.CashShop.SpecialCashItem; @@ -5580,8 +5581,8 @@ public class PacketCreator { public static Packet showMTSCash(Character chr) { final OutPacket p = OutPacket.create(SendOpcode.MTS_OPERATION2); - p.writeInt(chr.getCashShop().getCash(4)); - p.writeInt(chr.getCashShop().getCash(2)); + p.writeInt(chr.getCashShop().getCash(CashShop.NX_PREPAID)); + p.writeInt(chr.getCashShop().getCash(CashShop.MAPLE_POINT)); return p; } @@ -5689,9 +5690,9 @@ public class PacketCreator { public static Packet showCash(Character mc) { final OutPacket p = OutPacket.create(SendOpcode.QUERY_CASH_RESULT); - p.writeInt(mc.getCashShop().getCash(1)); - p.writeInt(mc.getCashShop().getCash(2)); - p.writeInt(mc.getCashShop().getCash(4)); + p.writeInt(mc.getCashShop().getCash(CashShop.NX_CREDIT)); + p.writeInt(mc.getCashShop().getCash(CashShop.MAPLE_POINT)); + p.writeInt(mc.getCashShop().getCash(CashShop.NX_PREPAID)); return p; } From c7b2d218ef27af06bc3a704a1139234833f45691 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 16 Jun 2024 12:59:30 +0200 Subject: [PATCH 29/55] Fix count not being updated after last Cash shop surprise --- .../handlers/CashShopSurpriseHandler.java | 29 +++++--- src/main/java/server/CashShop.java | 74 ++++++++++--------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java index 618c3f7d31..7b338dc082 100644 --- a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java +++ b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java @@ -24,26 +24,33 @@ import client.inventory.Item; import net.AbstractPacketHandler; import net.packet.InPacket; import server.CashShop; +import server.CashShop.CashShopSurpriseResult; import tools.PacketCreator; -import tools.Pair; + +import java.util.Optional; /** * @author RonanLana + * @author Ponk */ public class CashShopSurpriseHandler extends AbstractPacketHandler { + @Override public final void handlePacket(InPacket p, Client c) { CashShop cs = c.getPlayer().getCashShop(); - - if (cs.isOpened()) { - Pair cssResult = cs.openCashShopSurprise(); - - if (cssResult != null) { - Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight(); - c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getCashId(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true)); - } else { - c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); - } + if (!cs.isOpened()) { + return; } + + Optional result = cs.openCashShopSurprise(); + if (result.isEmpty()) { + c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); + return; + } + + Item usedCashShopSurprise = result.get().usedCashShopSurprise(); + Item reward = result.get().reward(); + c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), usedCashShopSurprise.getCashId(), + usedCashShopSurprise.getQuantity(), reward, reward.getItemId(), reward.getQuantity(), true)); } } diff --git a/src/main/java/server/CashShop.java b/src/main/java/server/CashShop.java index 6b93dea39d..ae9a45599e 100644 --- a/src/main/java/server/CashShop.java +++ b/src/main/java/server/CashShop.java @@ -47,6 +47,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -55,6 +56,7 @@ import static java.util.concurrent.TimeUnit.HOURS; /* * @author Flav + * @author Ponk */ public class CashShop { public static final int NX_CREDIT = 1; @@ -177,19 +179,20 @@ public class CashShop { if (ItemConstants.EXPIRING_ITEMS) { if (period == 1) { switch (itemId) { - case ItemId.DROP_COUPON_2X_4H, ItemId.EXP_COUPON_2X_4H: // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day. - item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(4)); + case ItemId.DROP_COUPON_2X_4H, + ItemId.EXP_COUPON_2X_4H: // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day. + item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(4)); /* } else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(3)); */ - break; - case ItemId.EXP_COUPON_3X_2H: - item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(2)); - break; - default: - item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(1)); - break; + break; + case ItemId.EXP_COUPON_3X_2H: + item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(2)); + break; + default: + item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(1)); + break; } } else { item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(period)); @@ -323,6 +326,9 @@ public class CashShop { } } + public record CashShopSurpriseResult(Item usedCashShopSurprise, Item reward) { + } + public int getCash(int type) { return switch (type) { case NX_CREDIT -> nxCredit; @@ -546,33 +552,31 @@ public class CashShop { return null; } - public synchronized Pair openCashShopSurprise() { - Item css = getCashShopItemByItemid(ItemId.CASH_SHOP_SURPRISE); - - if (css != null) { - if (getItemsSize() >= 100) { - return null; - } - - CashItem cItem = CashItemFactory.getRandomCashItem(); - - if (cItem != null) { - if (css.getQuantity() > 1) { - css.setQuantity((short) (css.getQuantity() - 1)); - } else { - removeFromInventory(css); - } - - Item item = cItem.toItem(); - addToInventory(item); - - return new Pair<>(item, css); - } else { - return null; - } - } else { - return null; + public synchronized Optional openCashShopSurprise() { + Item cashShopSurprise = getCashShopItemByItemid(ItemId.CASH_SHOP_SURPRISE); + if (cashShopSurprise == null || cashShopSurprise.getQuantity() <= 0) { + return Optional.empty(); } + + if (getItemsSize() >= 100) { + return Optional.empty(); + } + + CashItem cashItemReward = CashItemFactory.getRandomCashItem(); + if (cashItemReward == null) { + return Optional.empty(); + } + + short newQuantity = (short) (cashShopSurprise.getQuantity() - 1); + cashShopSurprise.setQuantity(newQuantity); + if (newQuantity <= 0) { + removeFromInventory(cashShopSurprise); + } + + Item itemReward = cashItemReward.toItem(); + addToInventory(itemReward); + + return Optional.of(new CashShopSurpriseResult(cashShopSurprise, itemReward)); } public static Item generateCouponItem(int itemId, short quantity) { From 6ab1af99da81c8d89f52fd34a64a1c5fceacb412 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 16 Jun 2024 13:40:06 +0200 Subject: [PATCH 30/55] Add tests for CashShopSurpriseHandler --- pom.xml | 12 +++- src/main/java/net/packet/ByteBufInPacket.java | 5 ++ .../java/net/packet/ByteBufOutPacket.java | 5 ++ src/main/java/tools/PacketCreator.java | 11 ++-- .../java/net/packet/ByteBufInPacketTest.java | 10 ++- .../java/net/packet/ByteBufOutPacketTest.java | 12 +++- .../handlers/CashShopSurpriseHandlerTest.java | 66 +++++++++++++++++++ src/test/java/testutil/AnyValues.java | 4 ++ src/test/java/testutil/HandlerTest.java | 24 +++++++ src/test/java/testutil/Items.java | 10 +++ src/test/java/testutil/Packets.java | 12 ++++ 11 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java create mode 100644 src/test/java/testutil/HandlerTest.java create mode 100644 src/test/java/testutil/Items.java create mode 100644 src/test/java/testutil/Packets.java diff --git a/pom.xml b/pom.xml index 1c7ac8da10..ea27a32ed1 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 cosmic-maplestory @@ -52,7 +53,8 @@ 3.2.5 3.4.1 - 3.7.1 + 3.7.1 + 2.0.13 @@ -180,6 +182,12 @@ ${mockito.version} test + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + diff --git a/src/main/java/net/packet/ByteBufInPacket.java b/src/main/java/net/packet/ByteBufInPacket.java index e79326f7c6..25b139629c 100644 --- a/src/main/java/net/packet/ByteBufInPacket.java +++ b/src/main/java/net/packet/ByteBufInPacket.java @@ -82,6 +82,11 @@ public class ByteBufInPacket implements InPacket { return byteBuf.readerIndex(); } + @Override + public boolean equals(Object o) { + return o instanceof ByteBufInPacket other && byteBuf.equals(other.byteBuf); + } + @Override public String toString() { final int readerIndex = byteBuf.readerIndex(); diff --git a/src/main/java/net/packet/ByteBufOutPacket.java b/src/main/java/net/packet/ByteBufOutPacket.java index ce289603be..376253d280 100644 --- a/src/main/java/net/packet/ByteBufOutPacket.java +++ b/src/main/java/net/packet/ByteBufOutPacket.java @@ -91,4 +91,9 @@ public class ByteBufOutPacket implements OutPacket { public void skip(int numberOfBytes) { writeBytes(new byte[numberOfBytes]); } + + @Override + public boolean equals(Object o) { + return o instanceof ByteBufOutPacket other && byteBuf.equals(other.byteBuf); + } } diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index 29643ab662..a6cf3cf6eb 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -6480,14 +6480,15 @@ public class PacketCreator { return p; } - public static Packet onCashGachaponOpenSuccess(int accountid, long sn, int remainingBoxes, Item item, int itemid, int nSelectedItemCount, boolean bJackpot) { + public static Packet onCashGachaponOpenSuccess(int accountid, long boxCashId, int remainingBoxes, Item reward, + int rewardItemId, int rewardQuantity, boolean bJackpot) { OutPacket p = OutPacket.create(SendOpcode.CASHSHOP_CASH_ITEM_GACHAPON_RESULT); p.writeByte(0xE5); // subopcode thanks to Ubaware - p.writeLong(sn);// sn of the box used + p.writeLong(boxCashId); p.writeInt(remainingBoxes); - addCashItemInformation(p, item, accountid); - p.writeInt(itemid);// the itemid of the liSN? - p.writeByte(nSelectedItemCount);// the total count now? o.O + addCashItemInformation(p, reward, accountid); + p.writeInt(rewardItemId); + p.writeByte(rewardQuantity); // nSelectedItemCount p.writeBool(bJackpot);// "CashGachaponJackpot" return p; } diff --git a/src/test/java/net/packet/ByteBufInPacketTest.java b/src/test/java/net/packet/ByteBufInPacketTest.java index 6837ecfe1c..34b87e89b8 100644 --- a/src/test/java/net/packet/ByteBufInPacketTest.java +++ b/src/test/java/net/packet/ByteBufInPacketTest.java @@ -235,4 +235,12 @@ class ByteBufInPacketTest { assertEquals(initial.length(), afterReadingOpcode.length()); } -} \ No newline at end of file + + @Test + void equalsShouldCompareBytes() { + ByteBufInPacket packet1 = new ByteBufInPacket(Unpooled.wrappedBuffer(new byte[]{ 11, 22, 33, 44 })); + ByteBufInPacket packet2 = new ByteBufInPacket(Unpooled.wrappedBuffer(new byte[]{ 11, 22, 33, 44 })); + + assertEquals(packet1, packet2); + } +} diff --git a/src/test/java/net/packet/ByteBufOutPacketTest.java b/src/test/java/net/packet/ByteBufOutPacketTest.java index d3f2892f99..1b54a5c32d 100644 --- a/src/test/java/net/packet/ByteBufOutPacketTest.java +++ b/src/test/java/net/packet/ByteBufOutPacketTest.java @@ -203,4 +203,14 @@ class ByteBufOutPacketTest { assertEquals(0, wrapped.readByte()); assertEquals(secondWrittenByte, wrapped.readByte()); } -} \ No newline at end of file + + @Test + void equalsShouldCompareBytes() { + ByteBufOutPacket packet1 = new ByteBufOutPacket(); + packet1.writeBytes(new byte[] { 55, 66, 77, 88 }); + ByteBufOutPacket packet2 = new ByteBufOutPacket(); + packet2.writeBytes(new byte[] { 55, 66, 77, 88 }); + + assertEquals(packet1, packet2); + } +} diff --git a/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java b/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java new file mode 100644 index 0000000000..ff97b11fe3 --- /dev/null +++ b/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java @@ -0,0 +1,66 @@ +package net.server.channel.handlers; + +import client.inventory.Item; +import constants.id.ItemId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import server.CashShop; +import testutil.HandlerTest; +import testutil.Items; +import testutil.Packets; +import tools.PacketCreator; + +import java.util.Optional; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CashShopSurpriseHandlerTest extends HandlerTest { + private final CashShopSurpriseHandler handler = new CashShopSurpriseHandler(); + + @Mock + private CashShop cashShop; + + @BeforeEach + void prepareCashShop() { + when(chr.getCashShop()).thenReturn(cashShop); + } + + @Test + void shouldDoNothingWhenCsIsNotOpened() { + when(cashShop.isOpened()).thenReturn(false); + + handler.handlePacket(Packets.emptyInPacket(), client); + + verify(cashShop, never()).openCashShopSurprise(); + } + + @Test + void shouldSendFailurePacketWhenFailToOpen() { + when(cashShop.isOpened()).thenReturn(true); + when(cashShop.openCashShopSurprise()).thenReturn(Optional.empty()); + + handler.handlePacket(Packets.emptyInPacket(), client); + + verify(client).sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); + } + + @Test + void shouldSendSuccessPacketWhenSuccessfullyOpen() { + when(cashShop.isOpened()).thenReturn(true); + Item cashShopSurprise = Items.itemWithQuantity(ItemId.CASH_SHOP_SURPRISE, 3); + Item reward = Items.itemWithQuantity(5000012, 1); + when(cashShop.openCashShopSurprise()).thenReturn(Optional.of(new CashShop.CashShopSurpriseResult( + cashShopSurprise, reward))); + + handler.handlePacket(Packets.emptyInPacket(), client); + + verify(client).sendPacket(PacketCreator.onCashGachaponOpenSuccess(ACCOUNT_ID, cashShopSurprise.getCashId(), 3, + reward, 5000012, 1, true)); + } +} diff --git a/src/test/java/testutil/AnyValues.java b/src/test/java/testutil/AnyValues.java index ccf9c0abbf..8c58a8c08b 100644 --- a/src/test/java/testutil/AnyValues.java +++ b/src/test/java/testutil/AnyValues.java @@ -8,6 +8,10 @@ public class AnyValues { return "string"; } + public static short anyShort() { + return 4; + } + public static DaoException daoException() { return new DaoException(string(), new RuntimeException()); } diff --git a/src/test/java/testutil/HandlerTest.java b/src/test/java/testutil/HandlerTest.java new file mode 100644 index 0000000000..32fcaa5299 --- /dev/null +++ b/src/test/java/testutil/HandlerTest.java @@ -0,0 +1,24 @@ +package testutil; + +import client.Character; +import client.Client; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mock; + +import static org.mockito.Mockito.lenient; + +public abstract class HandlerTest { + protected static final int ACCOUNT_ID = 1702; + + @Mock + protected Client client; + + @Mock + protected Character chr; + + @BeforeEach + void prepareClient() { + lenient().when(client.getAccID()).thenReturn(ACCOUNT_ID); + lenient().when(client.getPlayer()).thenReturn(chr); + } +} diff --git a/src/test/java/testutil/Items.java b/src/test/java/testutil/Items.java new file mode 100644 index 0000000000..aba37686f1 --- /dev/null +++ b/src/test/java/testutil/Items.java @@ -0,0 +1,10 @@ +package testutil; + +import client.inventory.Item; + +public class Items { + + public static Item itemWithQuantity(int itemId, int quantity) { + return new Item(itemId, AnyValues.anyShort(), (short) quantity); + } +} diff --git a/src/test/java/testutil/Packets.java b/src/test/java/testutil/Packets.java new file mode 100644 index 0000000000..f833694388 --- /dev/null +++ b/src/test/java/testutil/Packets.java @@ -0,0 +1,12 @@ +package testutil; + +import io.netty.buffer.Unpooled; +import net.packet.ByteBufInPacket; +import net.packet.InPacket; + +public class Packets { + + public static InPacket emptyInPacket() { + return new ByteBufInPacket(Unpooled.buffer()); + } +} From 5aeed01e381c52fa467e266a2a28ed710069fdca Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 16 Jun 2024 15:08:48 +0200 Subject: [PATCH 31/55] Fix not using the selected Cash shop surprise If you have multiple, it would always use the first one. Now it uses whichever you select (as expected). --- .../handlers/CashShopSurpriseHandler.java | 3 +- src/main/java/server/CashShop.java | 86 ++++++++++--------- .../handlers/CashShopSurpriseHandlerTest.java | 18 ++-- src/test/java/testutil/Packets.java | 10 ++- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java index 7b338dc082..9fff47092f 100644 --- a/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java +++ b/src/main/java/net/server/channel/handlers/CashShopSurpriseHandler.java @@ -42,7 +42,8 @@ public class CashShopSurpriseHandler extends AbstractPacketHandler { return; } - Optional result = cs.openCashShopSurprise(); + long cashId = p.readLong(); + Optional result = cs.openCashShopSurprise(cashId); if (result.isEmpty()) { c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); return; diff --git a/src/main/java/server/CashShop.java b/src/main/java/server/CashShop.java index ae9a45599e..498fe12fdc 100644 --- a/src/main/java/server/CashShop.java +++ b/src/main/java/server/CashShop.java @@ -29,6 +29,7 @@ import client.inventory.Pet; import config.YamlConfig; import constants.id.ItemId; import constants.inventory.ItemConstants; +import net.jcip.annotations.GuardedBy; import net.server.Server; import provider.Data; import provider.DataProvider; @@ -408,17 +409,6 @@ public class CashShop { } } - public int getItemsSize() { - int size = 0; - lock.lock(); - try { - size = inventory.size(); - } finally { - lock.unlock(); - } - return size; - } - public List getWishList() { return wishList; } @@ -537,46 +527,58 @@ public class CashShop { } } - private Item getCashShopItemByItemid(int itemid) { + public Optional openCashShopSurprise(long cashId) { lock.lock(); try { - for (Item it : inventory) { - if (it.getItemId() == itemid) { - return it; - } + Optional maybeCashShopSurprise = getItemByCashId(cashId); + if (maybeCashShopSurprise.isEmpty() || + maybeCashShopSurprise.get().getItemId() != ItemId.CASH_SHOP_SURPRISE) { + return Optional.empty(); } + + Item cashShopSurprise = maybeCashShopSurprise.get(); + if (cashShopSurprise.getQuantity() <= 0) { + return Optional.empty(); + } + + if (getItemsSize() >= 100) { + return Optional.empty(); + } + + CashItem cashItemReward = CashItemFactory.getRandomCashItem(); + if (cashItemReward == null) { + return Optional.empty(); + } + + short newQuantity = (short) (cashShopSurprise.getQuantity() - 1); + cashShopSurprise.setQuantity(newQuantity); + if (newQuantity <= 0) { + removeFromInventory(cashShopSurprise); + } + + Item itemReward = cashItemReward.toItem(); + addToInventory(itemReward); + + return Optional.of(new CashShopSurpriseResult(cashShopSurprise, itemReward)); } finally { lock.unlock(); } - - return null; } - public synchronized Optional openCashShopSurprise() { - Item cashShopSurprise = getCashShopItemByItemid(ItemId.CASH_SHOP_SURPRISE); - if (cashShopSurprise == null || cashShopSurprise.getQuantity() <= 0) { - return Optional.empty(); + @GuardedBy("lock") + private Optional getItemByCashId(long cashId) { + return inventory.stream() + .filter(item -> item.getCashId() == cashId) + .findAny(); + } + + public int getItemsSize() { + lock.lock(); + try { + return inventory.size(); + } finally { + lock.unlock(); } - - if (getItemsSize() >= 100) { - return Optional.empty(); - } - - CashItem cashItemReward = CashItemFactory.getRandomCashItem(); - if (cashItemReward == null) { - return Optional.empty(); - } - - short newQuantity = (short) (cashShopSurprise.getQuantity() - 1); - cashShopSurprise.setQuantity(newQuantity); - if (newQuantity <= 0) { - removeFromInventory(cashShopSurprise); - } - - Item itemReward = cashItemReward.toItem(); - addToInventory(itemReward); - - return Optional.of(new CashShopSurpriseResult(cashShopSurprise, itemReward)); } public static Item generateCouponItem(int itemId, short quantity) { diff --git a/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java b/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java index ff97b11fe3..71d0246ab1 100644 --- a/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java +++ b/src/test/java/net/server/channel/handlers/CashShopSurpriseHandlerTest.java @@ -2,6 +2,7 @@ package net.server.channel.handlers; import client.inventory.Item; import constants.id.ItemId; +import net.packet.InPacket; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -15,6 +16,7 @@ import tools.PacketCreator; import java.util.Optional; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,21 +33,25 @@ class CashShopSurpriseHandlerTest extends HandlerTest { when(chr.getCashShop()).thenReturn(cashShop); } + private InPacket useCashShopSurprisePacket(long cashId) { + return Packets.buildInPacket(out -> out.writeLong(cashId)); + } + @Test void shouldDoNothingWhenCsIsNotOpened() { when(cashShop.isOpened()).thenReturn(false); - handler.handlePacket(Packets.emptyInPacket(), client); + handler.handlePacket(useCashShopSurprisePacket(123), client); - verify(cashShop, never()).openCashShopSurprise(); + verify(cashShop, never()).openCashShopSurprise(anyLong()); } @Test void shouldSendFailurePacketWhenFailToOpen() { when(cashShop.isOpened()).thenReturn(true); - when(cashShop.openCashShopSurprise()).thenReturn(Optional.empty()); + when(cashShop.openCashShopSurprise(anyLong())).thenReturn(Optional.empty()); - handler.handlePacket(Packets.emptyInPacket(), client); + handler.handlePacket(useCashShopSurprisePacket(456), client); verify(client).sendPacket(PacketCreator.onCashItemGachaponOpenFailed()); } @@ -55,10 +61,10 @@ class CashShopSurpriseHandlerTest extends HandlerTest { when(cashShop.isOpened()).thenReturn(true); Item cashShopSurprise = Items.itemWithQuantity(ItemId.CASH_SHOP_SURPRISE, 3); Item reward = Items.itemWithQuantity(5000012, 1); - when(cashShop.openCashShopSurprise()).thenReturn(Optional.of(new CashShop.CashShopSurpriseResult( + when(cashShop.openCashShopSurprise(789)).thenReturn(Optional.of(new CashShop.CashShopSurpriseResult( cashShopSurprise, reward))); - handler.handlePacket(Packets.emptyInPacket(), client); + handler.handlePacket(useCashShopSurprisePacket(789), client); verify(client).sendPacket(PacketCreator.onCashGachaponOpenSuccess(ACCOUNT_ID, cashShopSurprise.getCashId(), 3, reward, 5000012, 1, true)); diff --git a/src/test/java/testutil/Packets.java b/src/test/java/testutil/Packets.java index f833694388..6bd9db8a9c 100644 --- a/src/test/java/testutil/Packets.java +++ b/src/test/java/testutil/Packets.java @@ -2,11 +2,17 @@ package testutil; import io.netty.buffer.Unpooled; import net.packet.ByteBufInPacket; +import net.packet.ByteBufOutPacket; import net.packet.InPacket; +import net.packet.OutPacket; + +import java.util.function.Consumer; public class Packets { - public static InPacket emptyInPacket() { - return new ByteBufInPacket(Unpooled.buffer()); + public static InPacket buildInPacket(Consumer contentProvider) { + OutPacket builderInput = new ByteBufOutPacket(); + contentProvider.accept(builderInput); + return new ByteBufInPacket(Unpooled.wrappedBuffer(builderInput.getBytes())); } } From 1791365e0f44531f0b5140dd7b726eee7f61489d Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 16 Jun 2024 16:06:29 +0200 Subject: [PATCH 32/55] Fix able to get package from Cash shop surprise Packages aren't real items and crash the client. --- src/main/java/constants/id/ItemId.java | 4 ++ src/main/java/server/CashShop.java | 45 ++++++++-------------- src/test/java/constants/id/ItemIdTest.java | 19 +++++++++ 3 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 src/test/java/constants/id/ItemIdTest.java diff --git a/src/main/java/constants/id/ItemId.java b/src/main/java/constants/id/ItemId.java index 2624966f0b..dda0323a2f 100644 --- a/src/main/java/constants/id/ItemId.java +++ b/src/main/java/constants/id/ItemId.java @@ -312,6 +312,10 @@ public class ItemId { return itemId == NX_CARD_100 || itemId == NX_CARD_250; } + public static boolean isCashPackage(int itemId) { + return itemId / 10000 == 910; + } + // Face expression private static final int FACE_EXPRESSION_MIN = 5160000; private static final int FACE_EXPRESSION_MAX = 5160014; diff --git a/src/main/java/server/CashShop.java b/src/main/java/server/CashShop.java index 498fe12fdc..6801764fe1 100644 --- a/src/main/java/server/CashShop.java +++ b/src/main/java/server/CashShop.java @@ -49,6 +49,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -231,7 +232,6 @@ public class CashShop { public static class CashItemFactory { private static volatile Map items = new HashMap<>(); - private static volatile List randomitemsns = new ArrayList<>(); private static volatile Map> packages = new HashMap<>(); private static volatile List specialcashitems = new ArrayList<>(); @@ -239,7 +239,6 @@ public class CashShop { DataProvider etc = DataProviderFactory.getDataProvider(WZFiles.ETC); Map loadedItems = new HashMap<>(); - List onSaleItems = new ArrayList<>(); for (Data item : etc.getData("Commodity.img").getChildren()) { int sn = DataTool.getIntConvert("SN", item); int itemId = DataTool.getIntConvert("ItemId", item); @@ -248,13 +247,8 @@ public class CashShop { short count = (short) DataTool.getIntConvert("Count", item, 1); boolean onSale = DataTool.getIntConvert("OnSale", item, 0) == 1; loadedItems.put(sn, new CashItem(sn, itemId, price, period, count, onSale)); - - if (onSale) { - onSaleItems.add(sn); - } } CashItemFactory.items = loadedItems; - CashItemFactory.randomitemsns = onSaleItems; Map> loadedPackages = new HashMap<>(); for (Data cashPackage : etc.getData("CashPackage.img").getChildren()) { @@ -281,13 +275,20 @@ public class CashShop { CashItemFactory.specialcashitems = loadedSpecialItems; } - public static CashItem getRandomCashItem() { - if (randomitemsns.isEmpty()) { - return null; + public static Optional getRandomCashItem() { + if (items.isEmpty()) { + return Optional.empty(); } - int rnd = (int) (Math.random() * randomitemsns.size()); - return items.get(randomitemsns.get(rnd)); + List itemPool = items.values().stream() + .filter(CashItem::isOnSale) + .filter(cashItem -> !ItemId.isCashPackage(cashItem.itemId)) + .toList(); + return Optional.of(getRandomItem(itemPool)); + } + + private static CashItem getRandomItem(List items) { + return items.get(new Random().nextInt(items.size())); } public static CashItem getItem(int sn) { @@ -311,20 +312,6 @@ public class CashShop { public static List getSpecialCashItems() { return specialcashitems; } - - public static void reloadSpecialCashItems() {//Yay? - List loadedSpecialItems = new ArrayList<>(); - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems"); - ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - loadedSpecialItems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info"))); - } - } catch (SQLException ex) { - ex.printStackTrace(); - } - CashItemFactory.specialcashitems = loadedSpecialItems; - } } public record CashShopSurpriseResult(Item usedCashShopSurprise, Item reward) { @@ -545,8 +532,8 @@ public class CashShop { return Optional.empty(); } - CashItem cashItemReward = CashItemFactory.getRandomCashItem(); - if (cashItemReward == null) { + Optional cashItemReward = CashItemFactory.getRandomCashItem(); + if (cashItemReward.isEmpty()) { return Optional.empty(); } @@ -556,7 +543,7 @@ public class CashShop { removeFromInventory(cashShopSurprise); } - Item itemReward = cashItemReward.toItem(); + Item itemReward = cashItemReward.get().toItem(); addToInventory(itemReward); return Optional.of(new CashShopSurpriseResult(cashShopSurprise, itemReward)); diff --git a/src/test/java/constants/id/ItemIdTest.java b/src/test/java/constants/id/ItemIdTest.java new file mode 100644 index 0000000000..988eebacea --- /dev/null +++ b/src/test/java/constants/id/ItemIdTest.java @@ -0,0 +1,19 @@ +package constants.id; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ItemIdTest { + + @Test + void isCashPackage() { + assertTrue(ItemId.isCashPackage(9102237)); + } + + @Test + void isNotCashPackage() { + assertFalse(ItemId.isCashPackage(4000000)); + } +} From 5a4bdd343cdfdeb27e36214243b1933ba8549b15 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 16 Jun 2024 19:09:21 +0200 Subject: [PATCH 33/55] Fix Energy charge crashing certain other players Crabo in #bug-report (2024-06-10): "(...) this will crash everyone in the map besides the bucc and 1 character, when a bucc (or TB) charges energy and a character with an ID that's a multiple of 102 is in the same map (and the energy reaches that number so if character ID is 204 it will reach that after 2 hits and DC the whole map besides the bucc and that char with id 204). Thanks to others for helping me fix it. Thought I'd report it!" --- src/main/java/client/Character.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 0bc5926a02..ea94e94acd 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -6063,7 +6063,8 @@ public class Character extends AbstractCharacterObject { sendPacket(PacketCreator.giveBuff(energybar, 0, stat)); sendPacket(PacketCreator.showOwnBuffEffect(energycharge.getId(), 2)); getMap().broadcastPacket(this, PacketCreator.showBuffEffect(id, energycharge.getId(), 2)); - getMap().broadcastPacket(this, PacketCreator.giveForeignBuff(energybar, stat)); + getMap().broadcastPacket(this, PacketCreator.giveForeignPirateBuff(id, energycharge.getId(), + ceffect.getDuration(), stat)); } if (energybar >= 10000 && energybar < 11000) { energybar = 15000; From a878a4f3f95f035950776f0551d40b190c4128df Mon Sep 17 00:00:00 2001 From: Noir Date: Mon, 17 Jun 2024 18:48:13 -0400 Subject: [PATCH 34/55] Fix minimum HP and MP checks on AP reset --- .../processor/stat/AssignAPProcessor.java | 128 ++++++++-- .../processor/stat/AssignAPProcessorTest.java | 237 ++++++++++++++++++ 2 files changed, 345 insertions(+), 20 deletions(-) create mode 100644 src/test/java/client/processor/stat/AssignAPProcessorTest.java diff --git a/src/main/java/client/processor/stat/AssignAPProcessor.java b/src/main/java/client/processor/stat/AssignAPProcessor.java index bc42c06cc5..c7897ce810 100644 --- a/src/main/java/client/processor/stat/AssignAPProcessor.java +++ b/src/main/java/client/processor/stat/AssignAPProcessor.java @@ -552,9 +552,7 @@ public class AssignAPProcessor { return false; } - int hp = player.getMaxHp(); - int level_ = player.getLevel(); - if (hp < level_ * 14 + 148) { + if (player.getMaxHp() < getMinHp(player.getJob(), player.getLevel())) { player.message("You don't have the minimum HP pool required to swap."); c.sendPacket(PacketCreator.enableActions()); return false; @@ -583,29 +581,14 @@ public class AssignAPProcessor { return false; } - int mp = player.getMaxMp(); - int level = player.getLevel(); - Job job = player.getJob(); - - boolean canWash = true; - if (job.isA(Job.SPEARMAN) && mp < 4 * level + 156) { - canWash = false; - } else if ((job.isA(Job.FIGHTER) || job.isA(Job.ARAN1)) && mp < 4 * level + 56) { - canWash = false; - } else if (job.isA(Job.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) { - canWash = false; - } else if (mp < level * 14 + 148) { - canWash = false; - } - - if (!canWash) { + if (player.getMaxMp() < getMinMp(player.getJob(), player.getLevel())) { player.message("You don't have the minimum MP pool required to swap."); c.sendPacket(PacketCreator.enableActions()); return false; } int curMp = player.getMp(); - int mplose = -takeMp(job); + int mplose = -takeMp(player.getJob()); player.assignMP(mplose, -1); if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) { player.updateMp(Math.max(0, curMp + mplose)); @@ -896,4 +879,109 @@ public class AssignAPProcessor { return MaxMP; } + public static int getMinHp(Job job, int level) { + int multiplier = 0; + int offset = 0; + + if (job == Job.WARRIOR || + job.isA(Job.PAGE) || + job.isA(Job.SPEARMAN) || + job == Job.DAWNWARRIOR1 || + job == Job.ARAN1) { + multiplier = 24; offset = 118; + + } else if (job.isA(Job.FIGHTER) || + job.isA(Job.DAWNWARRIOR2) || + job.isA(Job.ARAN2)) { + multiplier = 24; offset = 418; + + } else if (job.isA(Job.MAGICIAN) || + job.isA(Job.BLAZEWIZARD1)) { + multiplier = 10; offset = 54; + + } else if (job == Job.BOWMAN || + job == Job.THIEF || + job == Job.WINDARCHER1 || + job == Job.NIGHTWALKER1) { + multiplier = 20; offset = 58; + + } else if (job.isA(Job.HUNTER) || + job.isA(Job.CROSSBOWMAN) || + job.isA(Job.ASSASSIN) || + job.isA(Job.BANDIT) || + job.isA(Job.WINDARCHER2) || + job.isA(Job.NIGHTWALKER2)) { + multiplier = 20; offset = 358; + + } else if (job == Job.PIRATE || + job == Job.THUNDERBREAKER1) { + multiplier = 22; offset = 38; + + } else if (job.isA(Job.BRAWLER) || + job.isA(Job.GUNSLINGER) || + job.isA(Job.THUNDERBREAKER2)) { + multiplier = 22; offset = 338; + + } else if (job == Job.BEGINNER || + job == Job.NOBLESSE) { + multiplier = 12; offset = 38; + } + + return (multiplier * level) + offset; + } + + public static int getMinMp(Job job, int level) { + int multiplier = 0; + int offset = 0; + + if (job == Job.WARRIOR || + job.isA(Job.FIGHTER) || + job.isA(Job.DAWNWARRIOR1) || + job.isA(Job.ARAN1)) { + multiplier = 4; offset = 55; + + } else if (job.isA(Job.PAGE) || + job.isA(Job.SPEARMAN)) { + multiplier = 4; offset = 155; + + } else if (job == Job.MAGICIAN || + job == Job.BLAZEWIZARD1) { + multiplier = 22; offset = -1; + + } else if (job.isA(Job.FP_WIZARD) || + job.isA(Job.IL_WIZARD) || + job.isA(Job.CLERIC) || + job.isA(Job.BLAZEWIZARD2)) { + multiplier = 22; offset = 449; + + } else if (job == Job.BOWMAN || + job == Job.THIEF || + job == Job.WINDARCHER1 || + job == Job.NIGHTWALKER1) { + multiplier = 14; offset = -15; + + } else if (job.isA(Job.HUNTER) || + job.isA(Job.CROSSBOWMAN) || + job.isA(Job.ASSASSIN) || + job.isA(Job.BANDIT) || + job.isA(Job.WINDARCHER2) || + job.isA(Job.NIGHTWALKER2)) { + multiplier = 14; offset = 135; + + } else if (job == Job.PIRATE || + job == Job.THUNDERBREAKER1) { + multiplier = 18; offset = -55; + + } else if (job.isA(Job.BRAWLER) || + job.isA(Job.GUNSLINGER) || + job.isA(Job.THUNDERBREAKER2)) { + multiplier = 18; offset = 95; + + } else if (job == Job.BEGINNER || + job == Job.NOBLESSE) { + multiplier = 10; offset = -5; + } + + return (multiplier * level) + offset; + } } diff --git a/src/test/java/client/processor/stat/AssignAPProcessorTest.java b/src/test/java/client/processor/stat/AssignAPProcessorTest.java new file mode 100644 index 0000000000..d565bbb3f9 --- /dev/null +++ b/src/test/java/client/processor/stat/AssignAPProcessorTest.java @@ -0,0 +1,237 @@ +package client.processor.stat; + +import client.Job; +import org.junit.jupiter.api.Test; + +import java.util.function.BiFunction; + +import static org.junit.jupiter.api.Assertions.*; + +class AssignAPProcessorTest { + + @Test + void getMinHp() { + int max_level = 200; + int cygnus_max_level = 120; + + BiFunction f = AssignAPProcessor::getMinHp; + + assertAll( + // Beginners + () -> assertEquals(2438, f.apply(Job.BEGINNER, max_level)), + () -> assertEquals(1478, f.apply(Job.NOBLESSE, cygnus_max_level)), + + // Warrior (Explorer) + () -> assertEquals(4918, f.apply(Job.WARRIOR, max_level)), + + () -> assertEquals(5218, f.apply(Job.FIGHTER, max_level)), + () -> assertEquals(5218, f.apply(Job.CRUSADER, max_level)), + () -> assertEquals(5218, f.apply(Job.HERO, max_level)), + + () -> assertEquals(4918, f.apply(Job.PAGE, max_level)), + () -> assertEquals(4918, f.apply(Job.WHITEKNIGHT, max_level)), + () -> assertEquals(4918, f.apply(Job.PALADIN, max_level)), + + () -> assertEquals(4918, f.apply(Job.SPEARMAN, max_level)), + () -> assertEquals(4918, f.apply(Job.DRAGONKNIGHT, max_level)), + () -> assertEquals(4918, f.apply(Job.DARKKNIGHT, max_level)), + + // Warrior (Cygnus) + () -> assertEquals(2998, f.apply(Job.DAWNWARRIOR1, cygnus_max_level)), + () -> assertEquals(3298, f.apply(Job.DAWNWARRIOR2, cygnus_max_level)), + () -> assertEquals(3298, f.apply(Job.DAWNWARRIOR3, cygnus_max_level)), + () -> assertEquals(3298, f.apply(Job.DAWNWARRIOR4, cygnus_max_level)), + + // Warrior (Aran) + () -> assertEquals(4918, f.apply(Job.ARAN1, max_level)), + () -> assertEquals(5218, f.apply(Job.ARAN2, max_level)), + () -> assertEquals(5218, f.apply(Job.ARAN3, max_level)), + () -> assertEquals(5218, f.apply(Job.ARAN4, max_level)), + + // Magician (Explorer) + () -> assertEquals(2054, f.apply(Job.MAGICIAN, max_level)), + + () -> assertEquals(2054, f.apply(Job.FP_WIZARD, max_level)), + () -> assertEquals(2054, f.apply(Job.FP_MAGE, max_level)), + () -> assertEquals(2054, f.apply(Job.FP_ARCHMAGE, max_level)), + + () -> assertEquals(2054, f.apply(Job.IL_WIZARD, max_level)), + () -> assertEquals(2054, f.apply(Job.IL_MAGE, max_level)), + () -> assertEquals(2054, f.apply(Job.IL_ARCHMAGE, max_level)), + + () -> assertEquals(2054, f.apply(Job.CLERIC, max_level)), + () -> assertEquals(2054, f.apply(Job.PRIEST, max_level)), + () -> assertEquals(2054, f.apply(Job.BISHOP, max_level)), + + // Magician (Cygnus) + () -> assertEquals(1254, f.apply(Job.BLAZEWIZARD1, cygnus_max_level)), + () -> assertEquals(1254, f.apply(Job.BLAZEWIZARD2, cygnus_max_level)), + () -> assertEquals(1254, f.apply(Job.BLAZEWIZARD3, cygnus_max_level)), + () -> assertEquals(1254, f.apply(Job.BLAZEWIZARD4, cygnus_max_level)), + + // Bowman (Explorer) + () -> assertEquals(4058, f.apply(Job.BOWMAN, max_level)), + + () -> assertEquals(4358, f.apply(Job.HUNTER, max_level)), + () -> assertEquals(4358, f.apply(Job.RANGER, max_level)), + () -> assertEquals(4358, f.apply(Job.BOWMASTER, max_level)), + + () -> assertEquals(4358, f.apply(Job.CROSSBOWMAN, max_level)), + () -> assertEquals(4358, f.apply(Job.SNIPER, max_level)), + () -> assertEquals(4358, f.apply(Job.MARKSMAN, max_level)), + + // Bowman (Cygnus) + () -> assertEquals(2458, f.apply(Job.WINDARCHER1, cygnus_max_level)), + () -> assertEquals(2758, f.apply(Job.WINDARCHER2, cygnus_max_level)), + () -> assertEquals(2758, f.apply(Job.WINDARCHER3, cygnus_max_level)), + () -> assertEquals(2758, f.apply(Job.WINDARCHER4, cygnus_max_level)), + + // Thief (Explorer) + () -> assertEquals(4058, f.apply(Job.THIEF, max_level)), + + () -> assertEquals(4358, f.apply(Job.ASSASSIN, max_level)), + () -> assertEquals(4358, f.apply(Job.HERMIT, max_level)), + () -> assertEquals(4358, f.apply(Job.NIGHTLORD, max_level)), + + () -> assertEquals(4358, f.apply(Job.BANDIT, max_level)), + () -> assertEquals(4358, f.apply(Job.CHIEFBANDIT, max_level)), + () -> assertEquals(4358, f.apply(Job.SHADOWER, max_level)), + + // Thief (Cygnus) + () -> assertEquals(2458, f.apply(Job.NIGHTWALKER1, cygnus_max_level)), + () -> assertEquals(2758, f.apply(Job.NIGHTWALKER2, cygnus_max_level)), + () -> assertEquals(2758, f.apply(Job.NIGHTWALKER3, cygnus_max_level)), + () -> assertEquals(2758, f.apply(Job.NIGHTWALKER4, cygnus_max_level)), + + // Pirate (Explorer) + () -> assertEquals(4438, f.apply(Job.PIRATE, max_level)), + + () -> assertEquals(4738, f.apply(Job.BRAWLER, max_level)), + () -> assertEquals(4738, f.apply(Job.MARAUDER, max_level)), + () -> assertEquals(4738, f.apply(Job.BUCCANEER, max_level)), + + () -> assertEquals(4738, f.apply(Job.GUNSLINGER, max_level)), + () -> assertEquals(4738, f.apply(Job.OUTLAW, max_level)), + () -> assertEquals(4738, f.apply(Job.CORSAIR, max_level)), + + // Pirate (Cygnus) + () -> assertEquals(2678, f.apply(Job.THUNDERBREAKER1, cygnus_max_level)), + () -> assertEquals(2978, f.apply(Job.THUNDERBREAKER2, cygnus_max_level)), + () -> assertEquals(2978, f.apply(Job.THUNDERBREAKER3, cygnus_max_level)), + () -> assertEquals(2978, f.apply(Job.THUNDERBREAKER4, cygnus_max_level)) + ); + } + + @Test + void getMinMp() { + int max_level = 200; + int cygnus_max_level = 120; + + BiFunction f = AssignAPProcessor::getMinMp; + + assertAll( + // Beginners + () -> assertEquals(1995, f.apply(Job.BEGINNER, max_level)), + () -> assertEquals(1195, f.apply(Job.NOBLESSE, cygnus_max_level)), + + // Warrior (Explorer) + () -> assertEquals(855, f.apply(Job.WARRIOR, max_level)), + + () -> assertEquals(855, f.apply(Job.FIGHTER, max_level)), + () -> assertEquals(855, f.apply(Job.CRUSADER, max_level)), + () -> assertEquals(855, f.apply(Job.HERO, max_level)), + + () -> assertEquals(955, f.apply(Job.PAGE, max_level)), + () -> assertEquals(955, f.apply(Job.WHITEKNIGHT, max_level)), + () -> assertEquals(955, f.apply(Job.PALADIN, max_level)), + + () -> assertEquals(955, f.apply(Job.SPEARMAN, max_level)), + () -> assertEquals(955, f.apply(Job.DRAGONKNIGHT, max_level)), + () -> assertEquals(955, f.apply(Job.DARKKNIGHT, max_level)), + + // Warrior (Cygnus) + () -> assertEquals(535, f.apply(Job.DAWNWARRIOR1, cygnus_max_level)), + () -> assertEquals(535, f.apply(Job.DAWNWARRIOR2, cygnus_max_level)), + () -> assertEquals(535, f.apply(Job.DAWNWARRIOR3, cygnus_max_level)), + () -> assertEquals(535, f.apply(Job.DAWNWARRIOR4, cygnus_max_level)), + + // Warrior (Aran) + () -> assertEquals(855, f.apply(Job.ARAN1, max_level)), + () -> assertEquals(855, f.apply(Job.ARAN2, max_level)), + () -> assertEquals(855, f.apply(Job.ARAN3, max_level)), + () -> assertEquals(855, f.apply(Job.ARAN4, max_level)), + + // Magician (Explorer) + () -> assertEquals(4399, f.apply(Job.MAGICIAN, max_level)), + + () -> assertEquals(4849, f.apply(Job.FP_WIZARD, max_level)), + () -> assertEquals(4849, f.apply(Job.FP_MAGE, max_level)), + () -> assertEquals(4849, f.apply(Job.FP_ARCHMAGE, max_level)), + + () -> assertEquals(4849, f.apply(Job.IL_WIZARD, max_level)), + () -> assertEquals(4849, f.apply(Job.IL_MAGE, max_level)), + () -> assertEquals(4849, f.apply(Job.IL_ARCHMAGE, max_level)), + + () -> assertEquals(4849, f.apply(Job.CLERIC, max_level)), + () -> assertEquals(4849, f.apply(Job.PRIEST, max_level)), + () -> assertEquals(4849, f.apply(Job.BISHOP, max_level)), + + // Magician (Cygnus) + () -> assertEquals(2639, f.apply(Job.BLAZEWIZARD1, cygnus_max_level)), + () -> assertEquals(3089, f.apply(Job.BLAZEWIZARD2, cygnus_max_level)), + () -> assertEquals(3089, f.apply(Job.BLAZEWIZARD3, cygnus_max_level)), + () -> assertEquals(3089, f.apply(Job.BLAZEWIZARD4, cygnus_max_level)), + + // Bowman (Explorer) + () -> assertEquals(2785, f.apply(Job.BOWMAN, max_level)), + + () -> assertEquals(2935, f.apply(Job.HUNTER, max_level)), + () -> assertEquals(2935, f.apply(Job.RANGER, max_level)), + () -> assertEquals(2935, f.apply(Job.BOWMASTER, max_level)), + + () -> assertEquals(2935, f.apply(Job.CROSSBOWMAN, max_level)), + () -> assertEquals(2935, f.apply(Job.SNIPER, max_level)), + () -> assertEquals(2935, f.apply(Job.MARKSMAN, max_level)), + + // Bowman (Cygnus) + () -> assertEquals(1665, f.apply(Job.WINDARCHER1, cygnus_max_level)), + () -> assertEquals(1815, f.apply(Job.WINDARCHER2, cygnus_max_level)), + () -> assertEquals(1815, f.apply(Job.WINDARCHER3, cygnus_max_level)), + () -> assertEquals(1815, f.apply(Job.WINDARCHER4, cygnus_max_level)), + + // Thief (Explorer) + () -> assertEquals(2785, f.apply(Job.THIEF, max_level)), + + () -> assertEquals(2935, f.apply(Job.ASSASSIN, max_level)), + () -> assertEquals(2935, f.apply(Job.HERMIT, max_level)), + () -> assertEquals(2935, f.apply(Job.NIGHTLORD, max_level)), + + () -> assertEquals(2935, f.apply(Job.BANDIT, max_level)), + () -> assertEquals(2935, f.apply(Job.CHIEFBANDIT, max_level)), + () -> assertEquals(2935, f.apply(Job.SHADOWER, max_level)), + + // Thief (Cygnus) + () -> assertEquals(1665, f.apply(Job.NIGHTWALKER1, cygnus_max_level)), + () -> assertEquals(1815, f.apply(Job.NIGHTWALKER2, cygnus_max_level)), + () -> assertEquals(1815, f.apply(Job.NIGHTWALKER3, cygnus_max_level)), + () -> assertEquals(1815, f.apply(Job.NIGHTWALKER4, cygnus_max_level)), + + // Pirate (Explorer) + () -> assertEquals(3545, f.apply(Job.PIRATE, max_level)), + + () -> assertEquals(3695, f.apply(Job.BRAWLER, max_level)), + () -> assertEquals(3695, f.apply(Job.MARAUDER, max_level)), + () -> assertEquals(3695, f.apply(Job.BUCCANEER, max_level)), + + () -> assertEquals(3695, f.apply(Job.GUNSLINGER, max_level)), + () -> assertEquals(3695, f.apply(Job.OUTLAW, max_level)), + () -> assertEquals(3695, f.apply(Job.CORSAIR, max_level)), + + // Pirate (Cygnus) + () -> assertEquals(2105, f.apply(Job.THUNDERBREAKER1, cygnus_max_level)), + () -> assertEquals(2255, f.apply(Job.THUNDERBREAKER2, cygnus_max_level)), + () -> assertEquals(2255, f.apply(Job.THUNDERBREAKER3, cygnus_max_level)), + () -> assertEquals(2255, f.apply(Job.THUNDERBREAKER4, cygnus_max_level)) + ); + } +} \ No newline at end of file From 94a08d86a01fc2767b689808a96a14695aa86aa6 Mon Sep 17 00:00:00 2001 From: Noir Date: Tue, 18 Jun 2024 21:42:52 -0400 Subject: [PATCH 35/55] Min HP / MP needs to check against post-AP-reset value --- .../java/client/processor/stat/AssignAPProcessor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/client/processor/stat/AssignAPProcessor.java b/src/main/java/client/processor/stat/AssignAPProcessor.java index c7897ce810..1af21574f7 100644 --- a/src/main/java/client/processor/stat/AssignAPProcessor.java +++ b/src/main/java/client/processor/stat/AssignAPProcessor.java @@ -552,14 +552,14 @@ public class AssignAPProcessor { return false; } - if (player.getMaxHp() < getMinHp(player.getJob(), player.getLevel())) { + int hplose = -takeHp(player.getJob()); + if (player.getMaxHp() + hplose < getMinHp(player.getJob(), player.getLevel())) { player.message("You don't have the minimum HP pool required to swap."); c.sendPacket(PacketCreator.enableActions()); return false; } int curHp = player.getHp(); - int hplose = -takeHp(player.getJob()); player.assignHP(hplose, -1); if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) { player.updateHp(Math.max(1, curHp + hplose)); @@ -581,14 +581,14 @@ public class AssignAPProcessor { return false; } - if (player.getMaxMp() < getMinMp(player.getJob(), player.getLevel())) { + int mplose = -takeMp(player.getJob()); + if (player.getMaxMp() + mplose < getMinMp(player.getJob(), player.getLevel())) { player.message("You don't have the minimum MP pool required to swap."); c.sendPacket(PacketCreator.enableActions()); return false; } int curMp = player.getMp(); - int mplose = -takeMp(player.getJob()); player.assignMP(mplose, -1); if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) { player.updateMp(Math.max(0, curMp + mplose)); From 31e901ab6d3f961139bb1c5cb78a5fd186105479 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Fri, 1 Mar 2024 22:56:28 +0800 Subject: [PATCH 36/55] Ariant Coliseum: Fix score counting and trade conversation --- scripts/npc/2101015.js | 10 +++++----- .../server/channel/handlers/UseCatchItemHandler.java | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/npc/2101015.js b/scripts/npc/2101015.js index 70730a82f5..405db97083 100644 --- a/scripts/npc/2101015.js +++ b/scripts/npc/2101015.js @@ -32,14 +32,14 @@ function action(mode, type, selection) { } else if (status == 1) { if (selection == 0) { apqpoints = cm.getPlayer().getAriantPoints(); - if (apqpoints < 100) { - cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points. You need to surpass #b100 points#k so that I can give you the #bPalm Tree Beach Chair#k. Talk to me again when you have enough points."); - cm.dispose(); + if (apqpoints >= 100) { + cm.sendNext("Wow, it looks like you got the #b100#k points ready to trade, let's trade?!"); } else if (apqpoints + arena.getAriantRewardTier(cm.getPlayer()) >= 100) { - cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points and you pratically already have that score! Talk to my wife, #p2101016#to get them and then re-chat with me!"); + cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points and you pratically already have that score! Talk to my wife, #p2101016# to get them and then re-chat with me!"); cm.dispose(); } else { - cm.sendNext("Wow, it looks like you got the #b100#k points ready to trade, let's trade?!"); + cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points. You need to surpass #b100 points#k so that I can give you the #bPalm Tree Beach Chair#k. Talk to me again when you have enough points."); + cm.dispose(); } } else if (selection == 1) { cm.sendOk("The main objective of the Battle Arena is to allow the player to accumulate points so that they can be traded honorably for the highest prize: the #bPalm Tree Beach Chair#k. Collect points during the battles and talk to me when it's time to get the prize. In each battle, the player is given the opportunity to score points based on the amount of jewelry that the player has at the end. But be careful! If your points distance from other players #ris too high#k, this will have been all for nothing and you will earn mere #r1 point#k only."); diff --git a/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java b/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java index 85b724a826..ead6813e66 100644 --- a/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java +++ b/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java @@ -93,6 +93,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { mob.getMap().killMonster(mob, null, false); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.ARPQ_SPIRIT_JEWEL, (short) 1, "", -1); + chr.updateAriantScore(); } else { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 0)); } From 3f800c4a68f382e43b1f0329087769c806f6da44 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Sat, 2 Mar 2024 14:41:23 +0800 Subject: [PATCH 37/55] Ignore .DS_Store files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 8543b81417..27980c65f9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ # Database database/docker-db-data database/docker-pg-db-data + +# macOS files +.DS_Store From 0245e7d1afc55be284e5ec92af9a03d416c04689 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Wed, 13 Mar 2024 22:51:54 +0800 Subject: [PATCH 38/55] NPC (Vard): Fix "Plastic Surgery" option Correct typos for variable `fface_v`. --- scripts/npc/2100008.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/npc/2100008.js b/scripts/npc/2100008.js index 5f68b5c40b..8ea5cf2de0 100644 --- a/scripts/npc/2100008.js +++ b/scripts/npc/2100008.js @@ -58,8 +58,8 @@ function action(mode, type, selection) { } } if (cm.getChar().getGender() == 1) { - for (var i = 0; i < fface.length; i++) { - pushIfItemExists(facenew, fface[i] + cm.getChar().getFace() + for (var i = 0; i < fface_v.length; i++) { + pushIfItemExists(facenew, fface_v[i] + cm.getChar().getFace() % 1000 - (cm.getChar().getFace() % 100)); } From 205e263255234943296aee00112141350c1f1ac8 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 16 Jul 2024 20:16:26 +0200 Subject: [PATCH 39/55] Specify required Java version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61394d1710..694e61431d 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ You will start by installing the database server and client, and then run some s You will start by cloning the repository, then configure the database properties and lastly start the server. #### Prerequisites -* Java 21+ (I recommend [Amazon Corretto](https://aws.amazon.com/corretto)) +* Java 21 (I recommend [Amazon Corretto](https://aws.amazon.com/corretto)) * IDE (I recommend [IntelliJ IDEA](https://www.jetbrains.com/idea/)) #### Steps From 3356e42e71bc996328e6e259e64d8ff53ab6bbe4 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 16 Jul 2024 20:17:21 +0200 Subject: [PATCH 40/55] Remove "spikes avoid banish" feature --- config.yaml | 1 - src/main/java/client/Character.java | 8 -------- src/main/java/config/ServerConfig.java | 1 - 3 files changed, 10 deletions(-) diff --git a/config.yaml b/config.yaml index bcbb1c37db..c2bfa9426f 100644 --- a/config.yaml +++ b/config.yaml @@ -395,7 +395,6 @@ server: USE_EQUIPMNT_LVLUP_SLOTS: false #Equips can upgrade slots at level up. USE_EQUIPMNT_LVLUP_POWER: false #Enable more powerful stat upgrades at equip level up. USE_EQUIPMNT_LVLUP_CASH: false #Enable equip leveling up on cash equipments as well. - USE_SPIKES_AVOID_BANISH: false #Shoes equipped with spikes prevents mobs from banishing wearer. MAX_EQUIPMNT_LVLUP_STAT_UP: 10000 #Max stat upgrade an equipment can have on a levelup. MAX_EQUIPMNT_STAT: 32767 #Max stat on an equipment by leveling up. USE_EQUIPMNT_LVLUP: 1 #All equips lvlup at max level of N, set 1 to disable. diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index ea94e94acd..0b6fa2c144 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -1377,14 +1377,6 @@ public class Character extends AbstractCharacterObject { } public void changeMapBanish(int mapid, String portal, String msg) { - if (YamlConfig.config.server.USE_SPIKES_AVOID_BANISH) { - for (Item it : this.getInventory(InventoryType.EQUIPPED).list()) { - if ((it.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) { - return; - } - } - } - int banMap = this.getMapId(); int banSp = this.getMap().findClosestPlayerSpawnpoint(this.getPosition()).getId(); long banTime = System.currentTimeMillis(); diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 125675b68e..2962a21195 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -243,7 +243,6 @@ public class ServerConfig { public boolean USE_EQUIPMNT_LVLUP_SLOTS; public boolean USE_EQUIPMNT_LVLUP_POWER; public boolean USE_EQUIPMNT_LVLUP_CASH; - public boolean USE_SPIKES_AVOID_BANISH; public int MAX_EQUIPMNT_LVLUP_STAT_UP; public int MAX_EQUIPMNT_STAT; public int USE_EQUIPMNT_LVLUP; From 402163c33d0c59bb6c4a19df03876d7206e0be48 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 17 Jul 2024 17:45:38 +0200 Subject: [PATCH 41/55] Remove "banishable town scroll" feature --- config.yaml | 1 - src/main/java/client/Character.java | 30 ------------------- src/main/java/client/Client.java | 1 - src/main/java/config/ServerConfig.java | 1 - src/main/java/constants/id/ItemId.java | 1 - .../constants/inventory/ItemConstants.java | 6 +--- .../handlers/MobBanishPlayerHandler.java | 19 ++++++------ .../channel/handlers/UseItemHandler.java | 16 ---------- src/main/java/server/StatEffect.java | 14 ++------- 9 files changed, 13 insertions(+), 76 deletions(-) diff --git a/config.yaml b/config.yaml index c2bfa9426f..5765529882 100644 --- a/config.yaml +++ b/config.yaml @@ -257,7 +257,6 @@ server: USE_BUFF_MOST_SIGNIFICANT: true #When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats. USE_BUFF_EVERLASTING: false #Every applied buff on players holds expiration time so high it'd be considered permanent. Suggestion thanks to Vcoc. USE_MULTIPLE_SAME_EQUIP_DROP: true #Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data. - USE_BANISHABLE_TOWN_SCROLL: false #Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available. USE_ENABLE_FULL_RESPAWN: false #At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there. USE_ENABLE_CHAT_LOG: false #Write in-game chat to log USE_REBIRTH_SYSTEM: false #Flag to enable/disable rebirth system diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 0b6fa2c144..8e41772a39 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -352,9 +352,6 @@ public class Character extends AbstractCharacterObject { private int targetHpBarHash = 0; private long targetHpBarTime = 0; private long nextWarningTime = 0; - private int banishMap = -1; - private int banishSp = -1; - private long banishTime = 0; private long lastExpGainTime; private boolean pendingNameChange; //only used to change name on logout, not to be relied upon elsewhere private long loginTime; @@ -1356,31 +1353,7 @@ public class Character extends AbstractCharacterObject { } } - public boolean canRecoverLastBanish() { - return System.currentTimeMillis() - this.banishTime < MINUTES.toMillis(5); - } - - public Pair getLastBanishData() { - return new Pair<>(this.banishMap, this.banishSp); - } - - public void clearBanishPlayerData() { - this.banishMap = -1; - this.banishSp = -1; - this.banishTime = 0; - } - - public void setBanishPlayerData(int banishMap, int banishSp, long banishTime) { - this.banishMap = banishMap; - this.banishSp = banishSp; - this.banishTime = banishTime; - } - public void changeMapBanish(int mapid, String portal, String msg) { - int banMap = this.getMapId(); - int banSp = this.getMap().findClosestPlayerSpawnpoint(this.getPosition()).getId(); - long banTime = System.currentTimeMillis(); - if (msg != null) { dropMessage(5, msg); } @@ -1388,8 +1361,6 @@ public class Character extends AbstractCharacterObject { MapleMap map_ = getWarpMap(mapid); Portal portal_ = map_.getPortal(portal); changeMap(map_, portal_ != null ? portal_ : map_.getRandomPlayerSpawnpoint()); - - setBanishPlayerData(banMap, banSp, banTime); } public void changeMap(int map) { @@ -1772,7 +1743,6 @@ public class Character extends AbstractCharacterObject { this.mapTransitioning.set(true); this.unregisterChairBuff(); - this.clearBanishPlayerData(); Trade.cancelTrade(this, Trade.TradeResult.UNSUCCESSFUL_ANOTHER_MAP); this.closePlayerInteractions(); diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index b3292fd04d..2e0c2f94fc 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -1518,7 +1518,6 @@ public class Client extends ChannelInboundHandlerAdapter { player.getInventory(InventoryType.EQUIPPED).checked(false); //test player.getMap().removePlayer(player); - player.clearBanishPlayerData(); player.getClient().getChannelServer().removePlayer(player); player.saveCharToDB(); diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 2962a21195..f22a9f44fe 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -105,7 +105,6 @@ public class ServerConfig { public boolean USE_BUFF_MOST_SIGNIFICANT; public boolean USE_BUFF_EVERLASTING; public boolean USE_MULTIPLE_SAME_EQUIP_DROP; - public boolean USE_BANISHABLE_TOWN_SCROLL; public boolean USE_ENABLE_FULL_RESPAWN; public boolean USE_ENABLE_CHAT_LOG; public boolean USE_REBIRTH_SYSTEM; diff --git a/src/main/java/constants/id/ItemId.java b/src/main/java/constants/id/ItemId.java index dda0323a2f..82044196c2 100644 --- a/src/main/java/constants/id/ItemId.java +++ b/src/main/java/constants/id/ItemId.java @@ -165,7 +165,6 @@ public class ItemId { public static final int EYEDROP = 2050001; public static final int TONIC = 2050002; public static final int HOLY_WATER = 2050003; - public static final int ANTI_BANISH_SCROLL = 2030100; private static final int DOJO_PARTY_ALL_CURE = 2022433; private static final int CARNIVAL_PARTY_ALL_CURE = 2022163; public static final int WHITE_ELIXIR = 2022544; diff --git a/src/main/java/constants/inventory/ItemConstants.java b/src/main/java/constants/inventory/ItemConstants.java index 537cfc7e2c..fa36aff350 100644 --- a/src/main/java/constants/inventory/ItemConstants.java +++ b/src/main/java/constants/inventory/ItemConstants.java @@ -134,11 +134,7 @@ public final class ItemConstants { } public static boolean isTownScroll(int itemId) { - return itemId >= 2030000 && itemId < ItemId.ANTI_BANISH_SCROLL; - } - - public static boolean isAntibanishScroll(int itemId) { - return itemId == ItemId.ANTI_BANISH_SCROLL; + return itemId >= 2030000; } public static boolean isCleanSlate(int scrollId) { diff --git a/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java b/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java index 25db10357a..fe53ae11e4 100644 --- a/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java +++ b/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java @@ -29,17 +29,18 @@ import server.life.Monster; public final class MobBanishPlayerHandler extends AbstractPacketHandler { @Override - public final void handlePacket(InPacket p, Client c) { - int mobid = p.readInt(); // mob banish handling detected thanks to MedicOP + public void handlePacket(InPacket p, Client c) { + int mobId = p.readInt(); // mob banish handling detected thanks to MedicOP Character chr = c.getPlayer(); - Monster mob = chr.getMap().getMonsterById(mobid); + Monster mob = chr.getMap().getMonsterById(mobId); + if (mob == null) { + return; + } - if (mob != null) { - BanishInfo banishInfo = mob.getBanish(); - if (banishInfo != null) { - chr.changeMapBanish(banishInfo.getMap(), banishInfo.getPortal(), banishInfo.getMsg()); - } + BanishInfo banishInfo = mob.getBanish(); + if (banishInfo != null) { + chr.changeMapBanish(banishInfo.getMap(), banishInfo.getPortal(), banishInfo.getMsg()); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/UseItemHandler.java b/src/main/java/net/server/channel/handlers/UseItemHandler.java index 7cdb773220..f6d1a0498f 100644 --- a/src/main/java/net/server/channel/handlers/UseItemHandler.java +++ b/src/main/java/net/server/channel/handlers/UseItemHandler.java @@ -27,7 +27,6 @@ import client.Disease; import client.inventory.InventoryType; import client.inventory.Item; import client.inventory.manipulator.InventoryManipulator; -import config.YamlConfig; import constants.id.ItemId; import constants.inventory.ItemConstants; import net.AbstractPacketHandler; @@ -73,23 +72,8 @@ public final class UseItemHandler extends AbstractPacketHandler { remove(c, slot); return; } else if (ItemConstants.isTownScroll(itemId)) { - int banMap = chr.getMapId(); - int banSp = chr.getMap().findClosestPlayerSpawnpoint(chr.getPosition()).getId(); - long banTime = currentServerTime(); - - if (ii.getItemEffect(toUse.getItemId()).applyTo(chr)) { - if (YamlConfig.config.server.USE_BANISHABLE_TOWN_SCROLL) { - chr.setBanishPlayerData(banMap, banSp, banTime); - } - - remove(c, slot); - } - return; - } else if (ItemConstants.isAntibanishScroll(itemId)) { if (ii.getItemEffect(toUse.getItemId()).applyTo(chr)) { remove(c, slot); - } else { - chr.dropMessage(5, "You cannot recover from a banish state at the moment."); } return; } diff --git a/src/main/java/server/StatEffect.java b/src/main/java/server/StatEffect.java index 44cf094b63..4d6e0d9d62 100644 --- a/src/main/java/server/StatEffect.java +++ b/src/main/java/server/StatEffect.java @@ -977,18 +977,8 @@ public class StatEffect { Portal pt; if (moveTo == MapId.NONE) { - if (sourceid != ItemId.ANTI_BANISH_SCROLL) { - target = applyto.getMap().getReturnMap(); - pt = target.getRandomPlayerSpawnpoint(); - } else { - if (!applyto.canRecoverLastBanish()) { - return false; - } - - Pair lastBanishInfo = applyto.getLastBanishData(); - target = applyto.getWarpMap(lastBanishInfo.getLeft()); - pt = target.getPortal(lastBanishInfo.getRight()); - } + target = applyto.getMap().getReturnMap(); + pt = target.getRandomPlayerSpawnpoint(); } else { target = applyto.getClient().getWorldServer().getChannel(applyto.getClient().getChannel()).getMapFactory().getMap(moveTo); int targetid = target.getId() / 10000000; From 2324ae7f9ecc19509366798f2ba181056868ae48 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 17 Jul 2024 18:11:50 +0200 Subject: [PATCH 42/55] Refactor BanishInfo - make it a record --- src/main/java/client/Character.java | 9 +++--- .../handlers/MobBanishPlayerHandler.java | 7 +++-- .../channel/handlers/MoveLifeHandler.java | 2 +- .../channel/handlers/TakeDamageHandler.java | 2 +- src/main/java/server/life/BanishInfo.java | 9 ++++++ src/main/java/server/life/LifeFactory.java | 30 +++---------------- src/main/java/server/life/Monster.java | 1 - src/main/java/server/life/MonsterStats.java | 1 - .../tools/mapletools/MonsterStatFetcher.java | 7 +++-- 9 files changed, 29 insertions(+), 39 deletions(-) create mode 100644 src/main/java/server/life/BanishInfo.java diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 8e41772a39..9962b5c822 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -116,6 +116,7 @@ import server.events.Events; import server.events.RescueGaga; import server.events.gm.Fitness; import server.events.gm.Ola; +import server.life.BanishInfo; import server.life.MobSkill; import server.life.MobSkillFactory; import server.life.MobSkillId; @@ -1353,13 +1354,13 @@ public class Character extends AbstractCharacterObject { } } - public void changeMapBanish(int mapid, String portal, String msg) { - if (msg != null) { - dropMessage(5, msg); + public void changeMapBanish(BanishInfo banishInfo) { + if (banishInfo.msg() != null) { + dropMessage(5, banishInfo.msg()); } MapleMap map_ = getWarpMap(mapid); - Portal portal_ = map_.getPortal(portal); + Portal portal_ = map_.getPortal(banishInfo.portal()); changeMap(map_, portal_ != null ? portal_ : map_.getRandomPlayerSpawnpoint()); } diff --git a/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java b/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java index fe53ae11e4..1b084e59bf 100644 --- a/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java +++ b/src/main/java/net/server/channel/handlers/MobBanishPlayerHandler.java @@ -23,7 +23,7 @@ import client.Character; import client.Client; import net.AbstractPacketHandler; import net.packet.InPacket; -import server.life.LifeFactory.BanishInfo; +import server.life.BanishInfo; import server.life.Monster; public final class MobBanishPlayerHandler extends AbstractPacketHandler { @@ -39,8 +39,9 @@ public final class MobBanishPlayerHandler extends AbstractPacketHandler { } BanishInfo banishInfo = mob.getBanish(); - if (banishInfo != null) { - chr.changeMapBanish(banishInfo.getMap(), banishInfo.getPortal(), banishInfo.getMsg()); + if (banishInfo == null) { + return; } + chr.changeMapBanish(banishInfo); } } diff --git a/src/main/java/net/server/channel/handlers/MoveLifeHandler.java b/src/main/java/net/server/channel/handlers/MoveLifeHandler.java index 34ec1daad7..fd327edfa0 100644 --- a/src/main/java/net/server/channel/handlers/MoveLifeHandler.java +++ b/src/main/java/net/server/channel/handlers/MoveLifeHandler.java @@ -173,7 +173,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { if (banishPlayers != null) { for (Character chr : banishPlayers) { - chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg()); + chr.changeMapBanish(monster.getBanish()); } } } diff --git a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java index 234f56eb0c..5207307bfb 100644 --- a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java @@ -289,7 +289,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler { } for (Character player : banishPlayers) { // chill, if this list ever gets non-empty an attacker does exist, trust me :) - player.changeMapBanish(attacker.getBanish().getMap(), attacker.getBanish().getPortal(), attacker.getBanish().getMsg()); + player.changeMapBanish(attacker.getBanish()); } } } diff --git a/src/main/java/server/life/BanishInfo.java b/src/main/java/server/life/BanishInfo.java new file mode 100644 index 0000000000..4f6cf388fd --- /dev/null +++ b/src/main/java/server/life/BanishInfo.java @@ -0,0 +1,9 @@ +package server.life; + +import java.util.Objects; + +public record BanishInfo(int map, String portal, String msg) { + public BanishInfo { + Objects.requireNonNull(portal, "BanishInfo portal"); + } +} diff --git a/src/main/java/server/life/LifeFactory.java b/src/main/java/server/life/LifeFactory.java index 04e6cabe2a..e280b314d3 100644 --- a/src/main/java/server/life/LifeFactory.java +++ b/src/main/java/server/life/LifeFactory.java @@ -231,7 +231,10 @@ public class LifeFactory { Data banishData = monsterInfoData.getChildByPath("ban"); if (banishData != null) { - stats.setBanishInfo(new BanishInfo(DataTool.getString("banMsg", banishData), DataTool.getInt("banMap/0/field", banishData, -1), DataTool.getString("banMap/0/portal", banishData, "sp"))); + int map = DataTool.getInt("banMap/0/field", banishData, -1); + String portal = DataTool.getString("banMap/0/portal", banishData, "sp"); + String msg = DataTool.getString("banMsg", banishData); + stats.setBanishInfo(new BanishInfo(map, portal, msg)); } int noFlip = DataTool.getInt("noFlip", monsterInfoData, 0); @@ -296,31 +299,6 @@ public class LifeFactory { return DataTool.getString(nid + "/d0", npcStringData, "(...)"); } - public static class BanishInfo { - - private final int map; - private final String portal; - private final String msg; - - public BanishInfo(String msg, int map, String portal) { - this.msg = msg; - this.map = map; - this.portal = portal; - } - - public int getMap() { - return map; - } - - public String getPortal() { - return portal; - } - - public String getMsg() { - return msg; - } - } - public static class loseItem { private final int id; diff --git a/src/main/java/server/life/Monster.java b/src/main/java/server/life/Monster.java index 71f9b66c45..ea3beb85dd 100644 --- a/src/main/java/server/life/Monster.java +++ b/src/main/java/server/life/Monster.java @@ -56,7 +56,6 @@ import org.slf4j.LoggerFactory; import scripting.event.EventInstanceManager; import server.StatEffect; import server.TimerManager; -import server.life.LifeFactory.BanishInfo; import server.loot.LootManager; import server.maps.AbstractAnimatedMapObject; import server.maps.MapObjectType; diff --git a/src/main/java/server/life/MonsterStats.java b/src/main/java/server/life/MonsterStats.java index 5c091fd76d..5d18ab8910 100644 --- a/src/main/java/server/life/MonsterStats.java +++ b/src/main/java/server/life/MonsterStats.java @@ -21,7 +21,6 @@ */ package server.life; -import server.life.LifeFactory.BanishInfo; import server.life.LifeFactory.loseItem; import server.life.LifeFactory.selfDestruction; import tools.Pair; diff --git a/src/main/java/tools/mapletools/MonsterStatFetcher.java b/src/main/java/tools/mapletools/MonsterStatFetcher.java index 38033757b8..9026e39532 100644 --- a/src/main/java/tools/mapletools/MonsterStatFetcher.java +++ b/src/main/java/tools/mapletools/MonsterStatFetcher.java @@ -8,9 +8,9 @@ import provider.DataProviderFactory; import provider.DataTool; import provider.wz.DataType; import provider.wz.WZFiles; +import server.life.BanishInfo; import server.life.Element; import server.life.ElementalEffectiveness; -import server.life.LifeFactory.BanishInfo; import server.life.LifeFactory.loseItem; import server.life.LifeFactory.selfDestruction; import server.life.MobSkillId; @@ -134,7 +134,10 @@ public class MonsterStatFetcher { } Data banishData = monsterInfoData.getChildByPath("ban"); if (banishData != null) { - stats.setBanishInfo(new BanishInfo(DataTool.getString("banMsg", banishData), DataTool.getInt("banMap/0/field", banishData, -1), DataTool.getString("banMap/0/portal", banishData, "sp"))); + int map = DataTool.getInt("banMap/0/field", banishData, -1); + String portal = DataTool.getString("banMap/0/portal", banishData, "sp"); + String msg = DataTool.getString("banMsg", banishData); + stats.setBanishInfo(new BanishInfo(map, portal, msg)); } monsterStats.put(mid, stats); From 7071b13e41296e40487e1851370a7a5ae5261ae3 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 17 Jul 2024 18:39:50 +0200 Subject: [PATCH 43/55] Remove banish scroll from shops --- database/sql/3-db_shopupdate.sql | 37 +------------------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/database/sql/3-db_shopupdate.sql b/database/sql/3-db_shopupdate.sql index f11b84e1cd..6ed4753bd2 100644 --- a/database/sql/3-db_shopupdate.sql +++ b/database/sql/3-db_shopupdate.sql @@ -131,39 +131,6 @@ INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position` (9201101, 2012008, 4200000, 0, 164), (9201101, 2022251, 3800000, 0, 168); -# adding antibanish scrolls -INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position`) VALUES -(1001100, 2030100, 450, 0, 130), -(1011100, 2030100, 450, 0, 142), -(1021100, 2030100, 450, 0, 142), -(1031100, 2030100, 450, 0, 146), -(1051002, 2030100, 450, 0, 142), -(1052116, 2030100, 450, 0, 118), -(1061001, 2030100, 450, 0, 126), -(1061002, 2030100, 450, 0, 130), -(1091002, 2030100, 450, 0, 130), -(1100002, 2030100, 450, 0, 138), -(2012005, 2030100, 450, 0, 126), -(2022001, 2030100, 450, 0, 126), -(2030009, 2030100, 450, 0, 126), -(2040051, 2030100, 450, 0, 102), -(2041006, 2030100, 450, 0, 134), -(2051000, 2030100, 450, 0, 134), -(2060004, 2030100, 450, 0, 134), -(2070001, 2030100, 450, 0, 134), -(2080001, 2030100, 450, 0, 134), -(2090003, 2030100, 450, 0, 126), -(2093002, 2030100, 450, 0, 126), -(2100004, 2030100, 450, 0, 130), -(2110001, 2030100, 450, 0, 130), -(2130000, 2030100, 450, 0, 126), -(9201060, 2030100, 450, 0, 114), -(9270021, 2030100, 450, 0, 118), -(9270022, 2030100, 450, 0, 114), -- Thanks Rednor for finding duplicate item on NPC -(1338, 2030100, 450, 0, 114), -(9270057, 2030100, 450, 0, 4), -(9270065, 2030100, 450, 0, 3); - -- Thanks to Vcoc -- GMShop: Sacks, GmEquip, Cheese & Onyx, Utils, -- Arrows, Bullets, Throwings and Capsules, @@ -301,7 +268,6 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU (1200002, 2070000, 500, 0, 108), (1200002, 2061000, 1, 0, 120), (1200002, 2060000, 1, 0, 124), - (1200002, 2030100, 400, 0, 128), (1200002, 2030000, 400, 0, 132), (1200002, 2020028, 3000, 0, 136), (1200002, 2010004, 310, 0, 140), @@ -323,7 +289,6 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU (1301000, 2070000, 500, 0, 108), (1301000, 2061000, 1, 0, 112), (1301000, 2060000, 1, 0, 116), - (1301000, 2030100, 400, 0, 120), (1301000, 2030000, 400, 0, 124), (1301000, 2022000, 1650, 0, 128), (1301000, 2022003, 1100, 0, 132), @@ -348,4 +313,4 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU (9270019, 1482004, 75000, 0, 100), (9270020, 1052113, 120000, 0, 92), (9270020, 1052110, 100000, 0, 96), - (9270020, 1002625, 75000, 0, 100); \ No newline at end of file + (9270020, 1002625, 75000, 0, 100); From a7df8a4f49cd499c2decf7a837d2cbcd4cca57ad Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 17 Jul 2024 18:45:30 +0200 Subject: [PATCH 44/55] Remove banish scroll from WZ files --- README.md | 2 +- handbook/Use.txt | 1 - tools/input/CouponCodes.img.xml | 4 ---- wz/Item.wz/Consume/0203.img.xml | 20 +++----------------- wz/String.wz/Consume.img.xml | 4 ---- 5 files changed, 4 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 694e61431d..694de2a7dd 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ You will start by installing the game with the old installer, then overwrite som 1. Download _MapleGlobal-v83-setup.exe_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). This is the official installer from back then. 2. Install it in a directory of your choice. 3. Delete the following files from the installation directory: _HShield_ (entire directory), _ASPLnchr.exe_, _MapleStory.exe_, and _Patcher.exe_. -4. Download _CosmicWZ-2024-05-21-v0.13.0.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). +4. Download _CosmicWZ-2024-07-17-v0.14.0.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). 5. Unzip it and copy all .wz-files into the installation directory. Replace the existing ones. 6. Download _HeavenMS-localhost-WINDOW.exe_ from [hostr.co](https://hostr.co/amuX5SLeeVZx). This is a client modified to connect to your localhost instead of Nexon's server (along with some fixes and custom changes). - Your antivirus will likely detect the file as a trojan or similar and automatically delete it. To prevent this from happening, add your _Downloads_ directory and the installation directory as exclusions in your antivirus software. On W11, this is under "Virus & threat protection settings" -> "Add or remove exclusions". diff --git a/handbook/Use.txt b/handbook/Use.txt index 3cc5d46e54..6bf39435e1 100644 --- a/handbook/Use.txt +++ b/handbook/Use.txt @@ -385,7 +385,6 @@ 2030016 - Phyllia's Warp Powder - Warp powder made by fairy Phyllia. Teleports you to Magatia when used inside the Nihal desert region. 2030019 - Return Scroll to Nautilus - This scroll enables you to return to the Pirate village, Nautilus. This is a one use item and will disappear after use. 2030020 - Return Scroll to New Leaf City - Use this scroll to venture back to New Leaf City whenever you want! -2030100 - Return Scroll - Banished Area - Returns you to the map where you were last banished. Requires immediate use and have changed neither maps nor channels. 2031000 - Masked Man's Invitation - An invitation from the Masked Man to the Halloween Party at the Haunted Mansion. Double-click to move straight to the mansion. 2031001 - Studio Invitation - An invitation to the studio for the event "For Guild Only". 2040000 - Scroll for Helmet for DEF - Improves the helmet's weapon def.\nSuccess rate:100%, weapon def. +1 diff --git a/tools/input/CouponCodes.img.xml b/tools/input/CouponCodes.img.xml index 8ce30f6212..d7db72c8d0 100644 --- a/tools/input/CouponCodes.img.xml +++ b/tools/input/CouponCodes.img.xml @@ -12,10 +12,6 @@ - - - - diff --git a/wz/Item.wz/Consume/0203.img.xml b/wz/Item.wz/Consume/0203.img.xml index 94d4dce668..b4c4cfa19d 100644 --- a/wz/Item.wz/Consume/0203.img.xml +++ b/wz/Item.wz/Consume/0203.img.xml @@ -210,7 +210,7 @@ - + @@ -218,10 +218,10 @@ - + - + @@ -380,18 +380,4 @@ - - - - - - - - - - - - - - diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 3a4a74fc31..925939363e 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -1499,10 +1499,6 @@ - - - - From 3850b63cec33c0df01bb7fbb032025ac31353e98 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 17 Jul 2024 19:09:05 +0200 Subject: [PATCH 45/55] Add migration for Date: Fri, 19 Jul 2024 17:16:14 +0200 Subject: [PATCH 46/55] Remove rebirth system --- config.yaml | 2 - scripts/npc/rebirth.js | 79 ------------------- src/main/java/client/Character.java | 65 --------------- src/main/java/config/ServerConfig.java | 2 - .../channel/handlers/NPCTalkHandler.java | 4 +- .../handlers/PlayerLoggedinHandler.java | 5 -- .../tools/exceptions/NotEnabledException.java | 12 --- 7 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 scripts/npc/rebirth.js delete mode 100644 src/main/java/tools/exceptions/NotEnabledException.java diff --git a/config.yaml b/config.yaml index 5765529882..49c4572dca 100644 --- a/config.yaml +++ b/config.yaml @@ -259,7 +259,6 @@ server: USE_MULTIPLE_SAME_EQUIP_DROP: true #Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data. USE_ENABLE_FULL_RESPAWN: false #At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there. USE_ENABLE_CHAT_LOG: false #Write in-game chat to log - USE_REBIRTH_SYSTEM: false #Flag to enable/disable rebirth system USE_MAP_OWNERSHIP_SYSTEM: false #Flag to enable/disable map ownership system USE_FISHING_SYSTEM: false #Flag to enable/disable custom fishing system USE_NPCS_SCRIPTABLE: true #Flag to enable/disable serverside predefined script NPCs. @@ -317,7 +316,6 @@ server: NAME_CHANGE_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for name changes, default (GMS) is 30 days. WORLD_TRANSFER_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for world tranfers, default is same as name change (30 days). INSTANT_NAME_CHANGE: false #Whether or not to wait for server restart to apply name changes. Does on reconnect otherwise (requires queries on every login). - REBIRTH_NPC_ID: 9010021 #ID of the NPC that should be replaced with the rebirth mechanic, if enabled. #Dangling Items/Locks Configuration ITEM_EXPIRE_TIME: 180000 # (3 * 60 * 1000) Time before items start disappearing. Recommended to be set up to 3 minutes. diff --git a/scripts/npc/rebirth.js b/scripts/npc/rebirth.js deleted file mode 100644 index 7859407880..0000000000 --- a/scripts/npc/rebirth.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -/* Rebirth NPC - @author Ronan - @author wejrox -*/ -var status; -var jobId = 0; - -function start() { - status = -1; - const YamlConfig = Java.type('config.YamlConfig'); - if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { - cm.sendOk("Rebirths aren't enabled on this server, how did you get here?"); - cm.dispose(); - return; - } - action(1, 0, 0); -} - -function action(mode, type, selection) { - if (mode === 1) { - status++; - } else { - cm.dispose(); - return; - } - if (status === 0) { - cm.sendNext("Come to me when you want to be reborn again. You currently have a total of #r" + cm.getChar().getReborns() + " #krebirths."); - } else if (status === 1) { - cm.sendSimple("What do you want me to do today: \r\n \r\n #L0##bI want to be reborn!#l \r\n #L1##bNothing for now...#k#l"); - } else if (status === 2) { - if (selection === 0) { - if (cm.getChar().getLevel() === cm.getChar().getMaxClassLevel()) { - cm.sendSimple("I see... and which path would you like to take? \r\n\r\n #L0##bExplorer (Beginner)#l \r\n #L1##bCygnus Knight (Noblesse)#l \r\n #L2##bAran (Legend)#l"); - } else { - cm.sendOk("It looks like your journey has not yet ended... come back when you're level " + cm.getChar().getMaxClassLevel()); - cm.dispose(); - } - } else if (selection === 1) { - cm.sendOk("See you soon!") - cm.dispose(); - } - } else if (status === 3) { - // 0 => beginner, 1000 => noblesse, 2000 => legend - // makes this very easy :-) - jobId = selection * 1000; - - var job = ""; - if (selection === 0) job = "Beginner"; - else if (selection === 1) job = "Noblesse"; - else if (selection === 2) job = "Legend"; - cm.sendYesNo("Are you sure you want to be reborn as a " + job + "?"); - } - else if (status === 4 && type === 1) { - cm.getChar().executeRebornAsId(jobId); - cm.sendOk("You have now been reborn. That's a total of #r" + cm.getChar().getReborns() + "#k rebirths"); - cm.dispose(); - } -} \ No newline at end of file diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 9962b5c822..8a17639625 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -154,7 +154,6 @@ import tools.LongTool; import tools.PacketCreator; import tools.Pair; import tools.Randomizer; -import tools.exceptions.NotEnabledException; import tools.packets.WeddingPackets; import java.awt.*; @@ -10962,70 +10961,6 @@ public class Character extends AbstractCharacterObject { } } - public void setReborns(int value) { - if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { - yellowMessage("Rebirth system is not enabled!"); - throw new NotEnabledException(); - } - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE characters SET reborns=? WHERE id=?;")) { - ps.setInt(1, value); - ps.setInt(2, id); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - public void addReborns() { - setReborns(getReborns() + 1); - } - - public int getReborns() { - if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { - yellowMessage("Rebirth system is not enabled!"); - throw new NotEnabledException(); - } - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT reborns FROM characters WHERE id=?;")) { - ps.setInt(1, id); - - try (ResultSet rs = ps.executeQuery()) { - rs.next(); - return rs.getInt(1); - } - } catch (SQLException e) { - e.printStackTrace(); - } - throw new RuntimeException(); - } - - public void executeReborn() { - // default to beginner: job id = 0 - // this prevents a breaking change - executeRebornAs(Job.BEGINNER); - } - - public void executeRebornAsId(int jobId) { - executeRebornAs(Job.getById(jobId)); - } - - public void executeRebornAs(Job job) { - if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { - yellowMessage("Rebirth system is not enabled!"); - throw new NotEnabledException(); - } - if (getLevel() != getMaxClassLevel()) { - return; - } - addReborns(); - changeJob(job); - setLevel(0); - levelUp(true); - } - //EVENTS private byte team = 0; private Fitness fitness; diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index f22a9f44fe..3aef4172d9 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -107,7 +107,6 @@ public class ServerConfig { public boolean USE_MULTIPLE_SAME_EQUIP_DROP; public boolean USE_ENABLE_FULL_RESPAWN; public boolean USE_ENABLE_CHAT_LOG; - public boolean USE_REBIRTH_SYSTEM; public boolean USE_MAP_OWNERSHIP_SYSTEM; public boolean USE_FISHING_SYSTEM; public boolean USE_NPCS_SCRIPTABLE; @@ -165,7 +164,6 @@ public class ServerConfig { public long NAME_CHANGE_COOLDOWN; public long WORLD_TRANSFER_COOLDOWN = NAME_CHANGE_COOLDOWN;//Cooldown for world tranfers, default is same as name change (30 days). public boolean INSTANT_NAME_CHANGE; - public int REBIRTH_NPC_ID; //Dangling Items/Locks Configuration public int ITEM_EXPIRE_TIME; diff --git a/src/main/java/net/server/channel/handlers/NPCTalkHandler.java b/src/main/java/net/server/channel/handlers/NPCTalkHandler.java index 413b925642..e99f633452 100644 --- a/src/main/java/net/server/channel/handlers/NPCTalkHandler.java +++ b/src/main/java/net/server/channel/handlers/NPCTalkHandler.java @@ -70,8 +70,6 @@ public final class NPCTalkHandler extends AbstractPacketHandler { NPCScriptManager.getInstance().start(c, npc.getId(), "gachapon", null); } else if (npc.getName().endsWith("Maple TV")) { NPCScriptManager.getInstance().start(c, npc.getId(), "mapleTV", null); - } else if (YamlConfig.config.server.USE_REBIRTH_SYSTEM && npc.getId() == YamlConfig.config.server.REBIRTH_NPC_ID) { - NPCScriptManager.getInstance().start(c, npc.getId(), "rebirth", null); } else { boolean hasNpcScript = NPCScriptManager.getInstance().start(c, npc.getId(), oid, null); if (!hasNpcScript) { @@ -97,4 +95,4 @@ public final class NPCTalkHandler extends AbstractPacketHandler { } } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java index f3e2b9288c..d55064d757 100644 --- a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -448,11 +448,6 @@ public final class PlayerLoggedinHandler extends AbstractPacketHandler { Entry::getValue )); - // Any npc be specified as the rebirth npc. Allow the npc to use custom scripts explicitly. - if (YamlConfig.config.server.USE_REBIRTH_SYSTEM) { - npcsIds.put(YamlConfig.config.server.REBIRTH_NPC_ID, "Rebirth"); - } - c.sendPacket(PacketCreator.setNPCScriptable(npcsIds)); } diff --git a/src/main/java/tools/exceptions/NotEnabledException.java b/src/main/java/tools/exceptions/NotEnabledException.java deleted file mode 100644 index 8f6bd509d6..0000000000 --- a/src/main/java/tools/exceptions/NotEnabledException.java +++ /dev/null @@ -1,12 +0,0 @@ -package tools.exceptions; - -public class NotEnabledException extends RuntimeException { - - public NotEnabledException() { - super("Feature not enabled, please enable the feature in ServerConstant"); - } - - public NotEnabledException(String message) { - super(message); - } -} From 2ffca90d29f2d756d01bf8f78b89043d8c3e3aba Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 14 Aug 2024 07:45:06 +0200 Subject: [PATCH 47/55] Add attack delay to AttackInfo Meant to be used in item drop packet for timing handled by the client rather than scheduling item drop packets on the server. --- .../handlers/AbstractDealDamageHandler.java | 38 ++-- .../handlers/CloseRangeDamageHandler.java | 8 +- .../channel/handlers/MagicDamageHandler.java | 7 +- .../channel/handlers/RangedAttackHandler.java | 20 +- src/main/java/server/maps/MapleMap.java | 174 +++++++++--------- src/main/java/tools/PacketCreator.java | 44 +++-- 6 files changed, 163 insertions(+), 128 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index c661d03efc..b9b80b9b32 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -117,7 +117,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { public static class AttackInfo { public int numAttacked, numDamage, numAttackedAndDamage, skill, skilllevel, stance, direction, rangedirection, charge, display; - public Map> allDamage; + public Map targets; public boolean ranged, magic; public int speed = 4; public Point position = new Point(); @@ -146,6 +146,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } } + // TODO: add position + public record AttackTarget(short delay, List damageLines) {} + protected void applyAttack(AttackInfo attack, final Character player, int attackCount) { final MapleMap map = player.getMap(); if (map.isOwnershipRestricted(player)) { @@ -216,7 +219,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { if (attack.skill == ChiefBandit.MESO_EXPLOSION) { int delay = 0; - for (Integer oned : attack.allDamage.keySet()) { + for (Integer oned : attack.targets.keySet()) { MapObject mapobject = map.getMapObject(oned); if (mapobject != null && mapobject.getType() == MapObjectType.ITEM) { final MapItem mapitem = (MapItem) mapobject; @@ -249,7 +252,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } } } - for (Integer oned : attack.allDamage.keySet()) { + for (Integer oned : attack.targets.keySet()) { final Monster monster = map.getMonsterByOid(oned); if (monster != null) { double distance = player.getPosition().distanceSq(monster.getPosition()); @@ -285,7 +288,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } int totDamageToOneMonster = 0; - List onedList = attack.allDamage.get(oned); + List onedList = attack.targets.get(oned).damageLines(); if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) { @@ -600,7 +603,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { ret.numAttackedAndDamage = p.readByte(); ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF; ret.numDamage = ret.numAttackedAndDamage & 0xF; - ret.allDamage = new HashMap<>(); + ret.targets = new HashMap<>(); ret.skill = p.readInt(); ret.ranged = ranged; ret.magic = magic; @@ -629,7 +632,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { for (int j = 0; j < bullets; j++) { int mesoid = p.readInt(); p.skip(1); - ret.allDamage.put(mesoid, null); + ret.targets.put(mesoid, null); } return ret; } else { @@ -638,6 +641,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { for (int i = 0; i < ret.numAttacked + 1; i++) { int oid = p.readInt(); if (i < ret.numAttacked) { + // TODO: read delay in from these skipped bytes p.skip(12); int bullets = p.readByte(); List allDamageNumbers = new ArrayList<>(); @@ -645,14 +649,14 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { int damage = p.readInt(); allDamageNumbers.add(damage); } - ret.allDamage.put(oid, allDamageNumbers); + ret.targets.put(oid, new AttackTarget((short) 0, allDamageNumbers)); p.skip(4); } else { int bullets = p.readByte(); for (int j = 0; j < bullets; j++) { int mesoid = p.readInt(); p.skip(1); - ret.allDamage.put(mesoid, null); + ret.targets.put(mesoid, null); } } } @@ -814,10 +818,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } for (int i = 0; i < ret.numAttacked; i++) { int oid = p.readInt(); - p.skip(14); - List allDamageNumbers = new ArrayList<>(); - Monster monster = chr.getMap().getMonsterByOid(oid); - + p.skip(4); + Point curPos = p.readPos(); + Point nextPos = p.readPos(); + short delay = p.readShort(); + List damageLines = new ArrayList<>(); + final Monster monster = chr.getMap().getMonsterByOid(oid); if (chr.getBuffEffect(BuffStat.WK_CHARGE) != null) { // Charge, so now we need to check elemental effectiveness int sourceID = chr.getBuffSource(BuffStat.WK_CHARGE); @@ -941,12 +947,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } } - allDamageNumbers.add(damage); + damageLines.add(damage); } if (ret.skill != Corsair.RAPID_FIRE || ret.skill != Aran.HIDDEN_FULL_DOUBLE || ret.skill != Aran.HIDDEN_FULL_TRIPLE || ret.skill != Aran.HIDDEN_OVER_DOUBLE || ret.skill != Aran.HIDDEN_OVER_TRIPLE) { p.skip(4); } - ret.allDamage.put(oid, allDamageNumbers); + ret.targets.put(oid, new AttackTarget(delay, damageLines)); } if (ret.skill == NightWalker.POISON_BOMB) { // Poison Bomb p.skip(4); @@ -954,8 +960,4 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } return ret; } - - private static int rand(int l, int u) { - return (int) ((Math.random() * (u - l + 1)) + l); - } } diff --git a/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java index 5a8a9bef61..b25712d912 100644 --- a/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java @@ -78,7 +78,9 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { c.sendPacket(PacketCreator.getEnergy("energy", chr.getDojoEnergy())); } - chr.getMap().broadcastMessage(chr, PacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, attack.speed, attack.direction, attack.display), false, true); + chr.getMap().broadcastMessage(chr, PacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel, + attack.stance, attack.numAttackedAndDamage, attack.targets, attack.speed, attack.direction, + attack.display), false, true); int numFinisherOrbs = 0; Integer comboBuff = chr.getBuffedValue(BuffStat.COMBO); if (GameConstants.isFinisherSkill(attack.skill)) { @@ -139,9 +141,9 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { } if (attack.numAttacked > 0 && attack.skill == DragonKnight.SACRIFICE) { int totDamageToOneMonster = 0; // sacrifice attacks only 1 mob with 1 attack - final Iterator> dmgIt = attack.allDamage.values().iterator(); + final Iterator dmgIt = attack.targets.values().iterator(); if (dmgIt.hasNext()) { - totDamageToOneMonster = dmgIt.next().get(0); + totDamageToOneMonster = dmgIt.next().damageLines().getFirst(); } chr.safeAddHP(-1 * totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100); diff --git a/src/main/java/net/server/channel/handlers/MagicDamageHandler.java b/src/main/java/net/server/channel/handlers/MagicDamageHandler.java index 7350e55c55..3324ebb023 100644 --- a/src/main/java/net/server/channel/handlers/MagicDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/MagicDamageHandler.java @@ -66,7 +66,8 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler { } int charge = (attack.skill == Evan.FIRE_BREATH || attack.skill == Evan.ICE_BREATH || attack.skill == FPArchMage.BIG_BANG || attack.skill == ILArchMage.BIG_BANG || attack.skill == Bishop.BIG_BANG) ? attack.charge : -1; - Packet packet = PacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, charge, attack.speed, attack.direction, attack.display); + Packet packet = PacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance, + attack.numAttackedAndDamage, attack.targets, charge, attack.speed, attack.direction, attack.display); chr.getMap().broadcastMessage(chr, packet, false, true); StatEffect effect = attack.getAttackEffect(chr, null); @@ -84,8 +85,8 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler { Skill eaterSkill = SkillFactory.getSkill((chr.getJob().getId() - (chr.getJob().getId() % 10)) * 10000);// MP Eater, works with right job int eaterLevel = chr.getSkillLevel(eaterSkill); if (eaterLevel > 0) { - for (Integer singleDamage : attack.allDamage.keySet()) { - eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(singleDamage), 0); + for (Integer oid : attack.targets.keySet()) { + eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(oid), 0); } } } diff --git a/src/main/java/net/server/channel/handlers/RangedAttackHandler.java b/src/main/java/net/server/channel/handlers/RangedAttackHandler.java index 85d599b9fc..dff4e77d7a 100644 --- a/src/main/java/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/main/java/net/server/channel/handlers/RangedAttackHandler.java @@ -83,17 +83,23 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } if (attack.skill == Buccaneer.ENERGY_ORB || attack.skill == ThunderBreaker.SPARK || attack.skill == Shadower.TAUNT || attack.skill == NightLord.TAUNT) { - chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); + chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, + attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed, + attack.direction, attack.display), false); applyAttack(attack, chr, 1); } else if (attack.skill == ThunderBreaker.SHARK_WAVE && chr.getSkillLevel(ThunderBreaker.SHARK_WAVE) > 0) { - chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); + chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, + attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed, + attack.direction, attack.display), false); applyAttack(attack, chr, 1); for (int i = 0; i < attack.numAttacked; i++) { chr.handleEnergyChargeGain(); } } else if (attack.skill == Aran.COMBO_SMASH || attack.skill == Aran.COMBO_FENRIR || attack.skill == Aran.COMBO_TEMPEST) { - chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); + chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, + attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed, + attack.direction, attack.display), false); if (attack.skill == Aran.COMBO_SMASH && chr.getCombo() >= 30) { chr.setCombo((short) 0); applyAttack(attack, chr, 1); @@ -213,10 +219,14 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { case 3221001: // Pierce case 5221004: // Rapid Fire case 13111002: // KoC Hurricane - packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.rangedirection, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display); + packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.rangedirection, + attack.numAttackedAndDamage, visProjectile, attack.targets, attack.speed, + attack.direction, attack.display); break; default: - packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display); + packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, + attack.numAttackedAndDamage, visProjectile, attack.targets, attack.speed, + attack.direction, attack.display); break; } chr.getMap().broadcastMessage(chr, packet, false, true); diff --git a/src/main/java/server/maps/MapleMap.java b/src/main/java/server/maps/MapleMap.java index ceadc01aff..9440fc48ff 100644 --- a/src/main/java/server/maps/MapleMap.java +++ b/src/main/java/server/maps/MapleMap.java @@ -1337,22 +1337,23 @@ public class MapleMap { } } } - if (monster.isAlive()) { - boolean killed = monster.damage(chr, damage, false); - - selfDestruction selfDestr = monster.getStats().selfDestruction(); - if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p - if (monster.getHp() <= selfDestr.getHp()) { - killMonster(monster, chr, true, selfDestr.getAction()); - return true; - } - } - if (killed) { - killMonster(monster, chr, true); - } - return true; + if (!monster.isAlive()) { + return false; } - return false; + + boolean killed = monster.damage(chr, damage, false); + + selfDestruction selfDestr = monster.getStats().selfDestruction(); + if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p + if (monster.getHp() <= selfDestr.getHp()) { + killMonster(monster, chr, true, selfDestr.getAction()); + return true; + } + } + if (killed) { + killMonster(monster, chr, true); + } + return true; } public void broadcastBalrogVictory(String leaderName) { @@ -1406,12 +1407,17 @@ public class MapleMap { broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition()); monster.aggroSwitchController(null, false); } - } else { - if (removeKilledMonsterObject(monster)) { - try { - if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) { - AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher."); - } + return; + } + + if (!removeKilledMonsterObject(monster)) { + return; + } + + try { + if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) { + AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher."); + } /*if (chr.getQuest(Quest.getInstance(29400)).getStatus().equals(QuestStatus.Status.STARTED)) { if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) { @@ -1420,74 +1426,74 @@ public class MapleMap { } }*/ - if (monster.getCP() > 0 && chr.getMap().isCPQMap()) { - chr.gainCP(monster.getCP()); - } + if (monster.getCP() > 0 && chr.getMap().isCPQMap()) { + chr.gainCP(monster.getCP()); + } - int buff = monster.getBuffToGive(); - if (buff > -1) { - ItemInformationProvider mii = ItemInformationProvider.getInstance(); - for (MapObject mmo : this.getPlayers()) { - Character character = (Character) mmo; - if (character.isAlive()) { - StatEffect statEffect = mii.getItemEffect(buff); - character.sendPacket(PacketCreator.showOwnBuffEffect(buff, 1)); - broadcastMessage(character, PacketCreator.showBuffEffect(character.getId(), buff, 1), false); - statEffect.applyTo(character); - } - } + int buff = monster.getBuffToGive(); + if (buff > -1) { + ItemInformationProvider mii = ItemInformationProvider.getInstance(); + for (MapObject mmo : this.getPlayers()) { + Character character = (Character) mmo; + if (character.isAlive()) { + StatEffect statEffect = mii.getItemEffect(buff); + character.sendPacket(PacketCreator.showOwnBuffEffect(buff, 1)); + broadcastMessage(character, PacketCreator.showBuffEffect(character.getId(), buff, 1), false); + statEffect.applyTo(character); } - - if (MobId.isZakumArm(monster.getId())) { - boolean makeZakReal = true; - Collection objects = getMapObjects(); - for (MapObject object : objects) { - Monster mons = getMonsterByOid(object.getObjectId()); - if (mons != null) { - if (MobId.isZakumArm(mons.getId())) { - makeZakReal = false; - break; - } - } - } - if (makeZakReal) { - MapleMap map = chr.getMap(); - - for (MapObject object : objects) { - Monster mons = map.getMonsterByOid(object.getObjectId()); - if (mons != null) { - if (mons.getId() == MobId.ZAKUM_1) { - makeMonsterReal(mons); - break; - } - } - } - } - } - - Character dropOwner = monster.killBy(chr); - if (withDrops && !monster.dropsDisabled()) { - if (dropOwner == null) { - dropOwner = chr; - } - dropFromMonster(dropOwner, monster, false); - } - - if (monster.hasBossHPBar()) { - for (Character mc : this.getAllPlayers()) { - if (mc.getTargetHpBarHash() == monster.hashCode()) { - mc.resetPlayerAggro(); - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown - monster.dispatchMonsterKilled(true); - broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition()); } } + + if (MobId.isZakumArm(monster.getId())) { + boolean makeZakReal = true; + Collection objects = getMapObjects(); + for (MapObject object : objects) { + Monster mons = getMonsterByOid(object.getObjectId()); + if (mons != null) { + if (MobId.isZakumArm(mons.getId())) { + makeZakReal = false; + break; + } + } + } + if (makeZakReal) { + MapleMap map = chr.getMap(); + + for (MapObject object : objects) { + Monster mons = map.getMonsterByOid(object.getObjectId()); + if (mons != null) { + if (mons.getId() == MobId.ZAKUM_1) { + makeMonsterReal(mons); + break; + } + } + } + } + } + + Character dropOwner = monster.killBy(chr); + if (withDrops && !monster.dropsDisabled()) { + if (dropOwner == null) { + dropOwner = chr; + } + dropFromMonster(dropOwner, monster, false); + } + + if (monster.hasBossHPBar()) { + for (Character mc : this.getAllPlayers()) { + if (mc.getTargetHpBarHash() == monster.hashCode()) { + mc.resetPlayerAggro(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown + monster.dispatchMonsterKilled(true); + broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition()); } + + } public void killFriendlies(Monster mob) { diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index a6cf3cf6eb..3ee9d8fdff 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -56,6 +56,7 @@ import constants.id.MapId; import constants.id.NpcId; import constants.inventory.ItemConstants; import constants.skills.Buccaneer; +import constants.skills.ChiefBandit; import constants.skills.Corsair; import constants.skills.ThunderBreaker; import net.encryption.InitializationVector; @@ -67,6 +68,7 @@ import net.packet.Packet; import net.server.PlayerCoolDownValueHolder; import net.server.Server; import net.server.channel.Channel; +import net.server.channel.handlers.AbstractDealDamageHandler.AttackTarget; import net.server.channel.handlers.PlayerInteractionHandler; import net.server.channel.handlers.SummonDamageHandler.SummonAttackEntry; import net.server.channel.handlers.WhisperHandler; @@ -2338,29 +2340,40 @@ public class PacketCreator { } */ - public static Packet closeRangeAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, Map> damage, int speed, int direction, int display) { + public static Packet closeRangeAttack(Character chr, int skill, int skilllevel, int stance, + int numAttackedAndDamage, Map targets, int speed, + int direction, int display) { final OutPacket p = OutPacket.create(SendOpcode.CLOSE_RANGE_ATTACK); - addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, damage, speed, direction, display); + addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, targets, speed, direction, + display); return p; } - public static Packet rangedAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, int projectile, Map> damage, int speed, int direction, int display) { + public static Packet rangedAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, + int projectile, Map targets, int speed, int direction, + int display) { final OutPacket p = OutPacket.create(SendOpcode.RANGED_ATTACK); - addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, projectile, damage, speed, direction, display); + addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, projectile, targets, speed, direction, + display); p.writeInt(0); return p; } - public static Packet magicAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, Map> damage, int charge, int speed, int direction, int display) { + public static Packet magicAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, + Map targets, int charge, int speed, int direction, + int display) { final OutPacket p = OutPacket.create(SendOpcode.MAGIC_ATTACK); - addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, damage, speed, direction, display); + addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, targets, speed, direction, + display); if (charge != -1) { p.writeInt(charge); } return p; } - private static void addAttackBody(OutPacket p, Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, int projectile, Map> damage, int speed, int direction, int display) { + private static void addAttackBody(OutPacket p, Character chr, int skill, int skilllevel, int stance, + int numAttackedAndDamage, int projectile, Map targets, + int speed, int direction, int display) { p.writeInt(chr.getId()); p.writeByte(numAttackedAndDamage); p.writeByte(0x5B);//? @@ -2374,16 +2387,16 @@ public class PacketCreator { p.writeByte(speed); p.writeByte(0x0A); p.writeInt(projectile); - for (Integer oned : damage.keySet()) { - List onedList = damage.get(oned); - if (onedList != null) { - p.writeInt(oned); + for (Map.Entry target : targets.entrySet()) { + AttackTarget value = target.getValue(); + if (value != null) { + p.writeInt(target.getKey()); p.writeByte(0x0); - if (skill == 4211006) { - p.writeByte(onedList.size()); + if (skill == ChiefBandit.MESO_EXPLOSION) { + p.writeByte(value.damageLines().size()); } - for (Integer eachd : onedList) { - p.writeInt(eachd); + for (Integer damageLine : value.damageLines()) { + p.writeInt(damageLine); } } } @@ -2581,6 +2594,7 @@ public class PacketCreator { * @param slot * @return */ + // TODO: look for a "delay" in case animation = 4 (explode). Doesn't make sense for it to be server-sided. public static Packet removeItemFromMap(int objId, int animation, int chrId, boolean pet, int slot) { OutPacket p = OutPacket.create(SendOpcode.REMOVE_ITEM_FROM_MAP); p.writeByte(animation); // expire From 802cc2b5f5b252a2201215a4da4a4da0647fa6a7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 17 Aug 2024 18:49:28 +0200 Subject: [PATCH 48/55] Use delay from packets for drop timing --- .../command/commands/gm3/KillAllCommand.java | 2 +- .../handlers/AbstractDealDamageHandler.java | 25 +-- .../channel/handlers/AdminCommandHandler.java | 2 +- .../handlers/FieldDamageMobHandler.java | 2 +- .../channel/handlers/MesoDropHandler.java | 5 +- .../channel/handlers/MobDamageMobHandler.java | 31 ++-- .../channel/handlers/SummonDamageHandler.java | 4 +- .../channel/handlers/TakeDamageHandler.java | 4 +- .../channel/handlers/UseCatchItemHandler.java | 24 +-- .../reactor/ReactorActionManager.java | 9 +- src/main/java/server/life/Monster.java | 4 +- src/main/java/server/maps/MapItem.java | 5 +- src/main/java/server/maps/MapleMap.java | 167 ++++++++---------- src/main/java/tools/PacketCreator.java | 5 +- 14 files changed, 147 insertions(+), 142 deletions(-) diff --git a/src/main/java/client/command/commands/gm3/KillAllCommand.java b/src/main/java/client/command/commands/gm3/KillAllCommand.java index 787e48343f..398f270008 100644 --- a/src/main/java/client/command/commands/gm3/KillAllCommand.java +++ b/src/main/java/client/command/commands/gm3/KillAllCommand.java @@ -49,7 +49,7 @@ public class KillAllCommand extends Command { for (MapObject monstermo : monsters) { Monster monster = (Monster) monstermo; if (!monster.getStats().isFriendly() && !(monster.getId() >= MobId.DEAD_HORNTAIL_MIN && monster.getId() <= MobId.HORNTAIL)) { - map.damageMonster(player, monster, Integer.MAX_VALUE); + map.damageMonster(player, monster, Integer.MAX_VALUE, (short) 0); count++; } } diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index b9b80b9b32..cea8b1a937 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -252,8 +252,8 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } } } - for (Integer oned : attack.targets.keySet()) { - final Monster monster = map.getMonsterByOid(oned); + for (Map.Entry target : attack.targets.entrySet()) { + final Monster monster = map.getMonsterByOid(target.getKey()); if (monster != null) { double distance = player.getPosition().distanceSq(monster.getPosition()); double distanceToDetect = 200000.0; @@ -288,7 +288,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } int totDamageToOneMonster = 0; - List onedList = attack.targets.get(oned).damageLines(); + List onedList = target.getValue().damageLines(); if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) { @@ -324,7 +324,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { Skill pickpocket = SkillFactory.getSkill(ChiefBandit.PICKPOCKET); int picklv = (player.isGM()) ? pickpocket.getMaxLevel() : player.getSkillLevel(pickpocket); if (picklv > 0) { - int delay = 0; + short delay = 0; final int maxmeso = player.getBuffedValue(BuffStat.PICKPOCKET); for (Integer eachd : onedList) { eachd += Integer.MAX_VALUE; @@ -337,7 +337,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { eachdf = eachd; } - TimerManager.getInstance().schedule(() -> map.spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2), delay); + int meso = Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, 1), maxmeso); + Point position = new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())); + map.spawnMesoDrop(meso, position, monster, player, true, (byte) 2, delay); delay += 100; } } @@ -363,7 +365,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { List toSteal = new ArrayList<>(); toSteal.add(mi.retrieveDrop(monster.getId()).get(i)); - map.dropItemsFromMonster(toSteal, player, monster); + map.dropItemsFromMonster(toSteal, player, monster, target.getValue().delay()); monster.addStolen(toSteal.get(0).itemId); } } @@ -483,7 +485,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { StatEffect mortal = mortalBlow.getEffect(skillLevel); if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) { if (Randomizer.rand(1, 100) <= mortal.getY()) { - map.damageMonster(player, monster, Integer.MAX_VALUE); // thanks Conrad for noticing reduced EXP gain from skill kill + map.damageMonster(player, monster, Integer.MAX_VALUE, target.getValue().delay()); // thanks Conrad for noticing reduced EXP gain from skill kill } } } @@ -549,7 +551,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), totDamageToOneMonster)); } - map.damageMonster(player, monster, totDamageToOneMonster); + map.damageMonster(player, monster, totDamageToOneMonster, target.getValue().delay()); } if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) { for (MobSkillId msId : monster.getSkills()) { @@ -576,7 +578,8 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } } - private static void damageMonsterWithSkill(final Character attacker, final MapleMap map, final Monster monster, final int damage, int skillid, int fixedTime) { + private static void damageMonsterWithSkill(final Character attacker, final MapleMap map, final Monster monster, + final int damage, int skillid, int fixedTime) { int animationTime; if (fixedTime == 0) { @@ -588,11 +591,11 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { if (animationTime > 0) { // be sure to only use LIMITED ATTACKS with animation time here TimerManager.getInstance().schedule(() -> { map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), damage), monster.getPosition()); - map.damageMonster(attacker, monster, damage); + map.damageMonster(attacker, monster, damage, (short) 0); }, animationTime); } else { map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), damage), monster.getPosition()); - map.damageMonster(attacker, monster, damage); + map.damageMonster(attacker, monster, damage, (short) 0); } } diff --git a/src/main/java/net/server/channel/handlers/AdminCommandHandler.java b/src/main/java/net/server/channel/handlers/AdminCommandHandler.java index 3cd9dd6704..182087606a 100644 --- a/src/main/java/net/server/channel/handlers/AdminCommandHandler.java +++ b/src/main/java/net/server/channel/handlers/AdminCommandHandler.java @@ -133,7 +133,7 @@ public final class AdminCommandHandler extends AbstractPacketHandler { for (int x = 0; x < amount; x++) { Monster monster = (Monster) monsterx.get(x); if (monster.getId() == mobToKill) { - c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true); + c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true, (short) 0); } } break; diff --git a/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java b/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java index 6fdb98b197..2007c8822e 100644 --- a/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java +++ b/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java @@ -56,7 +56,7 @@ public class FieldDamageMobHandler extends AbstractPacketHandler { } map.broadcastMessage(chr, PacketCreator.damageMonster(mobOid, dmg), true); - map.damageMonster(chr, mob, dmg); + map.damageMonster(chr, mob, dmg, (short) 0); } } } diff --git a/src/main/java/net/server/channel/handlers/MesoDropHandler.java b/src/main/java/net/server/channel/handlers/MesoDropHandler.java index f99c7edbee..94389b96b7 100644 --- a/src/main/java/net/server/channel/handlers/MesoDropHandler.java +++ b/src/main/java/net/server/channel/handlers/MesoDropHandler.java @@ -67,7 +67,8 @@ public final class MesoDropHandler extends AbstractPacketHandler { if (player.attemptCatchFish(meso)) { player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition()); } else { - player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2); + player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2, + (short) 0); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java b/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java index ad50bd5afb..3b62ddcad4 100644 --- a/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java +++ b/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java @@ -57,21 +57,24 @@ public final class MobDamageMobHandler extends AbstractPacketHandler { Monster attacker = map.getMonsterByOid(from); Monster damaged = map.getMonsterByOid(to); - if (attacker != null && damaged != null) { - int maxDmg = calcMaxDamage(attacker, damaged, magic); // thanks Darter (YungMoozi) for reporting unchecked dmg - - if (dmg > maxDmg) { - AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing hypnotize damage exploit."); // thanks Rien dev team - String attackerName = MonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId()); - String damagedName = MonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId()); - log.warn("Chr {} had hypnotized {} to attack {} with damage {} (max: {})", c.getPlayer().getName(), - attackerName, damagedName, dmg, maxDmg); - dmg = maxDmg; - } - - map.damageMonster(chr, damaged, dmg); - map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false); + if (attacker == null || damaged == null) { + return; } + + int maxDmg = calcMaxDamage(attacker, damaged, magic); // thanks Darter (YungMoozi) for reporting unchecked dmg + + if (dmg > maxDmg) { + AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing hypnotize damage exploit."); // thanks Rien dev team + String attackerName = MonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId()); + String damagedName = MonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId()); + log.warn("Chr {} had hypnotized {} to attack {} with damage {} (max: {})", c.getPlayer().getName(), + attackerName, damagedName, dmg, maxDmg); + dmg = maxDmg; + } + + map.damageMonster(chr, damaged, dmg, (short) 0); + map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false); + } private static int calcMaxDamage(Monster attacker, Monster damaged, boolean magic) { diff --git a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java index 5506818992..837c8f357a 100644 --- a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java @@ -92,7 +92,7 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { p.skip(8); // I failed lol (mob x,y and summon x,y), Thanks Gerald for (int x = 0; x < numAttacked; x++) { int monsterOid = p.readInt(); // attacked oid - p.skip(18); + p.skip(18); // TODO: find "delay" among these 18 skipped bytes int damage = p.readInt(); allDamage.add(new SummonAttackEntry(monsterOid, damage)); } @@ -121,7 +121,7 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { target.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000); } } - player.getMap().damageMonster(player, target, damage); + player.getMap().damageMonster(player, target, damage, (short) 0); } } diff --git a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java index 5207307bfb..c8d2e0ddd9 100644 --- a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java @@ -178,7 +178,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler { if (bouncedamage > attacker.getMaxHp() / 5) { bouncedamage = attacker.getMaxHp() / 5; } - map.damageMonster(chr, attacker, bouncedamage); + map.damageMonster(chr, attacker, bouncedamage, (short) 0); map.broadcastMessage(chr, PacketCreator.damageMonster(oid, bouncedamage), true); chr.sendPacket(PacketCreator.showOwnBuffEffect(id, 5)); map.broadcastMessage(chr, PacketCreator.showBuffEffect(chr.getId(), id, 5), false); @@ -211,7 +211,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler { int bouncedamage = (int) (damage * (chr.getBuffedValue(BuffStat.POWERGUARD).doubleValue() / (attacker.isBoss() ? 200 : 100))); bouncedamage = Math.min(bouncedamage, attacker.getMaxHp() / 10); damage -= bouncedamage; - map.damageMonster(chr, attacker, bouncedamage); + map.damageMonster(chr, attacker, bouncedamage, (short) 0); map.broadcastMessage(chr, PacketCreator.damageMonster(oid, bouncedamage), false, true); attacker.aggroMonsterDamage(chr, bouncedamage); } diff --git a/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java b/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java index ead6813e66..781bdcd032 100644 --- a/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java +++ b/src/main/java/net/server/channel/handlers/UseCatchItemHandler.java @@ -61,7 +61,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { case ItemId.PHEROMONE_PERFUME: if (mob.getId() == MobId.TAMABLE_HOG) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.HOG, (short) 1, "", -1); } @@ -72,7 +72,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if ((abm.getLastSpam(10) + 1000) < currentServerTime()) { if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.GHOST_SACK, (short) 1, "", -1); } else { @@ -90,7 +90,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (chr.canHold(ItemId.ARPQ_SPIRIT_JEWEL, 1)) { if (Math.random() < 0.5) { // 50% chance chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.ARPQ_SPIRIT_JEWEL, (short) 1, "", -1); chr.updateAriantScore(); @@ -113,7 +113,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (mob.getId() == MobId.LOST_RUDOLPH) { if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.TAMED_RUDOLPH, (short) 1, "", -1); } else { @@ -126,7 +126,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (mob.getId() == MobId.KING_SLIME_DOJO) { if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_1, (short) 1, "", -1); } else { @@ -139,7 +139,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (mob.getId() == MobId.FAUST_DOJO) { if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_2, (short) 1, "", -1); } else { @@ -152,7 +152,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (mob.getId() == MobId.MUSHMOM_DOJO) { if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_3, (short) 1, "", -1); } else { @@ -165,7 +165,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (mob.getId() == MobId.POISON_FLOWER) { if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.EPQ_MONSTER_MARBLE, (short) 1, "", -1); } else { @@ -179,7 +179,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if ((abm.getLastSpam(10) + 3000) < currentServerTime()) { abm.spam(10); chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, ItemId.FISH_NET_WITH_A_CATCH, (short) 1, "", -1); } else { @@ -202,7 +202,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { if (timeCatch != 0 && (abm.getLastSpam(10) + timeCatch) < currentServerTime()) { if (mobHp != 0 && mob.getHp() < ((mob.getMaxHp() / 100) * mobHp)) { chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1)); - mob.getMap().killMonster(mob, null, false); + killMonster(mob); InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true); InventoryManipulator.addById(c, itemGanho, (short) 1, "", -1); } else if (mob.getId() != MobId.P_JUNIOR) { @@ -220,4 +220,8 @@ public final class UseCatchItemHandler extends AbstractPacketHandler { // System.out.println("UseCatchItemHandler: \r\n" + slea.toString()); } } + + private static void killMonster(Monster mob) { + mob.getMap().killMonster(mob, null, false, (short) 0); + } } diff --git a/src/main/java/scripting/reactor/ReactorActionManager.java b/src/main/java/scripting/reactor/ReactorActionManager.java index b7f7c4e6a7..b5a9a50cd0 100644 --- a/src/main/java/scripting/reactor/ReactorActionManager.java +++ b/src/main/java/scripting/reactor/ReactorActionManager.java @@ -172,7 +172,8 @@ public class ReactorActionManager extends AbstractPlayerInteraction { int range = maxMeso - minMeso; int displayDrop = (int) (Math.random() * range) + minMeso; int mesoDrop = (displayDrop * c.getWorldServer().getMesoRate()); - reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2); + reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, + reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2, (short) 0); } else { Item drop; @@ -192,6 +193,7 @@ public class ReactorActionManager extends AbstractPlayerInteraction { dropPos.x -= (12 * items.size()); + // TODO: simply use "delay" drop packet instead of this scheduled task sprayTask = TimerManager.getInstance().register(() -> { if (dropItems.isEmpty()) { sprayTask.cancel(false); @@ -203,7 +205,8 @@ public class ReactorActionManager extends AbstractPlayerInteraction { int range = maxMeso - minMeso; int displayDrop = (int) (Math.random() * range) + minMeso; int mesoDrop = (displayDrop * worldMesoRate); - r.getMap().spawnMesoDrop(mesoDrop, r.getMap().calcDropPos(dropPos, r.getPosition()), r, chr, false, (byte) 2); + r.getMap().spawnMesoDrop(mesoDrop, r.getMap().calcDropPos(dropPos, r.getPosition()), r, chr, + false, (byte) 2, (short) 0); } else { Item drop; @@ -333,4 +336,4 @@ public class ReactorActionManager extends AbstractPlayerInteraction { getPlayer().getMap().getBlueTeamBuffs().remove(skil); } } -} \ No newline at end of file +} diff --git a/src/main/java/server/life/Monster.java b/src/main/java/server/life/Monster.java index ea3beb85dd..e3ad4bf5b8 100644 --- a/src/main/java/server/life/Monster.java +++ b/src/main/java/server/life/Monster.java @@ -825,12 +825,12 @@ public class Monster extends AbstractLoadedLife { } if (htKilled) { - reviveMap.killMonster(ht, killer, true); + reviveMap.killMonster(ht, killer, true, (short) 0); } } for (int i = MobId.DEAD_HORNTAIL_MAX; i >= MobId.DEAD_HORNTAIL_MIN; i--) { - reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true); + reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true, (short) 0); } } else if (controller != null) { mob.aggroSwitchController(controller, aggro); diff --git a/src/main/java/server/maps/MapItem.java b/src/main/java/server/maps/MapItem.java index 9f1086d842..b87306990a 100644 --- a/src/main/java/server/maps/MapItem.java +++ b/src/main/java/server/maps/MapItem.java @@ -208,7 +208,8 @@ public class MapItem extends AbstractMapObject { if (chr.needQuestItem(questid, getItemId())) { this.lockItem(); try { - client.sendPacket(PacketCreator.dropItemFromMapObject(chr, this, null, getPosition(), (byte) 2)); + client.sendPacket(PacketCreator.dropItemFromMapObject(chr, this, null, getPosition(), + (byte) 2, (short) 0)); } finally { this.unlockItem(); } @@ -219,4 +220,4 @@ public class MapItem extends AbstractMapObject { public void sendDestroyData(final Client client) { client.sendPacket(PacketCreator.removeItemFromMap(getObjectId(), 1, 0)); } -} \ No newline at end of file +} diff --git a/src/main/java/server/maps/MapleMap.java b/src/main/java/server/maps/MapleMap.java index 9440fc48ff..f9f28d1363 100644 --- a/src/main/java/server/maps/MapleMap.java +++ b/src/main/java/server/maps/MapleMap.java @@ -654,7 +654,8 @@ public class MapleMap { } } - private byte dropItemsFromMonsterOnMap(List dropEntry, Point pos, byte d, int chRate, byte droptype, int mobpos, Character chr, Monster mob) { + private byte dropItemsFromMonsterOnMap(List dropEntry, Point pos, byte d, int chRate, + byte droptype, int mobpos, Character chr, Monster mob, short delay) { if (dropEntry.isEmpty()) { return d; } @@ -686,7 +687,8 @@ public class MapleMap { mesos = Integer.MAX_VALUE; } - spawnMesoDrop(mesos, calcDropPos(pos, mob.getPosition()), mob, chr, false, droptype); + spawnMesoDrop(mesos, calcDropPos(pos, mob.getPosition()), mob, chr, false, droptype, + delay); } } else { if (ItemConstants.getInventoryType(de.itemId) == InventoryType.EQUIP) { @@ -694,7 +696,7 @@ public class MapleMap { } else { idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1)); } - spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid); + spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay); } d++; } @@ -703,7 +705,8 @@ public class MapleMap { return d; } - private byte dropGlobalItemsFromMonsterOnMap(List globalEntry, Point pos, byte d, byte droptype, int mobpos, Character chr, Monster mob) { + private byte dropGlobalItemsFromMonsterOnMap(List globalEntry, Point pos, byte d, + byte droptype, int mobpos, Character chr, Monster mob, short delay) { Collections.shuffle(globalEntry); Item idrop; @@ -722,7 +725,7 @@ public class MapleMap { } else { idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1)); } - spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid); + spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay); d++; } } @@ -731,7 +734,7 @@ public class MapleMap { return d; } - private void dropFromMonster(final Character chr, final Monster mob, final boolean useBaseRate) { + private void dropFromMonster(final Character chr, final Monster mob, final boolean useBaseRate, short delay) { if (mob.dropsDisabled() || !dropsOn) { return; } @@ -765,10 +768,11 @@ public class MapleMap { return; } - registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob); + registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, + chr, mob, delay); } - public void dropItemsFromMonster(List list, final Character chr, final Monster mob) { + public void dropItemsFromMonster(List list, final Character chr, final Monster mob, short delay) { if (mob.dropsDisabled() || !dropsOn) { return; } @@ -779,15 +783,16 @@ public class MapleMap { byte d = 1; Point pos = new Point(0, mob.getPosition().y); - dropItemsFromMonsterOnMap(list, pos, d, chRate, droptype, mobpos, chr, mob); + dropItemsFromMonsterOnMap(list, pos, d, chRate, droptype, mobpos, chr, mob, delay); } public void dropFromFriendlyMonster(final Character chr, final Monster mob) { - dropFromMonster(chr, mob, true); + dropFromMonster(chr, mob, true, (short) 0); } public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid) { - spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr, (byte) (chr.getParty() != null ? 1 : 0), questid); + spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr, + (byte) (chr.getParty() != null ? 1 : 0), questid, (short) 0); } private void stopItemMonitor() { @@ -965,8 +970,12 @@ public class MapleMap { } } - private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, List dropEntry, List visibleQuestEntry, List otherQuestEntry, List globalEntry, Character chr, Monster mob) { - MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob); + private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, + List dropEntry, List visibleQuestEntry, + List otherQuestEntry, List globalEntry, + Character chr, Monster mob, short delay) { + MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, delay, dropEntry, visibleQuestEntry, + otherQuestEntry, globalEntry, chr, mob); if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { int animationTime = mob.getAnimationTime("die1"); @@ -1123,7 +1132,8 @@ public class MapleMap { } } - private void spawnDrop(final Item idrop, final Point dropPos, final MapObject dropper, final Character chr, final byte droptype, final short questid) { + private void spawnDrop(final Item idrop, final Point dropPos, final MapObject dropper, final Character chr, + final byte droptype, final short questid, short delay) { final MapItem mdrop = new MapItem(idrop, dropPos, dropper, chr, chr.getClient(), droptype, false, questid); mdrop.setDropTime(Server.getInstance().getCurrentTime()); spawnAndAddRangedMapObject(mdrop, c -> { @@ -1132,7 +1142,8 @@ public class MapleMap { if (chr1.needQuestItem(questid, idrop.getItemId())) { mdrop.lockItem(); try { - c.sendPacket(PacketCreator.dropItemFromMapObject(chr1, mdrop, dropper.getPosition(), dropPos, (byte) 1)); + c.sendPacket(PacketCreator.dropItemFromMapObject(chr1, mdrop, dropper.getPosition(), dropPos, + (byte) 1, delay)); } finally { mdrop.unlockItem(); } @@ -1143,7 +1154,8 @@ public class MapleMap { activateItemReactors(mdrop, chr.getClient()); } - public final void spawnMesoDrop(final int meso, final Point position, final MapObject dropper, final Character owner, final boolean playerDrop, final byte droptype) { + public final void spawnMesoDrop(final int meso, final Point position, final MapObject dropper, + final Character owner, final boolean playerDrop, final byte droptype, short delay) { final Point droppos = calcDropPos(position, position); final MapItem mdrop = new MapItem(meso, droppos, dropper, owner, owner.getClient(), droptype, playerDrop); mdrop.setDropTime(Server.getInstance().getCurrentTime()); @@ -1151,7 +1163,8 @@ public class MapleMap { spawnAndAddRangedMapObject(mdrop, c -> { mdrop.lockItem(); try { - c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, (byte) 1)); + c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, + (byte) 1, delay)); } finally { mdrop.unlockItem(); } @@ -1166,7 +1179,7 @@ public class MapleMap { mdrop.lockItem(); try { - broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, mdrop.getPosition()); + broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, (short) 0, mdrop.getPosition()); } finally { mdrop.unlockItem(); } @@ -1178,7 +1191,7 @@ public class MapleMap { mdrop.lockItem(); try { - broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, mdrop.getPosition()); + broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, (short) 0, mdrop.getPosition()); } finally { mdrop.unlockItem(); } @@ -1326,7 +1339,13 @@ public class MapleMap { return count; } - public boolean damageMonster(final Character chr, final Monster monster, final int damage) { + /* TODO: start using once all erroneous damageMonster() callers have been fixed + public boolean damageMonster(final Character chr, final Monster monster, final int damage) { + damageMonster(chr, monster, damage, (short) 0); + } + */ + + public boolean damageMonster(final Character chr, final Monster monster, final int damage, short delay) { if (monster.getId() == MobId.ZAKUM_1) { for (MapObject object : chr.getMap().getMapObjects()) { Monster mons = chr.getMap().getMonsterByOid(object.getObjectId()); @@ -1351,7 +1370,7 @@ public class MapleMap { } } if (killed) { - killMonster(monster, chr, true); + killMonster(monster, chr, true, delay); } return true; } @@ -1392,11 +1411,12 @@ public class MapleMap { } } - public void killMonster(final Monster monster, final Character chr, final boolean withDrops) { - killMonster(monster, chr, withDrops, 1); + public void killMonster(final Monster monster, final Character chr, final boolean withDrops, short dropDelay) { + killMonster(monster, chr, withDrops, 1, dropDelay); } - public void killMonster(final Monster monster, final Character chr, final boolean withDrops, int animation) { + public void killMonster(final Monster monster, final Character chr, final boolean withDrops, int animation, + short dropDelay) { if (monster == null) { return; } @@ -1476,7 +1496,7 @@ public class MapleMap { if (dropOwner == null) { dropOwner = chr; } - dropFromMonster(dropOwner, monster, false); + dropFromMonster(dropOwner, monster, false, dropDelay); } if (monster.hasBossHPBar()) { @@ -1497,7 +1517,7 @@ public class MapleMap { } public void killFriendlies(Monster mob) { - this.killMonster(mob, (Character) getPlayers().get(0), false); + this.killMonster(mob, (Character) getPlayers().get(0), false, (short) 0); } public void killMonster(int mobId) { @@ -1506,7 +1526,7 @@ public class MapleMap { for (Monster mob : mobList) { if (mob.getId() == mobId) { - this.killMonster(mob, chr, false); + this.killMonster(mob, chr, false, (short) 0); } } } @@ -1525,7 +1545,7 @@ public class MapleMap { chr = defaultChr; } - this.killMonster(mob, chr, true); + this.killMonster(mob, chr, true, (short) 0); } } } @@ -1555,7 +1575,7 @@ public class MapleMap { continue; } - killMonster(monster, null, false, 1); + killMonster(monster, null, false, 1, (short) 0); } } @@ -1565,7 +1585,7 @@ public class MapleMap { for (MapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.MONSTER))) { Monster monster = (Monster) monstermo; - killMonster(monster, null, false, 1); + killMonster(monster, null, false, 1, (short) 0); } } @@ -1912,7 +1932,7 @@ public class MapleMap { Runnable removeAfterAction; if (selfDestruction == null) { - removeAfterAction = () -> killMonster(monster, null, false); + removeAfterAction = () -> killMonster(monster, null, false, (short) 0); registerMapSchedule(removeAfterAction, SECONDS.toMillis(monster.getStats().removeAfter())); } else { @@ -2160,11 +2180,13 @@ public class MapleMap { getWorldServer().registerTimedMapObject(expireKite, YamlConfig.config.server.KITE_EXPIRE_TIME); } - public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, final boolean ffaDrop, final boolean playerDrop) { + public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, + final boolean ffaDrop, final boolean playerDrop) { spawnItemDrop(dropper, owner, item, pos, (byte) (ffaDrop ? 2 : 0), playerDrop); } - public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, final byte dropType, final boolean playerDrop) { + public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, + final byte dropType, final boolean playerDrop) { if (FieldLimit.DROP_LIMIT.check(this.getFieldLimit())) { // thanks Conrad for noticing some maps shouldn't have loots available this.disappearingItemDrop(dropper, owner, item, pos); return; @@ -2177,7 +2199,8 @@ public class MapleMap { spawnAndAddRangedMapObject(mdrop, c -> { mdrop.lockItem(); try { - c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, (byte) 1)); + c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, + (byte) 1, (short) 0)); } finally { mdrop.unlockItem(); } @@ -2185,7 +2208,7 @@ public class MapleMap { mdrop.lockItem(); try { - broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 0); + broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 0, (short) 0); } finally { mdrop.unlockItem(); } @@ -2194,49 +2217,6 @@ public class MapleMap { activateItemReactors(mdrop, owner.getClient()); } - public final void spawnItemDropList(List list, final MapObject dropper, final Character owner, Point pos) { - spawnItemDropList(list, 1, 1, dropper, owner, pos, true, false); - } - - public final void spawnItemDropList(List list, int minCopies, int maxCopies, final MapObject dropper, final Character owner, Point pos) { - spawnItemDropList(list, minCopies, maxCopies, dropper, owner, pos, true, false); - } - - // spawns item instances of all defined item ids on a list - public final void spawnItemDropList(List list, int minCopies, int maxCopies, final MapObject dropper, final Character owner, Point pos, final boolean ffaDrop, final boolean playerDrop) { - int copies = (maxCopies - minCopies) + 1; - if (copies < 1) { - return; - } - - Collections.shuffle(list); - - ItemInformationProvider ii = ItemInformationProvider.getInstance(); - Random rnd = new Random(); - - final Point dropPos = new Point(pos); - dropPos.x -= (12 * list.size()); - - for (Integer integer : list) { - if (integer == 0) { - spawnMesoDrop(owner != null ? 10 * owner.getMesoRate() : 10, calcDropPos(dropPos, pos), dropper, owner, playerDrop, (byte) (ffaDrop ? 2 : 0)); - } else { - final Item drop; - int randomedId = integer; - - if (ItemConstants.getInventoryType(randomedId) != InventoryType.EQUIP) { - drop = new Item(randomedId, (short) 0, (short) (rnd.nextInt(copies) + minCopies)); - } else { - drop = ii.randomizeStats((Equip) ii.getEquipById(randomedId)); - } - - spawnItemDrop(dropper, owner, drop, calcDropPos(dropPos, pos), ffaDrop, playerDrop); - } - - dropPos.x += 25; - } - } - private void registerMapSchedule(Runnable r, long delay) { OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL); service.registerOverallAction(mapid, r, delay); @@ -2867,20 +2847,23 @@ public class MapleMap { } } - private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, Point rangedFrom) { - broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, getRangedDistance(), rangedFrom); + private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay, + Point rangedFrom) { + broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, delay, getRangedDistance(), rangedFrom); } - private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod) { - broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, Double.POSITIVE_INFINITY, null); + private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay) { + broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, delay, Double.POSITIVE_INFINITY, null); } - private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, double rangeSq, Point rangedFrom) { + private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay, + double rangeSq, Point rangedFrom) { chrRLock.lock(); try { for (Character chr : characters) { - Packet packet = PacketCreator.dropItemFromMapObject(chr, mdrop, dropperPos, dropPos, mod); + Packet packet = PacketCreator.dropItemFromMapObject(chr, mdrop, dropperPos, dropPos, mod, delay); + // TODO: remove along with USE_MAXRANGE config if (rangeSq < Double.POSITIVE_INFINITY) { if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) { chr.sendPacket(packet); @@ -3405,12 +3388,14 @@ public class MapleMap { return false; } + // TODO: no reason to implement runnable - this is not intended to be submitted to another thread private class MobLootEntry implements Runnable { private final byte droptype; private final int mobpos; private final int chRate; private final Point pos; + private final short delay; private final List dropEntry; private final List visibleQuestEntry; private final List otherQuestEntry; @@ -3418,11 +3403,15 @@ public class MapleMap { private final Character chr; private final Monster mob; - protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, List dropEntry, List visibleQuestEntry, List otherQuestEntry, List globalEntry, Character chr, Monster mob) { + protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, short delay, + List dropEntry, List visibleQuestEntry, + List otherQuestEntry, List globalEntry, + Character chr, Monster mob) { this.droptype = droptype; this.mobpos = mobpos; this.chRate = chRate; this.pos = pos; + this.delay = delay; this.dropEntry = dropEntry; this.visibleQuestEntry = visibleQuestEntry; this.otherQuestEntry = otherQuestEntry; @@ -3436,14 +3425,14 @@ public class MapleMap { byte d = 1; // Normal Drops - d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob); + d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay); // Global Drops - d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob); + d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob, delay); // Quest Drops - d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob); - dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob); + d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay); + dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay); } } diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index 3ee9d8fdff..3b328fe1b0 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -1816,7 +1816,8 @@ public class PacketCreator { return p; } - public static Packet dropItemFromMapObject(Character player, MapItem drop, Point dropfrom, Point dropto, byte mod) { + public static Packet dropItemFromMapObject(Character player, MapItem drop, Point dropfrom, Point dropto, byte mod, + short delay) { int dropType = drop.getDropType(); if (drop.hasClientsideOwnership(player) && dropType < 3) { dropType = 2; @@ -1834,7 +1835,7 @@ public class PacketCreator { if (mod != 2) { p.writePos(dropfrom); - p.writeShort(0);//Fh? + p.writeShort(delay); } if (drop.getMeso() == 0) { addExpirationTime(p, drop.getItem().getExpiration()); From 2d40a89c557afa967f393e0cdcbfa3aa10a87e14 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 17 Aug 2024 18:51:50 +0200 Subject: [PATCH 49/55] Overload damageMonster for when no delay is needed --- .../java/client/command/commands/gm3/KillAllCommand.java | 2 +- .../server/channel/handlers/AbstractDealDamageHandler.java | 4 ++-- .../net/server/channel/handlers/FieldDamageMobHandler.java | 2 +- .../net/server/channel/handlers/MobDamageMobHandler.java | 2 +- .../net/server/channel/handlers/SummonDamageHandler.java | 2 +- .../java/net/server/channel/handlers/TakeDamageHandler.java | 4 ++-- src/main/java/server/maps/MapleMap.java | 6 ++---- 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/client/command/commands/gm3/KillAllCommand.java b/src/main/java/client/command/commands/gm3/KillAllCommand.java index 398f270008..787e48343f 100644 --- a/src/main/java/client/command/commands/gm3/KillAllCommand.java +++ b/src/main/java/client/command/commands/gm3/KillAllCommand.java @@ -49,7 +49,7 @@ public class KillAllCommand extends Command { for (MapObject monstermo : monsters) { Monster monster = (Monster) monstermo; if (!monster.getStats().isFriendly() && !(monster.getId() >= MobId.DEAD_HORNTAIL_MIN && monster.getId() <= MobId.HORNTAIL)) { - map.damageMonster(player, monster, Integer.MAX_VALUE, (short) 0); + map.damageMonster(player, monster, Integer.MAX_VALUE); count++; } } diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index cea8b1a937..3ff31dcce6 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -591,11 +591,11 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { if (animationTime > 0) { // be sure to only use LIMITED ATTACKS with animation time here TimerManager.getInstance().schedule(() -> { map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), damage), monster.getPosition()); - map.damageMonster(attacker, monster, damage, (short) 0); + map.damageMonster(attacker, monster, damage); }, animationTime); } else { map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), damage), monster.getPosition()); - map.damageMonster(attacker, monster, damage, (short) 0); + map.damageMonster(attacker, monster, damage); } } diff --git a/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java b/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java index 2007c8822e..6fdb98b197 100644 --- a/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java +++ b/src/main/java/net/server/channel/handlers/FieldDamageMobHandler.java @@ -56,7 +56,7 @@ public class FieldDamageMobHandler extends AbstractPacketHandler { } map.broadcastMessage(chr, PacketCreator.damageMonster(mobOid, dmg), true); - map.damageMonster(chr, mob, dmg, (short) 0); + map.damageMonster(chr, mob, dmg); } } } diff --git a/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java b/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java index 3b62ddcad4..c11063d539 100644 --- a/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java +++ b/src/main/java/net/server/channel/handlers/MobDamageMobHandler.java @@ -72,7 +72,7 @@ public final class MobDamageMobHandler extends AbstractPacketHandler { dmg = maxDmg; } - map.damageMonster(chr, damaged, dmg, (short) 0); + map.damageMonster(chr, damaged, dmg); map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false); } diff --git a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java index 837c8f357a..82045ac142 100644 --- a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java @@ -121,7 +121,7 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { target.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000); } } - player.getMap().damageMonster(player, target, damage, (short) 0); + player.getMap().damageMonster(player, target, damage); } } diff --git a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java index c8d2e0ddd9..5207307bfb 100644 --- a/src/main/java/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/TakeDamageHandler.java @@ -178,7 +178,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler { if (bouncedamage > attacker.getMaxHp() / 5) { bouncedamage = attacker.getMaxHp() / 5; } - map.damageMonster(chr, attacker, bouncedamage, (short) 0); + map.damageMonster(chr, attacker, bouncedamage); map.broadcastMessage(chr, PacketCreator.damageMonster(oid, bouncedamage), true); chr.sendPacket(PacketCreator.showOwnBuffEffect(id, 5)); map.broadcastMessage(chr, PacketCreator.showBuffEffect(chr.getId(), id, 5), false); @@ -211,7 +211,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler { int bouncedamage = (int) (damage * (chr.getBuffedValue(BuffStat.POWERGUARD).doubleValue() / (attacker.isBoss() ? 200 : 100))); bouncedamage = Math.min(bouncedamage, attacker.getMaxHp() / 10); damage -= bouncedamage; - map.damageMonster(chr, attacker, bouncedamage, (short) 0); + map.damageMonster(chr, attacker, bouncedamage); map.broadcastMessage(chr, PacketCreator.damageMonster(oid, bouncedamage), false, true); attacker.aggroMonsterDamage(chr, bouncedamage); } diff --git a/src/main/java/server/maps/MapleMap.java b/src/main/java/server/maps/MapleMap.java index f9f28d1363..32b3d7cea1 100644 --- a/src/main/java/server/maps/MapleMap.java +++ b/src/main/java/server/maps/MapleMap.java @@ -1339,11 +1339,9 @@ public class MapleMap { return count; } - /* TODO: start using once all erroneous damageMonster() callers have been fixed - public boolean damageMonster(final Character chr, final Monster monster, final int damage) { - damageMonster(chr, monster, damage, (short) 0); + public boolean damageMonster(Character chr, Monster monster, int damage) { + return damageMonster(chr, monster, damage, (short) 0); } - */ public boolean damageMonster(final Character chr, final Monster monster, final int damage, short delay) { if (monster.getId() == MobId.ZAKUM_1) { From 994d1723b694404f688f5f1bc6068a3f25f23638 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 17 Aug 2024 19:11:53 +0200 Subject: [PATCH 50/55] Remove unnecessary "spawn loot on animation" feature No longer needed since item drop timing is now dictated by the client since two commits back. --- config.yaml | 1 - src/main/java/config/ServerConfig.java | 1 - src/main/java/server/maps/MapleMap.java | 111 ++++-------------------- 3 files changed, 17 insertions(+), 96 deletions(-) diff --git a/config.yaml b/config.yaml index 49c4572dca..980401a0cb 100644 --- a/config.yaml +++ b/config.yaml @@ -249,7 +249,6 @@ server: USE_ENFORCE_MERCHANT_SAVE: true #Forces automatic DB save on merchant owners, at every item movement on shop. USE_ENFORCE_MDOOR_POSITION: false #Forces mystic door to be spawned near spawnpoints. USE_SPAWN_CLEAN_MDOOR: false #Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment. - USE_SPAWN_LOOT_ON_ANIMATION: false #Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly). USE_SPAWN_RELEVANT_LOOT: true #Forces to only spawn loots that are collectable by the player or any of their party members. USE_ERASE_PERMIT_ON_OPENSHOP: true #Forces "shop permit" item to be consumed when player deploy his/her player shop. USE_ERASE_UNTRADEABLE_DROP: true #Forces flagged untradeable items to disappear when dropped. diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 3aef4172d9..fff7bca8da 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -97,7 +97,6 @@ public class ServerConfig { public boolean USE_ENFORCE_MERCHANT_SAVE; public boolean USE_ENFORCE_MDOOR_POSITION; public boolean USE_SPAWN_CLEAN_MDOOR; - public boolean USE_SPAWN_LOOT_ON_ANIMATION; public boolean USE_SPAWN_RELEVANT_LOOT; public boolean USE_ERASE_PERMIT_ON_OPENSHOP; public boolean USE_ERASE_UNTRADEABLE_DROP; diff --git a/src/main/java/server/maps/MapleMap.java b/src/main/java/server/maps/MapleMap.java index 32b3d7cea1..fc421c1a49 100644 --- a/src/main/java/server/maps/MapleMap.java +++ b/src/main/java/server/maps/MapleMap.java @@ -82,7 +82,6 @@ import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -121,7 +120,6 @@ public class MapleMap { private final Map environment = new LinkedHashMap<>(); private final Map droppedItems = new LinkedHashMap<>(); private final LinkedList> registeredDrops = new LinkedList<>(); - private final Map mobLootEntries = new HashMap(20); private final List statUpdateRunnables = new ArrayList(50); private final List areas = new ArrayList<>(); private FootholdTree footholds = null; @@ -160,7 +158,6 @@ public class MapleMap { private MonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor private ScheduledFuture itemMonitor = null; private ScheduledFuture expireItemsTask = null; - private ScheduledFuture mobSpawnLootTask = null; private ScheduledFuture characterStatUpdateTask = null; private short itemMonitorTimeout; private Pair timeMob = null; @@ -654,10 +651,10 @@ public class MapleMap { } } - private byte dropItemsFromMonsterOnMap(List dropEntry, Point pos, byte d, int chRate, + private byte dropItemsFromMonsterOnMap(List dropEntry, Point pos, byte index, int chRate, byte droptype, int mobpos, Character chr, Monster mob, short delay) { if (dropEntry.isEmpty()) { - return d; + return index; } Collections.shuffle(dropEntry); @@ -671,9 +668,9 @@ public class MapleMap { if (Randomizer.nextInt(999999) < dropChance) { if (droptype == 3) { - pos.x = mobpos + ((d % 2 == 0) ? (40 * ((d + 1) / 2)) : -(40 * (d / 2))); + pos.x = mobpos + ((index % 2 == 0) ? (40 * ((index + 1) / 2)) : -(40 * (index / 2))); } else { - pos.x = mobpos + ((d % 2 == 0) ? (25 * ((d + 1) / 2)) : -(25 * (d / 2))); + pos.x = mobpos + ((index % 2 == 0) ? (25 * ((index + 1) / 2)) : -(25 * (index / 2))); } if (de.itemId == 0) { // meso int mesos = Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum; @@ -698,11 +695,11 @@ public class MapleMap { } spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay); } - d++; + index++; } } - return d; + return index; } private byte dropGlobalItemsFromMonsterOnMap(List globalEntry, Point pos, byte d, @@ -742,7 +739,6 @@ public class MapleMap { final byte droptype = (byte) (mob.getStats().isExplosiveReward() ? 3 : mob.getStats().isFfaLoot() ? 2 : chr.getParty() != null ? 1 : 0); final int mobpos = mob.getPosition().x; int chRate = !mob.isBoss() ? chr.getDropRate() : chr.getBossDropRate(); - byte d = 1; Point pos = new Point(0, mob.getPosition().y); MonsterStatusEffect stati = mob.getStati(MonsterStatus.SHOWDOWN); @@ -768,8 +764,17 @@ public class MapleMap { return; } - registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, - chr, mob, delay); + + byte index = 1; + // Normal Drops + index = dropItemsFromMonsterOnMap(dropEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay); + + // Global Drops + index = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, index, droptype, mobpos, chr, mob, delay); + + // Quest Drops + index = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay); + dropItemsFromMonsterOnMap(otherQuestEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay); } public void dropItemsFromMonster(List list, final Character chr, final Monster mob, short delay) { @@ -802,11 +807,6 @@ public class MapleMap { expireItemsTask.cancel(false); expireItemsTask = null; - if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { - mobSpawnLootTask.cancel(false); - mobSpawnLootTask = null; - } - characterStatUpdateTask.cancel(false); characterStatUpdateTask = null; } @@ -863,17 +863,6 @@ public class MapleMap { expireItemsTask = TimerManager.getInstance().register(() -> makeDisappearExpiredItemDrops(), YamlConfig.config.server.ITEM_EXPIRE_CHECK, YamlConfig.config.server.ITEM_EXPIRE_CHECK); - if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { - lootLock.lock(); - try { - mobLootEntries.clear(); - } finally { - lootLock.unlock(); - } - - mobSpawnLootTask = TimerManager.getInstance().register(() -> spawnMobItemDrops(), 200, 200); - } - characterStatUpdateTask = TimerManager.getInstance().register(() -> runCharacterStatUpdate(), 200, 200); itemMonitorTimeout = 1; @@ -970,67 +959,6 @@ public class MapleMap { } } - private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, - List dropEntry, List visibleQuestEntry, - List otherQuestEntry, List globalEntry, - Character chr, Monster mob, short delay) { - MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, delay, dropEntry, visibleQuestEntry, - otherQuestEntry, globalEntry, chr, mob); - - if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { - int animationTime = mob.getAnimationTime("die1"); - - lootLock.lock(); - try { - long timeNow = Server.getInstance().getCurrentTime(); - mobLootEntries.put(mle, timeNow + ((long) (0.42 * animationTime))); - } finally { - lootLock.unlock(); - } - } else { - mle.run(); - } - } - - private void spawnMobItemDrops() { - Set> mleList; - - lootLock.lock(); - try { - mleList = new HashSet<>(mobLootEntries.entrySet()); - } finally { - lootLock.unlock(); - } - - long timeNow = Server.getInstance().getCurrentTime(); - List toRemove = new LinkedList<>(); - for (Entry mlee : mleList) { - if (mlee.getValue() < timeNow) { - toRemove.add(mlee.getKey()); - } - } - - if (!toRemove.isEmpty()) { - List toSpawnLoot = new LinkedList<>(); - - lootLock.lock(); - try { - for (MobLootEntry mle : toRemove) { - Long mler = mobLootEntries.remove(mle); - if (mler != null) { - toSpawnLoot.add(mle); - } - } - } finally { - lootLock.unlock(); - } - - for (MobLootEntry mle : toSpawnLoot) { - mle.run(); - } - } - } - private List getDroppedItems() { objectRLock.lock(); try { @@ -4450,11 +4378,6 @@ public class MapleMap { expireItemsTask = null; } - if (mobSpawnLootTask != null) { - mobSpawnLootTask.cancel(false); - mobSpawnLootTask = null; - } - if (characterStatUpdateTask != null) { characterStatUpdateTask.cancel(false); characterStatUpdateTask = null; From e30700de66a6c3ce46189c43ae04e04526b2f0a5 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 17 Aug 2024 21:27:43 +0200 Subject: [PATCH 51/55] Fix drop delay from summon attack --- .../channel/handlers/SummonDamageHandler.java | 72 ++++++++----------- src/main/java/tools/PacketCreator.java | 12 ++-- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java index 82045ac142..b22e473b75 100644 --- a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java @@ -41,31 +41,14 @@ import server.life.MonsterInformationProvider; import server.maps.Summon; import tools.PacketCreator; +import java.awt.*; import java.util.ArrayList; import java.util.List; public final class SummonDamageHandler extends AbstractDealDamageHandler { private static final Logger log = LoggerFactory.getLogger(SummonDamageHandler.class); - public final class SummonAttackEntry { - - private final int monsterOid; - private final int damage; - - public SummonAttackEntry(int monsterOid, int damage) { - this.monsterOid = monsterOid; - this.damage = damage; - } - - public int getMonsterOid() { - return monsterOid; - } - - public int getDamage() { - return damage; - } - - } + public record SummonAttackTarget(int monsterOid, int damage, short delay) {} @Override public void handlePacket(InPacket p, Client c) { @@ -86,17 +69,21 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { Skill summonSkill = SkillFactory.getSkill(summon.getSkill()); StatEffect summonEffect = summonSkill.getEffect(summon.getSkillLevel()); p.skip(4); - List allDamage = new ArrayList<>(); + List targets = new ArrayList<>(); byte direction = p.readByte(); int numAttacked = p.readByte(); p.skip(8); // I failed lol (mob x,y and summon x,y), Thanks Gerald for (int x = 0; x < numAttacked; x++) { int monsterOid = p.readInt(); // attacked oid - p.skip(18); // TODO: find "delay" among these 18 skipped bytes + p.skip(8); + Point curPos = p.readPos(); + Point nextPos = p.readPos(); + short delay = p.readShort(); int damage = p.readInt(); - allDamage.add(new SummonAttackEntry(monsterOid, damage)); + targets.add(new SummonAttackTarget(monsterOid, damage, delay)); } - player.getMap().broadcastMessage(player, PacketCreator.summonAttack(player.getId(), summon.getObjectId(), direction, allDamage), summon.getPosition()); + player.getMap().broadcastMessage(player, PacketCreator.summonAttack(player.getId(), summon.getObjectId(), + direction, targets), summon.getPosition()); if (player.getMap().isOwnershipRestricted(player)) { return; @@ -104,25 +91,28 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { boolean magic = summonEffect.getWatk() == 0; int maxDmg = calcMaxDamage(summonEffect, player, magic); // thanks Darter (YungMoozi) for reporting unchecked max dmg - for (SummonAttackEntry attackEntry : allDamage) { - int damage = attackEntry.getDamage(); - Monster target = player.getMap().getMonsterByOid(attackEntry.getMonsterOid()); - if (target != null) { - if (damage > maxDmg) { - AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit."); - final String mobName = MonsterInformationProvider.getInstance().getMobNameFromId(target.getId()); - log.info("Possible exploit - chr {} used a summon of skillId {} to attack {} with damage {} (max: {})", - c.getPlayer().getName(), summon.getSkill(), mobName, damage, maxDmg); - damage = maxDmg; - } - - if (damage > 0 && summonEffect.getMonsterStati().size() > 0) { - if (summonEffect.makeChanceResult()) { - target.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000); - } - } - player.getMap().damageMonster(player, target, damage); + for (SummonAttackTarget target : targets) { + int damage = target.damage(); + Monster mob = player.getMap().getMonsterByOid(target.monsterOid()); + if (mob == null) { + continue; } + + if (damage > maxDmg) { + AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit."); + final String mobName = MonsterInformationProvider.getInstance().getMobNameFromId(mob.getId()); + log.info("Possible exploit - chr {} used a summon of skillId {} to attack {} with damage {} (max: {})", + c.getPlayer().getName(), summon.getSkill(), mobName, damage, maxDmg); + damage = maxDmg; + } + + if (damage > 0 && summonEffect.getMonsterStati().size() > 0) { + if (summonEffect.makeChanceResult()) { + mob.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000); + } + } + player.getMap().damageMonster(player, mob, damage, target.delay()); + } if (summon.getSkill() == Outlaw.GAVIOTA) { // thanks Periwinks for noticing Gaviota not cancelling after grenade toss diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index 3b328fe1b0..c302cfdb29 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -70,7 +70,7 @@ import net.server.Server; import net.server.channel.Channel; import net.server.channel.handlers.AbstractDealDamageHandler.AttackTarget; import net.server.channel.handlers.PlayerInteractionHandler; -import net.server.channel.handlers.SummonDamageHandler.SummonAttackEntry; +import net.server.channel.handlers.SummonDamageHandler.SummonAttackTarget; import net.server.channel.handlers.WhisperHandler; import net.server.guild.Alliance; import net.server.guild.Guild; @@ -2305,18 +2305,18 @@ public class PacketCreator { return p; } - public static Packet summonAttack(int cid, int summonOid, byte direction, List allDamage) { + public static Packet summonAttack(int cid, int summonOid, byte direction, List targets) { OutPacket p = OutPacket.create(SendOpcode.SUMMON_ATTACK); //b2 00 29 f7 00 00 9a a3 04 00 c8 04 01 94 a3 04 00 06 ff 2b 00 p.writeInt(cid); p.writeInt(summonOid); p.writeByte(0); // char level p.writeByte(direction); - p.writeByte(allDamage.size()); - for (SummonAttackEntry attackEntry : allDamage) { - p.writeInt(attackEntry.getMonsterOid()); // oid + p.writeByte(targets.size()); + for (SummonAttackTarget target : targets) { + p.writeInt(target.monsterOid()); // oid p.writeByte(6); // who knows - p.writeInt(attackEntry.getDamage()); // damage + p.writeInt(target.damage()); // damage } return p; From 439753eb6de6ee238886e5b1f1b3cf3084585159 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 20 Aug 2024 19:44:16 +0200 Subject: [PATCH 52/55] Fix drop delay for Meso Explosion --- .../handlers/AbstractDealDamageHandler.java | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index 3ff31dcce6..032b3e29dd 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -121,6 +121,8 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { public boolean ranged, magic; public int speed = 4; public Point position = new Point(); + public List explodedMesos; + public Short attackDelay; public StatEffect getAttackEffect(Character chr, Skill theSkill) { Skill mySkill = theSkill; @@ -629,42 +631,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { ret.direction = p.readByte(); ret.stance = p.readByte(); if (ret.skill == ChiefBandit.MESO_EXPLOSION) { - if (ret.numAttackedAndDamage == 0) { - p.skip(10); - int bullets = p.readByte(); - for (int j = 0; j < bullets; j++) { - int mesoid = p.readInt(); - p.skip(1); - ret.targets.put(mesoid, null); - } - return ret; - } else { - p.skip(6); - } - for (int i = 0; i < ret.numAttacked + 1; i++) { - int oid = p.readInt(); - if (i < ret.numAttacked) { - // TODO: read delay in from these skipped bytes - p.skip(12); - int bullets = p.readByte(); - List allDamageNumbers = new ArrayList<>(); - for (int j = 0; j < bullets; j++) { - int damage = p.readInt(); - allDamageNumbers.add(damage); - } - ret.targets.put(oid, new AttackTarget((short) 0, allDamageNumbers)); - p.skip(4); - } else { - int bullets = p.readByte(); - for (int j = 0; j < bullets; j++) { - int mesoid = p.readInt(); - p.skip(1); - ret.targets.put(mesoid, null); - } - } - } - return ret; + return parseMesoExplosion(p, ret); } + if (ranged) { p.readByte(); ret.speed = p.readByte(); @@ -963,4 +932,43 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } return ret; } + + private AttackInfo parseMesoExplosion(InPacket p, AttackInfo attackInfo) { + p.skip(6); + + Map> targetDamage = new HashMap<>(); + for (int i = 0; i < attackInfo.numAttacked; i++) { + int mobOid = p.readInt(); + p.skip(4); + Point curPos = p.readPos(); + Point nextPos = p.readPos(); + int damageLines = p.readByte(); + List allDamageNumbers = new ArrayList<>(); + for (int j = 0; j < damageLines; j++) { + int damage = p.readInt(); + allDamageNumbers.add(damage); + } + p.skip(4); + targetDamage.put(mobOid, allDamageNumbers); + } + + p.skip(4); + + List explodedMesos = new ArrayList<>(); + int explodedMesoCount = p.readByte(); + for (int j = 0; j < explodedMesoCount; j++) { + int mesoOid = p.readInt(); + p.skip(1); + explodedMesos.add(mesoOid); + } + attackInfo.explodedMesos = explodedMesos; + + final short attackDelay = p.readShort(); + attackInfo.attackDelay = attackDelay; + + Map targets = new HashMap<>(); + targetDamage.forEach((id, damage) -> targets.put(id, new AttackTarget(attackDelay, damage))); + attackInfo.targets = targets; + return attackInfo; + } } From cdd1c8cb619a17d63b5dccd04920e397f901dc93 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 20 Aug 2024 20:10:32 +0200 Subject: [PATCH 53/55] Proper timing on removing exploded meso No longer using scheduling on server side but rather a delay value inherent to the "remove item from map" packet. --- .../handlers/AbstractDealDamageHandler.java | 65 ++++++++----------- src/main/java/tools/PacketCreator.java | 9 ++- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index 032b3e29dd..793a6199b2 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -96,7 +96,6 @@ import server.life.MonsterDropEntry; import server.life.MonsterInformationProvider; import server.maps.MapItem; import server.maps.MapObject; -import server.maps.MapObjectType; import server.maps.MapleMap; import tools.PacketCreator; import tools.Randomizer; @@ -212,48 +211,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { return; } - //WTF IS THIS F3,1 - /*if (attackCount != attack.numDamage && attack.skill != ChiefBandit.MESO_EXPLOSION && attack.skill != NightWalker.VAMPIRE && attack.skill != WindArcher.WIND_SHOT && attack.skill != Aran.COMBO_SMASH && attack.skill != Aran.COMBO_FENRIR && attack.skill != Aran.COMBO_TEMPEST && attack.skill != NightLord.NINJA_AMBUSH && attack.skill != Shadower.NINJA_AMBUSH) { - return; - }*/ - int totDamage = 0; if (attack.skill == ChiefBandit.MESO_EXPLOSION) { - int delay = 0; - for (Integer oned : attack.targets.keySet()) { - MapObject mapobject = map.getMapObject(oned); - if (mapobject != null && mapobject.getType() == MapObjectType.ITEM) { - final MapItem mapitem = (MapItem) mapobject; - if (mapitem.getMeso() == 0) { //Maybe it is possible some how? - return; - } - - mapitem.lockItem(); - try { - if (mapitem.isPickedUp()) { - return; - } - TimerManager.getInstance().schedule(() -> { - mapitem.lockItem(); - try { - if (mapitem.isPickedUp()) { - return; - } - map.pickItemDrop(PacketCreator.removeItemFromMap(mapitem.getObjectId(), 4, 0), mapitem); - } finally { - mapitem.unlockItem(); - } - }, delay); - delay += 100; - } finally { - mapitem.unlockItem(); - } - } else if (mapobject != null && mapobject.getType() != MapObjectType.MONSTER) { - return; - } - } + removeExplodedMesos(map, attack); } + for (Map.Entry target : attack.targets.entrySet()) { final Monster monster = map.getMonsterByOid(target.getKey()); if (monster != null) { @@ -971,4 +934,28 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { attackInfo.targets = targets; return attackInfo; } + + private void removeExplodedMesos(MapleMap map, AttackInfo attack) { + short delay = attack.attackDelay; + for (Integer mesoId : attack.explodedMesos) { + MapObject mapobject = map.getMapObject(mesoId); + if (!(mapobject instanceof MapItem mapItem)) { + return; + } + if (mapItem.getMeso() == 0) { + return; + } + + mapItem.lockItem(); + try { + if (mapItem.isPickedUp()) { + return; + } + map.pickItemDrop(PacketCreator.removeExplodedMesoFromMap(mapItem.getObjectId(), delay), mapItem); + } finally { + mapItem.unlockItem(); + } + delay += 100; + } + } } diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index c302cfdb29..e7ff75486e 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -2595,7 +2595,6 @@ public class PacketCreator { * @param slot * @return */ - // TODO: look for a "delay" in case animation = 4 (explode). Doesn't make sense for it to be server-sided. public static Packet removeItemFromMap(int objId, int animation, int chrId, boolean pet, int slot) { OutPacket p = OutPacket.create(SendOpcode.REMOVE_ITEM_FROM_MAP); p.writeByte(animation); // expire @@ -2609,6 +2608,14 @@ public class PacketCreator { return p; } + public static Packet removeExplodedMesoFromMap(int mapObjectId, short delay) { + OutPacket p = OutPacket.create(SendOpcode.REMOVE_ITEM_FROM_MAP); + p.writeByte(4); + p.writeInt(mapObjectId); + p.writeShort(delay); + return p; + } + public static Packet updateCharLook(Client target, Character chr) { OutPacket p = OutPacket.create(SendOpcode.UPDATE_CHAR_LOOK); p.writeInt(chr.getId()); From acac203e42cc0e8315bd5c9d237fcee036dbf539 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 20 Aug 2024 20:32:33 +0200 Subject: [PATCH 54/55] Scheduler free timing for reactor drops --- .../reactor/ReactorActionManager.java | 33 +++++++------------ src/main/java/server/maps/MapleMap.java | 5 +-- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/java/scripting/reactor/ReactorActionManager.java b/src/main/java/scripting/reactor/ReactorActionManager.java index b5a9a50cd0..dd82320832 100644 --- a/src/main/java/scripting/reactor/ReactorActionManager.java +++ b/src/main/java/scripting/reactor/ReactorActionManager.java @@ -33,6 +33,7 @@ import server.TimerManager; import server.life.LifeFactory; import server.life.Monster; import server.maps.MapMonitor; +import server.maps.MapleMap; import server.maps.Reactor; import server.maps.ReactorDropEntry; import server.partyquest.CarnivalFactory; @@ -43,7 +44,6 @@ import java.awt.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.ScheduledFuture; /** * @author Lerk @@ -52,7 +52,6 @@ import java.util.concurrent.ScheduledFuture; public class ReactorActionManager extends AbstractPlayerInteraction { private final Reactor reactor; private final Invocable iv; - private ScheduledFuture sprayTask = null; public ReactorActionManager(Client c, Reactor reactor, Invocable iv) { super(c); @@ -183,33 +182,24 @@ public class ReactorActionManager extends AbstractPlayerInteraction { drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId)); } - reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid); + reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid, (short) 0); } } } else { - final Reactor r = reactor; - final List dropItems = items; final int worldMesoRate = c.getWorldServer().getMesoRate(); dropPos.x -= (12 * items.size()); - - // TODO: simply use "delay" drop packet instead of this scheduled task - sprayTask = TimerManager.getInstance().register(() -> { - if (dropItems.isEmpty()) { - sprayTask.cancel(false); - return; - } - - ReactorDropEntry d = dropItems.remove(0); + short delay = 0; + for (ReactorDropEntry d : items) { if (d.itemId == 0) { int range = maxMeso - minMeso; int displayDrop = (int) (Math.random() * range) + minMeso; - int mesoDrop = (displayDrop * worldMesoRate); - r.getMap().spawnMesoDrop(mesoDrop, r.getMap().calcDropPos(dropPos, r.getPosition()), r, chr, - false, (byte) 2, (short) 0); + int mesoDrop = displayDrop * worldMesoRate; + MapleMap map = reactor.getMap(); + map.spawnMesoDrop(mesoDrop, map.calcDropPos(dropPos, reactor.getPosition()), reactor, chr, + false, (byte) 2, delay); } else { - Item drop; - + final Item drop; if (ItemConstants.getInventoryType(d.itemId) != InventoryType.EQUIP) { drop = new Item(d.itemId, (short) 0, (short) 1); } else { @@ -217,11 +207,12 @@ public class ReactorActionManager extends AbstractPlayerInteraction { drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId)); } - r.getMap().dropFromReactor(getPlayer(), r, drop, dropPos, (short) d.questid); + reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid, delay); } dropPos.x += 25; - }, 200); + delay += 200; + } } } diff --git a/src/main/java/server/maps/MapleMap.java b/src/main/java/server/maps/MapleMap.java index fc421c1a49..d07fbddd66 100644 --- a/src/main/java/server/maps/MapleMap.java +++ b/src/main/java/server/maps/MapleMap.java @@ -795,9 +795,10 @@ public class MapleMap { dropFromMonster(chr, mob, true, (short) 0); } - public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid) { + public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid, + short delay) { spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr, - (byte) (chr.getParty() != null ? 1 : 0), questid, (short) 0); + (byte) (chr.getParty() != null ? 1 : 0), questid, delay); } private void stopItemMonitor() { From 5064ee936a854eb94b30ddf204111112577b4b6b Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 27 Aug 2024 07:41:24 +0200 Subject: [PATCH 55/55] Fix staggered exploding mesos Thanks teto: "the staggering should only go up to 400ms [attack.attackDelay + (index++ % 5) * 100], and the total delay should be capped at 1000ms according to the BMS implementation" --- .../channel/handlers/AbstractDealDamageHandler.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index 793a6199b2..01161a01ea 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -112,6 +112,8 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { + private static final int EXPLODED_MESO_SPREAD_DELAY = 100; + private static final int EXPLODED_MESO_MAX_DELAY = 1000; public static class AttackInfo { @@ -936,7 +938,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { } private void removeExplodedMesos(MapleMap map, AttackInfo attack) { - short delay = attack.attackDelay; + int index = 0; for (Integer mesoId : attack.explodedMesos) { MapObject mapobject = map.getMapObject(mesoId); if (!(mapobject instanceof MapItem mapItem)) { @@ -951,11 +953,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { if (mapItem.isPickedUp()) { return; } - map.pickItemDrop(PacketCreator.removeExplodedMesoFromMap(mapItem.getObjectId(), delay), mapItem); + int delay = attack.attackDelay + (index++ % 5) * EXPLODED_MESO_SPREAD_DELAY; + delay = Math.min(delay, EXPLODED_MESO_MAX_DELAY); + map.pickItemDrop(PacketCreator.removeExplodedMesoFromMap(mapItem.getObjectId(), (short) delay), mapItem); } finally { mapItem.unlockItem(); } - delay += 100; } } }