diff --git a/pom.xml b/pom.xml index 82b20119b..ae782333e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.2.000-BETA-01-SNAPSHOT + 2.2.000-BETA-02-SNAPSHOT mcMMO https://github.com/mcMMO-Dev/mcMMO diff --git a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java index 4a77ff1fc..dfc7c843c 100644 --- a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java @@ -233,7 +233,7 @@ public final class SQLDatabaseManager implements DatabaseManager { "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " + - "WHERE u.`USER` = ?"); + "WHERE u.`user` = ?"); statement.setString(1, playerName); @@ -402,7 +402,6 @@ public final class SQLDatabaseManager implements DatabaseManager { throw new InvalidSkillException("A plugin hooking into mcMMO that you are using is attempting to read leaderboard skills for child skills, child skills do not have leaderboards! This is NOT an mcMMO error!"); } - String query = skill == null ? ALL_QUERY_VERSION : skill.name().toLowerCase(Locale.ENGLISH); ResultSet resultSet = null; PreparedStatement statement = null; @@ -410,7 +409,7 @@ public final class SQLDatabaseManager implements DatabaseManager { try { connection = getConnection(PoolIdentifier.MISC); - statement = connection.prepareStatement("SELECT " + query + ", `USER` FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 AND NOT `USER` = '\\_INVALID\\_OLD\\_USERNAME\\_' ORDER BY " + query + " DESC, `USER` LIMIT ?, ?"); + statement = connection.prepareStatement("SELECT " + query + ", `user` FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 AND NOT `user` = '\\_INVALID\\_OLD\\_USERNAME\\_' ORDER BY " + query + " DESC, `user` LIMIT ?, ?"); statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage); statement.setInt(2, statsPerPage); resultSet = statement.executeQuery(); @@ -451,7 +450,7 @@ public final class SQLDatabaseManager implements DatabaseManager { // Get count of all users with higher skill level than player String sql = "SELECT COUNT(*) AS 'rank' FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " + "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE `USER` = ?)"; + "WHERE `user` = ?)"; statement = connection.prepareStatement(sql); statement.setString(1, playerName); @@ -464,7 +463,7 @@ public final class SQLDatabaseManager implements DatabaseManager { // Ties are settled by alphabetical order sql = "SELECT user, " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " + "AND " + skillName + " = (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE `USER` = '" + playerName + "') ORDER BY user"; + "WHERE `user` = '" + playerName + "') ORDER BY user"; resultSet.close(); statement.close(); @@ -487,7 +486,7 @@ public final class SQLDatabaseManager implements DatabaseManager { "WHERE " + ALL_QUERY_VERSION + " > 0 " + "AND " + ALL_QUERY_VERSION + " > " + "(SELECT " + ALL_QUERY_VERSION + " " + - "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE `USER` = ?)"; + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE `user` = ?)"; statement = connection.prepareStatement(sql); statement.setString(1, playerName); @@ -505,7 +504,7 @@ public final class SQLDatabaseManager implements DatabaseManager { "WHERE " + ALL_QUERY_VERSION + " > 0 " + "AND " + ALL_QUERY_VERSION + " = " + "(SELECT " + ALL_QUERY_VERSION + " " + - "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE `USER` = ?) ORDER BY user"; + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE `user` = ?) ORDER BY user"; statement = connection.prepareStatement(sql); statement.setString(1, playerName); @@ -573,8 +572,8 @@ public final class SQLDatabaseManager implements DatabaseManager { try { statement = connection.prepareStatement( "UPDATE `" + tablePrefix + "users` " - + "SET `USER` = ? " - + "WHERE `USER` = ?"); + + "SET `user` = ? " + + "WHERE `user` = ?"); statement.setString(1, "_INVALID_OLD_USERNAME_"); statement.setString(2, playerName); statement.executeUpdate(); @@ -632,7 +631,6 @@ public final class SQLDatabaseManager implements DatabaseManager { return loadPlayerFromDB(uuid, null); } - private PlayerProfile loadPlayerFromDB(@Nullable UUID uuid, @Nullable String playerName) throws RuntimeException { if(uuid == null && playerName == null) { throw new RuntimeException("Error looking up player, both UUID and playerName are null and one must not be."); @@ -655,22 +653,21 @@ public final class SQLDatabaseManager implements DatabaseManager { statement = connection.prepareStatement( "SELECT " + - "S.TAMING, S.MINING, S.REPAIR, S.WOODCUTTING, S.UNARMED, S.HERBALISM, S.EXCAVATION, S.ARCHERY, S.SWORDS, S.AXES, S.ACROBATICS, S.FISHING, S.ALCHEMY, S.CROSSBOWS, S.TRIDENTS, " + - "E.TAMING, E.MINING, E.REPAIR, E.WOODCUTTING, E.UNARMED, E.HERBALISM, E.EXCAVATION, E.ARCHERY, E.SWORDS, E.AXES, E.ACROBATICS, E.FISHING, E.ALCHEMY, E.CROSSBOWS, E.TRIDENTS, " + - "C.TAMING, C.MINING, C.REPAIR, C.WOODCUTTING, C.UNARMED, C.HERBALISM, C.EXCAVATION, C.ARCHERY, C.SWORDS, C.AXES, C.ACROBATICS, C.BLAST_MINING, C.CHIMAERA_WING, C.CROSSBOWS, C.TRIDENTS, " + - "H.MOBHEALTHBAR, H.SCOREBOARDTIPS, U.UUID, U.`USER` " + - "FROM " + tablePrefix + "USERS U " + - "JOIN " + tablePrefix + "SKILLS S ON U.ID = S.USER_ID " + - "JOIN " + tablePrefix + "EXPERIENCE E ON U.ID = E.USER_ID " + - "JOIN " + tablePrefix + "COOLDOWNS C ON U.ID = C.USER_ID " + - "JOIN " + tablePrefix + "HUDS H ON U.ID = H.USER_ID " + - "WHERE U.ID = ?" + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, s.crossbows, s.tridents, " + + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, e.crossbows, e.tridents, " + + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, c.chimaera_wing, c.crossbows, c.tridents, " + + "h.mobhealthbar, h.scoreboardtips, u.uuid, u.`user` " + + "FROM " + tablePrefix + "users u " + + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " + + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " + + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " + + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " + + "WHERE u.id = ?" ); statement.setInt(1, id); resultSet = statement.executeQuery(); - if (resultSet.next()) { try { PlayerProfile profile = loadFromResult(playerName, resultSet); @@ -684,15 +681,15 @@ public final class SQLDatabaseManager implements DatabaseManager { && uuid != null) { statement = connection.prepareStatement( "UPDATE `" + tablePrefix + "users` " - + "SET `USER` = ? " - + "WHERE `USER` = ?"); + + "SET `user` = ? " + + "WHERE `user` = ?"); statement.setString(1, "_INVALID_OLD_USERNAME_"); statement.setString(2, name); statement.executeUpdate(); statement.close(); statement = connection.prepareStatement( "UPDATE `" + tablePrefix + "users` " - + "SET `USER` = ?, uuid = ? " + + "SET `user` = ?, uuid = ? " + "WHERE id = ?"); statement.setString(1, playerName); statement.setString(2, uuid.toString()); @@ -740,7 +737,7 @@ public final class SQLDatabaseManager implements DatabaseManager { + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " - + "WHERE u.`USER` = ?"); + + "WHERE u.`user` = ?"); List usernames = getStoredUsers(); int convertedUsers = 0; long startMillis = System.currentTimeMillis(); @@ -779,7 +776,7 @@ public final class SQLDatabaseManager implements DatabaseManager { connection = getConnection(PoolIdentifier.MISC); statement = connection.prepareStatement( "UPDATE `" + tablePrefix + "users` SET " - + " uuid = ? WHERE `USER` = ?"); + + " uuid = ? WHERE `user` = ?"); statement.setString(1, uuid.toString()); statement.setString(2, userName); statement.execute(); @@ -803,7 +800,7 @@ public final class SQLDatabaseManager implements DatabaseManager { try { connection = getConnection(PoolIdentifier.MISC); - statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE `USER` = ?"); + statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE `user` = ?"); for (Map.Entry entry : fetchedUUIDs.entrySet()) { statement.setString(1, entry.getValue().toString()); @@ -845,7 +842,7 @@ public final class SQLDatabaseManager implements DatabaseManager { try { connection = getConnection(PoolIdentifier.MISC); statement = connection.createStatement(); - resultSet = statement.executeQuery("SELECT `USER` FROM " + tablePrefix + "users"); + resultSet = statement.executeQuery("SELECT `user` FROM " + tablePrefix + "users"); while (resultSet.next()) { users.add(resultSet.getString("user")); } @@ -1604,7 +1601,7 @@ public final class SQLDatabaseManager implements DatabaseManager { PreparedStatement statement = null; try { - statement = connection.prepareStatement("SELECT id, `USER` FROM " + tablePrefix + "users WHERE uuid = ? OR (uuid IS NULL AND `USER` = ?)"); + statement = connection.prepareStatement("SELECT id, `user` FROM " + tablePrefix + "users WHERE uuid = ? OR (uuid IS NULL AND `user` = ?)"); statement.setString(1, uuid.toString()); statement.setString(2, playerName); resultSet = statement.executeQuery(); @@ -1633,7 +1630,7 @@ public final class SQLDatabaseManager implements DatabaseManager { PreparedStatement statement = null; try { - statement = connection.prepareStatement("SELECT id, `USER` FROM " + tablePrefix + "users WHERE `USER` = ?"); + statement = connection.prepareStatement("SELECT id, `user` FROM " + tablePrefix + "users WHERE `user` = ?"); statement.setString(1, playerName); resultSet = statement.executeQuery(); @@ -1734,7 +1731,7 @@ public final class SQLDatabaseManager implements DatabaseManager { private String getUpdateUserInUsersTableSQLQuery() { return "ALTER TABLE\n" + " " + tablePrefix + "users\n" + - " CHANGE `USER` user\n" + + " CHANGE `user` user\n" + " " + USER_VARCHAR + "\n" + " CHARACTER SET utf8mb4\n" + " COLLATE utf8mb4_unicode_ci;"; diff --git a/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java b/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java index 78b1adef0..659a94fc1 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java +++ b/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java @@ -16,7 +16,7 @@ public abstract class Treasure { this.drop = drop; this.xp = xp; this.dropChance = dropChance; - this.dropProbability = Probability.ofPercent(dropChance / 100); + this.dropProbability = Probability.ofPercent(dropChance); this.dropLevel = dropLevel; } @@ -46,7 +46,7 @@ public abstract class Treasure { public void setDropChance(double dropChance) { this.dropChance = dropChance; - this.dropProbability = Probability.ofPercent(dropChance / 100); + this.dropProbability = Probability.ofPercent(dropChance); } public int getDropLevel() { diff --git a/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java b/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java index b487553f8..bc9120e12 100644 --- a/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java +++ b/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java @@ -2,6 +2,7 @@ package com.gmail.nossr50.skills.excavation; import com.gmail.nossr50.api.ItemSpawnReason; import com.gmail.nossr50.datatypes.experience.XPGainReason; +import com.gmail.nossr50.datatypes.experience.XPGainSource; import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType; @@ -16,9 +17,13 @@ import com.gmail.nossr50.util.skills.SkillUtils; import org.bukkit.Location; import org.bukkit.block.BlockState; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.VisibleForTesting; import java.util.List; +import static java.util.Objects.requireNonNull; + public class ExcavationManager extends SkillManager { public ExcavationManager(McMMOPlayer mcMMOPlayer) { super(mcMMOPlayer, PrimarySkillType.EXCAVATION); @@ -30,10 +35,9 @@ public class ExcavationManager extends SkillManager { * @param blockState The {@link BlockState} to check ability activation for */ public void excavationBlockCheck(BlockState blockState) { - int xp = Excavation.getBlockXP(blockState); - + requireNonNull(blockState, "excavationBlockCheck: blockState cannot be null"); if (Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.EXCAVATION_ARCHAEOLOGY)) { - List treasures = Excavation.getTreasures(blockState); + List treasures = getTreasures(blockState); if (!treasures.isEmpty()) { int skillLevel = getSkillLevel(); @@ -42,20 +46,31 @@ public class ExcavationManager extends SkillManager { for (ExcavationTreasure treasure : treasures) { if (skillLevel >= treasure.getDropLevel() && ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.EXCAVATION, getPlayer(), treasure.getDropProbability())) { - - //Spawn Vanilla XP orbs if a dice roll succeeds - if(ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.EXCAVATION, getPlayer(), getArchaelogyExperienceOrbChance())) { - Misc.spawnExperienceOrb(location, getExperienceOrbsReward()); - } - - xp += treasure.getXp(); - Misc.spawnItem(getPlayer(), location, treasure.getDrop(), ItemSpawnReason.EXCAVATION_TREASURE); + processExcavationBonusesOnBlock(blockState, treasure, location); } } } } + } - applyXpGain(xp, XPGainReason.PVE); + @VisibleForTesting + public List getTreasures(@NotNull BlockState blockState) { + requireNonNull(blockState, "blockState cannot be null"); + return Excavation.getTreasures(blockState); + } + + @VisibleForTesting + public void processExcavationBonusesOnBlock(BlockState blockState, ExcavationTreasure treasure, Location location) { + int xp = Excavation.getBlockXP(blockState); + + //Spawn Vanilla XP orbs if a dice roll succeeds + if(ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.EXCAVATION, getPlayer(), getArchaelogyExperienceOrbChance())) { + Misc.spawnExperienceOrb(location, getExperienceOrbsReward()); + } + + xp += treasure.getXp(); + Misc.spawnItem(getPlayer(), location, treasure.getDrop(), ItemSpawnReason.EXCAVATION_TREASURE); + applyXpGain(xp, XPGainReason.PVE, XPGainSource.SELF); } public int getExperienceOrbsReward() { diff --git a/src/test/java/com/gmail/nossr50/skills/excavation/ExcavationTest.java b/src/test/java/com/gmail/nossr50/skills/excavation/ExcavationTest.java new file mode 100644 index 000000000..631696f0a --- /dev/null +++ b/src/test/java/com/gmail/nossr50/skills/excavation/ExcavationTest.java @@ -0,0 +1,120 @@ +package com.gmail.nossr50.skills.excavation; + +import com.gmail.nossr50.MMOTestEnvironment; +import com.gmail.nossr50.api.exceptions.InvalidSkillException; +import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import com.gmail.nossr50.datatypes.skills.SubSkillType; +import com.gmail.nossr50.datatypes.treasure.ExcavationTreasure; +import com.gmail.nossr50.util.skills.RankUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +class ExcavationTest extends MMOTestEnvironment { + private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(ExcavationTest.class.getName()); + + + @BeforeEach + void setUp() throws InvalidSkillException { + mockBaseEnvironment(logger); + when(rankConfig.getSubSkillUnlockLevel(SubSkillType.EXCAVATION_ARCHAEOLOGY, 1)).thenReturn(1); + when(rankConfig.getSubSkillUnlockLevel(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER, 1)).thenReturn(1); + + // wire advanced config + + when(RankUtils.getRankUnlockLevel(SubSkillType.EXCAVATION_ARCHAEOLOGY, 1)).thenReturn(1); // needed? + when(RankUtils.getRankUnlockLevel(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER, 1)).thenReturn(1); // needed? + when(RankUtils.hasReachedRank(eq(1), any(Player.class), eq(SubSkillType.EXCAVATION_ARCHAEOLOGY))).thenReturn(true); + when(RankUtils.hasReachedRank(eq(1), any(Player.class), eq(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER))).thenReturn(true); + + // setup player and player related mocks after everything else + this.player = Mockito.mock(Player.class); + when(player.getUniqueId()).thenReturn(playerUUID); + + // wire inventory + this.playerInventory = Mockito.mock(PlayerInventory.class); + this.itemInMainHand = new ItemStack(Material.DIAMOND_SHOVEL); + when(player.getInventory()).thenReturn(playerInventory); + when(playerInventory.getItemInMainHand()).thenReturn(itemInMainHand); + + // Set up spy for Excavation Manager + + } + + @AfterEach + void tearDown() { + cleanupBaseEnvironment(); + } + + @Test + void excavationShouldHaveTreasureDrops() { + mmoPlayer.modifySkill(PrimarySkillType.EXCAVATION, 1000); + + // Wire block + BlockState blockState = Mockito.mock(BlockState.class); + BlockData blockData = Mockito.mock(BlockData.class); + Block block = Mockito.mock(Block.class); + when(blockState.getBlockData()).thenReturn(blockData); + when(blockState.getType()).thenReturn(Material.SAND); + when(blockData.getMaterial()).thenReturn(Material.SAND); + when(blockState.getBlock()).thenReturn(block); + when(blockState.getBlock().getDrops(any())).thenReturn(null); + + ExcavationManager excavationManager = Mockito.spy(new ExcavationManager(mmoPlayer)); + doReturn(getGuaranteedTreasureDrops()).when(excavationManager).getTreasures(blockState); + excavationManager.excavationBlockCheck(blockState); + + // verify ExcavationManager.processExcavationBonusesOnBlock was called + verify(excavationManager, atLeastOnce()).processExcavationBonusesOnBlock(any(BlockState.class), any(ExcavationTreasure.class), any(Location.class)); + } + + @Test + void excavationShouldNotDropTreasure() { + mmoPlayer.modifySkill(PrimarySkillType.EXCAVATION, 1000); + + // Wire block + BlockState blockState = Mockito.mock(BlockState.class); + BlockData blockData = Mockito.mock(BlockData.class); + Block block = Mockito.mock(Block.class); + when(blockState.getBlockData()).thenReturn(blockData); + when(blockState.getType()).thenReturn(Material.SAND); + when(blockData.getMaterial()).thenReturn(Material.SAND); + when(blockState.getBlock()).thenReturn(block); + when(blockState.getBlock().getDrops(any())).thenReturn(null); + + ExcavationManager excavationManager = Mockito.spy(new ExcavationManager(mmoPlayer)); + doReturn(getImpossibleTreasureDrops()).when(excavationManager).getTreasures(blockState); + excavationManager.excavationBlockCheck(blockState); + + // verify ExcavationManager.processExcavationBonusesOnBlock was called + verify(excavationManager, never()).processExcavationBonusesOnBlock(any(BlockState.class), any(ExcavationTreasure.class), any(Location.class)); + } + + private List getGuaranteedTreasureDrops() { + List treasures = new ArrayList<>();; + treasures.add(new ExcavationTreasure(new ItemStack(Material.CAKE), 1, 100, 1)); + return treasures; + } + + private List getImpossibleTreasureDrops() { + List treasures = new ArrayList<>();; + treasures.add(new ExcavationTreasure(new ItemStack(Material.CAKE), 1, 0, 1)); + return treasures; + } +}