Merge branch 'master' of github.com:mcMMO-Dev/mcMMO into tridentsxbows

This commit is contained in:
nossr50
2021-01-02 16:07:17 -08:00
43 changed files with 1052 additions and 613 deletions

View File

@@ -182,11 +182,14 @@ 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;
if (ExperienceConfig.getInstance().doesBlockGiveSkillXP(PrimarySkillType.MINING, blockState.getBlockData()))
return true;
return isOre(blockState) || mcMMO.getModManager().isCustomMiningBlock(blockState);
return mcMMO.getModManager().isCustomMiningBlock(blockState);
}
/**

View File

@@ -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;
import static org.bukkit.Material.AIR;
@@ -581,4 +586,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<EnchantmentWrapper> enchantmentWrappers) {
Collections.shuffle(enchantmentWrappers, Misc.getRandom());
int randomIndex = Misc.getRandom().nextInt(enchantmentWrappers.size());
return enchantmentWrappers.get(randomIndex);
}
}

View File

@@ -55,6 +55,8 @@ public class MaterialMapStore {
private final @NotNull HashSet<String> enchantables;
private final @NotNull HashSet<String> ores;
private final @NotNull HashSet<String> intendedToolPickAxe;
private final @NotNull HashSet<String> intendedToolShovel;
private final @NotNull HashMap<String, Integer> tierValue;
@@ -100,14 +102,16 @@ public class MaterialMapStore {
enchantables = new HashSet<>();
ores = new HashSet<>();
intendedToolPickAxe = new HashSet<>();
intendedToolShovel = new HashSet<>();
tierValue = new HashMap<>();
fillVanillaMaterialRegisters();
}
private void fillVanillaMaterialRegisters()
{
private void fillVanillaMaterialRegisters() {
//The order matters
fillAbilityBlackList();
fillToolBlackList();
fillMossyWhiteList();
@@ -122,6 +126,7 @@ public class MaterialMapStore {
fillTools();
fillEnchantables();
fillOres();
fillIntendedTools();
fillTierMap();
}
@@ -206,6 +211,190 @@ public class MaterialMapStore {
ores.add("gilded_blackstone");
}
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() {
fillLeatherArmorWhiteList();
fillIronArmorWhiteList();
@@ -1078,6 +1267,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<String> getNetheriteArmor() {
return netheriteArmor;
}

View File

@@ -0,0 +1,316 @@
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 com.google.common.collect.ImmutableSet;
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 {
//These two are updated in step with each other
private final @NotNull HashMap<UUID, HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>>> perPlayerTransientEntityMap;
private final @NotNull HashSet<LivingEntity> chunkLookupCache;
public TransientEntityTracker() {
perPlayerTransientEntityMap = new HashMap<>();
chunkLookupCache = new HashSet<>();
}
public synchronized @NotNull HashSet<LivingEntity> getChunkLookupCache() {
return chunkLookupCache;
}
public synchronized @NotNull HashMap<UUID, HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>>> getPerPlayerTransientEntityMap() {
return perPlayerTransientEntityMap;
}
public synchronized void initPlayer(@NotNull Player player) {
if (!isPlayerRegistered(player.getUniqueId())) {
registerPlayer(player.getUniqueId());
}
}
/**
* Removes a player from the tracker
*
* @param playerUUID target player
*/
public synchronized void cleanupPlayer(@NotNull UUID playerUUID) {
cleanPlayer(null, playerUUID);
}
/**
* 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 void removePlayerFromMap(@NotNull UUID playerUUID) {
getPerPlayerTransientEntityMap().remove(playerUUID);
}
/**
* 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<CallOfTheWildType, HashSet<TrackedTamingEntity>>());
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
getPerPlayerTransientEntityMap().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 synchronized @Nullable HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>> getPlayerTrackedEntityMap(@NotNull UUID playerUUID) {
return getPerPlayerTransientEntityMap().get(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);
//Add to cache for chunk lookups
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
*
* @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 synchronized @Nullable HashSet<TrackedTamingEntity> getTrackedEntities(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) {
HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>> playerEntityMap = getPlayerTrackedEntityMap(playerUUID);
if(playerEntityMap == null)
return null;
return playerEntityMap.get(callOfTheWildType);
}
/**
* 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());
}
/**
* 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) {
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 : getPerPlayerTransientEntityMap().keySet()) {
for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
HashSet<TrackedTamingEntity> trackedEntities = getTrackedEntities(uuid, callOfTheWildType);
if(trackedEntities == null)
continue;
Iterator<TrackedTamingEntity> iterator = trackedEntities.iterator();
while (iterator.hasNext()) {
if(iterator.next().getLivingEntity().equals(livingEntity)) {
iterator.remove();
return;
}
}
}
}
}
/**
* 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<LivingEntity> getAllTransientEntitiesInChunk(@NotNull Chunk chunk) {
ArrayList<LivingEntity> matchingEntities = new ArrayList<>();
for(LivingEntity livingEntity : getChunkLookupCache()) {
if(livingEntity.getLocation().getChunk().equals(chunk)) {
matchingEntities.add(livingEntity);
}
}
return matchingEntities;
}
/**
* 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 synchronized int getAmountCurrentlySummoned(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) {
HashSet<TrackedTamingEntity> 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 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
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<TrackedTamingEntity> trackedEntities = getTrackedEntities(playerUUID, callOfTheWildType);
if(trackedEntities == null) {
continue;
}
ImmutableSet<TrackedTamingEntity> immutableSet = ImmutableSet.copyOf(trackedEntities);
for(TrackedTamingEntity trackedTamingEntity : immutableSet) {
//Remove from existence
removeSummon(trackedTamingEntity.getLivingEntity(), player, false);
}
}
}
}

View File

@@ -2,32 +2,37 @@ 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;
public BitSetChunkStore(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);
private final int cx;
private final int cz;
private final int worldHeight;
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 @NotNull BitSet store;
private transient boolean dirty = false;
public BitSetChunkStore(@NotNull World world, int cx, int cz) {
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() {
@@ -50,7 +55,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
}
@Override
public UUID getWorldId() {
public @NotNull UUID getWorldId() {
return worldUid;
}
@@ -81,61 +86,27 @@ 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();
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(@NotNull 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;
return world.getMaxHeight();
}
@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();
}
private void serialize(DataOutputStream out) throws IOException {
private void serialize(@NotNull DataOutputStream out) throws IOException {
out.writeInt(MAGIC_NUMBER);
out.writeInt(CURRENT_VERSION);
@@ -153,7 +124,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
dirty = false;
}
private static BitSetChunkStore deserialize(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();
@@ -161,28 +132,36 @@ 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 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();
@@ -196,7 +175,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);
}
@@ -209,31 +188,85 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
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);
((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 {
public LegacyDeserializationInputStream(InputStream in) throws IOException {
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(@NotNull ObjectOutputStream out) throws IOException {
throw new UnsupportedOperationException("You goofed.");
}
@Deprecated
private void readObject(@NotNull 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 @NotNull 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(@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(BitSetChunkStore.class);
return ObjectStreamClass.lookup(LegacyChunkStoreDeserializer.class);
return read;
}
public ChunkStore readLegacyChunkStore(){
public @Nullable ChunkStore readLegacyChunkStore(){
try {
return (ChunkStore) readObject();
LegacyChunkStoreDeserializer deserializer = (LegacyChunkStoreDeserializer)readObject();
return deserializer.convert();
} catch (IOException | ClassNotFoundException e) {
return null;
}

View File

@@ -1,126 +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.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, 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, 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, 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(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(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, 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);
/**
* 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);
/**
* 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, 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);
/**
* 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);
/**
* Delete any ChunkletStores that are empty
*/
void cleanUp();
void chunkUnloaded(int cx, int cz, @NotNull World world);
void unloadWorld(@NotNull World world);
}

