Rewrite how mob/item/block metadata is tracked/retrieved

Fixes #4720
This commit is contained in:
nossr50
2022-01-17 15:32:02 -08:00
parent 74ced18bd0
commit 3be15d3f65
19 changed files with 477 additions and 600 deletions

View File

@ -0,0 +1,49 @@
package com.gmail.nossr50.metadata;
import com.gmail.nossr50.mcMMO;
import org.bukkit.block.Furnace;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import static com.gmail.nossr50.metadata.MetadataService.NSK_FURNACE_UUID_LEAST_SIG;
import static com.gmail.nossr50.metadata.MetadataService.NSK_FURNACE_UUID_MOST_SIG;
public class BlockMetadataService {
private final @NotNull mcMMO pluginRef;
public BlockMetadataService(@NotNull mcMMO pluginRef) {
this.pluginRef = pluginRef;
}
public @Nullable UUID getFurnaceOwner(@NotNull Furnace furnace) {
//Get container from entity
PersistentDataContainer dataContainer = ((PersistentDataHolder) furnace).getPersistentDataContainer();
//Too lazy to make a custom data type for this stuff
Long mostSigBits = dataContainer.get(NSK_FURNACE_UUID_MOST_SIG, PersistentDataType.LONG);
Long leastSigBits = dataContainer.get(NSK_FURNACE_UUID_LEAST_SIG, PersistentDataType.LONG);
if (mostSigBits != null && leastSigBits != null) {
return new UUID(mostSigBits, leastSigBits);
} else {
return null;
}
}
public void setFurnaceOwner(@NotNull Furnace furnace, @NotNull UUID uuid) {
PersistentDataContainer dataContainer = ((PersistentDataHolder) furnace).getPersistentDataContainer();
dataContainer.set(NSK_FURNACE_UUID_MOST_SIG, PersistentDataType.LONG, uuid.getMostSignificantBits());
dataContainer.set(NSK_FURNACE_UUID_LEAST_SIG, PersistentDataType.LONG, uuid.getLeastSignificantBits());
furnace.update();
}
}

View File

@ -0,0 +1,110 @@
package com.gmail.nossr50.metadata;
import com.gmail.nossr50.mcMMO;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.gmail.nossr50.metadata.MetadataService.NSK_SUPER_ABILITY_BOOSTED_ITEM;
public class ItemMetadataService {
public final @NotNull String LEGACY_ABILITY_TOOL_LORE = "mcMMO Ability Tool";
public final @NotNull mcMMO pluginRef;
public ItemMetadataService(@NotNull mcMMO pluginRef) {
this.pluginRef = pluginRef;
}
public void setSuperAbilityBoostedItem(@NotNull ItemStack itemStack, int originalDigSpeed) {
if (itemStack.getItemMeta() == null) {
mcMMO.p.getLogger().severe("Can not assign persistent data to an item with null item metadata");
return;
}
ItemMeta itemMeta = itemStack.getItemMeta();
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
dataContainer.set(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER, originalDigSpeed);
itemStack.setItemMeta(itemMeta);
}
public boolean isSuperAbilityBoosted(@NotNull ItemStack itemStack) {
if (itemStack.getItemMeta() == null)
return false;
ItemMeta itemMeta = itemStack.getItemMeta();
//Get container from entity
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
//If this value isn't null, then the tool can be considered dig speed boosted
Integer boostValue = dataContainer.get(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER);
return boostValue != null;
}
public int getSuperAbilityToolOriginalDigSpeed(@NotNull ItemStack itemStack) {
//Get container from entity
ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta == null)
return 0;
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
if (dataContainer.get(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER) == null) {
mcMMO.p.getLogger().severe("Value should never be null for a boosted item");
return 0;
} else {
//Too lazy to make a custom data type for this stuff
Integer boostValue = dataContainer.get(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER);
return Math.max(boostValue, 0);
}
}
public void removeBonusDigSpeedOnSuperAbilityTool(@NotNull ItemStack itemStack) {
int originalSpeed = getSuperAbilityToolOriginalDigSpeed(itemStack);
ItemMeta itemMeta = itemStack.getItemMeta();
if(itemMeta != null) {
//TODO: can be optimized
if (itemMeta.hasEnchant(Enchantment.DIG_SPEED)) {
itemMeta.removeEnchant(Enchantment.DIG_SPEED);
}
if (originalSpeed > 0) {
itemMeta.addEnchant(Enchantment.DIG_SPEED, originalSpeed, true);
}
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
dataContainer.remove(NSK_SUPER_ABILITY_BOOSTED_ITEM); //Remove persistent data
//TODO: needed?
itemStack.setItemMeta(itemMeta);
}
}
public boolean isLegacyAbilityTool(@NotNull ItemStack itemStack) {
ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta == null)
return false;
List<String> lore = itemMeta.getLore();
if (lore == null || lore.isEmpty())
return false;
return lore.contains(LEGACY_ABILITY_TOOL_LORE);
}
public @NotNull String getLegacyAbilityToolLore() {
return LEGACY_ABILITY_TOOL_LORE;
}
}

