| 
					
				 | 
			
			
				@@ -12,18 +12,19 @@ import org.bukkit.persistence.PersistentDataType; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.jetbrains.annotations.NotNull; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.util.EnumMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import java.util.HashSet; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Set; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.concurrent.ConcurrentHashMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.util.concurrent.ConcurrentMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import static com.gmail.nossr50.util.MetadataService.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public final class MobMetadataUtils { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private static final @NotNull ConcurrentMap<Entity, HashSet<MobMetaFlagType>> mobRegistry; //transient data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private static final @NotNull EnumMap<MobMetaFlagType, NamespacedKey> mobFlagKeyMap; //used for persistent data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final @NotNull ConcurrentMap<Entity, Set<MobMetaFlagType>> mobRegistry; // transient data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final @NotNull EnumMap<MobMetaFlagType, NamespacedKey> mobFlagKeyMap; // used for persistent data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private static boolean isUsingPersistentData = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private MobMetadataUtils() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // private ctor 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // private constructor to prevent instantiation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     static { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -39,8 +40,10 @@ public final class MobMetadataUtils { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         initMobFlagKeyMap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for (MobMetaFlagType metaFlagType : MobMetaFlagType.values()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (PersistentDataConfig.getInstance().isMobPersistent(metaFlagType)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (PersistentDataConfig.getInstance().isMobPersistent(metaFlagType)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 isUsingPersistentData = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -58,61 +61,58 @@ public final class MobMetadataUtils { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 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); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                default -> throw new IncompleteNamespacedKeyRegister("Missing namespaced key register for type: " + mobMetaFlagType); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * Whether a target {@link LivingEntity} has a specific mcMMO mob flags 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Checks if a {@link LivingEntity} has a specific mcMMO mob flag. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @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} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param livingEntity the living entity to check 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return true if the mob has the specified metadata flag 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public static 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; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            final Set<MobMetaFlagType> flags = mobRegistry.get(livingEntity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return flags != null && flags.contains(flag); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * Whether a target {@link LivingEntity} has any mcMMO mob flags 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @param livingEntity the living entity to check for metadata 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Checks if a {@link LivingEntity} has any mcMMO mob flags. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @return true if the mob has any mcMMO mob related metadata values 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param livingEntity the living entity to check 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return true if the mob has any mcMMO mob-related metadata flags 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public static boolean hasMobFlags(@NotNull LivingEntity livingEntity) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (isUsingPersistentData) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             for (MobMetaFlagType metaFlagType : MobMetaFlagType.values()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (hasMobFlag(metaFlagType, livingEntity)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (hasMobFlag(metaFlagType, livingEntity)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return mobRegistry.containsKey(livingEntity) && !mobRegistry.get(livingEntity).isEmpty(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            final Set<MobMetaFlagType> flags = mobRegistry.get(livingEntity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return flags != null && !flags.isEmpty(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * Copies all mcMMO mob flags from one {@link LivingEntity} to another {@link LivingEntity} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * This does not clear existing mcMMO mob flags on the target 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Copies all mcMMO mob flags from one {@link LivingEntity} to another. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * This does not clear existing mcMMO mob flags on the target. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @param sourceEntity entity to copy from 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @param targetEntity entity to copy to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public static void addMobFlags(@NotNull LivingEntity sourceEntity, @NotNull LivingEntity targetEntity) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (!hasMobFlags(sourceEntity)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!hasMobFlags(sourceEntity)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (isUsingPersistentData) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             for (MobMetaFlagType flag : MobMetaFlagType.values()) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -121,14 +121,17 @@ public final class MobMetadataUtils { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            HashSet<MobMetaFlagType> flags = new HashSet<>(mobRegistry.get(sourceEntity)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            mobRegistry.put(targetEntity, flags); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Set<MobMetaFlagType> sourceFlags = mobRegistry.get(sourceEntity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (sourceFlags != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Set<MobMetaFlagType> targetFlags = mobRegistry.computeIfAbsent(targetEntity, k -> ConcurrentHashMap.newKeySet()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                targetFlags.addAll(sourceFlags); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * 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 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Adds a mob flag to a {@link LivingEntity}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * The existence of the flag acts as a true value; non-existence is false. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @param flag         the desired flag to assign 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @param livingEntity the target living entity 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -140,16 +143,15 @@ public final class MobMetadataUtils { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 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 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            final Set<MobMetaFlagType> flags = mobRegistry.computeIfAbsent(livingEntity, k -> ConcurrentHashMap.newKeySet()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            flags.add(flag); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * Removes a specific mob flag from target {@link LivingEntity} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Removes a specific mob flag from a {@link LivingEntity}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @param flag         desired flag to remove 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param flag         the flag to remove 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @param livingEntity the target living entity 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public static void removeMobFlag(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -159,19 +161,20 @@ public final class MobMetadataUtils { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 persistentDataContainer.remove(mobFlagKeyMap.get(flag)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (mobRegistry.containsKey(livingEntity)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                mobRegistry.get(livingEntity).remove(flag); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (mobRegistry.get(livingEntity).size() == 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    mobRegistry.remove(livingEntity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            final Set<MobMetaFlagType> flags = mobRegistry.get(livingEntity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (flags != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flags.remove(flag); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (flags.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    mobRegistry.remove(livingEntity, flags); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * Remove all mcMMO related mob flags from the target {@link LivingEntity} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Removes all mcMMO-related mob flags from a {@link LivingEntity}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @param livingEntity target entity 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param livingEntity the target entity 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public static void removeMobFlags(@NotNull LivingEntity livingEntity) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (isUsingPersistentData) { 
			 |