mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2024-11-22 13:16:45 +01:00
update UserBlockTracker API
This commit is contained in:
parent
c2054a5d45
commit
8b82163e3d
@ -1,3 +1,11 @@
|
|||||||
|
Version 2.2.013
|
||||||
|
(API) Added new methods to com.gmail.nossr50.util.blockmeta.UserBlockTracker for easier readability
|
||||||
|
(API) Deprecated the old poorly named methods in UserBlockTracker
|
||||||
|
(Codebase) Cleaned up and organized unit tests relating to UserBlockTracker
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
Not planning to delete the deprecated methods in UserBlockTracker anytime soon, as nothing has really changed other than the names
|
||||||
|
|
||||||
Version 2.2.012
|
Version 2.2.012
|
||||||
Fixed a bug where Daze would cause an exception in older game versions (1.20.4 and older)
|
Fixed a bug where Daze would cause an exception in older game versions (1.20.4 and older)
|
||||||
|
|
||||||
|
2
pom.xml
2
pom.xml
@ -2,7 +2,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.gmail.nossr50.mcMMO</groupId>
|
<groupId>com.gmail.nossr50.mcMMO</groupId>
|
||||||
<artifactId>mcMMO</artifactId>
|
<artifactId>mcMMO</artifactId>
|
||||||
<version>2.2.012</version>
|
<version>2.2.013-SNAPSHOT</version>
|
||||||
<name>mcMMO</name>
|
<name>mcMMO</name>
|
||||||
<url>https://github.com/mcMMO-Dev/mcMMO</url>
|
<url>https://github.com/mcMMO-Dev/mcMMO</url>
|
||||||
<scm>
|
<scm>
|
||||||
|
@ -4,7 +4,6 @@ import com.gmail.nossr50.config.LegacyConfigLoader;
|
|||||||
import com.gmail.nossr50.datatypes.skills.alchemy.AlchemyPotion;
|
import com.gmail.nossr50.datatypes.skills.alchemy.AlchemyPotion;
|
||||||
import com.gmail.nossr50.mcMMO;
|
import com.gmail.nossr50.mcMMO;
|
||||||
import com.gmail.nossr50.util.ItemUtils;
|
import com.gmail.nossr50.util.ItemUtils;
|
||||||
import com.gmail.nossr50.util.PotionUtil;
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.gmail.nossr50.datatypes.skills.alchemy;
|
package com.gmail.nossr50.datatypes.skills.alchemy;
|
||||||
|
|
||||||
import com.gmail.nossr50.util.PotionUtil;
|
import com.gmail.nossr50.util.PotionUtil;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.inventory.meta.PotionMeta;
|
import org.bukkit.inventory.meta.PotionMeta;
|
||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ public class BlockListener implements Listener {
|
|||||||
|
|
||||||
// Minecraft is dumb, the events still throw when a plant "grows" higher than the max block height. Even though no new block is created
|
// Minecraft is dumb, the events still throw when a plant "grows" higher than the max block height. Even though no new block is created
|
||||||
if (BlockUtils.isWithinWorldBounds(block)) {
|
if (BlockUtils.isWithinWorldBounds(block)) {
|
||||||
mcMMO.getPlaceStore().setFalse(block);
|
mcMMO.getPlaceStore().setEligible(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,14 +400,14 @@ public class BlockListener implements Listener {
|
|||||||
else if (BlockUtils.affectedBySuperBreaker(blockState)
|
else if (BlockUtils.affectedBySuperBreaker(blockState)
|
||||||
&& (ItemUtils.isPickaxe(heldItem) || ItemUtils.isHoe(heldItem))
|
&& (ItemUtils.isPickaxe(heldItem) || ItemUtils.isHoe(heldItem))
|
||||||
&& mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.MINING)
|
&& mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.MINING)
|
||||||
&& !mcMMO.getPlaceStore().isTrue(blockState)) {
|
&& !mcMMO.getPlaceStore().isIneligible(blockState)) {
|
||||||
MiningManager miningManager = mcMMOPlayer.getMiningManager();
|
MiningManager miningManager = mcMMOPlayer.getMiningManager();
|
||||||
miningManager.miningBlockCheck(blockState);
|
miningManager.miningBlockCheck(blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WOOD CUTTING */
|
/* WOOD CUTTING */
|
||||||
else if (BlockUtils.hasWoodcuttingXP(blockState) && ItemUtils.isAxe(heldItem)
|
else if (BlockUtils.hasWoodcuttingXP(blockState) && ItemUtils.isAxe(heldItem)
|
||||||
&& mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.WOODCUTTING) && !mcMMO.getPlaceStore().isTrue(blockState)) {
|
&& mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.WOODCUTTING) && !mcMMO.getPlaceStore().isIneligible(blockState)) {
|
||||||
WoodcuttingManager woodcuttingManager = mcMMOPlayer.getWoodcuttingManager();
|
WoodcuttingManager woodcuttingManager = mcMMOPlayer.getWoodcuttingManager();
|
||||||
if (woodcuttingManager.canUseTreeFeller(heldItem)) {
|
if (woodcuttingManager.canUseTreeFeller(heldItem)) {
|
||||||
woodcuttingManager.processTreeFeller(blockState);
|
woodcuttingManager.processTreeFeller(blockState);
|
||||||
@ -422,7 +422,7 @@ public class BlockListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* EXCAVATION */
|
/* EXCAVATION */
|
||||||
else if (BlockUtils.affectedByGigaDrillBreaker(blockState) && ItemUtils.isShovel(heldItem) && mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.EXCAVATION) && !mcMMO.getPlaceStore().isTrue(blockState)) {
|
else if (BlockUtils.affectedByGigaDrillBreaker(blockState) && ItemUtils.isShovel(heldItem) && mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.EXCAVATION) && !mcMMO.getPlaceStore().isIneligible(blockState)) {
|
||||||
ExcavationManager excavationManager = mcMMOPlayer.getExcavationManager();
|
ExcavationManager excavationManager = mcMMOPlayer.getExcavationManager();
|
||||||
excavationManager.excavationBlockCheck(blockState);
|
excavationManager.excavationBlockCheck(blockState);
|
||||||
|
|
||||||
@ -687,7 +687,7 @@ public class BlockListener implements Listener {
|
|||||||
|
|
||||||
if (UserManager.getPlayer(player).isDebugMode())
|
if (UserManager.getPlayer(player).isDebugMode())
|
||||||
{
|
{
|
||||||
if (mcMMO.getPlaceStore().isTrue(blockState))
|
if (mcMMO.getPlaceStore().isIneligible(blockState))
|
||||||
player.sendMessage("[mcMMO DEBUG] This block is not natural and does not reward treasures/XP");
|
player.sendMessage("[mcMMO DEBUG] This block is not natural and does not reward treasures/XP");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,6 @@ import org.bukkit.metadata.FixedMetadataValue;
|
|||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
import org.bukkit.projectiles.ProjectileSource;
|
import org.bukkit.projectiles.ProjectileSource;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import static com.gmail.nossr50.util.MobMetadataUtils.*;
|
import static com.gmail.nossr50.util.MobMetadataUtils.*;
|
||||||
|
|
||||||
@ -207,8 +206,8 @@ public class EntityListener implements Listener {
|
|||||||
if (entity instanceof FallingBlock || entity instanceof Enderman) {
|
if (entity instanceof FallingBlock || entity instanceof Enderman) {
|
||||||
boolean isTracked = entity.hasMetadata(MetadataConstants.METADATA_KEY_TRAVELING_BLOCK);
|
boolean isTracked = entity.hasMetadata(MetadataConstants.METADATA_KEY_TRAVELING_BLOCK);
|
||||||
|
|
||||||
if (mcMMO.getPlaceStore().isTrue(block) && !isTracked) {
|
if (mcMMO.getPlaceStore().isIneligible(block) && !isTracked) {
|
||||||
mcMMO.getPlaceStore().setFalse(block);
|
mcMMO.getPlaceStore().setEligible(block);
|
||||||
|
|
||||||
entity.setMetadata(MetadataConstants.METADATA_KEY_TRAVELING_BLOCK, MetadataConstants.MCMMO_METADATA_VALUE);
|
entity.setMetadata(MetadataConstants.METADATA_KEY_TRAVELING_BLOCK, MetadataConstants.MCMMO_METADATA_VALUE);
|
||||||
TravelingBlockMetaCleanup metaCleanupTask = new TravelingBlockMetaCleanup(entity, pluginRef);
|
TravelingBlockMetaCleanup metaCleanupTask = new TravelingBlockMetaCleanup(entity, pluginRef);
|
||||||
@ -222,8 +221,8 @@ public class EntityListener implements Listener {
|
|||||||
//Redstone ore fire this event and should be ignored
|
//Redstone ore fire this event and should be ignored
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mcMMO.getPlaceStore().isTrue(block)) {
|
if (mcMMO.getPlaceStore().isIneligible(block)) {
|
||||||
mcMMO.getPlaceStore().setFalse(block);
|
mcMMO.getPlaceStore().setEligible(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,7 +852,7 @@ public class PlayerListener implements Listener {
|
|||||||
case "NETHER_WART_BLOCK":
|
case "NETHER_WART_BLOCK":
|
||||||
case "POTATO":
|
case "POTATO":
|
||||||
case "MANGROVE_PROPAGULE":
|
case "MANGROVE_PROPAGULE":
|
||||||
mcMMO.getPlaceStore().setFalse(blockState);
|
mcMMO.getPlaceStore().setEligible(blockState);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public class WorldListener implements Listener {
|
|||||||
// Using 50 ms later as I do not know of a way to run one tick later (safely)
|
// Using 50 ms later as I do not know of a way to run one tick later (safely)
|
||||||
plugin.getFoliaLib().getImpl().runLater(() -> {
|
plugin.getFoliaLib().getImpl().runLater(() -> {
|
||||||
for (BlockState blockState : event.getBlocks()) {
|
for (BlockState blockState : event.getBlocks()) {
|
||||||
mcMMO.getPlaceStore().setFalse(blockState);
|
mcMMO.getPlaceStore().setEligible(blockState);
|
||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
package com.gmail.nossr50.runnables;
|
|
||||||
|
|
||||||
import com.gmail.nossr50.mcMMO;
|
|
||||||
import com.gmail.nossr50.util.BlockUtils;
|
|
||||||
import com.gmail.nossr50.util.CancellableRunnable;
|
|
||||||
import com.gmail.nossr50.util.MetadataConstants;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.block.BlockFace;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PistonTrackerTask extends CancellableRunnable {
|
|
||||||
private final List<Block> blocks;
|
|
||||||
private final BlockFace direction;
|
|
||||||
private final Block futureEmptyBlock;
|
|
||||||
|
|
||||||
public PistonTrackerTask(List<Block> blocks, BlockFace direction, Block futureEmptyBlock) {
|
|
||||||
this.blocks = blocks;
|
|
||||||
this.direction = direction;
|
|
||||||
this.futureEmptyBlock = futureEmptyBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Check to see if futureEmptyBlock is empty - if it isn't; the blocks didn't move
|
|
||||||
if (!BlockUtils.isPistonPiece(futureEmptyBlock.getState())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcMMO.getPlaceStore().isTrue(futureEmptyBlock)) {
|
|
||||||
mcMMO.getPlaceStore().setFalse(futureEmptyBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Block b : blocks) {
|
|
||||||
Block nextBlock = b.getRelative(direction);
|
|
||||||
|
|
||||||
if (nextBlock.hasMetadata(MetadataConstants.METADATA_KEY_PISTON_TRACKING)) {
|
|
||||||
BlockUtils.setUnnaturalBlock(nextBlock);
|
|
||||||
nextBlock.removeMetadata(MetadataConstants.METADATA_KEY_PISTON_TRACKING, mcMMO.p);
|
|
||||||
}
|
|
||||||
else if (mcMMO.getPlaceStore().isTrue(nextBlock)) {
|
|
||||||
// Block doesn't have metadatakey but isTrue - set it to false
|
|
||||||
mcMMO.getPlaceStore().setFalse(nextBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,7 +19,7 @@ public class StickyPistonTrackerTask extends CancellableRunnable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!mcMMO.getPlaceStore().isTrue(movedBlock.getRelative(direction))) {
|
if (!mcMMO.getPlaceStore().isIneligible(movedBlock.getRelative(direction))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ public class StickyPistonTrackerTask extends CancellableRunnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The sticky piston actually pulled the block so move the PlaceStore data
|
// The sticky piston actually pulled the block so move the PlaceStore data
|
||||||
mcMMO.getPlaceStore().setFalse(movedBlock.getRelative(direction));
|
mcMMO.getPlaceStore().setEligible(movedBlock.getRelative(direction));
|
||||||
BlockUtils.setUnnaturalBlock(movedBlock);
|
BlockUtils.setUnnaturalBlock(movedBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.gmail.nossr50.runnables.skills;
|
|||||||
|
|
||||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||||
import com.gmail.nossr50.skills.alchemy.Alchemy;
|
import com.gmail.nossr50.skills.alchemy.Alchemy;
|
||||||
import com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer;
|
|
||||||
import com.gmail.nossr50.util.CancellableRunnable;
|
import com.gmail.nossr50.util.CancellableRunnable;
|
||||||
import com.gmail.nossr50.util.ContainerMetadataUtils;
|
import com.gmail.nossr50.util.ContainerMetadataUtils;
|
||||||
import com.gmail.nossr50.util.player.UserManager;
|
import com.gmail.nossr50.util.player.UserManager;
|
||||||
@ -17,7 +16,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer.isValidBrew;
|
import static com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer.isValidBrew;
|
||||||
import static com.gmail.nossr50.util.EventUtils.getMcMMOPlayer;
|
|
||||||
|
|
||||||
public class AlchemyBrewCheckTask extends CancellableRunnable {
|
public class AlchemyBrewCheckTask extends CancellableRunnable {
|
||||||
private final BrewingStand brewingStand;
|
private final BrewingStand brewingStand;
|
||||||
|
@ -282,7 +282,7 @@ public class HerbalismManager extends SkillManager {
|
|||||||
if (brokenPlant.getLocation().equals(originalBreak.getBlock().getLocation())) {
|
if (brokenPlant.getLocation().equals(originalBreak.getBlock().getLocation())) {
|
||||||
//If its the same block as the original, we are going to directly check it for being a valid XP gain and add it to the nonChorusBlocks list even if its a chorus block
|
//If its the same block as the original, we are going to directly check it for being a valid XP gain and add it to the nonChorusBlocks list even if its a chorus block
|
||||||
//This stops a delay from happening when bringing up the XP bar for chorus trees
|
//This stops a delay from happening when bringing up the XP bar for chorus trees
|
||||||
if (!mcMMO.getPlaceStore().isTrue(originalBreak)) {
|
if (!mcMMO.getPlaceStore().isIneligible(originalBreak)) {
|
||||||
//Even if its a chorus block, the original break will be moved to nonChorusBlocks for immediate XP rewards
|
//Even if its a chorus block, the original break will be moved to nonChorusBlocks for immediate XP rewards
|
||||||
noDelayPlantBlocks.add(brokenPlant);
|
noDelayPlantBlocks.add(brokenPlant);
|
||||||
} else {
|
} else {
|
||||||
@ -335,7 +335,7 @@ public class HerbalismManager extends SkillManager {
|
|||||||
BlockData plantData = brokenPlantState.getBlockData();
|
BlockData plantData = brokenPlantState.getBlockData();
|
||||||
|
|
||||||
//Check for double drops
|
//Check for double drops
|
||||||
if (!mcMMO.getPlaceStore().isTrue(brokenPlant)) {
|
if (!mcMMO.getPlaceStore().isIneligible(brokenPlant)) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@ -413,7 +413,7 @@ public class HerbalismManager extends SkillManager {
|
|||||||
BlockState brokenBlockNewState = brokenPlantBlock.getState();
|
BlockState brokenBlockNewState = brokenPlantBlock.getState();
|
||||||
BlockData plantData = brokenBlockNewState.getBlockData();
|
BlockData plantData = brokenBlockNewState.getBlockData();
|
||||||
|
|
||||||
if (mcMMO.getPlaceStore().isTrue(brokenBlockNewState)) {
|
if (mcMMO.getPlaceStore().isIneligible(brokenBlockNewState)) {
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Unnatural Blocks
|
* Unnatural Blocks
|
||||||
@ -427,7 +427,7 @@ public class HerbalismManager extends SkillManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Mark it as natural again as it is being broken
|
//Mark it as natural again as it is being broken
|
||||||
mcMMO.getPlaceStore().setFalse(brokenBlockNewState);
|
mcMMO.getPlaceStore().setEligible(brokenBlockNewState);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@ -489,9 +489,9 @@ public class HerbalismManager extends SkillManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mcMMO.getPlaceStore().isTrue(brokenBlockNewState)) {
|
if (mcMMO.getPlaceStore().isIneligible(brokenBlockNewState)) {
|
||||||
//Mark it as natural again as it is being broken
|
//Mark it as natural again as it is being broken
|
||||||
mcMMO.getPlaceStore().setFalse(brokenBlockNewState);
|
mcMMO.getPlaceStore().setEligible(brokenBlockNewState);
|
||||||
} else {
|
} else {
|
||||||
//TODO: Do we care about chorus flower age?
|
//TODO: Do we care about chorus flower age?
|
||||||
//Calculate XP for the old type
|
//Calculate XP for the old type
|
||||||
|
@ -181,7 +181,7 @@ public class MiningManager extends SkillManager {
|
|||||||
//Containers usually have 0 XP unless someone edited their config in a very strange way
|
//Containers usually have 0 XP unless someone edited their config in a very strange way
|
||||||
if (ExperienceConfig.getInstance().getXp(PrimarySkillType.MINING, targetBlock) != 0
|
if (ExperienceConfig.getInstance().getXp(PrimarySkillType.MINING, targetBlock) != 0
|
||||||
&& !(targetBlock instanceof Container)
|
&& !(targetBlock instanceof Container)
|
||||||
&& !mcMMO.getPlaceStore().isTrue(targetBlock)) {
|
&& !mcMMO.getPlaceStore().isIneligible(targetBlock)) {
|
||||||
if (BlockUtils.isOre(blockState)) {
|
if (BlockUtils.isOre(blockState)) {
|
||||||
ores.add(blockState);
|
ores.add(blockState);
|
||||||
} else {
|
} else {
|
||||||
@ -216,7 +216,7 @@ public class MiningManager extends SkillManager {
|
|||||||
|
|
||||||
Misc.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_ORES); // Initial block that would have been dropped
|
Misc.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_ORES); // Initial block that would have been dropped
|
||||||
|
|
||||||
if (mcMMO.p.getAdvancedConfig().isBlastMiningBonusDropsEnabled() && !mcMMO.getPlaceStore().isTrue(blockState)) {
|
if (mcMMO.p.getAdvancedConfig().isBlastMiningBonusDropsEnabled() && !mcMMO.getPlaceStore().isIneligible(blockState)) {
|
||||||
for (int i = 1; i < dropMultiplier; i++) {
|
for (int i = 1; i < dropMultiplier; i++) {
|
||||||
// Bukkit.broadcastMessage("Bonus Drop on Ore: "+blockState.getType().toString());
|
// Bukkit.broadcastMessage("Bonus Drop on Ore: "+blockState.getType().toString());
|
||||||
Misc.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_ORES_BONUS_DROP); // Initial block that would have been dropped
|
Misc.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_ORES_BONUS_DROP); // Initial block that would have been dropped
|
||||||
|
@ -113,7 +113,7 @@ public class WoodcuttingManager extends SkillManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void processWoodcuttingBlockXP(@NotNull BlockState blockState) {
|
public void processWoodcuttingBlockXP(@NotNull BlockState blockState) {
|
||||||
if (mcMMO.getPlaceStore().isTrue(blockState))
|
if (mcMMO.getPlaceStore().isIneligible(blockState))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int xp = getExperienceFromLog(blockState);
|
int xp = getExperienceFromLog(blockState);
|
||||||
@ -269,7 +269,7 @@ public class WoodcuttingManager extends SkillManager {
|
|||||||
* in treeFellerBlocks.
|
* in treeFellerBlocks.
|
||||||
*/
|
*/
|
||||||
private boolean processTreeFellerTargetBlock(@NotNull BlockState blockState, @NotNull List<BlockState> futureCenterBlocks, @NotNull Set<BlockState> treeFellerBlocks) {
|
private boolean processTreeFellerTargetBlock(@NotNull BlockState blockState, @NotNull List<BlockState> futureCenterBlocks, @NotNull Set<BlockState> treeFellerBlocks) {
|
||||||
if (treeFellerBlocks.contains(blockState) || mcMMO.getPlaceStore().isTrue(blockState)) {
|
if (treeFellerBlocks.contains(blockState) || mcMMO.getPlaceStore().isIneligible(blockState)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +373,7 @@ public class WoodcuttingManager extends SkillManager {
|
|||||||
* @return Amount of experience
|
* @return Amount of experience
|
||||||
*/
|
*/
|
||||||
private static int processTreeFellerXPGains(BlockState blockState, int woodCount) {
|
private static int processTreeFellerXPGains(BlockState blockState, int woodCount) {
|
||||||
if (mcMMO.getPlaceStore().isTrue(blockState))
|
if (mcMMO.getPlaceStore().isIneligible(blockState))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
int rawXP = ExperienceConfig.getInstance().getXp(PrimarySkillType.WOODCUTTING, blockState.getType());
|
int rawXP = ExperienceConfig.getInstance().getXp(PrimarySkillType.WOODCUTTING, blockState.getType());
|
||||||
|
@ -65,7 +65,7 @@ public final class BlockUtils {
|
|||||||
* @param block target block
|
* @param block target block
|
||||||
*/
|
*/
|
||||||
public static void setUnnaturalBlock(@NotNull Block block) {
|
public static void setUnnaturalBlock(@NotNull Block block) {
|
||||||
mcMMO.getPlaceStore().setTrue(block);
|
mcMMO.getPlaceStore().setIneligible(block);
|
||||||
|
|
||||||
// Failsafe against lingering metadata
|
// Failsafe against lingering metadata
|
||||||
if (block.hasMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS))
|
if (block.hasMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS))
|
||||||
@ -82,7 +82,7 @@ public final class BlockUtils {
|
|||||||
block.removeMetadata(MetadataConstants.METADATA_KEY_REPLANT, mcMMO.p);
|
block.removeMetadata(MetadataConstants.METADATA_KEY_REPLANT, mcMMO.p);
|
||||||
}
|
}
|
||||||
|
|
||||||
mcMMO.getPlaceStore().setFalse(block);
|
mcMMO.getPlaceStore().setEligible(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.gmail.nossr50.util;
|
package com.gmail.nossr50.util;
|
||||||
|
|
||||||
import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
|
import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
|
||||||
import com.gmail.nossr50.mcMMO;
|
|
||||||
import com.gmail.nossr50.skills.taming.TrackedTamingEntity;
|
import com.gmail.nossr50.skills.taming.TrackedTamingEntity;
|
||||||
import com.gmail.nossr50.util.player.NotificationManager;
|
import com.gmail.nossr50.util.player.NotificationManager;
|
||||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||||
|
@ -152,7 +152,7 @@ public class HashChunkManager implements ChunkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean isTrue(int x, int y, int z, @NotNull World world) {
|
private synchronized boolean isIneligible(int x, int y, int z, @NotNull World world) {
|
||||||
CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z);
|
CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z);
|
||||||
|
|
||||||
// Get chunk, load from file if necessary
|
// Get chunk, load from file if necessary
|
||||||
@ -178,32 +178,42 @@ public class HashChunkManager implements ChunkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean isTrue(@NotNull Block block) {
|
public synchronized boolean isIneligible(@NotNull Block block) {
|
||||||
return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
return isIneligible(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean isTrue(@NotNull BlockState blockState) {
|
public synchronized boolean isIneligible(@NotNull BlockState blockState) {
|
||||||
return isTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld());
|
return isIneligible(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setTrue(@NotNull Block block) {
|
public synchronized boolean isEligible(@NotNull Block block) {
|
||||||
|
return !isIneligible(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean isEligible(@NotNull BlockState blockState) {
|
||||||
|
return !isIneligible(blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setIneligible(@NotNull Block block) {
|
||||||
set(block.getX(), block.getY(), block.getZ(), block.getWorld(), true);
|
set(block.getX(), block.getY(), block.getZ(), block.getWorld(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setTrue(@NotNull BlockState blockState) {
|
public synchronized void setIneligible(@NotNull BlockState blockState) {
|
||||||
set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), true);
|
set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setFalse(@NotNull Block block) {
|
public synchronized void setEligible(@NotNull Block block) {
|
||||||
set(block.getX(), block.getY(), block.getZ(), block.getWorld(), false);
|
set(block.getX(), block.getY(), block.getZ(), block.getWorld(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setFalse(@NotNull BlockState blockState) {
|
public synchronized void setEligible(@NotNull BlockState blockState) {
|
||||||
set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), false);
|
set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,24 +17,34 @@ public class NullChunkManager implements ChunkManager {
|
|||||||
public void unloadWorld(@NotNull World world) {}
|
public void unloadWorld(@NotNull World world) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTrue(@NotNull Block block) {
|
public boolean isIneligible(@NotNull Block block) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTrue(@NotNull BlockState blockState) {
|
public boolean isIneligible(@NotNull BlockState blockState) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTrue(@NotNull Block block) {}
|
public boolean isEligible(@NotNull Block block) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTrue(@NotNull BlockState blockState) {}
|
public boolean isEligible(@NotNull BlockState blockState) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFalse(@NotNull Block block) {}
|
public void setIneligible(@NotNull Block block) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFalse(@NotNull BlockState blockState) {}
|
public void setIneligible(@NotNull BlockState blockState) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEligible(@NotNull Block block) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEligible(@NotNull BlockState blockState) {}
|
||||||
}
|
}
|
||||||
|
@ -11,46 +11,136 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
*/
|
*/
|
||||||
public interface UserBlockTracker {
|
public interface UserBlockTracker {
|
||||||
/**
|
/**
|
||||||
* Check to see if a given block location is set to true
|
* Check to see if a given {@link Block} is ineligible for rewards.
|
||||||
|
* This is a location-based lookup, and the other properties of the {@link Block} do not matter.
|
||||||
*
|
*
|
||||||
* @param block Block location to check
|
* @param block Block to check
|
||||||
* @return true if the given block location is set to true, false if otherwise
|
* @return true if the given block should not give rewards, false if otherwise
|
||||||
*/
|
*/
|
||||||
boolean isTrue(@NotNull Block block);
|
boolean isIneligible(@NotNull Block block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if a given BlockState location is set to true
|
* Check to see if a given {@link Block} is eligible for rewards.
|
||||||
|
* This is a location-based lookup, and the other properties of the {@link Block} do not matter.
|
||||||
|
*
|
||||||
|
* @param block Block to check
|
||||||
|
* @return true if the given block should give rewards, false if otherwise
|
||||||
|
*/
|
||||||
|
boolean isEligible(@NotNull Block block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if a given {@link BlockState} is eligible for rewards.
|
||||||
|
* This is a location-based lookup, and the other properties of the {@link BlockState} do not matter.
|
||||||
*
|
*
|
||||||
* @param blockState BlockState to check
|
* @param blockState BlockState to check
|
||||||
* @return true if the given BlockState location is set to true, false if otherwise
|
* @return true if the given BlockState location is set to true, false if otherwise
|
||||||
*/
|
*/
|
||||||
boolean isTrue(@NotNull BlockState blockState);
|
boolean isEligible(@NotNull BlockState blockState);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a given block location to true
|
* Check to see if a given {@link BlockState} is ineligible for rewards.
|
||||||
|
* This is a location-based lookup, and the other properties of the {@link BlockState} do not matter.
|
||||||
*
|
*
|
||||||
* @param block Block location to set
|
* @param blockState BlockState to check
|
||||||
|
* @return true if the given BlockState location is set to true, false if otherwise
|
||||||
*/
|
*/
|
||||||
void setTrue(@NotNull Block block);
|
boolean isIneligible(@NotNull BlockState blockState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a given {@link Block} as ineligible for rewards.
|
||||||
|
* This is a location-based lookup, and the other properties of the {@link Block} do not matter.
|
||||||
|
*
|
||||||
|
* @param block block whose location to set as ineligible
|
||||||
|
*/
|
||||||
|
void setIneligible(@NotNull Block block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a given BlockState location to true
|
* Set a given BlockState location to true
|
||||||
*
|
*
|
||||||
* @param blockState BlockState location to set
|
* @param blockState BlockState location to set
|
||||||
*/
|
*/
|
||||||
void setTrue(@NotNull BlockState blockState);
|
void setIneligible(@NotNull BlockState blockState);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a given block location to false
|
* Set a given {@link Block} as eligible for rewards.
|
||||||
|
* This is a location-based lookup, and the other properties of the {@link Block} do not matter.
|
||||||
*
|
*
|
||||||
* @param block Block location to set
|
* @param block block whose location to set as eligible
|
||||||
*/
|
*/
|
||||||
void setFalse(@NotNull Block block);
|
void setEligible(@NotNull Block block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a given BlockState location to false
|
* Set a given BlockState location to false
|
||||||
*
|
*
|
||||||
* @param blockState BlockState location to set
|
* @param blockState BlockState location to set
|
||||||
*/
|
*/
|
||||||
void setFalse(@NotNull BlockState blockState);
|
void setEligible(@NotNull BlockState blockState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if a given block location is set to true
|
||||||
|
*
|
||||||
|
* @param block Block location to check
|
||||||
|
* @return true if the given block location is set to true, false if otherwise
|
||||||
|
* @deprecated Use {@link #isIneligible(Block)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "2.2.013")
|
||||||
|
default boolean isTrue(@NotNull Block block) {
|
||||||
|
return isIneligible(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if a given BlockState location is set to true
|
||||||
|
*
|
||||||
|
* @param blockState BlockState to check
|
||||||
|
* @return true if the given BlockState location is set to true, false if otherwise
|
||||||
|
* @deprecated Use {@link #isIneligible(BlockState)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "2.2.013")
|
||||||
|
default boolean isTrue(@NotNull BlockState blockState) {
|
||||||
|
return isIneligible(blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a given block location to true
|
||||||
|
*
|
||||||
|
* @param block Block location to set
|
||||||
|
* @deprecated Use {@link #setIneligible(Block)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "2.2.013")
|
||||||
|
default void setTrue(@NotNull Block block) {
|
||||||
|
setIneligible(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a given BlockState location to true
|
||||||
|
*
|
||||||
|
* @param blockState BlockState location to set
|
||||||
|
* @deprecated Use {@link #setIneligible(BlockState)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "2.2.013")
|
||||||
|
default void setTrue(@NotNull BlockState blockState) {
|
||||||
|
setIneligible(blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a given block location to false
|
||||||
|
*
|
||||||
|
* @param block Block location to set
|
||||||
|
* @deprecated Use {@link #setEligible(Block)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "2.2.013")
|
||||||
|
default void setFalse(@NotNull Block block) {
|
||||||
|
setEligible(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a given BlockState location to false
|
||||||
|
*
|
||||||
|
* @param blockState BlockState location to set
|
||||||
|
* @deprecated Use {@link #setEligible(BlockState)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "2.2.013")
|
||||||
|
default void setFalse(@NotNull BlockState blockState) {
|
||||||
|
setEligible(blockState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,8 @@ import org.mockito.MockedStatic;
|
|||||||
import static com.gmail.nossr50.util.PotionEffectUtil.getNauseaPotionEffectType;
|
import static com.gmail.nossr50.util.PotionEffectUtil.getNauseaPotionEffectType;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
class PotionEffectUtilTest {
|
class PotionEffectUtilTest {
|
||||||
private MockedStatic<mcMMO> mockedStaticMcMMO;
|
private MockedStatic<mcMMO> mockedStaticMcMMO;
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
package com.gmail.nossr50.util.blockmeta;
|
||||||
|
|
||||||
|
import com.gmail.nossr50.mcMMO;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.gmail.nossr50.util.blockmeta.BlockStoreTestUtils.*;
|
||||||
|
import static com.gmail.nossr50.util.blockmeta.UserBlockTrackerTest.recursiveDelete;
|
||||||
|
import static org.bukkit.Bukkit.getWorld;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class BitSetChunkStoreTest {
|
||||||
|
private static File tempDir;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setUpClass() {
|
||||||
|
tempDir = Files.createTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public static void tearDownClass() {
|
||||||
|
recursiveDelete(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private World mockWorld;
|
||||||
|
|
||||||
|
private MockedStatic<Bukkit> bukkitMock;
|
||||||
|
private MockedStatic<mcMMO> mcMMOMock;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUpMock() {
|
||||||
|
UUID worldUUID = UUID.randomUUID();
|
||||||
|
mockWorld = Mockito.mock(World.class);
|
||||||
|
when(mockWorld.getUID()).thenReturn(worldUUID);
|
||||||
|
when(mockWorld.getMaxHeight()).thenReturn(256);
|
||||||
|
when(mockWorld.getWorldFolder()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
bukkitMock = mockStatic(Bukkit.class);
|
||||||
|
bukkitMock.when(() -> getWorld(worldUUID)).thenReturn(mockWorld);
|
||||||
|
|
||||||
|
mcMMOMock = mockStatic(mcMMO.class);
|
||||||
|
|
||||||
|
when(mockWorld.getMinHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MIN);
|
||||||
|
when(mockWorld.getMaxHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void teardownMock() {
|
||||||
|
bukkitMock.close();
|
||||||
|
mcMMOMock.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSetValue() {
|
||||||
|
final BitSetChunkStore original = new BitSetChunkStore(mockWorld, 0, 0);
|
||||||
|
original.setTrue(0, 0, 0);
|
||||||
|
assertTrue(original.isTrue(0, 0, 0));
|
||||||
|
original.setFalse(0, 0, 0);
|
||||||
|
assertFalse(original.isTrue(0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIsEmpty() {
|
||||||
|
final BitSetChunkStore original = new BitSetChunkStore(mockWorld, 0, 0);
|
||||||
|
assertTrue(original.isEmpty());
|
||||||
|
original.setTrue(0, 0, 0);
|
||||||
|
original.setFalse(0, 0, 0);
|
||||||
|
assertTrue(original.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRoundTrip() throws IOException {
|
||||||
|
final BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
|
||||||
|
original.setTrue(14, 89, 12);
|
||||||
|
original.setTrue(14, 90, 12);
|
||||||
|
original.setTrue(13, 89, 12);
|
||||||
|
byte[] serializedBytes = serializeChunkStore(original);
|
||||||
|
final ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
||||||
|
assertChunkStoreEquals(original, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNegativeWorldMin() throws IOException {
|
||||||
|
when(mockWorld.getMinHeight()).thenReturn(-64);
|
||||||
|
|
||||||
|
final BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
|
||||||
|
original.setTrue(14, -32, 12);
|
||||||
|
original.setTrue(14, -64, 12);
|
||||||
|
original.setTrue(13, -63, 12);
|
||||||
|
byte[] serializedBytes = serializeChunkStore(original);
|
||||||
|
final ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
||||||
|
assertChunkStoreEquals(original, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNegativeWorldMinUpgrade() throws IOException {
|
||||||
|
final BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
|
||||||
|
original.setTrue(14, 1, 12);
|
||||||
|
original.setTrue(14, 2, 12);
|
||||||
|
original.setTrue(13, 3, 12);
|
||||||
|
byte[] serializedBytes = serializeChunkStore(original);
|
||||||
|
|
||||||
|
when(mockWorld.getMinHeight()).thenReturn(-64);
|
||||||
|
final ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
||||||
|
assert deserialized != null;
|
||||||
|
assertEqualIgnoreMinMax(original, deserialized);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.gmail.nossr50.util.blockmeta;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class BlockStoreTestUtils {
|
||||||
|
public static final int LEGACY_WORLD_HEIGHT_MAX = 256;
|
||||||
|
public static final int LEGACY_WORLD_HEIGHT_MIN = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the two ChunkStores are equal.
|
||||||
|
* @param expected The expected ChunkStore
|
||||||
|
* @param actual The actual ChunkStore
|
||||||
|
*/
|
||||||
|
static void assertChunkStoreEquals(ChunkStore expected, ChunkStore actual) {
|
||||||
|
assertEquals(expected.getChunkMin(), actual.getChunkMin());
|
||||||
|
assertEquals(expected.getChunkMax(), actual.getChunkMax());
|
||||||
|
assertEqualIgnoreMinMax(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] serializeChunkStore(@NotNull ChunkStore chunkStore) throws IOException {
|
||||||
|
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
if (chunkStore instanceof BitSetChunkStore)
|
||||||
|
BitSetChunkStore.Serialization.writeChunkStore(new DataOutputStream(byteArrayOutputStream), chunkStore);
|
||||||
|
else
|
||||||
|
new UnitTestObjectOutputStream(byteArrayOutputStream).writeObject(chunkStore); // Serializes the class as if
|
||||||
|
// it were the old
|
||||||
|
// PrimitiveChunkStore
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertEqualIgnoreMinMax(ChunkStore expected, ChunkStore actual) {
|
||||||
|
assertEquals(expected.getChunkX(), actual.getChunkX());
|
||||||
|
assertEquals(expected.getChunkZ(), actual.getChunkZ());
|
||||||
|
assertEquals(expected.getWorldId(), actual.getWorldId());
|
||||||
|
for (int y = Math.min(actual.getChunkMin(), expected.getChunkMin()); y < Math.max(actual.getChunkMax(), expected.getChunkMax()); y++) {
|
||||||
|
if (expected.getChunkMin() > y || actual.getChunkMin() > y || expected.getChunkMax() <= y || actual.getChunkMax() <= y)
|
||||||
|
continue; // Ignore
|
||||||
|
for (int x = 0; x < 16; x++)
|
||||||
|
for (int z = 0; z < 16; z++)
|
||||||
|
assertEquals(expected.isTrue(x, y, z), actual.isTrue(x, y, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,9 @@
|
|||||||
package com.gmail.nossr50.util.blockmeta;
|
package com.gmail.nossr50.util.blockmeta;
|
||||||
|
|
||||||
|
|
||||||
import com.gmail.nossr50.mcMMO;
|
import com.gmail.nossr50.mcMMO;
|
||||||
import com.gmail.nossr50.util.BlockUtils;
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
@ -15,14 +11,13 @@ import org.mockito.Mockito;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
import static com.gmail.nossr50.util.blockmeta.BlockStoreTestUtils.*;
|
||||||
* Could be a lot better. But some tests are better than none! Tests the major things, still kinda unit-testy. Verifies
|
import static com.gmail.nossr50.util.blockmeta.UserBlockTrackerTest.recursiveDelete;
|
||||||
* that the serialization isn't completely broken.
|
import static org.bukkit.Bukkit.getWorld;
|
||||||
*/
|
import static org.mockito.Mockito.mockStatic;
|
||||||
class ChunkStoreTest {
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public static final int LEGACY_WORLD_HEIGHT_MAX = 256;
|
class ChunkStoreTest {
|
||||||
public static final int LEGACY_WORLD_HEIGHT_MIN = 0;
|
|
||||||
private static File tempDir;
|
private static File tempDir;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
@ -44,17 +39,17 @@ class ChunkStoreTest {
|
|||||||
void setUpMock() {
|
void setUpMock() {
|
||||||
UUID worldUUID = UUID.randomUUID();
|
UUID worldUUID = UUID.randomUUID();
|
||||||
mockWorld = Mockito.mock(World.class);
|
mockWorld = Mockito.mock(World.class);
|
||||||
Mockito.when(mockWorld.getUID()).thenReturn(worldUUID);
|
when(mockWorld.getUID()).thenReturn(worldUUID);
|
||||||
Mockito.when(mockWorld.getMaxHeight()).thenReturn(256);
|
when(mockWorld.getMaxHeight()).thenReturn(256);
|
||||||
Mockito.when(mockWorld.getWorldFolder()).thenReturn(tempDir);
|
when(mockWorld.getWorldFolder()).thenReturn(tempDir);
|
||||||
|
|
||||||
bukkitMock = Mockito.mockStatic(Bukkit.class);
|
bukkitMock = mockStatic(Bukkit.class);
|
||||||
bukkitMock.when(() -> Bukkit.getWorld(worldUUID)).thenReturn(mockWorld);
|
bukkitMock.when(() -> getWorld(worldUUID)).thenReturn(mockWorld);
|
||||||
|
|
||||||
mcMMOMock = Mockito.mockStatic(mcMMO.class);
|
mcMMOMock = mockStatic(mcMMO.class);
|
||||||
|
|
||||||
Mockito.when(mockWorld.getMinHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MIN);
|
when(mockWorld.getMinHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MIN);
|
||||||
Mockito.when(mockWorld.getMaxHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MAX);
|
when(mockWorld.getMaxHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -63,132 +58,20 @@ class ChunkStoreTest {
|
|||||||
mcMMOMock.close();
|
mcMMOMock.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testIndexOutOfBounds() {
|
|
||||||
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
|
|
||||||
HashChunkManager hashChunkManager = new HashChunkManager();
|
|
||||||
|
|
||||||
// Top Block
|
|
||||||
Block illegalHeightBlock = initMockBlock(1337, 256, -1337);
|
|
||||||
Assertions.assertFalse(hashChunkManager.isTrue(illegalHeightBlock));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> hashChunkManager.setTrue(illegalHeightBlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetTrue() {
|
|
||||||
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
|
|
||||||
HashChunkManager hashChunkManager = new HashChunkManager();
|
|
||||||
int radius = 2; // Could be anything but drastically changes test time
|
|
||||||
|
|
||||||
for (int x = -radius; x <= radius; x++) {
|
|
||||||
for (int y = mockWorld.getMinHeight(); y < mockWorld.getMaxHeight(); y++) {
|
|
||||||
for (int z = -radius; z <= radius; z++) {
|
|
||||||
Block testBlock = initMockBlock(x, y, z);
|
|
||||||
|
|
||||||
hashChunkManager.setTrue(testBlock);
|
|
||||||
Assertions.assertTrue(hashChunkManager.isTrue(testBlock));
|
|
||||||
hashChunkManager.setFalse(testBlock);
|
|
||||||
Assertions.assertFalse(hashChunkManager.isTrue(testBlock));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bot Block
|
|
||||||
Block bottomBlock = initMockBlock(1337, 0, -1337);
|
|
||||||
Assertions.assertFalse(hashChunkManager.isTrue(bottomBlock));
|
|
||||||
|
|
||||||
Assertions.assertTrue(BlockUtils.isWithinWorldBounds(bottomBlock));
|
|
||||||
hashChunkManager.setTrue(bottomBlock);
|
|
||||||
Assertions.assertTrue(hashChunkManager.isTrue(bottomBlock));
|
|
||||||
|
|
||||||
// Top Block
|
|
||||||
Block topBlock = initMockBlock(1337, 255, -1337);
|
|
||||||
Assertions.assertFalse(hashChunkManager.isTrue(topBlock));
|
|
||||||
|
|
||||||
Assertions.assertTrue(BlockUtils.isWithinWorldBounds(topBlock));
|
|
||||||
hashChunkManager.setTrue(topBlock);
|
|
||||||
Assertions.assertTrue(hashChunkManager.isTrue(topBlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetValue() {
|
|
||||||
BitSetChunkStore original = new BitSetChunkStore(mockWorld, 0, 0);
|
|
||||||
original.setTrue(0, 0, 0);
|
|
||||||
Assertions.assertTrue(original.isTrue(0, 0, 0));
|
|
||||||
original.setFalse(0, 0, 0);
|
|
||||||
Assertions.assertFalse(original.isTrue(0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testIsEmpty() {
|
|
||||||
BitSetChunkStore original = new BitSetChunkStore(mockWorld, 0, 0);
|
|
||||||
Assertions.assertTrue(original.isEmpty());
|
|
||||||
original.setTrue(0, 0, 0);
|
|
||||||
original.setFalse(0, 0, 0);
|
|
||||||
Assertions.assertTrue(original.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testRoundTrip() throws IOException {
|
|
||||||
BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
|
|
||||||
original.setTrue(14, 89, 12);
|
|
||||||
original.setTrue(14, 90, 12);
|
|
||||||
original.setTrue(13, 89, 12);
|
|
||||||
byte[] serializedBytes = serializeChunkstore(original);
|
|
||||||
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
|
||||||
assertEqual(original, deserialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNegativeWorldMin() throws IOException {
|
|
||||||
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
|
|
||||||
|
|
||||||
BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
|
|
||||||
original.setTrue(14, -32, 12);
|
|
||||||
original.setTrue(14, -64, 12);
|
|
||||||
original.setTrue(13, -63, 12);
|
|
||||||
byte[] serializedBytes = serializeChunkstore(original);
|
|
||||||
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
|
||||||
assertEqual(original, deserialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNegativeWorldMinUpgrade() throws IOException {
|
|
||||||
BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
|
|
||||||
original.setTrue(14, 1, 12);
|
|
||||||
original.setTrue(14, 2, 12);
|
|
||||||
original.setTrue(13, 3, 12);
|
|
||||||
byte[] serializedBytes = serializeChunkstore(original);
|
|
||||||
|
|
||||||
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
|
|
||||||
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
|
||||||
assert deserialized != null;
|
|
||||||
assertEqualIgnoreMinMax(original, deserialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testChunkCoords() throws IOException {
|
|
||||||
for (int x = -96; x < 0; x++) {
|
|
||||||
int cx = x >> 4;
|
|
||||||
int ix = Math.abs(x) % 16;
|
|
||||||
//System.out.print(cx + ":" + ix + " ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpgrade() throws IOException {
|
void testUpgrade() throws IOException {
|
||||||
LegacyChunkStore original = new LegacyChunkStore(mockWorld, 12, 32);
|
LegacyChunkStore original = new LegacyChunkStore(mockWorld, 12, 32);
|
||||||
original.setTrue(14, 89, 12);
|
original.setTrue(14, 89, 12);
|
||||||
original.setTrue(14, 90, 12);
|
original.setTrue(14, 90, 12);
|
||||||
original.setTrue(13, 89, 12);
|
original.setTrue(13, 89, 12);
|
||||||
byte[] serializedBytes = serializeChunkstore(original);
|
byte[] serializedBytes = serializeChunkStore(original);
|
||||||
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
|
||||||
assert deserialized != null;
|
assert deserialized != null;
|
||||||
assertEqual(original, deserialized);
|
assertChunkStoreEquals(original, deserialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSimpleRegionRoundtrip() throws IOException {
|
void testSimpleRegionRoundTrip() throws IOException {
|
||||||
LegacyChunkStore original = new LegacyChunkStore(mockWorld, 12, 12);
|
LegacyChunkStore original = new LegacyChunkStore(mockWorld, 12, 12);
|
||||||
original.setTrue(14, 89, 12);
|
original.setTrue(14, 89, 12);
|
||||||
original.setTrue(14, 90, 12);
|
original.setTrue(14, 90, 12);
|
||||||
@ -196,7 +79,7 @@ class ChunkStoreTest {
|
|||||||
File file = new File(tempDir, "SimpleRegionRoundTrip.region");
|
File file = new File(tempDir, "SimpleRegionRoundTrip.region");
|
||||||
McMMOSimpleRegionFile region = new McMMOSimpleRegionFile(file, 0, 0);
|
McMMOSimpleRegionFile region = new McMMOSimpleRegionFile(file, 0, 0);
|
||||||
try (DataOutputStream outputStream = region.getOutputStream(12, 12)) {
|
try (DataOutputStream outputStream = region.getOutputStream(12, 12)) {
|
||||||
outputStream.write(serializeChunkstore(original));
|
outputStream.write(serializeChunkStore(original));
|
||||||
}
|
}
|
||||||
region.close();
|
region.close();
|
||||||
region = new McMMOSimpleRegionFile(file, 0, 0);
|
region = new McMMOSimpleRegionFile(file, 0, 0);
|
||||||
@ -204,229 +87,10 @@ class ChunkStoreTest {
|
|||||||
Assertions.assertNotNull(is);
|
Assertions.assertNotNull(is);
|
||||||
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(is);
|
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(is);
|
||||||
assert deserialized != null;
|
assert deserialized != null;
|
||||||
assertEqual(original, deserialized);
|
assertChunkStoreEquals(original, deserialized);
|
||||||
}
|
}
|
||||||
region.close();
|
region.close();
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSimpleRegionRejectsOutOfBounds() {
|
|
||||||
File file = new File(tempDir, "SimpleRegionRoundTrip.region");
|
|
||||||
McMMOSimpleRegionFile region = new McMMOSimpleRegionFile(file, 0, 0);
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(-1, 0));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(0, -1));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(32, 0));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(0, 32));
|
|
||||||
region.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testChunkStoreRejectsOutOfBounds() {
|
|
||||||
ChunkStore chunkStore = new BitSetChunkStore(mockWorld, 0, 0);
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(-1, 0, 0));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, -1, 0));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, 0, -1));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(16, 0, 0));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, mockWorld.getMaxHeight(), 0));
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, 0, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testRegressionChunkMirrorBug() {
|
|
||||||
ChunkManager chunkManager = new HashChunkManager();
|
|
||||||
Block mockBlockA = Mockito.mock(Block.class);
|
|
||||||
Mockito.when(mockBlockA.getX()).thenReturn(15);
|
|
||||||
Mockito.when(mockBlockA.getZ()).thenReturn(15);
|
|
||||||
Mockito.when(mockBlockA.getY()).thenReturn(0);
|
|
||||||
Mockito.when(mockBlockA.getWorld()).thenReturn(mockWorld);
|
|
||||||
Block mockBlockB = Mockito.mock(Block.class);
|
|
||||||
Mockito.when(mockBlockB.getX()).thenReturn(-15);
|
|
||||||
Mockito.when(mockBlockB.getZ()).thenReturn(-15);
|
|
||||||
Mockito.when(mockBlockB.getY()).thenReturn(0);
|
|
||||||
Mockito.when(mockBlockB.getWorld()).thenReturn(mockWorld);
|
|
||||||
|
|
||||||
chunkManager.setTrue(mockBlockA);
|
|
||||||
chunkManager.setFalse(mockBlockB);
|
|
||||||
Assertions.assertTrue(chunkManager.isTrue(mockBlockA));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertEqual(ChunkStore expected, ChunkStore actual) {
|
|
||||||
Assertions.assertEquals(expected.getChunkMin(), actual.getChunkMin());
|
|
||||||
Assertions.assertEquals(expected.getChunkMax(), actual.getChunkMax());
|
|
||||||
assertEqualIgnoreMinMax(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertEqualIgnoreMinMax(ChunkStore expected, ChunkStore actual) {
|
|
||||||
Assertions.assertEquals(expected.getChunkX(), actual.getChunkX());
|
|
||||||
Assertions.assertEquals(expected.getChunkZ(), actual.getChunkZ());
|
|
||||||
Assertions.assertEquals(expected.getWorldId(), actual.getWorldId());
|
|
||||||
for (int y = Math.min(actual.getChunkMin(), expected.getChunkMin()); y < Math.max(actual.getChunkMax(), expected.getChunkMax()); y++) {
|
|
||||||
if (expected.getChunkMin() > y || actual.getChunkMin() > y || expected.getChunkMax() <= y || actual.getChunkMax() <= y)
|
|
||||||
continue; // Ignore
|
|
||||||
for (int x = 0; x < 16; x++)
|
|
||||||
for (int z = 0; z < 16; z++)
|
|
||||||
Assertions.assertEquals(expected.isTrue(x, y, z), actual.isTrue(x, y, z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] serializeChunkstore(@NotNull ChunkStore chunkStore) throws IOException {
|
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
||||||
if (chunkStore instanceof BitSetChunkStore)
|
|
||||||
BitSetChunkStore.Serialization.writeChunkStore(new DataOutputStream(byteArrayOutputStream), chunkStore);
|
|
||||||
else
|
|
||||||
new UnitTestObjectOutputStream(byteArrayOutputStream).writeObject(chunkStore); // Serializes the class as if
|
|
||||||
// it were the old
|
|
||||||
// PrimitiveChunkStore
|
|
||||||
return byteArrayOutputStream.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LegacyChunkStore implements ChunkStore, Serializable {
|
|
||||||
private static final long serialVersionUID = -1L;
|
|
||||||
transient private boolean dirty = false;
|
|
||||||
public boolean[][][] store;
|
|
||||||
private static final int CURRENT_VERSION = 7;
|
|
||||||
private static final int MAGIC_NUMBER = 0xEA5EDEBB;
|
|
||||||
private final int cx;
|
|
||||||
private final int cz;
|
|
||||||
private final @NotNull UUID worldUid;
|
|
||||||
|
|
||||||
public LegacyChunkStore(@NotNull World world, int cx, int cz) {
|
|
||||||
this.cx = cx;
|
|
||||||
this.cz = cz;
|
|
||||||
this.worldUid = world.getUID();
|
|
||||||
this.store = new boolean[16][16][world.getMaxHeight()];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDirty() {
|
|
||||||
return dirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDirty(boolean dirty) {
|
|
||||||
this.dirty = dirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getChunkX() {
|
|
||||||
return cx;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getChunkZ() {
|
|
||||||
return cz;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getChunkMin() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getChunkMax() {
|
|
||||||
return store[0][0].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull UUID getWorldId() {
|
|
||||||
return worldUid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTrue(int x, int y, int z) {
|
|
||||||
return store[x][z][y];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTrue(int x, int y, int z) {
|
|
||||||
if (y >= store[0][0].length || y < 0)
|
|
||||||
return;
|
|
||||||
store[x][z][y] = true;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFalse(int x, int y, int z) {
|
|
||||||
if (y >= store[0][0].length || y < 0)
|
|
||||||
return;
|
|
||||||
store[x][z][y] = false;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(int x, int y, int z, boolean value) {
|
|
||||||
if (y >= store[0][0].length || y < 0)
|
|
||||||
return;
|
|
||||||
store[x][z][y] = value;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
for (int y = 0; y < store[0][0].length; y++) {
|
|
||||||
if (store[x][z][y]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeObject(@NotNull ObjectOutputStream out) throws IOException {
|
|
||||||
out.writeInt(MAGIC_NUMBER);
|
|
||||||
out.writeInt(CURRENT_VERSION);
|
|
||||||
|
|
||||||
out.writeLong(worldUid.getLeastSignificantBits());
|
|
||||||
out.writeLong(worldUid.getMostSignificantBits());
|
|
||||||
out.writeInt(cx);
|
|
||||||
out.writeInt(cz);
|
|
||||||
out.writeObject(store);
|
|
||||||
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readObject(@NotNull ObjectInputStream in) throws IOException, ClassNotFoundException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class UnitTestObjectOutputStream extends ObjectOutputStream {
|
|
||||||
|
|
||||||
public UnitTestObjectOutputStream(@NotNull OutputStream outputStream) throws IOException {
|
|
||||||
super(outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeUTF(@NotNull String str) throws IOException {
|
|
||||||
// Pretend to be the old class
|
|
||||||
if (str.equals(LegacyChunkStore.class.getName()))
|
|
||||||
str = "com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore";
|
|
||||||
super.writeUTF(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Block initMockBlock(int x, int y, int z) {
|
|
||||||
Block mockBlock = Mockito.mock(Block.class);
|
|
||||||
Mockito.when(mockBlock.getX()).thenReturn(x);
|
|
||||||
Mockito.when(mockBlock.getY()).thenReturn(y);
|
|
||||||
Mockito.when(mockBlock.getZ()).thenReturn(z);
|
|
||||||
Mockito.when(mockBlock.getWorld()).thenReturn(mockWorld);
|
|
||||||
return mockBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void recursiveDelete(@NotNull File directoryToBeDeleted) {
|
|
||||||
if (directoryToBeDeleted.isDirectory()) {
|
|
||||||
for (File file : directoryToBeDeleted.listFiles()) {
|
|
||||||
recursiveDelete(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
directoryToBeDeleted.delete();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
package com.gmail.nossr50.util.blockmeta;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for unit testing upgrades from the old ChunkStore class.
|
||||||
|
*/
|
||||||
|
class LegacyChunkStore implements ChunkStore, Serializable {
|
||||||
|
private static final long serialVersionUID = -1L;
|
||||||
|
transient private boolean dirty = false;
|
||||||
|
public boolean[][][] store;
|
||||||
|
private static final int CURRENT_VERSION = 7;
|
||||||
|
private static final int MAGIC_NUMBER = 0xEA5EDEBB;
|
||||||
|
private final int cx;
|
||||||
|
private final int cz;
|
||||||
|
private final @NotNull UUID worldUid;
|
||||||
|
|
||||||
|
public LegacyChunkStore(@NotNull World world, int cx, int cz) {
|
||||||
|
this.cx = cx;
|
||||||
|
this.cz = cz;
|
||||||
|
this.worldUid = world.getUID();
|
||||||
|
this.store = new boolean[16][16][world.getMaxHeight()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDirty() {
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDirty(boolean dirty) {
|
||||||
|
this.dirty = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChunkX() {
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChunkZ() {
|
||||||
|
return cz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChunkMin() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChunkMax() {
|
||||||
|
return store[0][0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull UUID getWorldId() {
|
||||||
|
return worldUid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(int x, int y, int z) {
|
||||||
|
return store[x][z][y];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTrue(int x, int y, int z) {
|
||||||
|
if (y >= store[0][0].length || y < 0)
|
||||||
|
return;
|
||||||
|
store[x][z][y] = true;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFalse(int x, int y, int z) {
|
||||||
|
if (y >= store[0][0].length || y < 0)
|
||||||
|
return;
|
||||||
|
store[x][z][y] = false;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int x, int y, int z, boolean value) {
|
||||||
|
if (y >= store[0][0].length || y < 0)
|
||||||
|
return;
|
||||||
|
store[x][z][y] = value;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int y = 0; y < store[0][0].length; y++) {
|
||||||
|
if (store[x][z][y]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeObject(@NotNull ObjectOutputStream out) throws IOException {
|
||||||
|
out.writeInt(MAGIC_NUMBER);
|
||||||
|
out.writeInt(CURRENT_VERSION);
|
||||||
|
|
||||||
|
out.writeLong(worldUid.getLeastSignificantBits());
|
||||||
|
out.writeLong(worldUid.getMostSignificantBits());
|
||||||
|
out.writeInt(cx);
|
||||||
|
out.writeInt(cz);
|
||||||
|
out.writeObject(store);
|
||||||
|
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readObject(@NotNull ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.gmail.nossr50.util.blockmeta;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
class UnitTestObjectOutputStream extends ObjectOutputStream {
|
||||||
|
|
||||||
|
public UnitTestObjectOutputStream(@NotNull OutputStream outputStream) throws IOException {
|
||||||
|
super(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeUTF(@NotNull String str) throws IOException {
|
||||||
|
// Pretend to be the old class
|
||||||
|
if (str.equals(LegacyChunkStore.class.getName()))
|
||||||
|
str = "com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore";
|
||||||
|
super.writeUTF(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,192 @@
|
|||||||
|
package com.gmail.nossr50.util.blockmeta;
|
||||||
|
|
||||||
|
import com.gmail.nossr50.mcMMO;
|
||||||
|
import com.gmail.nossr50.util.BlockUtils;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.gmail.nossr50.util.blockmeta.BlockStoreTestUtils.LEGACY_WORLD_HEIGHT_MAX;
|
||||||
|
import static com.gmail.nossr50.util.blockmeta.BlockStoreTestUtils.LEGACY_WORLD_HEIGHT_MIN;
|
||||||
|
import static org.bukkit.Bukkit.getWorld;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Could be a lot better. But some tests are better than none! Tests the major things, still kinda unit-testy. Verifies
|
||||||
|
* that the serialization isn't completely broken.
|
||||||
|
*/
|
||||||
|
class UserBlockTrackerTest {
|
||||||
|
private static File tempDir;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setUpClass() {
|
||||||
|
tempDir = Files.createTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public static void tearDownClass() {
|
||||||
|
recursiveDelete(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private World mockWorld;
|
||||||
|
|
||||||
|
private MockedStatic<Bukkit> bukkitMock;
|
||||||
|
private MockedStatic<mcMMO> mcMMOMock;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUpMock() {
|
||||||
|
UUID worldUUID = UUID.randomUUID();
|
||||||
|
mockWorld = Mockito.mock(World.class);
|
||||||
|
when(mockWorld.getUID()).thenReturn(worldUUID);
|
||||||
|
when(mockWorld.getMaxHeight()).thenReturn(256);
|
||||||
|
when(mockWorld.getWorldFolder()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
bukkitMock = mockStatic(Bukkit.class);
|
||||||
|
bukkitMock.when(() -> getWorld(worldUUID)).thenReturn(mockWorld);
|
||||||
|
|
||||||
|
mcMMOMock = mockStatic(mcMMO.class);
|
||||||
|
|
||||||
|
when(mockWorld.getMinHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MIN);
|
||||||
|
when(mockWorld.getMaxHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void teardownMock() {
|
||||||
|
bukkitMock.close();
|
||||||
|
mcMMOMock.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setIneligibleShouldThrowIndexOutOfBoundsException() {
|
||||||
|
when(mockWorld.getMinHeight()).thenReturn(-64);
|
||||||
|
final HashChunkManager hashChunkManager = new HashChunkManager();
|
||||||
|
|
||||||
|
// Top Block
|
||||||
|
final Block illegalHeightBlock = initMockBlock(1337, 256, -1337);
|
||||||
|
assertFalse(hashChunkManager.isIneligible(illegalHeightBlock));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> hashChunkManager.setIneligible(illegalHeightBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSetEligibility() {
|
||||||
|
when(mockWorld.getMinHeight()).thenReturn(-64);
|
||||||
|
final HashChunkManager hashChunkManager = new HashChunkManager();
|
||||||
|
int radius = 2; // Could be anything but drastically changes test time
|
||||||
|
|
||||||
|
for (int x = -radius; x <= radius; x++) {
|
||||||
|
for (int y = mockWorld.getMinHeight(); y < mockWorld.getMaxHeight(); y++) {
|
||||||
|
for (int z = -radius; z <= radius; z++) {
|
||||||
|
final Block testBlock = initMockBlock(x, y, z);
|
||||||
|
// mark ineligible
|
||||||
|
hashChunkManager.setIneligible(testBlock);
|
||||||
|
assertTrue(hashChunkManager.isIneligible(testBlock));
|
||||||
|
|
||||||
|
// mark eligible
|
||||||
|
hashChunkManager.setEligible(testBlock);
|
||||||
|
// Might as well test both isIneligible and isEligible while we are here
|
||||||
|
assertFalse(hashChunkManager.isIneligible(testBlock));
|
||||||
|
assertTrue(hashChunkManager.isEligible(testBlock));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Whatever is going on down here should be in its own test
|
||||||
|
// Bot Block
|
||||||
|
final Block bottomBlock = initMockBlock(1337, 0, -1337);
|
||||||
|
assertFalse(hashChunkManager.isIneligible(bottomBlock));
|
||||||
|
|
||||||
|
assertTrue(BlockUtils.isWithinWorldBounds(bottomBlock));
|
||||||
|
hashChunkManager.setIneligible(bottomBlock);
|
||||||
|
assertTrue(hashChunkManager.isIneligible(bottomBlock));
|
||||||
|
|
||||||
|
// Top Block
|
||||||
|
final Block topBlock = initMockBlock(1337, 255, -1337);
|
||||||
|
assertFalse(hashChunkManager.isIneligible(topBlock));
|
||||||
|
|
||||||
|
assertTrue(BlockUtils.isWithinWorldBounds(topBlock));
|
||||||
|
hashChunkManager.setIneligible(topBlock);
|
||||||
|
assertTrue(hashChunkManager.isIneligible(topBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testChunkCoords() throws IOException {
|
||||||
|
// TODO: Unfinished test?
|
||||||
|
for (int x = -96; x < 0; x++) {
|
||||||
|
int cx = x >> 4;
|
||||||
|
int ix = Math.abs(x) % 16;
|
||||||
|
//System.out.print(cx + ":" + ix + " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimpleRegionRejectsOutOfBounds() {
|
||||||
|
File file = new File(tempDir, "SimpleRegionRoundTrip.region");
|
||||||
|
McMMOSimpleRegionFile region = new McMMOSimpleRegionFile(file, 0, 0);
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(-1, 0));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(0, -1));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(32, 0));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> region.getOutputStream(0, 32));
|
||||||
|
region.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testChunkStoreRejectsOutOfBounds() {
|
||||||
|
ChunkStore chunkStore = new BitSetChunkStore(mockWorld, 0, 0);
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(-1, 0, 0));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, -1, 0));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, 0, -1));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(16, 0, 0));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, mockWorld.getMaxHeight(), 0));
|
||||||
|
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> chunkStore.setTrue(0, 0, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegressionChunkMirrorBug() {
|
||||||
|
final UserBlockTracker chunkManager = new HashChunkManager();
|
||||||
|
Block mockBlockA = Mockito.mock(Block.class);
|
||||||
|
when(mockBlockA.getX()).thenReturn(15);
|
||||||
|
when(mockBlockA.getZ()).thenReturn(15);
|
||||||
|
when(mockBlockA.getY()).thenReturn(0);
|
||||||
|
when(mockBlockA.getWorld()).thenReturn(mockWorld);
|
||||||
|
Block mockBlockB = Mockito.mock(Block.class);
|
||||||
|
when(mockBlockB.getX()).thenReturn(-15);
|
||||||
|
when(mockBlockB.getZ()).thenReturn(-15);
|
||||||
|
when(mockBlockB.getY()).thenReturn(0);
|
||||||
|
when(mockBlockB.getWorld()).thenReturn(mockWorld);
|
||||||
|
|
||||||
|
chunkManager.setIneligible(mockBlockA);
|
||||||
|
chunkManager.setEligible(mockBlockB);
|
||||||
|
assertTrue(chunkManager.isIneligible(mockBlockA));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Block initMockBlock(int x, int y, int z) {
|
||||||
|
final Block mockBlock = Mockito.mock(Block.class);
|
||||||
|
when(mockBlock.getX()).thenReturn(x);
|
||||||
|
when(mockBlock.getY()).thenReturn(y);
|
||||||
|
when(mockBlock.getZ()).thenReturn(z);
|
||||||
|
when(mockBlock.getWorld()).thenReturn(mockWorld);
|
||||||
|
return mockBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void recursiveDelete(@NotNull File directoryToBeDeleted) {
|
||||||
|
if (directoryToBeDeleted.isDirectory()) {
|
||||||
|
for (File file : directoryToBeDeleted.listFiles()) {
|
||||||
|
recursiveDelete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
directoryToBeDeleted.delete();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user