View File

@ -0,0 +1,71 @@
package com.gmail.nossr50.metadata;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MetadataConstants;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
public class MetadataService {
private final @NotNull mcMMO pluginRef;
protected static final @NotNull NamespacedKey NSK_SUPER_ABILITY_BOOSTED_ITEM;
protected static final @NotNull NamespacedKey NSK_MOB_SPAWNER_MOB;
protected static final @NotNull NamespacedKey NSK_EGG_MOB;
protected static final @NotNull NamespacedKey NSK_NETHER_GATE_MOB;
protected static final @NotNull NamespacedKey NSK_COTW_SUMMONED_MOB;
protected static final @NotNull NamespacedKey NSK_PLAYER_BRED_MOB;
protected static final @NotNull NamespacedKey NSK_PLAYER_TAMED_MOB;
protected static final @NotNull NamespacedKey NSK_VILLAGER_TRADE_ORIGIN_ITEM;
protected static final @NotNull NamespacedKey NSK_EXPLOITED_ENDERMEN;
protected static final @NotNull NamespacedKey NSK_FURNACE_UUID_MOST_SIG;
protected static final @NotNull NamespacedKey NSK_FURNACE_UUID_LEAST_SIG;
static {
NSK_SUPER_ABILITY_BOOSTED_ITEM = getNamespacedKey(MetadataConstants.METADATA_KEY_SUPER_ABILITY_BOOSTED_ITEM);
NSK_MOB_SPAWNER_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_MOB_SPAWNER_MOB);
NSK_EGG_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_EGG_MOB);
NSK_NETHER_GATE_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_NETHER_PORTAL_MOB);
NSK_COTW_SUMMONED_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_COTW_SUMMONED_MOB);
NSK_PLAYER_BRED_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_PLAYER_BRED_MOB);
NSK_PLAYER_TAMED_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_PLAYER_TAMED_MOB);
NSK_VILLAGER_TRADE_ORIGIN_ITEM = getNamespacedKey(MetadataConstants.METADATA_KEY_VILLAGER_TRADE_ORIGIN_ITEM);
NSK_EXPLOITED_ENDERMEN = getNamespacedKey(MetadataConstants.METADATA_KEY_EXPLOITED_ENDERMEN);
NSK_FURNACE_UUID_MOST_SIG = getNamespacedKey(MetadataConstants.METADATA_KEY_FURNACE_UUID_MOST_SIG);
NSK_FURNACE_UUID_LEAST_SIG = getNamespacedKey(MetadataConstants.METADATA_KEY_FURNACE_UUID_LEAST_SIG);
}
private final @NotNull ItemMetadataService itemMetadataService;
private final @NotNull MobMetadataService mobMetadataService;
private final @NotNull BlockMetadataService blockMetadataService;
public MetadataService(@NotNull mcMMO pluginRef) {
this.pluginRef = pluginRef;
blockMetadataService = new BlockMetadataService(pluginRef);
mobMetadataService = new MobMetadataService(pluginRef);
itemMetadataService = new ItemMetadataService(pluginRef);
}
/**
* Helper method to simplify generating namespaced keys
*
* @param key the {@link String} value of the key
*
* @return the generated {@link NamespacedKey}
*/
public static @NotNull NamespacedKey getNamespacedKey(@NotNull String key) {
return new NamespacedKey(mcMMO.p, key);
}
public @NotNull ItemMetadataService getItemMetadataService() {
return itemMetadataService;
}
public @NotNull MobMetadataService getMobMetadataService() {
return mobMetadataService;
}
public @NotNull BlockMetadataService getBlockMetadataService() {
return blockMetadataService;
}
}

