diff --git a/src/main/java/client/command/commands/gm3/ReloadDropsCommand.java b/src/main/java/client/command/commands/gm3/ReloadDropsCommand.java index 05f6d1afc2..ee23bf4fac 100644 --- a/src/main/java/client/command/commands/gm3/ReloadDropsCommand.java +++ b/src/main/java/client/command/commands/gm3/ReloadDropsCommand.java @@ -27,7 +27,6 @@ import client.Character; import client.Client; import client.command.Command; import client.command.CommandContext; -import server.life.MonsterInformationProvider; public class ReloadDropsCommand extends Command { { @@ -37,7 +36,7 @@ public class ReloadDropsCommand extends Command { @Override public void execute(Client c, String[] params, CommandContext ctx) { Character player = c.getPlayer(); - MonsterInformationProvider.getInstance().clearDrops(); + ctx.dropProvider().clearCaches(); player.dropMessage(5, "Reloaded Drops"); } } diff --git a/src/main/java/database/drop/DropProvider.java b/src/main/java/database/drop/DropProvider.java index 0860548ea4..8cb05fac95 100644 --- a/src/main/java/database/drop/DropProvider.java +++ b/src/main/java/database/drop/DropProvider.java @@ -103,4 +103,10 @@ public class DropProvider { ItemInformationProvider ii = ItemInformationProvider.getInstance(); return !ii.isQuestItem(drop.itemId()) && !ii.isPartyQuestItem(drop.itemId()); } + + public void clearCaches() { + this.monsterDropCache.invalidateAll(); + this.globalContinentDropCache.invalidateAll(); + this.globalMonsterDrops = null; + } } diff --git a/src/main/java/server/life/MonsterDropEntry.java b/src/main/java/server/life/MonsterDropEntry.java index 44a6843f48..1f22400d39 100644 --- a/src/main/java/server/life/MonsterDropEntry.java +++ b/src/main/java/server/life/MonsterDropEntry.java @@ -23,7 +23,7 @@ package server.life; /** * @author LightPepsi */ - +// TODO: replace this with MonsterDrop everywhere, which is immutable and therefore threadsafe public class MonsterDropEntry { public MonsterDropEntry(int itemId, int chance, int Minimum, int Maximum, short questid) { this.itemId = itemId; @@ -35,4 +35,4 @@ public class MonsterDropEntry { public short questid; public int itemId, chance, Minimum, Maximum; -} \ No newline at end of file +} diff --git a/src/main/java/server/life/MonsterGlobalDropEntry.java b/src/main/java/server/life/MonsterGlobalDropEntry.java index 383ded6958..a9d487cc64 100644 --- a/src/main/java/server/life/MonsterGlobalDropEntry.java +++ b/src/main/java/server/life/MonsterGlobalDropEntry.java @@ -23,6 +23,7 @@ package server.life; /** * @author LightPepsi */ +// TODO: replace this with MonsterGlobalDrop, which is immutable and therefore threadsafe public class MonsterGlobalDropEntry { public MonsterGlobalDropEntry(int itemId, int chance, int continent, int Minimum, int Maximum, short questid) { this.itemId = itemId; diff --git a/src/main/java/server/life/MonsterInformationProvider.java b/src/main/java/server/life/MonsterInformationProvider.java index 446f53d724..75511062f5 100644 --- a/src/main/java/server/life/MonsterInformationProvider.java +++ b/src/main/java/server/life/MonsterInformationProvider.java @@ -27,13 +27,8 @@ import provider.DataProvider; import provider.DataProviderFactory; import provider.DataTool; import provider.wz.WZFiles; -import tools.DatabaseConnection; import tools.Pair; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.*; public class MonsterInformationProvider { @@ -46,9 +41,6 @@ public class MonsterInformationProvider { return instance; } - private final List globaldrops = new ArrayList<>(); - private final Map> continentdrops = new HashMap<>(); - private final Map, Integer> mobAttackAnimationTime = new HashMap<>(); private final Map mobSkillAnimationTime = new HashMap<>(); @@ -57,45 +49,7 @@ public class MonsterInformationProvider { private final Map mobBossCache = new HashMap<>(); private final Map mobNameCache = new HashMap<>(); - protected MonsterInformationProvider() { - retrieveGlobal(); - } - - public final List getRelevantGlobalDrops(int mapid) { - int continentid = mapid / 100000000; - - List contiItems = continentdrops.get(continentid); - if (contiItems == null) { // continent separated global drops found thanks to marcuswoon - contiItems = new ArrayList<>(); - - for (MonsterGlobalDropEntry e : globaldrops) { - if (e.continentid < 0 || e.continentid == continentid) { - contiItems.add(e); - } - } - - continentdrops.put(continentid, contiItems); - } - - return contiItems; - } - - private void retrieveGlobal() { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM drop_data_global WHERE chance > 0"); - ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - globaldrops.add(new MonsterGlobalDropEntry( - rs.getInt("itemid"), - rs.getInt("chance"), - rs.getByte("continent"), - rs.getInt("minimum_quantity"), - rs.getInt("maximum_quantity"), - rs.getShort("questid"))); - } - } catch (SQLException e) { - log.error("Error retrieving global drops", e); - } + private MonsterInformationProvider() { } public final void setMobAttackAnimationTime(int monsterId, int attackPos, int animationTime) { @@ -176,10 +130,4 @@ public class MonsterInformationProvider { return mobName; } - - public final void clearDrops() { - globaldrops.clear(); - continentdrops.clear(); - retrieveGlobal(); - } } diff --git a/src/test/java/database/drop/DropProviderTest.java b/src/test/java/database/drop/DropProviderTest.java index d8b3d180aa..704218fdee 100644 --- a/src/test/java/database/drop/DropProviderTest.java +++ b/src/test/java/database/drop/DropProviderTest.java @@ -127,6 +127,39 @@ class DropProviderTest { assertTrue(dropEntries.isEmpty()); } + @Test + void clearCaches_shouldClearMonsterDrops() { + MonsterDrop drop = snailShellDrop(); + when(dropDao.getMonsterDrops(anyInt())).thenReturn(List.of(drop)); + int monsterId = 100100; + + List drops1 = dropProvider.getMonsterDropEntries(monsterId); + dropProvider.clearCaches(); + List drops2 = dropProvider.getMonsterDropEntries(monsterId); + + verify(dropDao, times(2)).getMonsterDrops(anyInt()); + assertEquals(1, drops1.size()); + assertEquals(1, drops2.size()); + } + + @Test + void clearCaches_shouldClearGlobalDrops() { + when(dropDao.getGlobalMonsterDrops()).thenReturn(List.of(globalDrop())); + int mapId = 100_000_123; + + List drops1 = dropProvider.getRelevantGlobalDrops(mapId); + dropProvider.clearCaches(); + List drops2 = dropProvider.getRelevantGlobalDrops(mapId); + + verify(dropDao, times(2)).getGlobalMonsterDrops(); + assertEquals(1, drops1.size()); + assertEquals(1, drops2.size()); + } + + private GlobalMonsterDrop globalDrop() { + return new GlobalMonsterDrop(2049100, -1, 1, 1, null, 150); + } + // 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. }