From aed4cb87be9944c3a7ade8d3e81f19b33fe05401 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 13:21:55 -0800 Subject: [PATCH 01/30] Fix enchanted books not being created with the proper data --- Changelog.txt | 2 ++ .../com/gmail/nossr50/skills/fishing/FishingManager.java | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 278a9f4d3..d45f0f9b3 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,9 +1,11 @@ Version 2.1.165 + Fixed a bug where Enchanted Books dropped by mcMMO (in Fishing) did not function correctly The mcMMO system which tracks player placed blocks has had some major rewrites (thanks t00thpick1) mcMMO will now be compatible with changes to world height (1.17 compatibility) Added missing cooldown locale message 'Commands.Database.Cooldown' NOTES: + Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names. t00thpick1 has taken time to rewrite our block meta tracking system to be more efficient, easier to maintain, and support upcoming features such as world height changes This new system is compatible with the old one, it will convert old files to the new format as needed. This update shouldn't break anything as the API is the same diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index 3e0ec850e..90e909525 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -37,6 +37,7 @@ import org.bukkit.entity.*; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.util.BoundingBox; @@ -466,11 +467,13 @@ public class FishingManager extends SkillManager { EnchantmentWrapper enchantmentWrapper = getRandomEnchantment(fishingTreasureBook.getLegalEnchantments()); ItemMeta itemMeta = itemStack.getItemMeta(); - if(itemMeta == null) + if(itemMeta == null) { return itemStack; + } - itemMeta.addEnchant(enchantmentWrapper.getEnchantment(), enchantmentWrapper.getEnchantmentLevel(), ExperienceConfig.getInstance().allowUnsafeEnchantments()); - itemStack.setItemMeta(itemMeta); + EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta; + enchantmentStorageMeta.addStoredEnchant(enchantmentWrapper.getEnchantment(), enchantmentWrapper.getEnchantmentLevel(), ExperienceConfig.getInstance().allowUnsafeEnchantments()); + itemStack.setItemMeta(enchantmentStorageMeta); return itemStack; } From 2664ae4bd609261fcb448c871ecbaca3d0dc8f2e Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 15:25:21 -0800 Subject: [PATCH 02/30] Optimize ChunkUnloadEvent & Partial rewrite to COTW entity tracking + some tweaks to COTW entity removal --- Changelog.txt | 4 + .../nossr50/listeners/ChunkListener.java | 20 +- .../nossr50/listeners/EntityListener.java | 10 +- src/main/java/com/gmail/nossr50/mcMMO.java | 7 + .../skills/fishing/FishingManager.java | 27 +-- .../nossr50/skills/taming/TamingManager.java | 86 +------ .../skills/taming/TrackedTamingEntity.java | 43 +--- .../com/gmail/nossr50/util/ItemUtils.java | 27 +++ .../nossr50/util/TransientEntityTracker.java | 225 ++++++++++++++++++ .../resources/locale/locale_en_US.properties | 1 + 10 files changed, 294 insertions(+), 156 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java diff --git a/Changelog.txt b/Changelog.txt index d45f0f9b3..1be88f3c3 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,8 +1,12 @@ Version 2.1.165 Fixed a bug where Enchanted Books dropped by mcMMO (in Fishing) did not function correctly The mcMMO system which tracks player placed blocks has had some major rewrites (thanks t00thpick1) + Optimized our ChunkUnloadEvent, this should improve timings in this area + How mcMMO tracks COTW entities has been rewritten + When COTW summons are killed players are now informed (from anything other than the time expiring). mcMMO will now be compatible with changes to world height (1.17 compatibility) Added missing cooldown locale message 'Commands.Database.Cooldown' + Added new locale message 'Taming.Summon.COTW.Removed' NOTES: Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names. diff --git a/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java b/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java index 406a02436..761516deb 100644 --- a/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java @@ -1,30 +1,20 @@ package com.gmail.nossr50.listeners; import com.gmail.nossr50.mcMMO; -import com.gmail.nossr50.util.compat.layers.persistentdata.MobMetaFlagType; -import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; +import java.util.List; + public class ChunkListener implements Listener { @EventHandler(ignoreCancelled = true) public void onChunkUnload(ChunkUnloadEvent event) { - for(Entity entity : event.getChunk().getEntities()) { - if(entity instanceof LivingEntity) { - LivingEntity livingEntity = (LivingEntity) entity; - if(mcMMO.getCompatibilityManager().getPersistentDataLayer().hasMobFlag(MobMetaFlagType.COTW_SUMMONED_MOB, livingEntity)) { - - //Remove from existence - if(livingEntity.isValid()) { - mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity); - livingEntity.setHealth(0); - livingEntity.remove(); - } - } - } + List matchingEntities = mcMMO.getTransientEntityTracker().getAllTransientEntitiesInChunk(event.getChunk()); + for(LivingEntity livingEntity : matchingEntities) { + mcMMO.getTransientEntityTracker().removeSummon(livingEntity, null, false); } } } diff --git a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index 73f9b064b..24dd48031 100644 --- a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java @@ -644,11 +644,13 @@ public class EntityListener implements Listener { */ @EventHandler(ignoreCancelled = true) public void onEntityDeath(EntityDeathEvent event) { - /* WORLD BLACKLIST CHECK */ - if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) - return; - LivingEntity entity = event.getEntity(); + mcMMO.getTransientEntityTracker().removeSummon(entity, null, false); + + /* WORLD BLACKLIST CHECK */ + if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) { + return; + } if (ExperienceConfig.getInstance().isNPCInteractionPrevented() && Misc.isNPCEntityExcludingVillagers(entity)) { return; diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index 529f70fb5..06347a952 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -87,6 +87,7 @@ public class mcMMO extends JavaPlugin { private static TransientMetadataTools transientMetadataTools; private static ChatManager chatManager; private static CommandManager commandManager; //ACF + private static TransientEntityTracker transientEntityTracker; /* Adventure */ private static BukkitAudiences audiences; @@ -289,6 +290,8 @@ public class mcMMO extends JavaPlugin { chatManager = new ChatManager(this); commandManager = new CommandManager(this); + + transientEntityTracker = new TransientEntityTracker(); } public static PlayerLevelUtils getPlayerLevelUtils() { @@ -720,4 +723,8 @@ public class mcMMO extends JavaPlugin { public CommandManager getCommandManager() { return commandManager; } + + public static TransientEntityTracker getTransientEntityTracker() { + return transientEntityTracker; + } } diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index 90e909525..8360825c9 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -37,8 +37,6 @@ import org.bukkit.entity.*; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; -import org.bukkit.inventory.meta.EnchantmentStorageMeta; -import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; @@ -397,7 +395,7 @@ public class FishingManager extends SkillManager { if (treasure != null) { if(treasure instanceof FishingTreasureBook) { - treasureDrop = createEnchantBook((FishingTreasureBook) treasure); + treasureDrop = ItemUtils.createEnchantBook((FishingTreasureBook) treasure); } else { treasureDrop = treasure.getDrop().clone(); // Not cloning is bad, m'kay? @@ -461,29 +459,6 @@ public class FishingManager extends SkillManager { applyXpGain(fishXp + treasureXp, XPGainReason.PVE); } - - private @NotNull ItemStack createEnchantBook(@NotNull FishingTreasureBook fishingTreasureBook) { - ItemStack itemStack = fishingTreasureBook.getDrop().clone(); - EnchantmentWrapper enchantmentWrapper = getRandomEnchantment(fishingTreasureBook.getLegalEnchantments()); - ItemMeta itemMeta = itemStack.getItemMeta(); - - if(itemMeta == null) { - return itemStack; - } - - EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta; - enchantmentStorageMeta.addStoredEnchant(enchantmentWrapper.getEnchantment(), enchantmentWrapper.getEnchantmentLevel(), ExperienceConfig.getInstance().allowUnsafeEnchantments()); - itemStack.setItemMeta(enchantmentStorageMeta); - return itemStack; - } - - private @NotNull EnchantmentWrapper getRandomEnchantment(@NotNull List enchantmentWrappers) { - Collections.shuffle(enchantmentWrappers, Misc.getRandom()); - - int randomIndex = Misc.getRandom().nextInt(enchantmentWrappers.size()); - return enchantmentWrappers.get(randomIndex); - } - /** * Handle the vanilla XP boost for Fishing * diff --git a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java index 3fce43f26..d3160f50c 100644 --- a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java @@ -33,9 +33,7 @@ import org.bukkit.entity.*; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; public class TamingManager extends SkillManager { //TODO: Temporary static cache, will be changed in 2.2 @@ -43,8 +41,6 @@ public class TamingManager extends SkillManager { private static HashMap cotwSummonDataProperties; private long lastSummonTimeStamp; - private HashMap> playerSummonedEntities; - public TamingManager(McMMOPlayer mcMMOPlayer) { super(mcMMOPlayer, PrimarySkillType.TAMING); init(); @@ -56,20 +52,12 @@ public class TamingManager extends SkillManager { lastSummonTimeStamp = 0L; //Init per-player tracking of summoned entities - initPerPlayerSummonTracking(); + mcMMO.getTransientEntityTracker().initPlayer(mmoPlayer.getPlayer().getUniqueId()); //Hacky stuff used as a band-aid initStaticCaches(); } - private void initPerPlayerSummonTracking() { - playerSummonedEntities = new HashMap<>(); - - for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { - playerSummonedEntities.put(callOfTheWildType, new ArrayList<>()); - } - } - private void initStaticCaches() { //TODO: Temporary static cache, will be changed in 2.2 //This is shared between instances of TamingManager @@ -500,62 +488,16 @@ public class TamingManager extends SkillManager { * @param itemStack target ItemStack * @return true if it is used for any COTW */ - public boolean isCOTWItem(ItemStack itemStack) { + public boolean isCOTWItem(@NotNull ItemStack itemStack) { return summoningItems.containsKey(itemStack.getType()); } - //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update - private int getAmountCurrentlySummoned(CallOfTheWildType callOfTheWildType) { - //The tracker is unreliable so validate its contents first - recalibrateTracker(); - - return playerSummonedEntities.get(callOfTheWildType).size(); + private int getAmountCurrentlySummoned(@NotNull CallOfTheWildType callOfTheWildType) { + return mcMMO.getTransientEntityTracker().getAmountCurrentlySummoned(getPlayer().getUniqueId(), callOfTheWildType); } - //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update - private void addToTracker(LivingEntity livingEntity, CallOfTheWildType callOfTheWildType) { - TrackedTamingEntity trackedEntity = new TrackedTamingEntity(livingEntity, callOfTheWildType, this); - - playerSummonedEntities.get(callOfTheWildType).add(trackedEntity); - } - - //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update - public List getTrackedEntities(CallOfTheWildType callOfTheWildType) { - return playerSummonedEntities.get(callOfTheWildType); - } - - //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update - public void removeFromTracker(TrackedTamingEntity trackedEntity) { - playerSummonedEntities.get(trackedEntity.getCallOfTheWildType()).remove(trackedEntity); - - NotificationManager.sendPlayerInformationChatOnly(getPlayer(), "Taming.Summon.COTW.TimeExpired", StringUtils.getPrettyEntityTypeString(trackedEntity.getLivingEntity().getType())); - } - - /** - * Builds a new tracked list by determining which tracked things are still valid - */ - //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update - private void recalibrateTracker() { - for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { - ArrayList validEntities = getValidTrackedEntities(callOfTheWildType); - playerSummonedEntities.put(callOfTheWildType, validEntities); //Replace the old list with the new list - } - } - - //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update - private ArrayList getValidTrackedEntities(CallOfTheWildType callOfTheWildType) { - ArrayList validTrackedEntities = new ArrayList<>(); - - for(TrackedTamingEntity trackedTamingEntity : getTrackedEntities(callOfTheWildType)) { - LivingEntity livingEntity = trackedTamingEntity.getLivingEntity(); - - //Remove from existence - if(livingEntity != null && livingEntity.isValid()) { - validTrackedEntities.add(trackedTamingEntity); - } - } - - return validTrackedEntities; + private void addToTracker(@NotNull LivingEntity livingEntity, @NotNull CallOfTheWildType callOfTheWildType) { + mcMMO.getTransientEntityTracker().registerEntity(getPlayer().getUniqueId(), new TrackedTamingEntity(livingEntity, callOfTheWildType, getPlayer())); } /** @@ -564,20 +506,6 @@ public class TamingManager extends SkillManager { */ //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update public void cleanupAllSummons() { - for(List trackedTamingEntities : playerSummonedEntities.values()) { - for(TrackedTamingEntity trackedTamingEntity : trackedTamingEntities) { - LivingEntity livingEntity = trackedTamingEntity.getLivingEntity(); - - //Remove from existence - if(livingEntity != null && livingEntity.isValid()) { - mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity); - livingEntity.setHealth(0); - livingEntity.remove(); - } - } - - //Clear the list - trackedTamingEntities.clear(); - } + mcMMO.getTransientEntityTracker().cleanupPlayer(getPlayer()); } } diff --git a/src/main/java/com/gmail/nossr50/skills/taming/TrackedTamingEntity.java b/src/main/java/com/gmail/nossr50/skills/taming/TrackedTamingEntity.java index aabcd44da..9116c4fdf 100644 --- a/src/main/java/com/gmail/nossr50/skills/taming/TrackedTamingEntity.java +++ b/src/main/java/com/gmail/nossr50/skills/taming/TrackedTamingEntity.java @@ -4,61 +4,40 @@ import com.gmail.nossr50.config.Config; import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.util.Misc; -import com.gmail.nossr50.util.skills.ParticleEffectUtils; -import org.bukkit.Location; -import org.bukkit.Sound; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; - -import java.util.UUID; +import org.jetbrains.annotations.NotNull; public class TrackedTamingEntity extends BukkitRunnable { - private final LivingEntity livingEntity; - private final CallOfTheWildType callOfTheWildType; - private final UUID id; - private int length; - private final TamingManager tamingManagerRef; + private final @NotNull LivingEntity livingEntity; + private final @NotNull CallOfTheWildType callOfTheWildType; + private final @NotNull Player player; - protected TrackedTamingEntity(LivingEntity livingEntity, CallOfTheWildType callOfTheWildType, TamingManager tamingManagerRef) { - this.tamingManagerRef = tamingManagerRef; + protected TrackedTamingEntity(@NotNull LivingEntity livingEntity, @NotNull CallOfTheWildType callOfTheWildType, @NotNull Player player) { + this.player = player; this.callOfTheWildType = callOfTheWildType; this.livingEntity = livingEntity; - this.id = livingEntity.getUniqueId(); int tamingCOTWLength = Config.getInstance().getTamingCOTWLength(callOfTheWildType.getConfigEntityTypeEntry()); if (tamingCOTWLength > 0) { - this.length = tamingCOTWLength * Misc.TICK_CONVERSION_FACTOR; + int length = tamingCOTWLength * Misc.TICK_CONVERSION_FACTOR; this.runTaskLater(mcMMO.p, length); } } @Override public void run() { - if (livingEntity.isValid()) { - Location location = livingEntity.getLocation(); - location.getWorld().playSound(location, Sound.BLOCK_FIRE_EXTINGUISH, 0.8F, 0.8F); - ParticleEffectUtils.playCallOfTheWildEffect(livingEntity); - - if(tamingManagerRef != null) - tamingManagerRef.removeFromTracker(this); - - livingEntity.setHealth(0); - livingEntity.remove(); - } - + mcMMO.getTransientEntityTracker().removeSummon(this.getLivingEntity(), player, true); this.cancel(); } - public CallOfTheWildType getCallOfTheWildType() { + public @NotNull CallOfTheWildType getCallOfTheWildType() { return callOfTheWildType; } - public LivingEntity getLivingEntity() { + public @NotNull LivingEntity getLivingEntity() { return livingEntity; } - - public UUID getID() { - return id; - } } diff --git a/src/main/java/com/gmail/nossr50/util/ItemUtils.java b/src/main/java/com/gmail/nossr50/util/ItemUtils.java index 4e70625e6..62c8d2165 100644 --- a/src/main/java/com/gmail/nossr50/util/ItemUtils.java +++ b/src/main/java/com/gmail/nossr50/util/ItemUtils.java @@ -2,7 +2,10 @@ package com.gmail.nossr50.util; import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.config.party.ItemWeightConfig; +import com.gmail.nossr50.datatypes.treasure.EnchantmentWrapper; +import com.gmail.nossr50.datatypes.treasure.FishingTreasureBook; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.mcMMO; import org.bukkit.ChatColor; @@ -12,9 +15,11 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.FurnaceRecipe; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Recipe; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.List; public final class ItemUtils { @@ -538,4 +543,26 @@ public final class ItemUtils { public static boolean canBeSuperAbilityDigBoosted(@NotNull ItemStack itemStack) { return isShovel(itemStack) || isPickaxe(itemStack); } + + public static @NotNull ItemStack createEnchantBook(@NotNull FishingTreasureBook fishingTreasureBook) { + ItemStack itemStack = fishingTreasureBook.getDrop().clone(); + EnchantmentWrapper enchantmentWrapper = getRandomEnchantment(fishingTreasureBook.getLegalEnchantments()); + ItemMeta itemMeta = itemStack.getItemMeta(); + + if(itemMeta == null) { + return itemStack; + } + + EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta; + enchantmentStorageMeta.addStoredEnchant(enchantmentWrapper.getEnchantment(), enchantmentWrapper.getEnchantmentLevel(), ExperienceConfig.getInstance().allowUnsafeEnchantments()); + itemStack.setItemMeta(enchantmentStorageMeta); + return itemStack; + } + + public static @NotNull EnchantmentWrapper getRandomEnchantment(@NotNull List enchantmentWrappers) { + Collections.shuffle(enchantmentWrappers, Misc.getRandom()); + + int randomIndex = Misc.getRandom().nextInt(enchantmentWrappers.size()); + return enchantmentWrappers.get(randomIndex); + } } diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java new file mode 100644 index 000000000..a5495864e --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -0,0 +1,225 @@ +package com.gmail.nossr50.util; + +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.util.player.NotificationManager; +import com.gmail.nossr50.util.skills.ParticleEffectUtils; +import com.gmail.nossr50.util.text.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class TransientEntityTracker { + private final @NotNull HashMap>> perPlayerTransientEntityMap; + private final @NotNull HashSet chunkLookupCache; + + public TransientEntityTracker() { + perPlayerTransientEntityMap = new HashMap<>(); + chunkLookupCache = new HashSet<>(); + } + + public void initPlayer(@NotNull UUID playerUUID) { + if (!isPlayerRegistered(playerUUID)) { + registerPlayer(playerUUID); + } + } + + public void cleanupPlayer(@NotNull UUID playerUUID) { + cleanupAllSummons(null, playerUUID); + } + + public void cleanupPlayer(@NotNull Player player) { + //First remove all entities related to this player + cleanupAllSummons(player, player.getUniqueId()); + } + + private boolean isPlayerRegistered(@NotNull UUID playerUUID) { + return perPlayerTransientEntityMap.get(playerUUID) != null; + } + + private void registerPlayer(@NotNull UUID playerUUID) { + for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { + perPlayerTransientEntityMap.get(playerUUID).put(callOfTheWildType, new HashSet<>()); + } + } + + /** + * Get the tracked transient entities map for a specific player + * + * @param playerUUID the target uuid of the player + * @return the tracked entities map for the player, null if the player isn't registered + */ + public @Nullable HashMap> getPlayerTrackedEntityMap(@NotNull UUID playerUUID) { + return perPlayerTransientEntityMap.get(playerUUID); + } + + public void registerEntity(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { + if(!isPlayerRegistered(playerUUID)) { + mcMMO.p.getLogger().severe("Attempting to register entity to a player which hasn't been initialized!"); + initPlayer(playerUUID); + } + + //Add to map entry + getTrackedEntities(playerUUID, trackedTamingEntity.getCallOfTheWildType()).add(trackedTamingEntity); + + //Add to cache for chunk lookups + addToChunkLookupCache(trackedTamingEntity); + } + + /** + * Get the tracked taming entities for a player + * If the player isn't registered this will return null + * + * @param playerUUID the target uuid of the player + * @param callOfTheWildType target type + * @return the set of tracked entities for the player, null if the player isn't registered, the set can be empty + */ + private @Nullable HashSet getTrackedEntities(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { + HashMap> playerEntityMap = getPlayerTrackedEntityMap(playerUUID); + + if(playerEntityMap == null) + return null; + + return playerEntityMap.get(callOfTheWildType); + } + + private void addToChunkLookupCache(@NotNull TrackedTamingEntity trackedTamingEntity) { + chunkLookupCache.add(trackedTamingEntity.getLivingEntity()); + } + + public void unregisterEntity(@NotNull LivingEntity livingEntity) { + chunkLookupCacheCleanup(livingEntity); + perPlayerTransientMapCleanup(livingEntity); + } + + private void chunkLookupCacheCleanup(@NotNull LivingEntity livingEntity) { + chunkLookupCache.remove(livingEntity); + } + + private void perPlayerTransientMapCleanup(@NotNull LivingEntity livingEntity) { + for(UUID uuid : perPlayerTransientEntityMap.keySet()) { + for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { + + HashSet trackedEntities = getTrackedEntities(uuid, callOfTheWildType); + + if(trackedEntities == null) + continue; + + Iterator iterator = trackedEntities.iterator(); + while (iterator.hasNext()) { + if(iterator.next().getLivingEntity().equals(livingEntity)) { + iterator.remove(); + return; + } + } + } + } + } + + public @NotNull List getAllTransientEntitiesInChunk(@NotNull Chunk chunk) { + ArrayList matchingEntities = new ArrayList<>(); + + for(LivingEntity livingEntity : chunkLookupCache) { + if(livingEntity.getLocation().getChunk().equals(chunk)) { + matchingEntities.add(livingEntity); + } + } + + return matchingEntities; + } + + /* + * Gross code below + */ + + /** + * Get the amount of a summon currently active for a player + * @param playerUUID target player + * @param callOfTheWildType summon type + * @return the amount of summons currently active for player of target type + */ + public int getAmountCurrentlySummoned(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { + HashSet trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType); + + if(trackedEntities == null) + return 0; + + return trackedEntities.size(); + } + + /** + * Kills a summon and removes its metadata + * Then it removes it from the tracker / chunk lookup cache + * + * @param livingEntity entity to remove + * @param player associated player + */ + public void removeSummon(@NotNull LivingEntity livingEntity, @Nullable Player player, boolean timeExpired) { + //Kill the summon & remove it + if(livingEntity.isValid()) { + livingEntity.setHealth(0); //Should trigger entity death events + livingEntity.remove(); + + Location location = livingEntity.getLocation(); + + if (location.getWorld() != null) { + location.getWorld().playSound(location, Sound.BLOCK_FIRE_EXTINGUISH, 0.8F, 0.8F); + ParticleEffectUtils.playCallOfTheWildEffect(livingEntity); + } + + //Inform player of summon death + if(player != null && player.isOnline()) { + if(timeExpired) { + NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.TimeExpired", StringUtils.getPrettyEntityTypeString(livingEntity.getType())); + } else { + NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.Removed", StringUtils.getPrettyEntityTypeString(livingEntity.getType())); + } + } + } + + //Remove our metadata + mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity); + + //Clean from trackers + unregisterEntity(livingEntity); + } + + /** + * Remove all tracked entities from existence if they currently exist + * Clear the tracked entity lists afterwards + * + * @deprecated use {@link #cleanupAllSummons(Player, UUID)} instead + */ + @Deprecated + private void cleanupAllSummons(@NotNull UUID playerUUID) { + cleanupAllSummons(Bukkit.getPlayer(playerUUID), playerUUID); + } + + /** + * Kills and cleans up all data related to all summoned entities for a player + * + * @param player used to send messages, can be null + * @param playerUUID used to grab associated data, cannot be null + */ + private void cleanupAllSummons(@Nullable Player player, @NotNull UUID playerUUID) { + for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { + HashSet trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType); + + if(trackedEntities == null) + continue; + + for(TrackedTamingEntity trackedTamingEntity : trackedEntities) { + //Remove from existence + removeSummon(trackedTamingEntity.getLivingEntity(), player, false); + } + } + } +} diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index 939132271..1020210c1 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -487,6 +487,7 @@ Taming.Summon.COTW.Success.WithoutLifespan=&a(Call Of The Wild) &7You have summo Taming.Summon.COTW.Success.WithLifespan=&a(Call Of The Wild) &7You have summoned a &6{0}&7 and it has a duration of &6{1}&7 seconds. Taming.Summon.COTW.Limit=&a(Call Of The Wild) &7You can only have &c{0} &7summoned &7{1} pets at the same time. Taming.Summon.COTW.TimeExpired=&a(Call Of The Wild) &7Time is up, your &6{0}&7 departs. +Taming.Summon.COTW.Removed=&a(Call Of The Wild) &7Your summoned &6{0}&7 has vanished from this world. Taming.Summon.COTW.BreedingDisallowed=&a(Call Of The Wild) &cYou cannot breed a summoned animal. Taming.Summon.COTW.NeedMoreItems=&a(Call Of The Wild) &7You need &e{0}&7 more &3{1}&7(s) Taming.Summon.Name.Format=&6(COTW) &f{0}'s {1} From 8ee405fbfde34c12ade27518c61b4c8a1c3948fc Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 15:49:32 -0800 Subject: [PATCH 03/30] Ignore "fake player" npcs in EntityPickupItemEvent --- Changelog.txt | 1 + .../com/gmail/nossr50/listeners/PlayerListener.java | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 1be88f3c3..9d7f4f4a6 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -7,6 +7,7 @@ Version 2.1.165 mcMMO will now be compatible with changes to world height (1.17 compatibility) Added missing cooldown locale message 'Commands.Database.Cooldown' Added new locale message 'Taming.Summon.COTW.Removed' + mcMMO will ignore PlayerPickupItem events from "Fake-Player" NPCs if it recognizes them as such, this will prevent some issues NOTES: Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names. diff --git a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java index 1339a8850..bf47ef5a0 100644 --- a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java @@ -447,6 +447,10 @@ public class PlayerListener implements Listener { if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) return; + if(Misc.isNPCEntityExcludingVillagers(event.getEntity())) { + return; + } + if(event.getEntity() instanceof Player) { Player player = (Player) event.getEntity(); @@ -463,13 +467,11 @@ public class PlayerListener implements Listener { } //Profile not loaded - if(UserManager.getPlayer(player) == null) - { + McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); + if(mcMMOPlayer == null) { return; } - McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); - Item drop = event.getItem(); ItemStack dropStack = drop.getItemStack(); From 3f6de1c4ba5782a23bede55a5af8c95ab858057c Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 15:53:01 -0800 Subject: [PATCH 04/30] Fix giving double treasures if the setting to give extra fish is on Fixes #4363 --- Changelog.txt | 2 +- .../java/com/gmail/nossr50/skills/fishing/FishingManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 9d7f4f4a6..9f3daebcf 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -7,7 +7,7 @@ Version 2.1.165 mcMMO will now be compatible with changes to world height (1.17 compatibility) Added missing cooldown locale message 'Commands.Database.Cooldown' Added new locale message 'Taming.Summon.COTW.Removed' - mcMMO will ignore PlayerPickupItem events from "Fake-Player" NPCs if it recognizes them as such, this will prevent some issues + mcMMO will ignore EntityPickupItemEvents from "Fake-Player" NPCs if it recognizes them as such, this will prevent some compatibility issues with some plugins NOTES: Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names. diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index 8360825c9..ab597a8da 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -449,11 +449,11 @@ public class FishingManager extends SkillManager { } if(fishingSucceeds) { - fishingCatch.setItemStack(treasureDrop); - if (Config.getInstance().getFishingExtraFish()) { Misc.spawnItem(player.getEyeLocation(), fishingCatch.getItemStack(), ItemSpawnReason.FISHING_EXTRA_FISH); } + + fishingCatch.setItemStack(treasureDrop); } applyXpGain(fishXp + treasureXp, XPGainReason.PVE); From c05c8e1b1d40edf5db8c73b6e8f99acc9fdc088d Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 16:08:23 -0800 Subject: [PATCH 05/30] Fix init player in TransientyEntityTracker --- .../com/gmail/nossr50/skills/taming/TamingManager.java | 2 +- .../java/com/gmail/nossr50/util/MaterialMapStore.java | 9 +++++++++ .../com/gmail/nossr50/util/TransientEntityTracker.java | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java index d3160f50c..0ec720ec1 100644 --- a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java @@ -41,7 +41,7 @@ public class TamingManager extends SkillManager { private static HashMap cotwSummonDataProperties; private long lastSummonTimeStamp; - public TamingManager(McMMOPlayer mcMMOPlayer) { + public TamingManager(@NotNull McMMOPlayer mcMMOPlayer) { super(mcMMOPlayer, PrimarySkillType.TAMING); init(); } diff --git a/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java b/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java index 6be54c63c..0f0bcc25c 100644 --- a/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java +++ b/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java @@ -54,6 +54,8 @@ public class MaterialMapStore { private final @NotNull HashSet enchantables; private final @NotNull HashSet ores; + private final @NotNull HashSet intendedToolPickAxe; + private final @NotNull HashSet intendedToolShovel; private final @NotNull HashMap tierValue; @@ -99,6 +101,8 @@ public class MaterialMapStore { enchantables = new HashSet<>(); ores = new HashSet<>(); + intendedToolPickAxe = new HashSet<>(); + intendedToolShovel = new HashSet<>(); tierValue = new HashMap<>(); @@ -206,6 +210,11 @@ public class MaterialMapStore { ores.add("gilded_blackstone"); } + private void fillIntendedTools() { + intendedToolPickAxe.addAll(ores); + + } + private void fillArmors() { fillLeatherArmorWhiteList(); fillIronArmorWhiteList(); diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java index a5495864e..486842f7f 100644 --- a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -46,6 +46,8 @@ public class TransientEntityTracker { } private void registerPlayer(@NotNull UUID playerUUID) { + perPlayerTransientEntityMap.put(playerUUID, new HashMap>()); + for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { perPlayerTransientEntityMap.get(playerUUID).put(callOfTheWildType, new HashSet<>()); } From c408c7d057b3162f05cdefe60973286f34c4b6b0 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 16:26:37 -0800 Subject: [PATCH 06/30] avoid concurrent exceptions on TransientEntityTracker --- .../com/gmail/nossr50/util/TransientEntityTracker.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java index 486842f7f..4b22abaea 100644 --- a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -6,6 +6,7 @@ import com.gmail.nossr50.skills.taming.TrackedTamingEntity; import com.gmail.nossr50.util.player.NotificationManager; import com.gmail.nossr50.util.skills.ParticleEffectUtils; import com.gmail.nossr50.util.text.StringUtils; +import com.google.common.collect.ImmutableSet; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; @@ -215,13 +216,17 @@ public class TransientEntityTracker { for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { HashSet trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType); - if(trackedEntities == null) + if(trackedEntities == null) { continue; + } - for(TrackedTamingEntity trackedTamingEntity : trackedEntities) { + ImmutableSet immutableSet = ImmutableSet.copyOf(trackedEntities); + + for(TrackedTamingEntity trackedTamingEntity : immutableSet) { //Remove from existence removeSummon(trackedTamingEntity.getLivingEntity(), player, false); } + } } } From 56b376eb540347284cd9cbf1721c1147cebe3945 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 16:41:08 -0800 Subject: [PATCH 07/30] SuperBreaker will always activate if the fastest tool for the block is a PickAxe Fixes #4362 --- Changelog.txt | 1 + .../com/gmail/nossr50/util/BlockUtils.java | 5 +- .../gmail/nossr50/util/MaterialMapStore.java | 187 ++++++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) diff --git a/Changelog.txt b/Changelog.txt index 9f3daebcf..44960faf2 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -8,6 +8,7 @@ Version 2.1.165 Added missing cooldown locale message 'Commands.Database.Cooldown' Added new locale message 'Taming.Summon.COTW.Removed' mcMMO will ignore EntityPickupItemEvents from "Fake-Player" NPCs if it recognizes them as such, this will prevent some compatibility issues with some plugins + SuperBreaker will now always activate if the target blocks fastest tool is a Pickaxe (used to require the block giving XP or being considered an ore) NOTES: Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names. diff --git a/src/main/java/com/gmail/nossr50/util/BlockUtils.java b/src/main/java/com/gmail/nossr50/util/BlockUtils.java index 67da9f4af..1c68e9747 100644 --- a/src/main/java/com/gmail/nossr50/util/BlockUtils.java +++ b/src/main/java/com/gmail/nossr50/util/BlockUtils.java @@ -141,10 +141,13 @@ public final class BlockUtils { * otherwise */ public static Boolean affectedBySuperBreaker(BlockState blockState) { + if(mcMMO.getMaterialMapStore().isIntendedToolPickaxe(blockState.getType())) + return true; + if (ExperienceConfig.getInstance().doesBlockGiveSkillXP(PrimarySkillType.MINING, blockState.getBlockData())) return true; - return isOre(blockState) || mcMMO.getModManager().isCustomMiningBlock(blockState); + return mcMMO.getModManager().isCustomMiningBlock(blockState); } /** diff --git a/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java b/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java index 0f0bcc25c..61b772eec 100644 --- a/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java +++ b/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java @@ -213,6 +213,185 @@ public class MaterialMapStore { private void fillIntendedTools() { intendedToolPickAxe.addAll(ores); + intendedToolPickAxe.add("ice"); + intendedToolPickAxe.add("packed_ice"); + intendedToolPickAxe.add("blue_ice"); + intendedToolPickAxe.add("frosted_ice"); + intendedToolPickAxe.add("anvil"); + intendedToolPickAxe.add("bell"); + intendedToolPickAxe.add("block_of_redstone"); + intendedToolPickAxe.add("brewing_stand"); + intendedToolPickAxe.add("cauldron"); + intendedToolPickAxe.add("chain"); + intendedToolPickAxe.add("hopper"); + intendedToolPickAxe.add("iron_bars"); + intendedToolPickAxe.add("iron_door"); + intendedToolPickAxe.add("iron_trapdoor"); + intendedToolPickAxe.add("lantern"); + intendedToolPickAxe.add("weighted_pressure_plates"); + intendedToolPickAxe.add("block_of_iron"); + intendedToolPickAxe.add("copper_blocks"); + intendedToolPickAxe.add("cut_copper"); + intendedToolPickAxe.add("cut_copper_slab"); + intendedToolPickAxe.add("cut_copper_stairs"); + intendedToolPickAxe.add("lapis_lazuli_block"); + intendedToolPickAxe.add("lightning_rod"); + intendedToolPickAxe.add("block_of_diamond"); + intendedToolPickAxe.add("block_of_emerald"); + intendedToolPickAxe.add("block_of_gold"); + intendedToolPickAxe.add("block_of_netherite"); + intendedToolPickAxe.add("piston"); + intendedToolPickAxe.add("sticky_piston"); + intendedToolPickAxe.add("conduit"); + intendedToolPickAxe.add("shulker_box"); + intendedToolPickAxe.add("element_constructor"); //be & ee + intendedToolPickAxe.add("compound_creator"); //be & ee + intendedToolPickAxe.add("material_reducer"); //be & ee + intendedToolPickAxe.add("activator_rail"); + intendedToolPickAxe.add("detector_rail"); + intendedToolPickAxe.add("powered_rail"); + intendedToolPickAxe.add("rail"); + intendedToolPickAxe.add("andesite"); + intendedToolPickAxe.add("basalt"); + intendedToolPickAxe.add("blackstone"); + intendedToolPickAxe.add("blast_furnace"); + intendedToolPickAxe.add("block_of_coal"); + intendedToolPickAxe.add("block_of_quartz"); + intendedToolPickAxe.add("bricks"); + intendedToolPickAxe.add("cobblestone"); + intendedToolPickAxe.add("cobblestone_wall"); + intendedToolPickAxe.add("concrete"); + intendedToolPickAxe.add("dark_prismarine"); + intendedToolPickAxe.add("diorite"); + intendedToolPickAxe.add("dispenser"); + intendedToolPickAxe.add("dripstone_block"); + intendedToolPickAxe.add("dropper"); + intendedToolPickAxe.add("enchantment_table"); + intendedToolPickAxe.add("end_stone"); + intendedToolPickAxe.add("ender_chest"); + intendedToolPickAxe.add("furnace"); + intendedToolPickAxe.add("glazed_terracotta"); + intendedToolPickAxe.add("granite"); + intendedToolPickAxe.add("grindstone"); + intendedToolPickAxe.add("heat_block"); //be & ee + intendedToolPickAxe.add("lodestone"); + intendedToolPickAxe.add("mossy_cobblestone"); + intendedToolPickAxe.add("nether_bricks"); + intendedToolPickAxe.add("nether_brick_fence"); + intendedToolPickAxe.add("nether_gold_ore"); + intendedToolPickAxe.add("nether_quartz_ore"); + intendedToolPickAxe.add("netherrack"); + intendedToolPickAxe.add("observer"); + intendedToolPickAxe.add("prismarine"); + intendedToolPickAxe.add("prismarine_bricks"); + intendedToolPickAxe.add("pointed_dripstone"); + intendedToolPickAxe.add("polished_andesite"); + intendedToolPickAxe.add("polished_blackstone"); + intendedToolPickAxe.add("polished_blackstone_bricks"); + intendedToolPickAxe.add("polished_diorite"); + intendedToolPickAxe.add("polished_granite"); + intendedToolPickAxe.add("red_sandstone"); + intendedToolPickAxe.add("sandstone"); + intendedToolPickAxe.add("smoker"); + intendedToolPickAxe.add("spawner"); + intendedToolPickAxe.add("stonecutter"); +// intendedToolPickAxe.add("slabs"); + intendedToolPickAxe.add("colored_terracotta"); +// intendedToolPickAxe.add("stairs"); + intendedToolPickAxe.add("smooth_stone"); + intendedToolPickAxe.add("stone"); + intendedToolPickAxe.add("stone_bricks"); + intendedToolPickAxe.add("stone_button"); + intendedToolPickAxe.add("stone_pressure_plate"); + intendedToolPickAxe.add("terracotta"); + intendedToolPickAxe.add("amethyst_bud"); + intendedToolPickAxe.add("amethyst_cluster"); + intendedToolPickAxe.add("block_of_amethyst"); + intendedToolPickAxe.add("budding_amethyst"); + intendedToolPickAxe.add("ancient_debris"); + intendedToolPickAxe.add("crying_obsidian"); + intendedToolPickAxe.add("glowing_obsidian"); //be + intendedToolPickAxe.add("obsidian"); + intendedToolPickAxe.add("respawn_anchor"); + + //slabs + intendedToolPickAxe.add("petrified_oak_slab"); + intendedToolPickAxe.add("stone_slab"); + intendedToolPickAxe.add("smooth_stone_slab"); + intendedToolPickAxe.add("cobblestone_slab"); + intendedToolPickAxe.add("mossy_cobblestone_slab"); + intendedToolPickAxe.add("stone_brick_slab"); + intendedToolPickAxe.add("mossy_stone_brick_slab"); + intendedToolPickAxe.add("andesite_slab"); + intendedToolPickAxe.add("polished_andesite_slab"); + intendedToolPickAxe.add("diorite_slab"); + intendedToolPickAxe.add("polished_diorite_slab"); + intendedToolPickAxe.add("granite_slab"); + intendedToolPickAxe.add("polished_granite_slab"); + intendedToolPickAxe.add("sandstone_slab"); + intendedToolPickAxe.add("cut_sandstone_slab"); + intendedToolPickAxe.add("smooth_sandstone_slab"); + intendedToolPickAxe.add("red_sandstone_slab"); + intendedToolPickAxe.add("cut_red_sandstone_slab"); + intendedToolPickAxe.add("smooth_red_sandstone_slab"); + intendedToolPickAxe.add("brick_slab"); + intendedToolPickAxe.add("prismarine_brick_slab"); + intendedToolPickAxe.add("dark_prismarine_slab"); + intendedToolPickAxe.add("nether_brick_slab"); + intendedToolPickAxe.add("red_netherbrick_slab"); + intendedToolPickAxe.add("quartz_slab"); + intendedToolPickAxe.add("smooth_quartz_slab"); + intendedToolPickAxe.add("purpur_slab"); + intendedToolPickAxe.add("end_stone_brick_slab"); + intendedToolPickAxe.add("blackstone_slab"); + intendedToolPickAxe.add("polished_blackstone_slab"); + intendedToolPickAxe.add("polished_blackstone_brick_slab"); + intendedToolPickAxe.add("lightly_weathered_cut_copper_slab"); + intendedToolPickAxe.add("semi_weathered_cut_copper_slab"); + intendedToolPickAxe.add("waxed_semi_weathered_cut_copper_slab"); + intendedToolPickAxe.add("weathered_cut_copper_slab"); + intendedToolPickAxe.add("waxed_cut_copper_slab"); + intendedToolPickAxe.add("waxed_lightly_weathered_cut_copper_slab"); + + //stairs (not all of these exist, just copied the above list and replaced slab with stairs) + intendedToolPickAxe.add("petrified_oak_stairs"); + intendedToolPickAxe.add("stone_stairs"); + intendedToolPickAxe.add("smooth_stone_stairs"); + intendedToolPickAxe.add("cobblestone_stairs"); + intendedToolPickAxe.add("mossy_cobblestone_stairs"); + intendedToolPickAxe.add("stone_brick_stairs"); + intendedToolPickAxe.add("mossy_stone_brick_stairs"); + intendedToolPickAxe.add("andesite_stairs"); + intendedToolPickAxe.add("polished_andesite_stairs"); + intendedToolPickAxe.add("diorite_stairs"); + intendedToolPickAxe.add("polished_diorite_stairs"); + intendedToolPickAxe.add("granite_stairs"); + intendedToolPickAxe.add("polished_granite_stairs"); + intendedToolPickAxe.add("sandstone_stairs"); + intendedToolPickAxe.add("cut_sandstone_stairs"); + intendedToolPickAxe.add("smooth_sandstone_stairs"); + intendedToolPickAxe.add("red_sandstone_stairs"); + intendedToolPickAxe.add("cut_red_sandstone_stairs"); + intendedToolPickAxe.add("smooth_red_sandstone_stairs"); + intendedToolPickAxe.add("brick_stairs"); + intendedToolPickAxe.add("prismarine_brick_stairs"); + intendedToolPickAxe.add("dark_prismarine_stairs"); + intendedToolPickAxe.add("nether_brick_stairs"); + intendedToolPickAxe.add("red_netherbrick_stairs"); + intendedToolPickAxe.add("quartz_stairs"); + intendedToolPickAxe.add("smooth_quartz_stairs"); + intendedToolPickAxe.add("purpur_stairs"); + intendedToolPickAxe.add("end_stone_brick_stairs"); + intendedToolPickAxe.add("blackstone_stairs"); + intendedToolPickAxe.add("polished_blackstone_stairs"); + intendedToolPickAxe.add("polished_blackstone_brick_stairs"); + intendedToolPickAxe.add("lightly_weathered_cut_copper_stairs"); + intendedToolPickAxe.add("semi_weathered_cut_copper_stairs"); + intendedToolPickAxe.add("waxed_semi_weathered_cut_copper_stairs"); + intendedToolPickAxe.add("weathered_cut_copper_stairs"); + intendedToolPickAxe.add("waxed_cut_copper_stairs"); + intendedToolPickAxe.add("waxed_lightly_weathered_cut_copper_stairs"); + } private void fillArmors() { @@ -1072,6 +1251,14 @@ public class MaterialMapStore { toolBlackList.add("respawn_anchor"); } + public boolean isIntendedToolPickaxe(Material material) { + return intendedToolPickAxe.contains(material.getKey().getKey()); + } + + public boolean isIntendedToolPickaxe(String string) { + return intendedToolPickAxe.contains(string); + } + public @NotNull HashSet getNetheriteArmor() { return netheriteArmor; } From 9ba8af9f6e2a35c4eb99d1231ac966e2c8241ad8 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 16:42:37 -0800 Subject: [PATCH 08/30] Forgot to register the intended tool set --- src/main/java/com/gmail/nossr50/util/MaterialMapStore.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java b/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java index 61b772eec..8a0c378ed 100644 --- a/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java +++ b/src/main/java/com/gmail/nossr50/util/MaterialMapStore.java @@ -109,8 +109,8 @@ public class MaterialMapStore { fillVanillaMaterialRegisters(); } - private void fillVanillaMaterialRegisters() - { + private void fillVanillaMaterialRegisters() { + //The order matters fillAbilityBlackList(); fillToolBlackList(); fillMossyWhiteList(); @@ -125,6 +125,7 @@ public class MaterialMapStore { fillTools(); fillEnchantables(); fillOres(); + fillIntendedTools(); fillTierMap(); } From e4af53a535e87ed7ebade872fcb3d80506dc06a2 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 16:46:49 -0800 Subject: [PATCH 09/30] Use boolean over Boolean --- src/main/java/com/gmail/nossr50/util/BlockUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gmail/nossr50/util/BlockUtils.java b/src/main/java/com/gmail/nossr50/util/BlockUtils.java index 1c68e9747..8a443c119 100644 --- a/src/main/java/com/gmail/nossr50/util/BlockUtils.java +++ b/src/main/java/com/gmail/nossr50/util/BlockUtils.java @@ -140,7 +140,7 @@ public final class BlockUtils { * @return true if the block should affected by Super Breaker, false * otherwise */ - public static Boolean affectedBySuperBreaker(BlockState blockState) { + public static boolean affectedBySuperBreaker(BlockState blockState) { if(mcMMO.getMaterialMapStore().isIntendedToolPickaxe(blockState.getType())) return true; From 42d3dc3925af3069069ad728c55f6fa457315470 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 16:58:46 -0800 Subject: [PATCH 10/30] Master Angler now mentions it works better with a boat in its hover tip --- src/main/resources/locale/locale_cs_CZ.properties | 1 - src/main/resources/locale/locale_de.properties | 1 - src/main/resources/locale/locale_en_US.properties | 2 +- src/main/resources/locale/locale_es.properties | 1 - src/main/resources/locale/locale_fr.properties | 1 - src/main/resources/locale/locale_hu_HU.properties | 1 - src/main/resources/locale/locale_it.properties | 1 - src/main/resources/locale/locale_ja_JP.properties | 1 - src/main/resources/locale/locale_ko.properties | 1 - src/main/resources/locale/locale_lt_LT.properties | 1 - src/main/resources/locale/locale_nl.properties | 1 - src/main/resources/locale/locale_pl.properties | 1 - src/main/resources/locale/locale_ru.properties | 1 - src/main/resources/locale/locale_th_TH.properties | 1 - src/main/resources/locale/locale_zh_CN.properties | 1 - src/main/resources/locale/locale_zh_TW.properties | 1 - 16 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/resources/locale/locale_cs_CZ.properties b/src/main/resources/locale/locale_cs_CZ.properties index b6be2e519..d56a7d4b7 100644 --- a/src/main/resources/locale/locale_cs_CZ.properties +++ b/src/main/resources/locale/locale_cs_CZ.properties @@ -90,7 +90,6 @@ Fishing.SubSkill.Shake.Description=Vyklepni p\u0159edm\u011bty z p\u0159\u00ed\u Fishing.SubSkill.FishermansDiet.Name=Ryb\u00e1\u0159\u016fv apetit Fishing.SubSkill.FishermansDiet.Description=Zlep\u0161uje dopl\u0148ov\u00e1n\u00ed hladu z naryba\u0159en\u00fdch j\u00eddel Fishing.SubSkill.MasterAngler.Name=Mistr Ryb\u00e1\u0159 -Fishing.SubSkill.MasterAngler.Description=Zvy\u0161uje \u0161anci zah\u00e1knut\u00ed ryby p\u0159i ryba\u0159en\u00ed Fishing.SubSkill.IceFishing.Name=Ryba\u0159en\u00ed v ledu Fishing.SubSkill.IceFishing.Description=Umo\u017e\u0148uje v\u00e1m ryba\u0159it v ledov\u00fdch prost\u0159ed\u00edch Fishing.Chance.Raining=&9 De\u0161\u0165ov\u00fd bonus diff --git a/src/main/resources/locale/locale_de.properties b/src/main/resources/locale/locale_de.properties index d6525a129..108e9cb63 100644 --- a/src/main/resources/locale/locale_de.properties +++ b/src/main/resources/locale/locale_de.properties @@ -372,7 +372,6 @@ Fishing.SubSkill.IceFishing.Stat = Eisangeln Fishing.SubSkill.MagicHunter.Description = Finde verzauberte Gegenst\u00E4nde Fishing.SubSkill.MagicHunter.Name = Zauber J\u00E4ger Fishing.SubSkill.MagicHunter.Stat = Zauber J\u00E4ger Chance -Fishing.SubSkill.MasterAngler.Description = Erh\u00F6ht die Chance des Anbei\u00DFens beim Angeln Fishing.SubSkill.MasterAngler.Name = Superangel Fishing.SubSkill.Shake.Description = Rei\u00DFe Gegenst\u00E4nde weg von Lebewesen und Spielern mit deiner Angel Fishing.SubSkill.Shake.Name = Rei\u00DFen diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index 1020210c1..9e98a9420 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -252,7 +252,7 @@ Fishing.SubSkill.FishermansDiet.Name=Fisherman's Diet Fishing.SubSkill.FishermansDiet.Description=Improves hunger restored from fished foods Fishing.SubSkill.FishermansDiet.Stat=Fisherman's Diet:&a Rank {0} Fishing.SubSkill.MasterAngler.Name=Master Angler -Fishing.SubSkill.MasterAngler.Description=Fish are caught more frequently +Fishing.SubSkill.MasterAngler.Description=Fish are caught more frequently, works better when fishing from a boat. Fishing.SubSkill.MasterAngler.Stat=Fishing min wait time reduction: &a-{0} seconds Fishing.SubSkill.MasterAngler.Stat.Extra=Fishing max wait time reduction: &a-{0} seconds Fishing.SubSkill.IceFishing.Name=Ice Fishing diff --git a/src/main/resources/locale/locale_es.properties b/src/main/resources/locale/locale_es.properties index 6aa857cc3..cdce790bd 100644 --- a/src/main/resources/locale/locale_es.properties +++ b/src/main/resources/locale/locale_es.properties @@ -91,7 +91,6 @@ Fishing.SubSkill.Shake.Description=Sacudir los items fuera de los monstruos con Fishing.SubSkill.FishermansDiet.Name=Dieta del pescador Fishing.SubSkill.FishermansDiet.Description=Mejora el hambre restaurada a partir de alimentos pescados Fishing.SubSkill.MasterAngler.Name=Maestro pescador -Fishing.SubSkill.MasterAngler.Description=Aumenta la probabilidad de ser mordido mientras se pesca Fishing.SubSkill.IceFishing.Name=Pesca de hielo Fishing.SubSkill.IceFishing.Description=Te permite pescar en biomas de hielo Fishing.Chance.Raining=&9 Lluvia de Bonus diff --git a/src/main/resources/locale/locale_fr.properties b/src/main/resources/locale/locale_fr.properties index c773ad65c..c69bd5eb1 100644 --- a/src/main/resources/locale/locale_fr.properties +++ b/src/main/resources/locale/locale_fr.properties @@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=R\u00e9gime de fermier Fishing.SubSkill.FishermansDiet.Description=Am\u00e9liore la nutrition des produits de la ferme Fishing.SubSkill.FishermansDiet.Stat=R\u00e9gime de fermier:&a Grade {0} Fishing.SubSkill.MasterAngler.Name=Ma\u00eetre P\u00eacheur -Fishing.SubSkill.MasterAngler.Description=Augmente les chances que \u00e7a morde lors de la p\u00eache Fishing.SubSkill.IceFishing.Name=P\u00eache sur Glace Fishing.SubSkill.IceFishing.Description=Vous permet de p\u00eacher dans les biomes glac\u00e9s Fishing.SubSkill.IceFishing.Stat=P\u00eache sur Glace diff --git a/src/main/resources/locale/locale_hu_HU.properties b/src/main/resources/locale/locale_hu_HU.properties index 71f11514c..60776b3f7 100644 --- a/src/main/resources/locale/locale_hu_HU.properties +++ b/src/main/resources/locale/locale_hu_HU.properties @@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=Horg\u00E1szok Di\u00E9t\u00E1ja Fishing.SubSkill.FishermansDiet.Description=N\u00F6veli a kihal\u00E1szott \u00E9telek t\u00E1p\u00E9rt\u00E9k\u00E9t Fishing.SubSkill.FishermansDiet.Stat=Horg\u00E1szok Di\u00E9t\u00E1ja:&a Szint {0} Fishing.SubSkill.MasterAngler.Name=Mester Horg\u00E1sz -Fishing.SubSkill.MasterAngler.Description=N\u00F6veli a Kap\u00E1s es\u00E9ly\u00E9t horg\u00E1szat k\u00F6zben Fishing.SubSkill.IceFishing.Name=J\u00E9g Horg\u00E1szat Fishing.SubSkill.IceFishing.Description=Lehet\u0151v\u00E9 teszi sz\u00E1modra, hogy fagyos t\u00E1jakon is horg\u00E1szhass Fishing.SubSkill.IceFishing.Stat=J\u00E9g Horg\u00E1szat diff --git a/src/main/resources/locale/locale_it.properties b/src/main/resources/locale/locale_it.properties index c5f293733..d6df1ef1e 100644 --- a/src/main/resources/locale/locale_it.properties +++ b/src/main/resources/locale/locale_it.properties @@ -258,7 +258,6 @@ Fishing.SubSkill.FishermansDiet.Name=Dieta del Pescatore Fishing.SubSkill.FishermansDiet.Description=Aumenta la fame recuperata tramite cibi pescati Fishing.SubSkill.FishermansDiet.Stat=Dieta del Pescatore:&a Grado {0} Fishing.SubSkill.MasterAngler.Name=Pescatore Provetto -Fishing.SubSkill.MasterAngler.Description=Migliora la possibilit\u00E0 di ottenere un morso durante la pesca Fishing.SubSkill.IceFishing.Name=Pesca sul Ghiaccio Fishing.SubSkill.IceFishing.Description=Ti permette di pescare in biomi ghiacciati Fishing.SubSkill.IceFishing.Stat=Pesca sul Ghiaccio diff --git a/src/main/resources/locale/locale_ja_JP.properties b/src/main/resources/locale/locale_ja_JP.properties index 1064c29ac..f93cda4be 100644 --- a/src/main/resources/locale/locale_ja_JP.properties +++ b/src/main/resources/locale/locale_ja_JP.properties @@ -241,7 +241,6 @@ Fishing.SubSkill.FishermansDiet.Name=\u6f01\u5e2b\u306e\u98df\u4e8b Fishing.SubSkill.FishermansDiet.Description=\u9b5a\u4ecb\u985e\u304b\u3089\u56de\u5fa9\u3059\u308b\u6e80\u8179\u5ea6\u3092\u6539\u5584\u3059\u308b\u3002 Fishing.SubSkill.FishermansDiet.Stat=\u6f01\u5e2b\u306e\u98df\u4e8b:&a \u30e9\u30f3\u30af {0} Fishing.SubSkill.MasterAngler.Name=\u30de\u30b9\u30bf\u30fc\u30a2\u30f3\u30b0\u30e9\u30fc -Fishing.SubSkill.MasterAngler.Description=\u91e3\u308c\u308b\u78ba\u7387\u304c\u4e0a\u304c\u308a\u307e\u3059\u3002 Fishing.SubSkill.IceFishing.Name=\u7a74\u91e3\u308a Fishing.SubSkill.IceFishing.Description=\u5bd2\u3044\u30d0\u30a4\u30aa\u30fc\u30e0\u3067\u306e\u91e3\u308a\u304c\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u3002 Fishing.SubSkill.IceFishing.Stat=\u7a74\u91e3\u308a diff --git a/src/main/resources/locale/locale_ko.properties b/src/main/resources/locale/locale_ko.properties index 6a5865391..e6f9e7c30 100644 --- a/src/main/resources/locale/locale_ko.properties +++ b/src/main/resources/locale/locale_ko.properties @@ -132,7 +132,6 @@ Fishing.SubSkill.Shake.Description=\uC544\uC774\uD15C\uC744 \uBAB9\uC774\uB098 \ Fishing.SubSkill.FishermansDiet.Name=\uC5B4\uBD80\uC758 \uB2E4\uC774\uC5B4\uD2B8 Fishing.SubSkill.FishermansDiet.Description=\uC5B4\uB958 \uC74C\uC2DD \uD5C8\uAE30 \uD68C\uBCF5 \uC99D\uAC00 Fishing.SubSkill.MasterAngler.Name=\uB09A\uC2DC\uAFBC \uC7A5\uC778 -Fishing.SubSkill.MasterAngler.Description=\uB09A\uC2DC\uC911 \uC785\uC9C8 \uD655\uB960 \uC99D\uAC00 Fishing.SubSkill.IceFishing.Name=\uC5BC\uC74C \uB09A\uC2DC Fishing.SubSkill.IceFishing.Description=\uC5BC\uC74C\uC774 \uB36E\uD600\uC788\uB294 \uD658\uACBD\uC5D0\uC11C \uB09A\uC2DC \uAC00\uB2A5 Fishing.Chance.Raining=&9 \uBE44 \uD2B9\uD61C diff --git a/src/main/resources/locale/locale_lt_LT.properties b/src/main/resources/locale/locale_lt_LT.properties index 80e26d8e4..f9d8bb9e4 100644 --- a/src/main/resources/locale/locale_lt_LT.properties +++ b/src/main/resources/locale/locale_lt_LT.properties @@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=Fisherman's Diet Fishing.SubSkill.FishermansDiet.Description=Improves hunger restored from fished foods Fishing.SubSkill.FishermansDiet.Stat=Fisherman's Diet:&a Rank {0} Fishing.SubSkill.MasterAngler.Name=Master Angler -Fishing.SubSkill.MasterAngler.Description=Improves chance of getting a bite while fishing Fishing.SubSkill.IceFishing.Name=Ice Fishing Fishing.SubSkill.IceFishing.Description=Allows you to fish in icy biomes Fishing.SubSkill.IceFishing.Stat=Ice Fishing diff --git a/src/main/resources/locale/locale_nl.properties b/src/main/resources/locale/locale_nl.properties index cd2ae6e67..19dea9c04 100644 --- a/src/main/resources/locale/locale_nl.properties +++ b/src/main/resources/locale/locale_nl.properties @@ -71,7 +71,6 @@ Fishing.SubSkill.Shake.Description=Schud items af van mobs w/ hengel Fishing.SubSkill.FishermansDiet.Name=Visserman\'s dieet Fishing.SubSkill.FishermansDiet.Description=Verbetert de honger hersteld vanaf geviste voedingsmiddelen Fishing.SubSkill.MasterAngler.Name=Meester Hengelaar -Fishing.SubSkill.MasterAngler.Description=Verbetert de kans op het bijten tijdens het vissen Fishing.SubSkill.IceFishing.Name=Ijs Vissen Fishing.SubSkill.IceFishing.Description=Stelt je in staat om te vissen in de ijzige biomen Fishing.Chance.Raining=&9 Regen Bonus diff --git a/src/main/resources/locale/locale_pl.properties b/src/main/resources/locale/locale_pl.properties index 960803ebf..cf4f89579 100644 --- a/src/main/resources/locale/locale_pl.properties +++ b/src/main/resources/locale/locale_pl.properties @@ -89,7 +89,6 @@ Fishing.SubSkill.Shake.Name=Potrz\u0105\u015bni\u0119cie (przeciwko jednostkom) Fishing.SubSkill.Shake.Description=Okradaj potwory z przedmiot\u00f3w u\u017cywaj\u0105c w\u0119dki. Fishing.SubSkill.FishermansDiet.Name=Dieta Rybaka Fishing.SubSkill.FishermansDiet.Description=Zwi\u0119ksza nasycenie posi\u0142k\u00f3w (ryby) -Fishing.SubSkill.MasterAngler.Description=Zwieksza szanse na zlapanie ryby na haczyk Fishing.SubSkill.IceFishing.Name=Lodowe lowienie ryb Fishing.SubSkill.IceFishing.Description=Pozwala na lowienie ryb w zimowych biomach Fishing.Chance.Raining=&9 Bonus od Deszczu diff --git a/src/main/resources/locale/locale_ru.properties b/src/main/resources/locale/locale_ru.properties index 9d36e5e2d..7510220b4 100644 --- a/src/main/resources/locale/locale_ru.properties +++ b/src/main/resources/locale/locale_ru.properties @@ -199,7 +199,6 @@ Fishing.SubSkill.FishermansDiet.Name=\u0420\u044b\u0431\u0430\u0446\u043a\u0430\ Fishing.SubSkill.FishermansDiet.Description=\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0443\u0442\u043e\u043b\u0435\u043d\u0438\u0435 \u0433\u043e\u043b\u043e\u0434\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u044b\u0431\u0430\u0446\u043a\u043e\u0439 \u0435\u0434\u044b Fishing.SubSkill.FishermansDiet.Stat=\u0420\u044b\u0431\u0430\u0446\u043a\u0430\u044f \u0414\u0438\u0435\u0442\u0430:&a \u0420\u0430\u043d\u0433 {0} Fishing.SubSkill.MasterAngler.Name=\u041c\u0430\u0441\u0442\u0435\u0440 \u0420\u044b\u0431\u043e\u043b\u043e\u0432 -Fishing.SubSkill.MasterAngler.Description=\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0448\u0430\u043d\u0441 \u043f\u043e\u043a\u043b\u0435\u0432\u043a\u0438 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u044b\u0431\u0430\u043b\u043a\u0438 Fishing.SubSkill.IceFishing.Name=\u041f\u043e\u0434\u043b\u0435\u0434\u043d\u0430\u044f \u0420\u044b\u0431\u0430\u043b\u043a\u0430 Fishing.SubSkill.IceFishing.Description=\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0430\u043c \u0440\u044b\u0431\u0430\u0447\u0438\u0442\u044c \u0432 \u0441\u043d\u0435\u0436\u043d\u044b\u0445 \u0431\u0438\u043e\u043c\u0430\u0445 Fishing.SubSkill.IceFishing.Stat=\u041f\u043e\u0434\u043b\u0435\u0434\u043d\u0430\u044f \u0420\u044b\u0431\u0430\u043b\u043a\u0430 diff --git a/src/main/resources/locale/locale_th_TH.properties b/src/main/resources/locale/locale_th_TH.properties index 1882de9eb..56695b922 100644 --- a/src/main/resources/locale/locale_th_TH.properties +++ b/src/main/resources/locale/locale_th_TH.properties @@ -90,7 +90,6 @@ Fishing.SubSkill.Shake.Description=\u0e40\u0e02\u0e22\u0e48\u0e32\u0e40\u0e2d\u0 Fishing.SubSkill.FishermansDiet.Name=Fisherman\'s Diet Fishing.SubSkill.FishermansDiet.Description=\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e04\u0e27\u0e32\u0e21\u0e2d\u0e34\u0e48\u0e21\u0e08\u0e32\u0e01\u0e01\u0e32\u0e23\u0e01\u0e34\u0e19\u0e1b\u0e25\u0e32 Fishing.SubSkill.MasterAngler.Name=Master Angler -Fishing.SubSkill.MasterAngler.Description=\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e42\u0e2d\u0e01\u0e32\u0e2a\u0e44\u0e14\u0e49\u0e23\u0e31\u0e1a\u0e1b\u0e25\u0e32\u0e21\u0e32\u0e01\u0e02\u0e36\u0e49\u0e19\u0e08\u0e32\u0e01\u0e01\u0e32\u0e23\u0e15\u0e01\u0e1b\u0e25\u0e32 Fishing.SubSkill.IceFishing.Name=Ice Fishing Fishing.SubSkill.IceFishing.Description=\u0e2d\u0e19\u0e38\u0e0d\u0e32\u0e15\u0e34\u0e43\u0e2b\u0e49\u0e15\u0e01\u0e1b\u0e25\u0e32\u0e43\u0e19\u0e19\u0e49\u0e33\u0e41\u0e02\u0e47\u0e07 Fishing.Chance.Raining=&9 Rain Bonus diff --git a/src/main/resources/locale/locale_zh_CN.properties b/src/main/resources/locale/locale_zh_CN.properties index 7557b6cd3..338737994 100644 --- a/src/main/resources/locale/locale_zh_CN.properties +++ b/src/main/resources/locale/locale_zh_CN.properties @@ -251,7 +251,6 @@ Fishing.SubSkill.FishermansDiet.Name=\u6e14\u592b\u7684\u98df\u8c31 Fishing.SubSkill.FishermansDiet.Description=\u63d0\u9ad8\u9c7c\u7c7b\u98df\u7269\u6062\u590d\u7684\u9971\u98df\u5ea6 Fishing.SubSkill.FishermansDiet.Stat=\u6e14\u592b\u7684\u98df\u8c31:&a \u7b49\u7ea7 {0} Fishing.SubSkill.MasterAngler.Name=\u9493\u9c7c\u5927\u5e08 -Fishing.SubSkill.MasterAngler.Description=\u63d0\u9ad8\u9493\u9c7c\u54ac\u94a9\u51e0\u7387 Fishing.SubSkill.IceFishing.Name=\u51b0\u9493 Fishing.SubSkill.IceFishing.Description=\u5141\u8bb8\u4f60\u5728\u51b0\u51b7\u7684\u73af\u5883\u4e0b\u9493\u9c7c Fishing.SubSkill.IceFishing.Stat=\u51b0\u9493 diff --git a/src/main/resources/locale/locale_zh_TW.properties b/src/main/resources/locale/locale_zh_TW.properties index f20ff761f..01a0c15ac 100644 --- a/src/main/resources/locale/locale_zh_TW.properties +++ b/src/main/resources/locale/locale_zh_TW.properties @@ -93,7 +93,6 @@ Fishing.SubSkill.Shake.Description=\u7528\u91e3\u7aff\u628a\u602a\u7269\u7684\u7 Fishing.SubSkill.FishermansDiet.Name=\u6f01\u4eba\u4fbf\u7576 Fishing.SubSkill.FishermansDiet.Description=\u98df\u7528\u9b5a\u98df\u54c1\u6642\u984d\u5916\u6062\u5fa9\u98fd\u98df\u5ea6 Fishing.SubSkill.MasterAngler.Name=\u5782\u91e3\u5927\u5e2b -Fishing.SubSkill.MasterAngler.Description=\u589e\u52a0\u5728\u91e3\u9b5a\u6642\u4e0a\u9264\u7684\u6a5f\u7387 Fishing.SubSkill.IceFishing.Name=\u51b0\u91e3 Fishing.SubSkill.IceFishing.Description=\u5141\u8a31\u4f60\u5728\u51b0\u5929\u96ea\u5730\u88e1\u91e3\u9b5a Fishing.Chance.Raining=&9 \u5927\u91cf\u734e\u52f5 From a8d81a2080fff952ee0c0dd41bfeea41064a408a Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 17:02:38 -0800 Subject: [PATCH 11/30] 2.1.165 --- Changelog.txt | 12 ++++++++---- pom.xml | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 44960faf2..abc04fb7c 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -5,16 +5,20 @@ Version 2.1.165 How mcMMO tracks COTW entities has been rewritten When COTW summons are killed players are now informed (from anything other than the time expiring). mcMMO will now be compatible with changes to world height (1.17 compatibility) - Added missing cooldown locale message 'Commands.Database.Cooldown' - Added new locale message 'Taming.Summon.COTW.Removed' mcMMO will ignore EntityPickupItemEvents from "Fake-Player" NPCs if it recognizes them as such, this will prevent some compatibility issues with some plugins SuperBreaker will now always activate if the target blocks fastest tool is a Pickaxe (used to require the block giving XP or being considered an ore) + Master Angler mentions that it works better in its hover tip + Added missing cooldown locale message 'Commands.Database.Cooldown' + Added new locale message 'Taming.Summon.COTW.Removed' + Updated locale entry 'Fishing.SubSkill.MasterAngler.Description' NOTES: Books dropped before this fix will not be usable and should just be chucked in lava, the broken books have blue names, the working books have yellow names. t00thpick1 has taken time to rewrite our block meta tracking system to be more efficient, easier to maintain, and support upcoming features such as world height changes - This new system is compatible with the old one, it will convert old files to the new format as needed. - This update shouldn't break anything as the API is the same + This new system is compatible with the old one, it will convert old files to the new format as needed. You won't even know it is doing anything. + This update shouldn't break anything as the API is the same. + + Alright back to work on T&C unless some major bugs come out of this... Version 2.1.164 mcMMO will now let players use vanilla blocks that have interactions (such as the vanilla Anvil) which are assigned as either Repair or Salvage blocks if a player is sneaking (see notes) diff --git a/pom.xml b/pom.xml index 755d4490f..d12b264c6 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.1.165-SNAPSHOT + 2.1.165 mcMMO https://github.com/mcMMO-Dev/mcMMO From 0d4e1b3ba665aadb4ce011caaa9f402b14dbf99a Mon Sep 17 00:00:00 2001 From: nossr50 Date: Thu, 31 Dec 2020 17:13:56 -0800 Subject: [PATCH 12/30] update changes --- Changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.txt b/Changelog.txt index abc04fb7c..f619cdea0 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -7,7 +7,7 @@ Version 2.1.165 mcMMO will now be compatible with changes to world height (1.17 compatibility) mcMMO will ignore EntityPickupItemEvents from "Fake-Player" NPCs if it recognizes them as such, this will prevent some compatibility issues with some plugins SuperBreaker will now always activate if the target blocks fastest tool is a Pickaxe (used to require the block giving XP or being considered an ore) - Master Angler mentions that it works better in its hover tip + Master Angler mentions that it works better with a boat in its hover tip Added missing cooldown locale message 'Commands.Database.Cooldown' Added new locale message 'Taming.Summon.COTW.Removed' Updated locale entry 'Fishing.SubSkill.MasterAngler.Description' From 959d1e4a05423f0bbd9185f38947b327ca1d2d11 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 1 Jan 2021 12:46:08 -0800 Subject: [PATCH 13/30] Remove music discs from the default loot table --- Changelog.txt | 5 +++ pom.xml | 2 +- src/main/resources/fishing_treasures.yml | 44 ------------------------ 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index f619cdea0..0a1a046a1 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,8 @@ +Version 2.1.166 + Music discs removed from the default fishing_treasures.yml + + NOTES: + No one likes fishing up music discs Version 2.1.165 Fixed a bug where Enchanted Books dropped by mcMMO (in Fishing) did not function correctly The mcMMO system which tracks player placed blocks has had some major rewrites (thanks t00thpick1) diff --git a/pom.xml b/pom.xml index d12b264c6..1a419ab4c 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.1.165 + 2.1.166-SNAPSHOT mcMMO https://github.com/mcMMO-Dev/mcMMO diff --git a/src/main/resources/fishing_treasures.yml b/src/main/resources/fishing_treasures.yml index d839da2fd..3b9f796dc 100644 --- a/src/main/resources/fishing_treasures.yml +++ b/src/main/resources/fishing_treasures.yml @@ -207,50 +207,6 @@ Fishing: Amount: 1 XP: 200 Rarity: LEGENDARY - MUSIC_DISC_BLOCKS: - Amount: 1 - XP: 200 - Rarity: UNCOMMON - MUSIC_DISC_CHIRP: - Amount: 1 - XP: 200 - Rarity: UNCOMMON - MUSIC_DISC_FAR: - Amount: 1 - XP: 200 - Rarity: RARE - MUSIC_DISC_MALL: - Amount: 1 - XP: 200 - Rarity: RARE - MUSIC_DISC_MELLOHI: - Amount: 1 - XP: 200 - Rarity: RARE - MUSIC_DISC_STAL: - Amount: 1 - XP: 200 - Rarity: EPIC - MUSIC_DISC_STRAD: - Amount: 1 - XP: 200 - Rarity: EPIC - MUSIC_DISC_WARD: - Amount: 1 - XP: 200 - Rarity: EPIC - MUSIC_DISC_11: - Amount: 1 - XP: 200 - Rarity: LEGENDARY - MUSIC_DISC_WAIT: - Amount: 1 - XP: 200 - Rarity: LEGENDARY - MUSIC_DISC_13: - Amount: 1 - XP: 200 - Rarity: MYTHIC NETHERITE_SWORD: Amount: 1 XP: 200 From fbecbc167a489962c070959db68bb3d6683ce652 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 1 Jan 2021 14:04:36 -0800 Subject: [PATCH 14/30] Optimizations and potentially fixing a ConcurrentModificationException involving the TransientEntityTracker Fixes #4368 --- Changelog.txt | 8 +- .../datatypes/player/PlayerProfile.java | 5 +- .../nossr50/listeners/PlayerListener.java | 11 +- src/main/java/com/gmail/nossr50/mcMMO.java | 25 ++- .../nossr50/skills/taming/TamingManager.java | 2 +- .../nossr50/util/TransientEntityTracker.java | 142 +++++++++++++----- 6 files changed, 144 insertions(+), 49 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 0a1a046a1..a829e376d 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,8 +1,14 @@ Version 2.1.166 + Fixed a small memory leak in the new COTW tracker + Potentially fixed a ConcurrentModificationException involving the TransientEntityTracker (report this error if you encounter it) Music discs removed from the default fishing_treasures.yml + Optimized how mcMMO saves player data (should improve timings on servers with bad disk speeds and or bad connectivity to their SQL server instance) NOTES: - No one likes fishing up music discs + No one likes fishing up music discs, if you want this change it is recommended you delete fishing_treasures.yml and let it regenerate + If any of you encounter a ConcurrentModificationException error that mentions TransientEntityTracker in its stack trace after this update let me know, I have another fix in mind for this if this update didn't fix it. + (You won't have this file if you haven't updated in a while, if so you don't need to do anything) + Version 2.1.165 Fixed a bug where Enchanted Books dropped by mcMMO (in Fishing) did not function correctly The mcMMO system which tracks player placed blocks has had some major rewrites (thanks t00thpick1) diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java index 49fc3d6f7..1d1cb53c2 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java @@ -131,10 +131,11 @@ public class PlayerProfile { { saveAttempts++; - if(useSync) + //Back out of async saving if we detect a server shutdown, this is not always going to be caught + if(mcMMO.isServerShutdownExecuted() || useSync) scheduleSyncSave(); //Execute sync saves immediately else - scheduleAsyncSaveDelay(); + scheduleAsyncSave(); } else { mcMMO.p.getLogger().severe("mcMMO has failed to save the profile for " diff --git a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java index bf47ef5a0..b410a330d 100644 --- a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java @@ -526,16 +526,15 @@ public class PlayerListener implements Listener { return; } + McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); + //Profile not loaded - if(UserManager.getPlayer(player) == null) - { + if(mcMMOPlayer == null) { return; } - McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); - //There's an issue with using Async saves on player quit - //Basically there are conditions in which an async task does not execute fast enough to save the data if the server shutdown shortly after this task was scheduled - mcMMOPlayer.logout(true); + //Use a sync save if the server is shutting down to avoid race conditions + mcMMOPlayer.logout(mcMMO.isServerShutdownExecuted()); } /** diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index 06347a952..f3f6bb9ac 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -88,6 +88,7 @@ public class mcMMO extends JavaPlugin { private static ChatManager chatManager; private static CommandManager commandManager; //ACF private static TransientEntityTracker transientEntityTracker; + private static boolean serverShutdownExecuted = false; /* Adventure */ private static BukkitAudiences audiences; @@ -292,6 +293,7 @@ public class mcMMO extends JavaPlugin { commandManager = new CommandManager(this); transientEntityTracker = new TransientEntityTracker(); + setServerShutdown(false); //Reset flag, used to make decisions about async saves } public static PlayerLevelUtils getPlayerLevelUtils() { @@ -327,6 +329,10 @@ public class mcMMO extends JavaPlugin { */ @Override public void onDisable() { + setServerShutdown(true); + //TODO: Write code to catch unfinished async save tasks, for now we just hope they finish in time, which they should in most cases + mcMMO.p.getLogger().info("Server shutdown has been executed, saving and cleaning up data..."); + try { UserManager.saveAll(); // Make sure to save player information if the server shuts down UserManager.clearAll(); @@ -345,11 +351,6 @@ public class mcMMO extends JavaPlugin { catch (Exception e) { e.printStackTrace(); } - debug("Canceling all tasks..."); - getServer().getScheduler().cancelTasks(this); // This removes our tasks - debug("Unregister all events..."); - HandlerList.unregisterAll(this); // Cancel event registrations - if (Config.getInstance().getBackupsEnabled()) { // Remove other tasks BEFORE starting the Backup, or we just cancel it straight away. try { @@ -369,6 +370,11 @@ public class mcMMO extends JavaPlugin { } } + debug("Canceling all tasks..."); + getServer().getScheduler().cancelTasks(this); // This removes our tasks + debug("Unregister all events..."); + HandlerList.unregisterAll(this); // Cancel event registrations + databaseManager.onDisable(); debug("Was disabled."); // How informative! } @@ -727,4 +733,13 @@ public class mcMMO extends JavaPlugin { public static TransientEntityTracker getTransientEntityTracker() { return transientEntityTracker; } + + public static synchronized boolean isServerShutdownExecuted() { + return serverShutdownExecuted; + } + + private static synchronized void setServerShutdown(boolean bool) { + serverShutdownExecuted = bool; + } + } diff --git a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java index 0ec720ec1..6a902dd43 100644 --- a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java @@ -52,7 +52,7 @@ public class TamingManager extends SkillManager { lastSummonTimeStamp = 0L; //Init per-player tracking of summoned entities - mcMMO.getTransientEntityTracker().initPlayer(mmoPlayer.getPlayer().getUniqueId()); + mcMMO.getTransientEntityTracker().initPlayer(mmoPlayer.getPlayer()); //Hacky stuff used as a band-aid initStaticCaches(); diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java index 4b22abaea..9ee1807b4 100644 --- a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -19,6 +19,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; public class TransientEntityTracker { + //These two are updated in step with each other private final @NotNull HashMap>> perPlayerTransientEntityMap; private final @NotNull HashSet chunkLookupCache; @@ -27,30 +28,74 @@ public class TransientEntityTracker { chunkLookupCache = new HashSet<>(); } - public void initPlayer(@NotNull UUID playerUUID) { - if (!isPlayerRegistered(playerUUID)) { - registerPlayer(playerUUID); + public synchronized @NotNull HashSet getChunkLookupCache() { + return chunkLookupCache; + } + + public synchronized @NotNull HashMap>> getPerPlayerTransientEntityMap() { + return perPlayerTransientEntityMap; + } + + public synchronized void initPlayer(@NotNull Player player) { + if (!isPlayerRegistered(player.getUniqueId())) { + registerPlayer(player.getUniqueId()); } } - public void cleanupPlayer(@NotNull UUID playerUUID) { - cleanupAllSummons(null, playerUUID); + /** + * Removes a player from the tracker + * + * @param playerUUID target player + */ + public synchronized void cleanupPlayer(@NotNull UUID playerUUID) { + cleanPlayer(null, playerUUID); } - public void cleanupPlayer(@NotNull Player player) { - //First remove all entities related to this player + /** + * Removes a player from the tracker + * + * @param player target player + */ + public synchronized void cleanupPlayer(@NotNull Player player) { + cleanPlayer(player, player.getUniqueId()); + } + + /** + * Removes a player from the tracker + * + * @param player target player + * @param playerUUID target player UUID + */ + private void cleanPlayer(@Nullable Player player, @NotNull UUID playerUUID) { cleanupAllSummons(player, player.getUniqueId()); + removePlayerFromMap(playerUUID); } - private boolean isPlayerRegistered(@NotNull UUID playerUUID) { - return perPlayerTransientEntityMap.get(playerUUID) != null; + private void removePlayerFromMap(@NotNull UUID playerUUID) { + getPerPlayerTransientEntityMap().remove(playerUUID); } - private void registerPlayer(@NotNull UUID playerUUID) { - perPlayerTransientEntityMap.put(playerUUID, new HashMap>()); + /** + * Checks if a player has already been registered + * Being registered constitutes having necessary values initialized in our per-player map + * + * @param playerUUID target player + * @return true if the player is registered + */ + private synchronized boolean isPlayerRegistered(@NotNull UUID playerUUID) { + return getPerPlayerTransientEntityMap().get(playerUUID) != null; + } + + /** + * Register a player to our tracker, which initializes the necessary values in our per-player map + * + * @param playerUUID player to register + */ + private synchronized void registerPlayer(@NotNull UUID playerUUID) { + getPerPlayerTransientEntityMap().put(playerUUID, new HashMap>()); for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { - perPlayerTransientEntityMap.get(playerUUID).put(callOfTheWildType, new HashSet<>()); + getPerPlayerTransientEntityMap().get(playerUUID).put(callOfTheWildType, new HashSet<>()); } } @@ -60,16 +105,18 @@ public class TransientEntityTracker { * @param playerUUID the target uuid of the player * @return the tracked entities map for the player, null if the player isn't registered */ - public @Nullable HashMap> getPlayerTrackedEntityMap(@NotNull UUID playerUUID) { - return perPlayerTransientEntityMap.get(playerUUID); + public synchronized @Nullable HashMap> getPlayerTrackedEntityMap(@NotNull UUID playerUUID) { + return getPerPlayerTransientEntityMap().get(playerUUID); } - public void registerEntity(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { - if(!isPlayerRegistered(playerUUID)) { - mcMMO.p.getLogger().severe("Attempting to register entity to a player which hasn't been initialized!"); - initPlayer(playerUUID); - } - + /** + * Registers an entity to a player + * This includes registration to our per-player map and our chunk lookup cache + * + * @param playerUUID target player's UUID + * @param trackedTamingEntity target entity + */ + public synchronized void registerEntity(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { //Add to map entry getTrackedEntities(playerUUID, trackedTamingEntity.getCallOfTheWildType()).add(trackedTamingEntity); @@ -85,7 +132,7 @@ public class TransientEntityTracker { * @param callOfTheWildType target type * @return the set of tracked entities for the player, null if the player isn't registered, the set can be empty */ - private @Nullable HashSet getTrackedEntities(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { + private synchronized @Nullable HashSet getTrackedEntities(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { HashMap> playerEntityMap = getPlayerTrackedEntityMap(playerUUID); if(playerEntityMap == null) @@ -94,21 +141,45 @@ public class TransientEntityTracker { return playerEntityMap.get(callOfTheWildType); } - private void addToChunkLookupCache(@NotNull TrackedTamingEntity trackedTamingEntity) { - chunkLookupCache.add(trackedTamingEntity.getLivingEntity()); + /** + * Adds an entity to our chunk lookup cache + * + * @param trackedTamingEntity target tracked taming entity + */ + private synchronized void addToChunkLookupCache(@NotNull TrackedTamingEntity trackedTamingEntity) { + getChunkLookupCache().add(trackedTamingEntity.getLivingEntity()); } - public void unregisterEntity(@NotNull LivingEntity livingEntity) { + /** + * Removes an entity from our tracker + * This includes removal from our per-player map and our chunk lookup cache + * + * @param livingEntity target entity + */ + private void unregisterEntity(@NotNull LivingEntity livingEntity) { chunkLookupCacheCleanup(livingEntity); perPlayerTransientMapCleanup(livingEntity); } + /** + * Removes an entity from our chunk lookup cache + * + * @param livingEntity target entity + */ private void chunkLookupCacheCleanup(@NotNull LivingEntity livingEntity) { - chunkLookupCache.remove(livingEntity); + getChunkLookupCache().remove(livingEntity); } + /** + * Clean a living entity from our tracker + * Iterates over all players and their registered entities + * Doesn't do any kind of failure checking, if it doesn't find any player with a registered entity nothing bad happens or is reported + * However it should never happen like that, so maybe we could consider adding some failure to execute checking in the future + * + * @param livingEntity + */ private void perPlayerTransientMapCleanup(@NotNull LivingEntity livingEntity) { - for(UUID uuid : perPlayerTransientEntityMap.keySet()) { + for(UUID uuid : getPerPlayerTransientEntityMap().keySet()) { for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) { HashSet trackedEntities = getTrackedEntities(uuid, callOfTheWildType); @@ -127,10 +198,16 @@ public class TransientEntityTracker { } } - public @NotNull List getAllTransientEntitiesInChunk(@NotNull Chunk chunk) { + /** + * Get all transient entities that exist in a specific chunk + * + * @param chunk the chunk to match + * @return a list of transient entities that are located in the provided chunk + */ + public synchronized @NotNull List getAllTransientEntitiesInChunk(@NotNull Chunk chunk) { ArrayList matchingEntities = new ArrayList<>(); - for(LivingEntity livingEntity : chunkLookupCache) { + for(LivingEntity livingEntity : getChunkLookupCache()) { if(livingEntity.getLocation().getChunk().equals(chunk)) { matchingEntities.add(livingEntity); } @@ -139,17 +216,14 @@ public class TransientEntityTracker { return matchingEntities; } - /* - * Gross code below - */ - /** * Get the amount of a summon currently active for a player + * * @param playerUUID target player * @param callOfTheWildType summon type * @return the amount of summons currently active for player of target type */ - public int getAmountCurrentlySummoned(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { + public synchronized int getAmountCurrentlySummoned(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { HashSet trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType); if(trackedEntities == null) @@ -165,7 +239,7 @@ public class TransientEntityTracker { * @param livingEntity entity to remove * @param player associated player */ - public void removeSummon(@NotNull LivingEntity livingEntity, @Nullable Player player, boolean timeExpired) { + public synchronized void removeSummon(@NotNull LivingEntity livingEntity, @Nullable Player player, boolean timeExpired) { //Kill the summon & remove it if(livingEntity.isValid()) { livingEntity.setHealth(0); //Should trigger entity death events From 91808791643951e257761f08effe26942d6e3ea8 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 1 Jan 2021 14:06:15 -0800 Subject: [PATCH 15/30] 2.1.166 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a419ab4c..77cb38b4a 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.1.166-SNAPSHOT + 2.1.166 mcMMO https://github.com/mcMMO-Dev/mcMMO From 0466a28a5f5d3b20af23fbccd462439ca58dff2d Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 1 Jan 2021 14:08:52 -0800 Subject: [PATCH 16/30] Update changelog --- Changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.txt b/Changelog.txt index a829e376d..37fd225ad 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -6,8 +6,8 @@ Version 2.1.166 NOTES: No one likes fishing up music discs, if you want this change it is recommended you delete fishing_treasures.yml and let it regenerate - If any of you encounter a ConcurrentModificationException error that mentions TransientEntityTracker in its stack trace after this update let me know, I have another fix in mind for this if this update didn't fix it. (You won't have this file if you haven't updated in a while, if so you don't need to do anything) + If any of you encounter a ConcurrentModificationException error that mentions TransientEntityTracker in its stack trace after this update let me know, I have another fix in mind for this if this update didn't fix it. Version 2.1.165 Fixed a bug where Enchanted Books dropped by mcMMO (in Fishing) did not function correctly From 556515eefde184c5e71e9c1df6658a7f39169481 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Tue, 20 Oct 2020 09:43:44 +0100 Subject: [PATCH 17/30] Add SkillActivationPerkEvent --- pom.xml | 2 +- .../skills/SkillActivationPerkEvent.java | 49 +++++++++++++++++++ .../gmail/nossr50/util/skills/PerksUtils.java | 7 ++- 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/events/skills/SkillActivationPerkEvent.java diff --git a/pom.xml b/pom.xml index 77cb38b4a..a6c89b72a 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.1.166 + 2.1.167-SNAPSHOT mcMMO https://github.com/mcMMO-Dev/mcMMO diff --git a/src/main/java/com/gmail/nossr50/events/skills/SkillActivationPerkEvent.java b/src/main/java/com/gmail/nossr50/events/skills/SkillActivationPerkEvent.java new file mode 100644 index 000000000..89c41dd6f --- /dev/null +++ b/src/main/java/com/gmail/nossr50/events/skills/SkillActivationPerkEvent.java @@ -0,0 +1,49 @@ +package com.gmail.nossr50.events.skills; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class SkillActivationPerkEvent extends Event { + + + + + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private int ticks; + private final int maxTicks; + + public SkillActivationPerkEvent(Player player, int ticks, int maxTicks) { + + this.player = player; + this.ticks = ticks; + this.maxTicks = maxTicks; + } + + public Player getPlayer() { + return player; + } + + public int getTicks() { + return ticks; + } + + public void setTicks(int ticks) { + this.ticks = ticks; + } + + public int getMaxTicks() { + return maxTicks; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/com/gmail/nossr50/util/skills/PerksUtils.java b/src/main/java/com/gmail/nossr50/util/skills/PerksUtils.java index 165008068..588b944e1 100644 --- a/src/main/java/com/gmail/nossr50/util/skills/PerksUtils.java +++ b/src/main/java/com/gmail/nossr50/util/skills/PerksUtils.java @@ -2,8 +2,11 @@ package com.gmail.nossr50.util.skills; import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import com.gmail.nossr50.events.skills.SkillActivationPerkEvent; import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.player.UserManager; + +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; @@ -42,7 +45,9 @@ public final class PerksUtils { ticks += 4; } - return ticks; + final SkillActivationPerkEvent skillActivationPerkEvent = new SkillActivationPerkEvent(player, ticks, maxTicks); + Bukkit.getPluginManager().callEvent(skillActivationPerkEvent); + return skillActivationPerkEvent.getTicks(); } public static float handleXpPerks(Player player, float xp, PrimarySkillType skill) { From 67a4b6c7c136a394580f4b6c6f5b1887875fd97a Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Sun, 25 Oct 2020 14:43:33 +0000 Subject: [PATCH 18/30] Experience PreGain event --- .../nossr50/datatypes/player/McMMOPlayer.java | 7 +++ .../experience/McMMOPlayerPreXpGainEvent.java | 50 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/main/java/com/gmail/nossr50/events/experience/McMMOPlayerPreXpGainEvent.java diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java index 4d94f0d4b..cc5aebefe 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java @@ -17,6 +17,7 @@ import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SuperAbilityType; import com.gmail.nossr50.datatypes.skills.ToolType; +import com.gmail.nossr50.events.experience.McMMOPlayerPreXpGainEvent; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.party.PartyManager; @@ -56,6 +57,8 @@ import com.gmail.nossr50.util.sounds.SoundManager; import com.gmail.nossr50.util.sounds.SoundType; import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identity; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.block.Block; @@ -601,6 +604,10 @@ public class McMMOPlayer implements Identified { return; } + final McMMOPlayerPreXpGainEvent mcMMOPlayerPreXpGainEvent = new McMMOPlayerPreXpGainEvent(player, primarySkillType, xp, xpGainReason); + Bukkit.getPluginManager().callEvent(mcMMOPlayerPreXpGainEvent); + xp = mcMMOPlayerPreXpGainEvent.getXpGained(); + if (primarySkillType.isChildSkill()) { Set parentSkills = FamilyTree.getParents(primarySkillType); diff --git a/src/main/java/com/gmail/nossr50/events/experience/McMMOPlayerPreXpGainEvent.java b/src/main/java/com/gmail/nossr50/events/experience/McMMOPlayerPreXpGainEvent.java new file mode 100644 index 000000000..849d7ab48 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/events/experience/McMMOPlayerPreXpGainEvent.java @@ -0,0 +1,50 @@ +package com.gmail.nossr50.events.experience; + +import com.gmail.nossr50.datatypes.experience.XPGainReason; +import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a player gains XP in a skill + */ +public class McMMOPlayerPreXpGainEvent extends McMMOPlayerExperienceEvent { + private float xpGained; + + @Deprecated + public McMMOPlayerPreXpGainEvent(Player player, PrimarySkillType skill, float xpGained) { + super(player, skill, XPGainReason.UNKNOWN); + this.xpGained = xpGained; + } + + public McMMOPlayerPreXpGainEvent(Player player, PrimarySkillType skill, float xpGained, XPGainReason xpGainReason) { + super(player, skill, xpGainReason); + this.xpGained = xpGained; + } + + /** + * @return int amount of experience gained in this event + */ + public int getXpGained() { + return (int) xpGained; + } + + /** + * @param xpGained int amount of experience gained in this event + */ + public void setXpGained(int xpGained) { + this.xpGained = xpGained; + } + + private static final HandlerList handlers = new HandlerList(); + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} From 140806576858623b57db78fa5311a7398d162c39 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Sat, 2 Jan 2021 01:07:15 +0000 Subject: [PATCH 19/30] update changelog --- Changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.txt b/Changelog.txt index 37fd225ad..9724aac43 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,8 @@ +Version 2.1.167 + Add McMMOPlayerPreXpGainEvent event for plugins to modify given exp + Add SkillActivationPerkEvent + + Version 2.1.166 Fixed a small memory leak in the new COTW tracker Potentially fixed a ConcurrentModificationException involving the TransientEntityTracker (report this error if you encounter it) From bec54d93ac8628689561d1976e949044a89d2f9c Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 1 Jan 2021 22:15:44 -0800 Subject: [PATCH 20/30] Fix dupe bug --- Changelog.txt | 8 ++++++-- .../com/gmail/nossr50/listeners/EntityListener.java | 5 ++++- .../com/gmail/nossr50/util/TransientEntityTracker.java | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 9724aac43..03b0c066b 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,6 +1,10 @@ Version 2.1.167 - Add McMMOPlayerPreXpGainEvent event for plugins to modify given exp - Add SkillActivationPerkEvent + Fixed a serious dupe bug + Add McMMOPlayerPreXpGainEvent event for plugins to modify given exp (thanks electronicboy) + Add SkillActivationPerkEvent (thanks electronicboy) + + NOTE: + This bug was introduced in 2.1.165, update immediately if you are on 2.1.165 or 2.1.166 Version 2.1.166 diff --git a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index 24dd48031..13c65f3ec 100644 --- a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java @@ -645,7 +645,10 @@ public class EntityListener implements Listener { @EventHandler(ignoreCancelled = true) public void onEntityDeath(EntityDeathEvent event) { LivingEntity entity = event.getEntity(); - mcMMO.getTransientEntityTracker().removeSummon(entity, null, false); + + if(mcMMO.getTransientEntityTracker().isTransientSummon(entity)) { + mcMMO.getTransientEntityTracker().removeSummon(entity, null, false); + } /* WORLD BLACKLIST CHECK */ if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) { diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java index 9ee1807b4..6e1b44938 100644 --- a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -124,6 +124,16 @@ public class TransientEntityTracker { addToChunkLookupCache(trackedTamingEntity); } + /** + * Checks if a living entity is a summon + * + * @param livingEntity target livinig entity + * @return true if target living entity is a summon + */ + public synchronized boolean isTransientSummon(@NotNull LivingEntity livingEntity) { + return getChunkLookupCache().contains(livingEntity); + } + /** * Get the tracked taming entities for a player * If the player isn't registered this will return null From 0fa1d822d4497b123780c11ca999d4d53bc60d04 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 1 Jan 2021 22:16:52 -0800 Subject: [PATCH 21/30] 2.1.167 --- Changelog.txt | 1 - pom.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 03b0c066b..00979b96a 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -6,7 +6,6 @@ Version 2.1.167 NOTE: This bug was introduced in 2.1.165, update immediately if you are on 2.1.165 or 2.1.166 - Version 2.1.166 Fixed a small memory leak in the new COTW tracker Potentially fixed a ConcurrentModificationException involving the TransientEntityTracker (report this error if you encounter it) diff --git a/pom.xml b/pom.xml index a6c89b72a..49d76da30 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.1.167-SNAPSHOT + 2.1.167 mcMMO https://github.com/mcMMO-Dev/mcMMO From e0d85f842b1b96b2b1c055e66d65b65d2f14c306 Mon Sep 17 00:00:00 2001 From: t00thpick1 Date: Sat, 2 Jan 2021 11:18:32 -0500 Subject: [PATCH 22/30] Fix exception thrown when plants fake grow at max world height. --- .../com/gmail/nossr50/listeners/BlockListener.java | 12 +++++++++--- .../nossr50/util/blockmeta/BitSetChunkStore.java | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java index a2a4c2a76..a6d8e7900 100644 --- a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java @@ -253,12 +253,18 @@ public class BlockListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockGrow(BlockGrowEvent event) { + Block block = event.getBlock(); + World world = block.getWorld(); + /* WORLD BLACKLIST CHECK */ - if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld())) + if(WorldBlacklist.isWorldBlacklisted(world)) return; - BlockState blockState = event.getBlock().getState(); - mcMMO.getPlaceStore().setFalse(blockState); + // 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 (block.getY() >= world.getMaxHeight()) + return; + + mcMMO.getPlaceStore().setFalse(block); } /** diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java index dce7ab174..a1fe92e4b 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java @@ -82,7 +82,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { private int coordToIndex(int x, int y, int z) { if (x < 0 || x >= 16 || y < 0 || y >= worldHeight || z < 0 || z >= 16) - throw new IndexOutOfBoundsException(); + throw new IndexOutOfBoundsException(String.format("x: %d y: %d z: %d World Height: %d", x, y, z, worldHeight)); return (z * 16 + x) + (256 * y); } From a39c7420b94d9c5a97f50a19025c5754ef819343 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 2 Jan 2021 13:32:30 -0800 Subject: [PATCH 23/30] Add relocation for Kyori's examination library --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index 49d76da30..a0e35a0be 100755 --- a/pom.xml +++ b/pom.xml @@ -121,6 +121,10 @@ + + net.kyori.examination + com.gmail.nossr50.kyori.examination + net.kyori.adventure com.gmail.nossr50.mcmmo.kyori.adventure From d08c9391b045b786700a38d6a0385ec95621e85b Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 2 Jan 2021 13:36:59 -0800 Subject: [PATCH 24/30] Add nullability annotations to blockmeta --- .../util/blockmeta/BitSetChunkStore.java | 10 ++-- .../nossr50/util/blockmeta/ChunkManager.java | 23 ++++---- .../util/blockmeta/ChunkManagerFactory.java | 3 +- .../nossr50/util/blockmeta/ChunkStore.java | 3 +- .../util/blockmeta/HashChunkManager.java | 56 ++++++++++--------- .../util/blockmeta/McMMOSimpleRegionFile.java | 11 ++-- src/test/java/ChunkStoreTest.java | 3 +- 7 files changed, 60 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java index a1fe92e4b..2f4ffe9ce 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java @@ -2,6 +2,8 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.Bukkit; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.BitSet; @@ -19,7 +21,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { private int worldHeight; private UUID worldUid; - public BitSetChunkStore(World world, int cx, int cz) { + public BitSetChunkStore(@NotNull World world, int cx, int cz) { this.cx = cx; this.cz = cz; this.worldUid = world.getUID(); @@ -50,7 +52,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { } @Override - public UUID getWorldId() { + public @NotNull UUID getWorldId() { return worldUid; } @@ -153,7 +155,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { dirty = false; } - private static BitSetChunkStore deserialize(DataInputStream in) throws IOException { + private static BitSetChunkStore deserialize(@NotNull DataInputStream in) throws IOException { int magic = in.readInt(); // Can be used to determine the format of the file int fileVersionNumber = in.readInt(); @@ -182,7 +184,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { public static final short STREAM_MAGIC = (short)0xACDC; - public static ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { + public static @NotNull ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { if (inputStream.markSupported()) inputStream.mark(2); short magicNumber = inputStream.readShort(); diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java index 5cb50ac7f..57c66a899 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java @@ -3,6 +3,7 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.jetbrains.annotations.Nullable; public interface ChunkManager { void closeAll(); @@ -14,7 +15,7 @@ public interface ChunkManager { * @param cz Chunk Z coordinate that is to be saved * @param world World that the Chunk is in */ - void saveChunk(int cx, int cz, World world); + void saveChunk(int cx, int cz, @Nullable World world); /** * Informs the ChunkletManager a chunk is unloaded @@ -23,7 +24,7 @@ public interface ChunkManager { * @param cz Chunk Z coordinate that is unloaded * @param world World that the chunk was unloaded in */ - void chunkUnloaded(int cx, int cz, World world); + void chunkUnloaded(int cx, int cz, @Nullable World world); /** * Save all ChunkletStores related to the given world @@ -53,7 +54,7 @@ public interface ChunkManager { * @param world World to check in * @return true if the given location is set to true, false if otherwise */ - boolean isTrue(int x, int y, int z, World world); + boolean isTrue(int x, int y, int z, @Nullable World world); /** * Check to see if a given block location is set to true @@ -61,7 +62,7 @@ public interface ChunkManager { * @param block Block location to check * @return true if the given block location is set to true, false if otherwise */ - boolean isTrue(Block block); + boolean isTrue(@Nullable Block block); /** * Check to see if a given BlockState location is set to true @@ -69,7 +70,7 @@ public interface ChunkManager { * @param blockState BlockState to check * @return true if the given BlockState location is set to true, false if otherwise */ - boolean isTrue(BlockState blockState); + boolean isTrue(@Nullable BlockState blockState); /** * Set a given location to true, should create stores as necessary if the location does not exist @@ -79,21 +80,21 @@ public interface ChunkManager { * @param z Z coordinate to set * @param world World to set in */ - void setTrue(int x, int y, int z, World world); + void setTrue(int x, int y, int z, @Nullable World world); /** * Set a given block location to true, should create stores as necessary if the location does not exist * * @param block Block location to set */ - void setTrue(Block block); + void setTrue(@Nullable Block block); /** * Set a given BlockState location to true, should create stores as necessary if the location does not exist * * @param blockState BlockState location to set */ - void setTrue(BlockState blockState); + void setTrue(@Nullable BlockState blockState); /** * Set a given location to false, should not create stores if one does not exist for the given location @@ -103,21 +104,21 @@ public interface ChunkManager { * @param z Z coordinate to set * @param world World to set in */ - void setFalse(int x, int y, int z, World world); + void setFalse(int x, int y, int z, @Nullable World world); /** * Set a given block location to false, should not create stores if one does not exist for the given location * * @param block Block location to set */ - void setFalse(Block block); + void setFalse(@Nullable Block block); /** * Set a given BlockState location to false, should not create stores if one does not exist for the given location * * @param blockState BlockState location to set */ - void setFalse(BlockState blockState); + void setFalse(@Nullable BlockState blockState); /** * Delete any ChunkletStores that are empty diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManagerFactory.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManagerFactory.java index a290c5e2a..e2c47662e 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManagerFactory.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManagerFactory.java @@ -1,9 +1,10 @@ package com.gmail.nossr50.util.blockmeta; import com.gmail.nossr50.config.HiddenConfig; +import org.jetbrains.annotations.NotNull; public class ChunkManagerFactory { - public static ChunkManager getChunkManager() { + public static @NotNull ChunkManager getChunkManager() { HiddenConfig hConfig = HiddenConfig.getInstance(); if (hConfig.getChunkletsEnabled()) { diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java index eca783ccd..df01b93ee 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java @@ -1,6 +1,7 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -36,7 +37,7 @@ public interface ChunkStore { */ int getChunkZ(); - UUID getWorldId(); + @NotNull UUID getWorldId(); /** * Checks the value at the given coordinates diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java index 888937872..f3023e0e3 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java @@ -5,14 +5,16 @@ import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.*; public class HashChunkManager implements ChunkManager { - private final HashMap regionMap = new HashMap<>(); // Tracks active regions - private final HashMap> chunkUsageMap = new HashMap<>(); // Tracks active chunks by region - private final HashMap chunkMap = new HashMap<>(); // Tracks active chunks + private final @NotNull HashMap regionMap = new HashMap<>(); // Tracks active regions + private final @NotNull HashMap> chunkUsageMap = new HashMap<>(); // Tracks active chunks by region + private final @NotNull HashMap chunkMap = new HashMap<>(); // Tracks active chunks @Override public synchronized void closeAll() { @@ -32,7 +34,7 @@ public class HashChunkManager implements ChunkManager { regionMap.clear(); } - private synchronized ChunkStore readChunkStore(World world, int cx, int cz) throws IOException { + private synchronized @Nullable ChunkStore readChunkStore(@NotNull World world, int cx, int cz) throws IOException { McMMOSimpleRegionFile rf = getSimpleRegionFile(world, cx, cz, false); if (rf == null) return null; // If there is no region file, there can't be a chunk @@ -43,7 +45,7 @@ public class HashChunkManager implements ChunkManager { } } - private synchronized void writeChunkStore(World world, ChunkStore data) { + private synchronized void writeChunkStore(@NotNull World world, @NotNull ChunkStore data) { if (!data.isDirty()) return; // Don't save unchanged data try { @@ -58,7 +60,7 @@ public class HashChunkManager implements ChunkManager { } } - private synchronized McMMOSimpleRegionFile getSimpleRegionFile(World world, int cx, int cz, boolean createIfAbsent) { + private synchronized @Nullable McMMOSimpleRegionFile getSimpleRegionFile(World world, int cx, int cz, boolean createIfAbsent) { CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz); return regionMap.computeIfAbsent(regionKey, k -> { @@ -73,7 +75,7 @@ public class HashChunkManager implements ChunkManager { }); } - private ChunkStore loadChunk(int cx, int cz, World world) { + private @Nullable ChunkStore loadChunk(int cx, int cz, World world) { try { return readChunkStore(world, cx, cz); } @@ -82,7 +84,7 @@ public class HashChunkManager implements ChunkManager { return null; } - private void unloadChunk(int cx, int cz, World world) { + private void unloadChunk(int cx, int cz, @NotNull World world) { CoordinateKey chunkKey = toChunkKey(world.getUID(), cx, cz); ChunkStore chunkStore = chunkMap.remove(chunkKey); // Remove from chunk map if (chunkStore == null) @@ -102,7 +104,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void saveChunk(int cx, int cz, World world) { + public synchronized void saveChunk(int cx, int cz, @Nullable World world) { if (world == null) return; @@ -120,7 +122,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void chunkUnloaded(int cx, int cz, World world) { + public synchronized void chunkUnloaded(int cx, int cz, @Nullable World world) { if (world == null) return; @@ -128,7 +130,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void saveWorld(World world) { + public synchronized void saveWorld(@Nullable World world) { if (world == null) return; @@ -148,7 +150,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void unloadWorld(World world) { + public synchronized void unloadWorld(@Nullable World world) { if (world == null) return; @@ -185,7 +187,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized boolean isTrue(int x, int y, int z, World world) { + public synchronized boolean isTrue(int x, int y, int z, @Nullable World world) { if (world == null) return false; @@ -214,7 +216,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized boolean isTrue(Block block) { + public synchronized boolean isTrue(@Nullable Block block) { if (block == null) return false; @@ -222,7 +224,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized boolean isTrue(BlockState blockState) { + public synchronized boolean isTrue(@Nullable BlockState blockState) { if (blockState == null) return false; @@ -230,12 +232,12 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void setTrue(int x, int y, int z, World world) { + public synchronized void setTrue(int x, int y, int z, @Nullable World world) { set(x, y, z, world, true); } @Override - public synchronized void setTrue(Block block) { + public synchronized void setTrue(@Nullable Block block) { if (block == null) return; @@ -243,7 +245,7 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void setTrue(BlockState blockState) { + public synchronized void setTrue(@Nullable BlockState blockState) { if (blockState == null) return; @@ -251,12 +253,12 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void setFalse(int x, int y, int z, World world) { + public synchronized void setFalse(int x, int y, int z, @Nullable World world) { set(x, y, z, world, false); } @Override - public synchronized void setFalse(Block block) { + public synchronized void setFalse(@Nullable Block block) { if (block == null) return; @@ -264,14 +266,14 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void setFalse(BlockState blockState) { + public synchronized void setFalse(@Nullable BlockState blockState) { if (blockState == null) return; setFalse(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); } - public synchronized void set(int x, int y, int z, World world, boolean value){ + public synchronized void set(int x, int y, int z, @Nullable World world, boolean value){ if (world == null) return; @@ -307,15 +309,15 @@ public class HashChunkManager implements ChunkManager { cStore.set(ix, y, iz, value); } - private CoordinateKey blockCoordinateToChunkKey(UUID worldUid, int x, int y, int z) { + private CoordinateKey blockCoordinateToChunkKey(@NotNull UUID worldUid, int x, int y, int z) { return toChunkKey(worldUid, x >> 4, z >> 4); } - private CoordinateKey toChunkKey(UUID worldUid, int cx, int cz){ + private CoordinateKey toChunkKey(@NotNull UUID worldUid, int cx, int cz){ return new CoordinateKey(worldUid, cx, cz); } - private CoordinateKey toRegionKey(UUID worldUid, int cx, int cz) { + private CoordinateKey toRegionKey(@NotNull UUID worldUid, int cx, int cz) { // Compute region index (32x32 chunk regions) int rx = cx >> 5; int rz = cz >> 5; @@ -323,11 +325,11 @@ public class HashChunkManager implements ChunkManager { } private static final class CoordinateKey { - public final UUID worldID; + public final @NotNull UUID worldID; public final int x; public final int z; - private CoordinateKey(UUID worldID, int x, int z) { + private CoordinateKey(@NotNull UUID worldID, int x, int z) { this.worldID = worldID; this.x = x; this.z = z; diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/McMMOSimpleRegionFile.java b/src/main/java/com/gmail/nossr50/util/blockmeta/McMMOSimpleRegionFile.java index bef730ff4..3f61c9bef 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/McMMOSimpleRegionFile.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/McMMOSimpleRegionFile.java @@ -19,6 +19,9 @@ */ package com.gmail.nossr50.util.blockmeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.io.*; import java.util.BitSet; import java.util.zip.DeflaterOutputStream; @@ -54,7 +57,7 @@ public class McMMOSimpleRegionFile { private final int segmentMask; // File location - private final File parent; + private final @NotNull File parent; // File access private final RandomAccessFile file; @@ -62,7 +65,7 @@ public class McMMOSimpleRegionFile { private final int rx; private final int rz; - public McMMOSimpleRegionFile(File f, int rx, int rz) { + public McMMOSimpleRegionFile(@NotNull File f, int rx, int rz) { this.rx = rx; this.rz = rz; this.parent = f; @@ -104,7 +107,7 @@ public class McMMOSimpleRegionFile { } } - public synchronized DataOutputStream getOutputStream(int x, int z) { + public synchronized @NotNull DataOutputStream getOutputStream(int x, int z) { int index = getChunkIndex(x, z); // Get chunk index return new DataOutputStream(new DeflaterOutputStream(new McMMOSimpleChunkBuffer(this, index))); } @@ -144,7 +147,7 @@ public class McMMOSimpleRegionFile { file.writeInt(chunkNumBytes[index]); } - public synchronized DataInputStream getInputStream(int x, int z) throws IOException { + public synchronized @Nullable DataInputStream getInputStream(int x, int z) throws IOException { int index = getChunkIndex(x, z); // Get chunk index int byteLength = chunkNumBytes[index]; // Get byte length of data diff --git a/src/test/java/ChunkStoreTest.java b/src/test/java/ChunkStoreTest.java index f45b0e0c9..43f105bad 100644 --- a/src/test/java/ChunkStoreTest.java +++ b/src/test/java/ChunkStoreTest.java @@ -2,6 +2,7 @@ import com.gmail.nossr50.util.blockmeta.*; import com.google.common.io.Files; import org.bukkit.Bukkit; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; import org.junit.*; import org.junit.runner.RunWith; import org.mockito.Mockito; @@ -227,7 +228,7 @@ public class ChunkStoreTest { } @Override - public UUID getWorldId() { + public @NotNull UUID getWorldId() { return worldUid; } From 10694042e9d6ebe8bff60f9571af978bbdcb8b8e Mon Sep 17 00:00:00 2001 From: t00thpick1 Date: Sat, 2 Jan 2021 16:46:35 -0500 Subject: [PATCH 25/30] Move legacy Serializable usage into a subclass. --- .../util/blockmeta/BitSetChunkStore.java | 172 ++++++++++-------- 1 file changed, 101 insertions(+), 71 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java index 2f4ffe9ce..a04ed501b 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java @@ -3,33 +3,35 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.Bukkit; import org.bukkit.World; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.BitSet; import java.util.UUID; -public class BitSetChunkStore implements ChunkStore, Serializable { - private static final long serialVersionUID = -1L; - transient private boolean dirty = false; - // Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits. - private BitSet store; +public class BitSetChunkStore implements ChunkStore { private static final int CURRENT_VERSION = 8; private static final int MAGIC_NUMBER = 0xEA5EDEBB; - private int cx; - private int cz; - private int worldHeight; - private UUID worldUid; + + private final int cx; + private final int cz; + private final int worldHeight; + private final UUID worldUid; + // Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits. + private final BitSet store; + + private transient boolean dirty = false; public BitSetChunkStore(@NotNull World world, int cx, int cz) { - this.cx = cx; - this.cz = cz; - this.worldUid = world.getUID(); - this.worldHeight = world.getMaxHeight(); - this.store = new BitSet(16 * 16 * worldHeight); + this(world.getUID(), world.getMaxHeight(), cx, cz); } - private BitSetChunkStore() {} + private BitSetChunkStore(@NotNull UUID worldUid, int worldHeight, int cx, int cz) { + this.cx = cx; + this.cz = cz; + this.worldUid = worldUid; + this.worldHeight = worldHeight; + this.store = new BitSet(16 * 16 * worldHeight); + } @Override public boolean isDirty() { @@ -83,58 +85,24 @@ public class BitSetChunkStore implements ChunkStore, Serializable { } private int coordToIndex(int x, int y, int z) { + return coordToIndex(x, y, z, worldHeight); + } + + private static int coordToIndex(int x, int y, int z, int worldHeight) { if (x < 0 || x >= 16 || y < 0 || y >= worldHeight || z < 0 || z >= 16) throw new IndexOutOfBoundsException(String.format("x: %d y: %d z: %d World Height: %d", x, y, z, worldHeight)); return (z * 16 + x) + (256 * y); } - private void fixWorldHeight() { + private static int getWorldHeight(UUID worldUid, int storedWorldHeight) + { World world = Bukkit.getWorld(worldUid); // Not sure how this case could come up, but might as well handle it gracefully. Loading a chunkstore for an unloaded world? if (world == null) - return; + return storedWorldHeight; - // Lop off any extra data if the world height has shrunk - int currentWorldHeight = world.getMaxHeight(); - if (currentWorldHeight < worldHeight) - { - store.clear(coordToIndex(16, currentWorldHeight, 16), store.length()); - worldHeight = currentWorldHeight; - dirty = true; - } - // If the world height has grown, update the worldHeight variable, but don't bother marking it dirty as unless something else changes we don't need to force a file write; - else if (currentWorldHeight > worldHeight) - worldHeight = currentWorldHeight; - } - - @Deprecated - private void writeObject(ObjectOutputStream out) throws IOException { - throw new UnsupportedOperationException("Serializable support should only be used for legacy deserialization"); - } - - @Deprecated - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.readInt(); // Magic number - in.readInt(); // Format version - long lsb = in.readLong(); - long msb = in.readLong(); - worldUid = new UUID(msb, lsb); - cx = in.readInt(); - cz = in.readInt(); - - boolean[][][] oldStore = (boolean[][][]) in.readObject(); - worldHeight = oldStore[0][0].length; - store = new BitSet(16 * 16 * worldHeight / 8); - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < worldHeight; y++) { - store.set(coordToIndex(x, y, z), oldStore[x][z][y]); - } - } - } - dirty = true; - fixWorldHeight(); + return world.getMaxHeight(); } private void serialize(DataOutputStream out) throws IOException { @@ -163,26 +131,34 @@ public class BitSetChunkStore implements ChunkStore, Serializable { if (magic != MAGIC_NUMBER || fileVersionNumber != CURRENT_VERSION) throw new IOException(); - BitSetChunkStore chunkStore = new BitSetChunkStore(); - long lsb = in.readLong(); long msb = in.readLong(); - chunkStore.worldUid = new UUID(msb, lsb); - chunkStore.cx = in.readInt(); - chunkStore.cz = in.readInt(); + UUID worldUid = new UUID(msb, lsb); + int cx = in.readInt(); + int cz = in.readInt(); - chunkStore.worldHeight = in.readInt(); + int worldHeight = in.readInt(); byte[] temp = new byte[in.readInt()]; in.readFully(temp); - chunkStore.store = BitSet.valueOf(temp); + BitSet stored = BitSet.valueOf(temp); + + int currentWorldHeight = getWorldHeight(worldUid, worldHeight); + + boolean worldHeightShrunk = currentWorldHeight < worldHeight; + // Lop off extra data if world height has shrunk + if (worldHeightShrunk) + stored.clear(coordToIndex(16, currentWorldHeight, 16, worldHeight), stored.length()); + + BitSetChunkStore chunkStore = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz); + chunkStore.store.or(stored); + chunkStore.dirty = worldHeightShrunk; // In the expanded case there is no reason to re-write it unless the data changes - chunkStore.fixWorldHeight(); return chunkStore; } public static class Serialization { - public static final short STREAM_MAGIC = (short)0xACDC; + public static final short STREAM_MAGIC = (short)0xACDC; // Rock on public static @NotNull ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { if (inputStream.markSupported()) @@ -198,7 +174,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { { // Creates a new stream with the two magic number bytes and then the rest of the original stream... Java is so dumb. I just wanted to look at two bytes. PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream, 2); - pushbackInputStream.unread((magicNumber >>> 0) & 0xFF); + pushbackInputStream.unread((magicNumber) & 0xFF); pushbackInputStream.unread((magicNumber >>> 8) & 0xFF); inputStream = new DataInputStream(pushbackInputStream); } @@ -218,8 +194,61 @@ public class BitSetChunkStore implements ChunkStore, Serializable { ((BitSetChunkStore)chunkStore).serialize(outputStream); } - // Handles loading the old serialized classes even though we have changed name/package + // Handles loading the old serialized class private static class LegacyDeserializationInputStream extends ObjectInputStream { + private static class LegacyChunkStoreDeserializer implements Serializable + { + private static final long serialVersionUID = -1L; + + private int cx; + private int cz; + private int worldHeight; + private UUID worldUid; + private boolean[][][] store; + + private LegacyChunkStoreDeserializer() {} + + @Deprecated + private void writeObject(ObjectOutputStream out) throws IOException { + throw new UnsupportedOperationException("You goofed."); + } + + @Deprecated + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.readInt(); // Magic number + in.readInt(); // Format version + long lsb = in.readLong(); + long msb = in.readLong(); + + worldUid = new UUID(msb, lsb); + cx = in.readInt(); + cz = in.readInt(); + + store = (boolean[][][]) in.readObject(); + worldHeight = store[0][0].length; + } + + public BitSetChunkStore convert() + { + int currentWorldHeight = getWorldHeight(worldUid, worldHeight); + + BitSetChunkStore converted = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz); + + // Read old data into new chunkstore + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < worldHeight && y < currentWorldHeight; y++) { + converted.store.set(converted.coordToIndex(x, y, z), store[x][z][y]); + } + } + } + // Mark dirty so it will be re-written in new format on close + converted.dirty = true; + return converted; + } + } + + public LegacyDeserializationInputStream(InputStream in) throws IOException { super(in); enableResolveObject(true); @@ -229,13 +258,14 @@ public class BitSetChunkStore implements ChunkStore, Serializable { protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass read = super.readClassDescriptor(); if (read.getName().contentEquals("com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore")) - return ObjectStreamClass.lookup(BitSetChunkStore.class); + return ObjectStreamClass.lookup(LegacyChunkStoreDeserializer.class); return read; } public ChunkStore readLegacyChunkStore(){ try { - return (ChunkStore) readObject(); + LegacyChunkStoreDeserializer deserializer = (LegacyChunkStoreDeserializer)readObject(); + return deserializer.convert(); } catch (IOException | ClassNotFoundException e) { return null; } From 5b41b04777c9bdd5a67bb5f643a4fae1c7ff15d3 Mon Sep 17 00:00:00 2001 From: t00thpick1 Date: Sat, 2 Jan 2021 17:29:15 -0500 Subject: [PATCH 26/30] Use @NotNull in methods rather than @Nullable Separate safe external methods from internal only methods Remove unnecessary methods --- src/main/java/com/gmail/nossr50/mcMMO.java | 1 - .../nossr50/util/blockmeta/ChunkManager.java | 125 +--------------- .../nossr50/util/blockmeta/ChunkStore.java | 1 - .../util/blockmeta/HashChunkManager.java | 134 ++++-------------- .../util/blockmeta/NullChunkManager.java | 40 ++---- .../util/blockmeta/UserBlockTracker.java | 56 ++++++++ src/test/java/ChunkStoreTest.java | 18 ++- 7 files changed, 109 insertions(+), 266 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/util/blockmeta/UserBlockTracker.java diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index f3f6bb9ac..c956b65f4 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -345,7 +345,6 @@ public class mcMMO extends JavaPlugin { formulaManager.saveFormula(); holidayManager.saveAnniversaryFiles(); - placeStore.cleanUp(); // Cleanup empty metadata stores placeStore.closeAll(); } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java index 57c66a899..a0b61ba6f 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkManager.java @@ -1,127 +1,10 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; -public interface ChunkManager { +public interface ChunkManager extends UserBlockTracker { void closeAll(); - - /** - * Saves a given Chunk's Chunklet data - * - * @param cx Chunk X coordinate that is to be saved - * @param cz Chunk Z coordinate that is to be saved - * @param world World that the Chunk is in - */ - void saveChunk(int cx, int cz, @Nullable World world); - - /** - * Informs the ChunkletManager a chunk is unloaded - * - * @param cx Chunk X coordinate that is unloaded - * @param cz Chunk Z coordinate that is unloaded - * @param world World that the chunk was unloaded in - */ - void chunkUnloaded(int cx, int cz, @Nullable World world); - - /** - * Save all ChunkletStores related to the given world - * - * @param world World to save - */ - void saveWorld(World world); - - /** - * Unload all ChunkletStores from memory related to the given world after saving them - * - * @param world World to unload - */ - void unloadWorld(World world); - - /** - * Save all ChunkletStores - */ - void saveAll(); - - /** - * Check to see if a given location is set to true - * - * @param x X coordinate to check - * @param y Y coordinate to check - * @param z Z coordinate to check - * @param world World to check in - * @return true if the given location is set to true, false if otherwise - */ - boolean isTrue(int x, int y, int z, @Nullable World world); - - /** - * 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 - */ - boolean isTrue(@Nullable Block 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 - */ - boolean isTrue(@Nullable BlockState blockState); - - /** - * Set a given location to true, should create stores as necessary if the location does not exist - * - * @param x X coordinate to set - * @param y Y coordinate to set - * @param z Z coordinate to set - * @param world World to set in - */ - void setTrue(int x, int y, int z, @Nullable World world); - - /** - * Set a given block location to true, should create stores as necessary if the location does not exist - * - * @param block Block location to set - */ - void setTrue(@Nullable Block block); - - /** - * Set a given BlockState location to true, should create stores as necessary if the location does not exist - * - * @param blockState BlockState location to set - */ - void setTrue(@Nullable BlockState blockState); - - /** - * Set a given location to false, should not create stores if one does not exist for the given location - * - * @param x X coordinate to set - * @param y Y coordinate to set - * @param z Z coordinate to set - * @param world World to set in - */ - void setFalse(int x, int y, int z, @Nullable World world); - - /** - * Set a given block location to false, should not create stores if one does not exist for the given location - * - * @param block Block location to set - */ - void setFalse(@Nullable Block block); - - /** - * Set a given BlockState location to false, should not create stores if one does not exist for the given location - * - * @param blockState BlockState location to set - */ - void setFalse(@Nullable BlockState blockState); - - /** - * Delete any ChunkletStores that are empty - */ - void cleanUp(); + void chunkUnloaded(int cx, int cz, @NotNull World world); + void unloadWorld(@NotNull World world); } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java index df01b93ee..e21006628 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkStore.java @@ -1,6 +1,5 @@ package com.gmail.nossr50.util.blockmeta; -import org.bukkit.World; import org.jetbrains.annotations.NotNull; import java.util.UUID; diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java index f3023e0e3..f6d88665f 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java @@ -1,6 +1,5 @@ package com.gmail.nossr50.util.blockmeta; -import com.gmail.nossr50.mcMMO; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Block; @@ -8,13 +7,16 @@ import org.bukkit.block.BlockState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; import java.util.*; public class HashChunkManager implements ChunkManager { - private final @NotNull HashMap regionMap = new HashMap<>(); // Tracks active regions - private final @NotNull HashMap> chunkUsageMap = new HashMap<>(); // Tracks active chunks by region - private final @NotNull HashMap chunkMap = new HashMap<>(); // Tracks active chunks + private final HashMap regionMap = new HashMap<>(); // Tracks active regions + private final HashMap> chunkUsageMap = new HashMap<>(); // Tracks active chunks by region + private final HashMap chunkMap = new HashMap<>(); // Tracks active chunks @Override public synchronized void closeAll() { @@ -23,7 +25,10 @@ public class HashChunkManager implements ChunkManager { { if (!chunkStore.isDirty()) continue; - writeChunkStore(Bukkit.getWorld(chunkStore.getWorldId()), chunkStore); + World world = Bukkit.getWorld(chunkStore.getWorldId()); + if (world == null) + continue; // Oh well + writeChunkStore(world, chunkStore); } // Clear in memory chunks chunkMap.clear(); @@ -104,56 +109,12 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized void saveChunk(int cx, int cz, @Nullable World world) { - if (world == null) - return; - - CoordinateKey chunkKey = toChunkKey(world.getUID(), cx, cz); - - ChunkStore out = chunkMap.get(chunkKey); - - if (out == null) - return; - - if (!out.isDirty()) - return; - - writeChunkStore(world, out); - } - - @Override - public synchronized void chunkUnloaded(int cx, int cz, @Nullable World world) { - if (world == null) - return; - + public synchronized void chunkUnloaded(int cx, int cz, @NotNull World world) { unloadChunk(cx, cz, world); } @Override - public synchronized void saveWorld(@Nullable World world) { - if (world == null) - return; - - UUID wID = world.getUID(); - - // Save all teh chunks - for (ChunkStore chunkStore : chunkMap.values()) { - if (!chunkStore.isDirty()) - continue; - if (!wID.equals(chunkStore.getWorldId())) - continue; - try { - writeChunkStore(world, chunkStore); - } - catch (Exception ignore) { } - } - } - - @Override - public synchronized void unloadWorld(@Nullable World world) { - if (world == null) - return; - + public synchronized void unloadWorld(@NotNull World world) { UUID wID = world.getUID(); // Save and remove all the chunks @@ -179,18 +140,7 @@ public class HashChunkManager implements ChunkManager { } } - @Override - public synchronized void saveAll() { - for (World world : mcMMO.p.getServer().getWorlds()) { - saveWorld(world); - } - } - - @Override - public synchronized boolean isTrue(int x, int y, int z, @Nullable World world) { - if (world == null) - return false; - + private synchronized boolean isTrue(int x, int y, int z, @NotNull World world) { CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z); // Get chunk, load from file if necessary @@ -216,67 +166,36 @@ public class HashChunkManager implements ChunkManager { } @Override - public synchronized boolean isTrue(@Nullable Block block) { - if (block == null) - return false; - + public synchronized boolean isTrue(@NotNull Block block) { return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld()); } @Override - public synchronized boolean isTrue(@Nullable BlockState blockState) { - if (blockState == null) - return false; - + public synchronized boolean isTrue(@NotNull BlockState blockState) { return isTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); } @Override - public synchronized void setTrue(int x, int y, int z, @Nullable World world) { - set(x, y, z, world, true); + public synchronized void setTrue(@NotNull Block block) { + set(block.getX(), block.getY(), block.getZ(), block.getWorld(), true); } @Override - public synchronized void setTrue(@Nullable Block block) { - if (block == null) - return; - - setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld()); + public synchronized void setTrue(@NotNull BlockState blockState) { + set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), true); } @Override - public synchronized void setTrue(@Nullable BlockState blockState) { - if (blockState == null) - return; - - setTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); + public synchronized void setFalse(@NotNull Block block) { + set(block.getX(), block.getY(), block.getZ(), block.getWorld(), false); } @Override - public synchronized void setFalse(int x, int y, int z, @Nullable World world) { - set(x, y, z, world, false); + public synchronized void setFalse(@NotNull BlockState blockState) { + set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), false); } - @Override - public synchronized void setFalse(@Nullable Block block) { - if (block == null) - return; - - setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld()); - } - - @Override - public synchronized void setFalse(@Nullable BlockState blockState) { - if (blockState == null) - return; - - setFalse(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); - } - - public synchronized void set(int x, int y, int z, @Nullable World world, boolean value){ - if (world == null) - return; - + private synchronized void set(int x, int y, int z, @NotNull World world, boolean value){ CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z); // Get/Load/Create chunkstore @@ -350,7 +269,4 @@ public class HashChunkManager implements ChunkManager { return Objects.hash(worldID, x, z); } } - - @Override - public synchronized void cleanUp() {} } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/NullChunkManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/NullChunkManager.java index b777fa349..203376780 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/NullChunkManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/NullChunkManager.java @@ -3,6 +3,7 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.jetbrains.annotations.NotNull; public class NullChunkManager implements ChunkManager { @@ -10,53 +11,30 @@ public class NullChunkManager implements ChunkManager { public void closeAll() {} @Override - public void saveChunk(int cx, int cz, World world) {} + public void chunkUnloaded(int cx, int cz, @NotNull World world) {} @Override - public void chunkUnloaded(int cx, int cz, World world) {} + public void unloadWorld(@NotNull World world) {} @Override - public void saveWorld(World world) {} - - @Override - public void unloadWorld(World world) {} - - @Override - public void saveAll() {} - - @Override - public boolean isTrue(int x, int y, int z, World world) { + public boolean isTrue(@NotNull Block block) { return false; } @Override - public boolean isTrue(Block block) { + public boolean isTrue(@NotNull BlockState blockState) { return false; } @Override - public boolean isTrue(BlockState blockState) { - return false; - } + public void setTrue(@NotNull Block block) {} @Override - public void setTrue(int x, int y, int z, World world) {} + public void setTrue(@NotNull BlockState blockState) {} @Override - public void setTrue(Block block) {} + public void setFalse(@NotNull Block block) {} @Override - public void setTrue(BlockState blockState) {} - - @Override - public void setFalse(int x, int y, int z, World world) {} - - @Override - public void setFalse(Block block) {} - - @Override - public void setFalse(BlockState blockState) {} - - @Override - public void cleanUp() {} + public void setFalse(@NotNull BlockState blockState) {} } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/UserBlockTracker.java b/src/main/java/com/gmail/nossr50/util/blockmeta/UserBlockTracker.java new file mode 100644 index 000000000..e5428b0c1 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/UserBlockTracker.java @@ -0,0 +1,56 @@ +package com.gmail.nossr50.util.blockmeta; + +import com.gmail.nossr50.mcMMO; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.jetbrains.annotations.NotNull; + +/** + * Contains blockstore methods that are safe for external plugins to access. + * An instance can be retrieved via {@link mcMMO#getPlaceStore() mcMMO.getPlaceStore()} + */ +public interface UserBlockTracker { + /** + * 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 + */ + boolean isTrue(@NotNull Block 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 + */ + boolean isTrue(@NotNull BlockState blockState); + + /** + * Set a given block location to true + * + * @param block Block location to set + */ + void setTrue(@NotNull Block block); + + /** + * Set a given BlockState location to true + * + * @param blockState BlockState location to set + */ + void setTrue(@NotNull BlockState blockState); + + /** + * Set a given block location to false + * + * @param block Block location to set + */ + void setFalse(@NotNull Block block); + + /** + * Set a given BlockState location to false + * + * @param blockState BlockState location to set + */ + void setFalse(@NotNull BlockState blockState); +} diff --git a/src/test/java/ChunkStoreTest.java b/src/test/java/ChunkStoreTest.java index 43f105bad..8a090dfaf 100644 --- a/src/test/java/ChunkStoreTest.java +++ b/src/test/java/ChunkStoreTest.java @@ -2,6 +2,7 @@ import com.gmail.nossr50.util.blockmeta.*; 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.*; import org.junit.runner.RunWith; @@ -141,9 +142,20 @@ public class ChunkStoreTest { @Test public void testRegressionChunkMirrorBug() { ChunkManager chunkManager = new HashChunkManager(); - chunkManager.setTrue(15,0,15, mockWorld); - chunkManager.setFalse(-15, 0, -15, mockWorld); - Assert.assertTrue(chunkManager.isTrue(15, 0, 15, mockWorld)); + Block mockBlockA = 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 = 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); + Assert.assertTrue(chunkManager.isTrue(mockBlockA)); } private interface Delegate { From cddf6195185396f56985481a871177e4549f98cb Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 2 Jan 2021 15:01:29 -0800 Subject: [PATCH 27/30] dev mode --- Changelog.txt | 4 ++++ pom.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.txt b/Changelog.txt index 00979b96a..5a72330a5 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,7 @@ +Version 2.1.168 + Fixed an IndexOutOfBoundsException error when trying to access UserBlockTracker from an invalid range (thanks t00thpick1) + (API) UserBlockTracker is now the interface by which our block-tracker will be known (thanks t00thpick1) + Version 2.1.167 Fixed a serious dupe bug Add McMMOPlayerPreXpGainEvent event for plugins to modify given exp (thanks electronicboy) diff --git a/pom.xml b/pom.xml index 49d76da30..6dce9e08b 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.1.167 + 2.1.168-SNAPSHOT mcMMO https://github.com/mcMMO-Dev/mcMMO From eff016c0a63acd95fef36133e45c021d92c320bd Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 2 Jan 2021 15:27:51 -0800 Subject: [PATCH 28/30] More nullability in blockmeta classes --- .../util/blockmeta/BitSetChunkStore.java | 25 ++++++++++--------- .../util/blockmeta/HashChunkManager.java | 4 +-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java index a04ed501b..c378d7489 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java @@ -3,6 +3,7 @@ package com.gmail.nossr50.util.blockmeta; import org.bukkit.Bukkit; import org.bukkit.World; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.BitSet; @@ -15,9 +16,9 @@ public class BitSetChunkStore implements ChunkStore { private final int cx; private final int cz; private final int worldHeight; - private final UUID worldUid; + private final @NotNull UUID worldUid; // Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits. - private final BitSet store; + private final @NotNull BitSet store; private transient boolean dirty = false; @@ -94,7 +95,7 @@ public class BitSetChunkStore implements ChunkStore { return (z * 16 + x) + (256 * y); } - private static int getWorldHeight(UUID worldUid, int storedWorldHeight) + private static int getWorldHeight(@NotNull UUID worldUid, int storedWorldHeight) { World world = Bukkit.getWorld(worldUid); @@ -105,7 +106,7 @@ public class BitSetChunkStore implements ChunkStore { return world.getMaxHeight(); } - private void serialize(DataOutputStream out) throws IOException { + private void serialize(@NotNull DataOutputStream out) throws IOException { out.writeInt(MAGIC_NUMBER); out.writeInt(CURRENT_VERSION); @@ -123,7 +124,7 @@ public class BitSetChunkStore implements ChunkStore { dirty = false; } - private static BitSetChunkStore deserialize(@NotNull DataInputStream in) throws IOException { + private static @NotNull BitSetChunkStore deserialize(@NotNull DataInputStream in) throws IOException { int magic = in.readInt(); // Can be used to determine the format of the file int fileVersionNumber = in.readInt(); @@ -187,7 +188,7 @@ public class BitSetChunkStore implements ChunkStore { throw new IOException("Bad Data Format"); } - public static void writeChunkStore(DataOutputStream outputStream, ChunkStore chunkStore) throws IOException { + public static void writeChunkStore(@NotNull DataOutputStream outputStream, @NotNull ChunkStore chunkStore) throws IOException { if (!(chunkStore instanceof BitSetChunkStore)) throw new InvalidClassException("ChunkStore must be instance of BitSetChunkStore"); outputStream.writeShort(STREAM_MAGIC); @@ -209,12 +210,12 @@ public class BitSetChunkStore implements ChunkStore { private LegacyChunkStoreDeserializer() {} @Deprecated - private void writeObject(ObjectOutputStream out) throws IOException { + private void writeObject(@NotNull ObjectOutputStream out) throws IOException { throw new UnsupportedOperationException("You goofed."); } @Deprecated - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + private void readObject(@NotNull ObjectInputStream in) throws IOException, ClassNotFoundException { in.readInt(); // Magic number in.readInt(); // Format version long lsb = in.readLong(); @@ -228,7 +229,7 @@ public class BitSetChunkStore implements ChunkStore { worldHeight = store[0][0].length; } - public BitSetChunkStore convert() + public @NotNull BitSetChunkStore convert() { int currentWorldHeight = getWorldHeight(worldUid, worldHeight); @@ -249,20 +250,20 @@ public class BitSetChunkStore implements ChunkStore { } - public LegacyDeserializationInputStream(InputStream in) throws IOException { + public LegacyDeserializationInputStream(@NotNull InputStream in) throws IOException { super(in); enableResolveObject(true); } @Override - protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { + protected @NotNull ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass read = super.readClassDescriptor(); if (read.getName().contentEquals("com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore")) return ObjectStreamClass.lookup(LegacyChunkStoreDeserializer.class); return read; } - public ChunkStore readLegacyChunkStore(){ + public @Nullable ChunkStore readLegacyChunkStore(){ try { LegacyChunkStoreDeserializer deserializer = (LegacyChunkStoreDeserializer)readObject(); return deserializer.convert(); diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java index f6d88665f..95397f89d 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkManager.java @@ -65,7 +65,7 @@ public class HashChunkManager implements ChunkManager { } } - private synchronized @Nullable McMMOSimpleRegionFile getSimpleRegionFile(World world, int cx, int cz, boolean createIfAbsent) { + private synchronized @Nullable McMMOSimpleRegionFile getSimpleRegionFile(@NotNull World world, int cx, int cz, boolean createIfAbsent) { CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz); return regionMap.computeIfAbsent(regionKey, k -> { @@ -80,7 +80,7 @@ public class HashChunkManager implements ChunkManager { }); } - private @Nullable ChunkStore loadChunk(int cx, int cz, World world) { + private @Nullable ChunkStore loadChunk(int cx, int cz, @NotNull World world) { try { return readChunkStore(world, cx, cz); } From 05f07c174bb93956a1677e6f9b124cae67109806 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 2 Jan 2021 15:30:00 -0800 Subject: [PATCH 29/30] Change readChunkStore to Nullable --- .../java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java index c378d7489..63f08d228 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java @@ -161,7 +161,7 @@ public class BitSetChunkStore implements ChunkStore { public static final short STREAM_MAGIC = (short)0xACDC; // Rock on - public static @NotNull ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { + public static @Nullable ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { if (inputStream.markSupported()) inputStream.mark(2); short magicNumber = inputStream.readShort(); From 26ef4cc4110ed4fef2c942a7f49d3cd63800545a Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 2 Jan 2021 15:55:37 -0800 Subject: [PATCH 30/30] More nullability annotations --- .../util/blockmeta/BitSetChunkStore.java | 2 +- src/test/java/ChunkStoreTest.java | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java index 63f08d228..272710aeb 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/BitSetChunkStore.java @@ -161,7 +161,7 @@ public class BitSetChunkStore implements ChunkStore { public static final short STREAM_MAGIC = (short)0xACDC; // Rock on - public static @Nullable ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { + public static @Nullable ChunkStore readChunkStore(@NotNull DataInputStream inputStream) throws IOException { if (inputStream.markSupported()) inputStream.mark(2); short magicNumber = inputStream.readShort(); diff --git a/src/test/java/ChunkStoreTest.java b/src/test/java/ChunkStoreTest.java index 8a090dfaf..2290ee103 100644 --- a/src/test/java/ChunkStoreTest.java +++ b/src/test/java/ChunkStoreTest.java @@ -4,6 +4,7 @@ import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Block; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.*; import org.junit.runner.RunWith; import org.mockito.Mockito; @@ -162,7 +163,7 @@ public class ChunkStoreTest { void run(); } - private void assertThrows(Delegate delegate, Class clazz) { + private void assertThrows(@NotNull Delegate delegate, @NotNull Class clazz) { try { delegate.run(); Assert.fail(); // We didn't throw @@ -183,7 +184,7 @@ public class ChunkStoreTest { Assert.assertTrue(expected.isTrue(x, y, z) == actual.isTrue(x, y, z)); } - private static void recursiveDelete(File directoryToBeDeleted) { + private static void recursiveDelete(@NotNull File directoryToBeDeleted) { if (directoryToBeDeleted.isDirectory()) { for (File file : directoryToBeDeleted.listFiles()) { recursiveDelete(file); @@ -192,7 +193,7 @@ public class ChunkStoreTest { directoryToBeDeleted.delete(); } - private static byte[] serializeChunkstore(ChunkStore chunkStore) throws IOException { + private static byte[] serializeChunkstore(@NotNull ChunkStore chunkStore) throws IOException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); if (chunkStore instanceof BitSetChunkStore) BitSetChunkStore.Serialization.writeChunkStore(new DataOutputStream(byteArrayOutputStream), chunkStore); @@ -201,18 +202,17 @@ public class ChunkStoreTest { 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 int cx; - private int cz; - private UUID worldUid; + private final int cx; + private final int cz; + private final @NotNull UUID worldUid; - public LegacyChunkStore(World world, int cx, int cz) { + public LegacyChunkStore(@NotNull World world, int cx, int cz) { this.cx = cx; this.cz = cz; this.worldUid = world.getUID(); @@ -287,7 +287,7 @@ public class ChunkStoreTest { return true; } - private void writeObject(ObjectOutputStream out) throws IOException { + private void writeObject(@NotNull ObjectOutputStream out) throws IOException { out.writeInt(MAGIC_NUMBER); out.writeInt(CURRENT_VERSION); @@ -300,18 +300,18 @@ public class ChunkStoreTest { dirty = false; } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + private void readObject(@NotNull ObjectInputStream in) throws IOException, ClassNotFoundException { throw new UnsupportedOperationException(); } } private static class UnitTestObjectOutputStream extends ObjectOutputStream { - public UnitTestObjectOutputStream(OutputStream outputStream) throws IOException { + public UnitTestObjectOutputStream(@NotNull OutputStream outputStream) throws IOException { super(outputStream); } @Override - public void writeUTF(String str) throws IOException { + 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";