View File

@ -0,0 +1,11 @@
package com.gmail.nossr50.metadata;
public enum MobMetaFlagType {
MOB_SPAWNER_MOB,
EGG_MOB,
NETHER_PORTAL_MOB,
COTW_SUMMONED_MOB,
PLAYER_BRED_MOB,
PLAYER_TAMED_MOB,
EXPLOITED_ENDERMEN,
}

View File

@ -0,0 +1,187 @@
package com.gmail.nossr50.metadata;
import com.gmail.nossr50.api.exceptions.IncompleteNamespacedKeyRegister;
import com.gmail.nossr50.config.PersistentDataConfig;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MetadataConstants;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.WeakHashMap;
import static com.gmail.nossr50.metadata.MetadataService.*;
public class MobMetadataService {
private final @NotNull WeakHashMap<Entity, HashSet<MobMetaFlagType>> mobRegistry; //transient data
private final @NotNull EnumMap<MobMetaFlagType, NamespacedKey> mobFlagKeyMap; //used for persistent data
private final @NotNull mcMMO pluginRef;
private boolean isUsingPersistentData = false;
public MobMetadataService(@NotNull mcMMO pluginRef) {
this.pluginRef = pluginRef;
mobFlagKeyMap = new EnumMap<>(MobMetaFlagType.class);
mobRegistry = new WeakHashMap<>();
initMobFlagKeyMap();
for (MobMetaFlagType metaFlagType : MobMetaFlagType.values()) {
if (PersistentDataConfig.getInstance().isMobPersistent(metaFlagType))
isUsingPersistentData = true;
}
}
/**
* Registers the namespaced keys required by the API (CB/Spigot)
* Used primarily for persistent data
*/
private void initMobFlagKeyMap() throws IncompleteNamespacedKeyRegister {
for (MobMetaFlagType mobMetaFlagType : MobMetaFlagType.values()) {
switch (mobMetaFlagType) {
case MOB_SPAWNER_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_MOB_SPAWNER_MOB);
case EGG_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_EGG_MOB);
case NETHER_PORTAL_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_NETHER_GATE_MOB);
case COTW_SUMMONED_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_COTW_SUMMONED_MOB);
case PLAYER_BRED_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_PLAYER_BRED_MOB);
case EXPLOITED_ENDERMEN -> mobFlagKeyMap.put(mobMetaFlagType, NSK_EXPLOITED_ENDERMEN);
case PLAYER_TAMED_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_PLAYER_TAMED_MOB);
default -> throw new IncompleteNamespacedKeyRegister("missing namespaced key register for type: " + mobMetaFlagType);
}
}
}
/**
* Helper method to simplify generating namespaced keys
*
* @param key the {@link String} value of the key
*
* @return the generated {@link NamespacedKey}
*/
private @NotNull NamespacedKey getNamespacedKey(@NotNull String key) {
return new NamespacedKey(mcMMO.p, key);
}
/**
* Whether or not a target {@link LivingEntity} has a specific mcMMO mob flags
*
* @param flag the type of mob flag to check for
* @param livingEntity the living entity to check for metadata
*
* @return true if the mob has metadata values for target {@link MobMetaFlagType}
*/
public boolean hasMobFlag(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) {
if (PersistentDataConfig.getInstance().isMobPersistent(flag)) {
return livingEntity.getPersistentDataContainer().has(mobFlagKeyMap.get(flag), PersistentDataType.BYTE);
} else {
if (mobRegistry.containsKey(livingEntity)) {
return mobRegistry.get(livingEntity).contains(flag);
}
return false;
}
}
/**
* Whether or not a target {@link LivingEntity} has any mcMMO mob flags
*
* @param livingEntity the living entity to check for metadata
*
* @return true if the mob has any mcMMO mob related metadata values
*/
public boolean hasMobFlags(@NotNull LivingEntity livingEntity) {
if (isUsingPersistentData) {
for (MobMetaFlagType metaFlagType : MobMetaFlagType.values()) {
if (hasMobFlag(metaFlagType, livingEntity))
return true;
}
return false;
} else {
return mobRegistry.containsKey(livingEntity) && mobRegistry.get(livingEntity).size() > 0;
}
}
/**
* Copies all mcMMO mob flags from one {@link LivingEntity} to another {@link LivingEntity}
* This does not clear existing mcMMO mob flags on the target
*
* @param sourceEntity entity to copy from
* @param targetEntity entity to copy to
*/
public void addMobFlags(@NotNull LivingEntity sourceEntity, @NotNull LivingEntity targetEntity) {
if (!hasMobFlags(sourceEntity))
return;
if (isUsingPersistentData) {
for (MobMetaFlagType flag : MobMetaFlagType.values()) {
if (hasMobFlag(flag, sourceEntity)) {
flagMetadata(flag, targetEntity);
}
}
} else {
HashSet<MobMetaFlagType> flags = new HashSet<>(mobRegistry.get(sourceEntity));
mobRegistry.put(targetEntity, flags);
}
}
/**
* Adds a mob flag to a {@link LivingEntity} which effectively acts a true/false boolean
* Existence of the flag can be considered a true value, non-existence can be considered false for all intents and purposes
*
* @param flag the desired flag to assign
* @param livingEntity the target living entity
*/
public void flagMetadata(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) {
if (PersistentDataConfig.getInstance().isMobPersistent(flag)) {
if (!hasMobFlag(flag, livingEntity)) {
PersistentDataContainer persistentDataContainer = livingEntity.getPersistentDataContainer();
persistentDataContainer.set(mobFlagKeyMap.get(flag), PersistentDataType.BYTE, MetadataConstants.SIMPLE_FLAG_VALUE);
}
} else {
HashSet<MobMetaFlagType> flags = mobRegistry.getOrDefault(livingEntity, new HashSet<>());
flags.add(flag); // add the new flag
mobRegistry.put(livingEntity, flags); //update registry
}
}
/**
* Removes a specific mob flag from target {@link LivingEntity}
*
* @param flag desired flag to remove
* @param livingEntity the target living entity
*/
public void removeMobFlag(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) {
if (PersistentDataConfig.getInstance().isMobPersistent(flag)) {
if (hasMobFlag(flag, livingEntity)) {
PersistentDataContainer persistentDataContainer = livingEntity.getPersistentDataContainer();
persistentDataContainer.remove(mobFlagKeyMap.get(flag));
}
} else {
if (mobRegistry.containsKey(livingEntity)) {
mobRegistry.get(livingEntity).remove(flag);
if (mobRegistry.get(livingEntity).size() == 0)
mobRegistry.remove(livingEntity);
}
}
}
/**
* Remove all mcMMO related mob flags from the target {@link LivingEntity}
*
* @param livingEntity target entity
*/
public void removeMobFlags(@NotNull LivingEntity livingEntity) {
if (isUsingPersistentData) {
for (MobMetaFlagType flag : MobMetaFlagType.values()) {
removeMobFlag(flag, livingEntity);
}
} else {
mobRegistry.remove(livingEntity);
}
}
}