View File

@@ -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()) {

View File

@@ -1,6 +1,6 @@
package com.gmail.nossr50.util.blockmeta;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@@ -36,7 +36,7 @@ public interface ChunkStore {
*/
int getChunkZ();
UUID getWorldId();
@NotNull UUID getWorldId();
/**
* Checks the value at the given coordinates

View File

@@ -1,12 +1,16 @@
package com.gmail.nossr50.util.blockmeta;
import com.gmail.nossr50.mcMMO;
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.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class HashChunkManager implements ChunkManager {
@@ -21,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();
@@ -32,7 +39,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 +50,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 +65,7 @@ public class HashChunkManager implements ChunkManager {
}
}
private synchronized 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 -> {
@@ -73,7 +80,7 @@ public class HashChunkManager implements ChunkManager {
});
}
private 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);
}
@@ -82,7 +89,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,56 +109,12 @@ public class HashChunkManager implements ChunkManager {
}
@Override
public synchronized void saveChunk(int cx, int cz, 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, 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(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(World world) {
if (world == null)
return;
public synchronized void unloadWorld(@NotNull World world) {
UUID wID = world.getUID();
// Save and remove all the chunks
@@ -177,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, 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
@@ -214,67 +166,36 @@ public class HashChunkManager implements ChunkManager {
}
@Override
public synchronized boolean isTrue(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(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, 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(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(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, 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(Block block) {
if (block == null)
return;
setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public synchronized void setFalse(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){
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
@@ -307,15 +228,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 +244,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;
@@ -348,7 +269,4 @@ public class HashChunkManager implements ChunkManager {
return Objects.hash(worldID, x, z);
}
}
@Override
public synchronized void cleanUp() {}
}

View File

@@ -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

View File

@@ -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) {}
}

View File

@@ -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);
}

View File

@@ -2,8 +2,12 @@ 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.mcMMO;
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;
import org.jetbrains.annotations.NotNull;
@@ -43,7 +47,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) {