From 703ae30a272eaaf232c26e0d319249fe57790d7d Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 15 Mar 2023 22:22:14 +0100 Subject: [PATCH] Move "steal item" logic to DropProvider --- src/main/java/database/drop/DropProvider.java | 41 ++++++++++++++++++- src/main/java/net/ChannelDependencies.java | 5 ++- src/main/java/net/PacketProcessor.java | 10 ++--- src/main/java/net/server/Server.java | 5 ++- .../handlers/AbstractDealDamageHandler.java | 27 +++++------- .../handlers/CloseRangeDamageHandler.java | 7 +++- .../channel/handlers/MagicDamageHandler.java | 5 +++ .../channel/handlers/RangedAttackHandler.java | 7 +++- .../channel/handlers/SummonDamageHandler.java | 5 +++ .../handlers/TouchMonsterDamageHandler.java | 6 +++ .../java/database/drop/DropProviderTest.java | 2 + 11 files changed, 94 insertions(+), 26 deletions(-) diff --git a/src/main/java/database/drop/DropProvider.java b/src/main/java/database/drop/DropProvider.java index 1cc76c019a..c43e383c98 100644 --- a/src/main/java/database/drop/DropProvider.java +++ b/src/main/java/database/drop/DropProvider.java @@ -2,10 +2,12 @@ package database.drop; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import server.ItemInformationProvider; import server.life.MonsterDropEntry; import server.life.MonsterGlobalDropEntry; import java.util.List; +import java.util.Optional; public class DropProvider { private final DropDao dropDao; @@ -19,8 +21,12 @@ public class DropProvider { this.dropDao = dropDao; } + private List getMonsterDrops(int monsterId) { + return monsterDropCache.get(monsterId, dropDao::getMonsterDrops); + } + public List getMonsterDropEntries(int monsterId) { - return monsterDropCache.get(monsterId, dropDao::getMonsterDrops).stream() + return getMonsterDrops(monsterId).stream() .map(this::mapToDropEntry) .toList(); } @@ -52,4 +58,37 @@ public class DropProvider { return new MonsterGlobalDropEntry(globalDrop.itemId(), globalDrop.chance(), globalDrop.continent(), globalDrop.minQuantity(), globalDrop.maxQuantity(), questId); } + + /** + * The chance of an item to be stolen is calculated like this: (item chance) / (sum of all item chances) + * It works just like "lottery scheduling", but with droppable items instead of OS processes. + */ + public Optional getRandomStealDrop(int monsterId) { + List relevantDrops = getMonsterDrops(monsterId).stream() + .filter(this::isNonQuestItem) + .toList(); + if (relevantDrops.isEmpty()) { + return Optional.empty(); + } + + final long totalChance = relevantDrops.stream() + .mapToLong(MonsterDrop::chance) + .sum(); + final long winningTicket = (long) Math.floor(Math.random() * totalChance); + + long remainingChance = totalChance; + for (MonsterDrop drop : relevantDrops) { + remainingChance -= drop.chance(); + if (winningTicket >= remainingChance) { + return Optional.of(mapToDropEntry(drop)); + } + } + + return Optional.empty(); + } + + private boolean isNonQuestItem(MonsterDrop drop) { + ItemInformationProvider ii = ItemInformationProvider.getInstance(); + return !ii.isQuestItem(drop.itemId()) && !ii.isPartyQuestItem(drop.itemId()); + } } diff --git a/src/main/java/net/ChannelDependencies.java b/src/main/java/net/ChannelDependencies.java index 40f8bce69b..bbc71e71e1 100644 --- a/src/main/java/net/ChannelDependencies.java +++ b/src/main/java/net/ChannelDependencies.java @@ -2,17 +2,20 @@ package net; import client.processor.action.MakerProcessor; import client.processor.npc.FredrickProcessor; +import database.drop.DropProvider; import service.NoteService; import java.util.Objects; public record ChannelDependencies( - NoteService noteService, FredrickProcessor fredrickProcessor, MakerProcessor makerProcessor + NoteService noteService, FredrickProcessor fredrickProcessor, MakerProcessor makerProcessor, + DropProvider dropProvider ) { public ChannelDependencies { Objects.requireNonNull(noteService); Objects.requireNonNull(fredrickProcessor); Objects.requireNonNull(makerProcessor); + Objects.requireNonNull(dropProvider); } } diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index 44de836339..491096bc69 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -155,9 +155,9 @@ public final class PacketProcessor { registerHandler(RecvOpcode.PLAYER_LOGGEDIN, new PlayerLoggedinHandler(channelDeps.noteService())); registerHandler(RecvOpcode.CHANGE_MAP, new ChangeMapHandler()); registerHandler(RecvOpcode.MOVE_LIFE, new MoveLifeHandler()); - registerHandler(RecvOpcode.CLOSE_RANGE_ATTACK, new CloseRangeDamageHandler()); - registerHandler(RecvOpcode.RANGED_ATTACK, new RangedAttackHandler()); - registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler()); + registerHandler(RecvOpcode.CLOSE_RANGE_ATTACK, new CloseRangeDamageHandler(channelDeps.dropProvider())); + registerHandler(RecvOpcode.RANGED_ATTACK, new RangedAttackHandler(channelDeps.dropProvider())); + registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler(channelDeps.dropProvider())); registerHandler(RecvOpcode.TAKE_DAMAGE, new TakeDamageHandler()); registerHandler(RecvOpcode.MOVE_PLAYER, new MovePlayerHandler()); registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler(channelDeps.noteService())); @@ -189,7 +189,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.ENTER_CASHSHOP, new EnterCashShopHandler()); registerHandler(RecvOpcode.DAMAGE_SUMMON, new DamageSummonHandler()); registerHandler(RecvOpcode.MOVE_SUMMON, new MoveSummonHandler()); - registerHandler(RecvOpcode.SUMMON_ATTACK, new SummonDamageHandler()); + registerHandler(RecvOpcode.SUMMON_ATTACK, new SummonDamageHandler(channelDeps.dropProvider())); registerHandler(RecvOpcode.BUDDYLIST_MODIFY, new BuddylistModifyHandler()); registerHandler(RecvOpcode.USE_ITEMEFFECT, new UseItemEffectHandler()); registerHandler(RecvOpcode.USE_CHAIR, new UseChairHandler()); @@ -225,7 +225,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.PET_EXCLUDE_ITEMS, new PetExcludeItemsHandler()); registerHandler(RecvOpcode.OWL_ACTION, new UseOwlOfMinervaHandler()); registerHandler(RecvOpcode.OWL_WARP, new OwlWarpHandler()); - registerHandler(RecvOpcode.TOUCH_MONSTER_ATTACK, new TouchMonsterDamageHandler()); + registerHandler(RecvOpcode.TOUCH_MONSTER_ATTACK, new TouchMonsterDamageHandler(channelDeps.dropProvider())); registerHandler(RecvOpcode.TROCK_ADD_MAP, new TrockAddMapHandler()); registerHandler(RecvOpcode.HIRED_MERCHANT_REQUEST, new HiredMerchantRequest()); registerHandler(RecvOpcode.MOB_BANISH_PLAYER, new MobBanishPlayerHandler()); diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index d985bbc185..188505cf1d 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -42,6 +42,8 @@ import constants.net.OpcodeConstants; import constants.net.ServerConstants; import database.PgDatabaseConfig; import database.PgDatabaseConnection; +import database.drop.DropDao; +import database.drop.DropProvider; import database.maker.MakerDao; import database.maker.MakerInfoProvider; import database.migration.FlywayRunner; @@ -974,8 +976,9 @@ public class Server { NoteService noteService = new NoteService(new NoteDao(connection)); MakerProcessor makerProcessor = new MakerProcessor(new MakerInfoProvider(new MakerDao(connection))); FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService); + DropProvider dropProvider = new DropProvider(new DropDao(connection)); ChannelDependencies channelDependencies = new ChannelDependencies(noteService, fredrickProcessor, - makerProcessor); + makerProcessor, dropProvider); PacketProcessor.registerGameHandlerDependencies(channelDependencies); diff --git a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java index 62dac683ce..a6ebe0605b 100644 --- a/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -32,6 +32,7 @@ import constants.id.ItemId; import constants.id.MapId; import constants.id.MobId; import constants.skills.*; +import database.drop.DropProvider; import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.PlayerBuffValueHolder; @@ -54,6 +55,11 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { + private final DropProvider dropProvider; + + public AbstractDealDamageHandler(DropProvider dropProvider) { + this.dropProvider = dropProvider; + } public static class AttackInfo { @@ -284,25 +290,14 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler { player.addHP(Math.min(monster.getMaxHp(), Math.min((int) ((double) totDamage * (double) SkillFactory.getSkill(attack.skill).getEffect(player.getSkillLevel(SkillFactory.getSkill(attack.skill))).getX() / 100.0), player.getCurrentMaxHp() / 2))); } else if (attack.skill == Bandit.STEAL) { Skill steal = SkillFactory.getSkill(Bandit.STEAL); - if (monster.getStolen().size() < 1) { // One steal per mob <3 + if (monster.getStolen().isEmpty()) { // One steal per mob <3 if (steal.getEffect(player.getSkillLevel(steal)).makeChanceResult()) { monster.addStolen(0); - MonsterInformationProvider mi = MonsterInformationProvider.getInstance(); - List dropPool = mi.retrieveDropPool(monster.getId()); - if (!dropPool.isEmpty()) { - int rndPool = (int) Math.floor(Math.random() * dropPool.get(dropPool.size() - 1)); - - int i = 0; - while (rndPool >= dropPool.get(i)) { - i++; - } - - List toSteal = new ArrayList<>(); - toSteal.add(mi.retrieveDrop(monster.getId()).get(i)); - - map.dropItemsFromMonster(toSteal, player, monster); - monster.addStolen(toSteal.get(0).itemId); + Optional stolenItem = dropProvider.getRandomStealDrop(monster.getId()); + if (stolenItem.isPresent()) { + map.dropItemsFromMonster(Collections.singletonList(stolenItem.get()), player, monster); + monster.addStolen(stolenItem.get().itemId); } } } diff --git a/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java index 756675623b..f374c98794 100644 --- a/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java @@ -27,6 +27,7 @@ import config.YamlConfig; import constants.game.GameConstants; import constants.id.MapId; import constants.skills.*; +import database.drop.DropProvider; import net.packet.InPacket; import server.StatEffect; import tools.PacketCreator; @@ -40,6 +41,10 @@ import static java.util.concurrent.TimeUnit.SECONDS; public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { + public CloseRangeDamageHandler(DropProvider dropProvider) { + super(dropProvider); + } + @Override public final void handlePacket(InPacket p, Client c) { Character chr = c.getPlayer(); @@ -183,4 +188,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/MagicDamageHandler.java b/src/main/java/net/server/channel/handlers/MagicDamageHandler.java index 8b8201e863..714fb1cadb 100644 --- a/src/main/java/net/server/channel/handlers/MagicDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/MagicDamageHandler.java @@ -29,6 +29,7 @@ import constants.skills.Bishop; import constants.skills.Evan; import constants.skills.FPArchMage; import constants.skills.ILArchMage; +import database.drop.DropProvider; import net.packet.InPacket; import net.packet.Packet; import server.StatEffect; @@ -37,6 +38,10 @@ import tools.PacketCreator; import static java.util.concurrent.TimeUnit.SECONDS; public final class MagicDamageHandler extends AbstractDealDamageHandler { + + public MagicDamageHandler(DropProvider dropProvider) { + super(dropProvider); + } @Override public final void handlePacket(InPacket p, Client c) { Character chr = c.getPlayer(); diff --git a/src/main/java/net/server/channel/handlers/RangedAttackHandler.java b/src/main/java/net/server/channel/handlers/RangedAttackHandler.java index a8ea010361..9018383116 100644 --- a/src/main/java/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/main/java/net/server/channel/handlers/RangedAttackHandler.java @@ -33,6 +33,7 @@ import constants.id.ItemId; import constants.id.MapId; import constants.inventory.ItemConstants; import constants.skills.*; +import database.drop.DropProvider; import net.packet.InPacket; import net.packet.Packet; import org.slf4j.Logger; @@ -48,6 +49,10 @@ import static java.util.concurrent.TimeUnit.SECONDS; public final class RangedAttackHandler extends AbstractDealDamageHandler { private static final Logger log = LoggerFactory.getLogger(RangedAttackHandler.class); + public RangedAttackHandler(DropProvider dropProvider) { + super(dropProvider); + } + @Override public void handlePacket(InPacket p, Client c) { Character chr = c.getPlayer(); @@ -237,4 +242,4 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } } } -} \ No newline at end of file +} diff --git a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java index 5506818992..2609676b20 100644 --- a/src/main/java/net/server/channel/handlers/SummonDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/SummonDamageHandler.java @@ -31,6 +31,7 @@ import client.inventory.Item; import client.inventory.WeaponType; import client.status.MonsterStatusEffect; import constants.skills.Outlaw; +import database.drop.DropProvider; import net.packet.InPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +48,10 @@ import java.util.List; public final class SummonDamageHandler extends AbstractDealDamageHandler { private static final Logger log = LoggerFactory.getLogger(SummonDamageHandler.class); + public SummonDamageHandler(DropProvider dropProvider) { + super(dropProvider); + } + public final class SummonAttackEntry { private final int monsterOid; diff --git a/src/main/java/net/server/channel/handlers/TouchMonsterDamageHandler.java b/src/main/java/net/server/channel/handlers/TouchMonsterDamageHandler.java index 4dbda6159b..110e1af365 100644 --- a/src/main/java/net/server/channel/handlers/TouchMonsterDamageHandler.java +++ b/src/main/java/net/server/channel/handlers/TouchMonsterDamageHandler.java @@ -24,9 +24,15 @@ package net.server.channel.handlers; import client.BuffStat; import client.Character; import client.Client; +import database.drop.DropProvider; import net.packet.InPacket; public final class TouchMonsterDamageHandler extends AbstractDealDamageHandler { + + public TouchMonsterDamageHandler(DropProvider dropProvider) { + super(dropProvider); + } + @Override public final void handlePacket(InPacket p, Client c) { Character chr = c.getPlayer(); diff --git a/src/test/java/database/drop/DropProviderTest.java b/src/test/java/database/drop/DropProviderTest.java index 3456ba04a6..04e9fa6630 100644 --- a/src/test/java/database/drop/DropProviderTest.java +++ b/src/test/java/database/drop/DropProviderTest.java @@ -104,4 +104,6 @@ class DropProviderTest { verify(dropDao, times(1)).getGlobalMonsterDrops(); } + // TODO: add tests for getRandomStealDrop() once ItemInformationProvider is able to be mocked. + // Currently, it does database calls (and a bunch of other stuff) in the constructor, which is problematic. }