From 0d684c1400a90b1ce261d0309d7cd0bdaf758f18 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Sat, 2 Mar 2024 02:19:59 +0800 Subject: [PATCH 01/10] 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 eed47a9064fb9450bda495f4c8ba42a27dfacd9a Mon Sep 17 00:00:00 2001 From: remsus Date: Tue, 11 Jun 2024 18:44:47 +0200 Subject: [PATCH 02/10] 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 03/10] 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 04/10] 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 05/10] 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 06/10] 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 07/10] 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 08/10] 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 09/10] 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 10/10] 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;