Hotfix for PaperSpigot/SportsBukkit issue with HashMap.KeySet() != Set

Should fix issues with

0265f4eaef
This commit is contained in:
NavidK0 2015-12-28 19:34:44 -05:00
parent 05effaa82c
commit e7a5d0763f
4 changed files with 632 additions and 608 deletions

View File

@ -1,10 +1,7 @@
package me.libraryaddict.disguise; package me.libraryaddict.disguise;
import java.lang.reflect.InvocationTargetException; import com.comphenix.protocol.ProtocolLibrary;
import java.util.HashMap; import com.comphenix.protocol.events.PacketContainer;
import java.util.HashSet;
import java.util.Random;
import me.libraryaddict.disguise.disguisetypes.Disguise; import me.libraryaddict.disguise.disguisetypes.Disguise;
import me.libraryaddict.disguise.disguisetypes.DisguiseType; import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.PlayerDisguise; import me.libraryaddict.disguise.disguisetypes.PlayerDisguise;
@ -13,7 +10,6 @@ import me.libraryaddict.disguise.disguisetypes.watchers.LivingWatcher;
import me.libraryaddict.disguise.utilities.DisguiseUtilities; import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.ReflectionManager; import me.libraryaddict.disguise.utilities.ReflectionManager;
import me.libraryaddict.disguise.utilities.UpdateChecker; import me.libraryaddict.disguise.utilities.UpdateChecker;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
@ -35,11 +31,13 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent; import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.vehicle.VehicleExitEvent; import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
public class DisguiseListener implements Listener { public class DisguiseListener implements Listener {
private String currentVersion; private String currentVersion;
@ -161,7 +159,6 @@ public class DisguiseListener implements Listener {
/** /**
* Most likely faster if we don't bother doing checks if he sees a player disguise * Most likely faster if we don't bother doing checks if he sees a player disguise
*
*/ */
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent event) { public void onMove(PlayerMoveEvent event) {

View File

@ -1,19 +1,13 @@
package me.libraryaddict.disguise.utilities; package me.libraryaddict.disguise.utilities;
import java.lang.reflect.Array; import com.comphenix.protocol.PacketType;
import java.lang.reflect.Field; import com.comphenix.protocol.ProtocolLibrary;
import java.lang.reflect.InvocationTargetException; import com.comphenix.protocol.ProtocolManager;
import java.lang.reflect.Method; import com.comphenix.protocol.events.PacketContainer;
import java.util.ArrayList; import com.comphenix.protocol.reflect.StructureModifier;
import java.util.Arrays; import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import java.util.HashMap; import com.comphenix.protocol.wrappers.WrappedGameProfile;
import java.util.HashSet; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import me.libraryaddict.disguise.DisguiseAPI; import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.DisguiseConfig; import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.LibsDisguises;
@ -22,11 +16,10 @@ import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.FlagWatcher; import me.libraryaddict.disguise.disguisetypes.FlagWatcher;
import me.libraryaddict.disguise.disguisetypes.PlayerDisguise; import me.libraryaddict.disguise.disguisetypes.PlayerDisguise;
import me.libraryaddict.disguise.disguisetypes.TargetedDisguise; import me.libraryaddict.disguise.disguisetypes.TargetedDisguise;
import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType;
import me.libraryaddict.disguise.disguisetypes.watchers.AgeableWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.AgeableWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.ZombieWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.ZombieWatcher;
import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -41,14 +34,21 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import com.comphenix.protocol.PacketType; import java.lang.reflect.Array;
import com.comphenix.protocol.ProtocolLibrary; import java.lang.reflect.Field;
import com.comphenix.protocol.ProtocolManager; import java.lang.reflect.InvocationTargetException;
import com.comphenix.protocol.events.PacketContainer; import java.lang.reflect.Method;
import com.comphenix.protocol.reflect.StructureModifier; import java.util.ArrayList;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; import java.util.Arrays;
import com.comphenix.protocol.wrappers.WrappedGameProfile; import java.util.HashMap;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
public class DisguiseUtilities { public class DisguiseUtilities {
@ -60,7 +60,6 @@ public class DisguiseUtilities {
private static LinkedHashMap<String, Disguise> clonedDisguises = new LinkedHashMap<>(); private static LinkedHashMap<String, Disguise> clonedDisguises = new LinkedHashMap<>();
/** /**
* A hashmap of the uuid's of entitys, alive and dead. And their disguises in use * A hashmap of the uuid's of entitys, alive and dead. And their disguises in use
*
*/ */
private static HashMap<UUID, HashSet<TargetedDisguise>> disguisesInUse = new HashMap<>(); private static HashMap<UUID, HashSet<TargetedDisguise>> disguisesInUse = new HashMap<>();
/** /**
@ -252,12 +251,16 @@ public class DisguiseUtilities {
try { try {
Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity()); Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity());
if (entityTrackerEntry != null) { if (entityTrackerEntry != null) {
HashSet trackedPlayers = (HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get( Set trackedPlayers = (Set) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(
entityTrackerEntry); entityTrackerEntry);
HashSet cloned = (HashSet) trackedPlayers.clone(); Object trackedPlayersObj = ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry);
// If the tracker exists. Remove himself from his tracker
if (isHashSet(trackedPlayersObj)) {
trackedPlayers = (Set) ((HashSet<Object>)trackedPlayers).clone();
}
PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
destroyPacket.getIntegerArrays().write(0, new int[]{disguise.getEntity().getEntityId()}); destroyPacket.getIntegerArrays().write(0, new int[]{disguise.getEntity().getEntityId()});
for (Object p : cloned) { for (Object p : trackedPlayers) {
Player player = (Player) ReflectionManager.getBukkitEntity(p); Player player = (Player) ReflectionManager.getBukkitEntity(p);
if (player == disguise.getEntity() || disguise.canSee(player)) { if (player == disguise.getEntity() || disguise.canSee(player)) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, destroyPacket); ProtocolLibrary.getProtocolManager().sendServerPacket(player, destroyPacket);
@ -440,7 +443,7 @@ public class DisguiseUtilities {
try { try {
Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity()); Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity());
if (entityTrackerEntry != null) { if (entityTrackerEntry != null) {
HashSet trackedPlayers = (HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get( Set trackedPlayers = (Set) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(
entityTrackerEntry); entityTrackerEntry);
for (Object p : trackedPlayers) { for (Object p : trackedPlayers) {
Player player = (Player) ReflectionManager.getBukkitEntity(p); Player player = (Player) ReflectionManager.getBukkitEntity(p);
@ -620,7 +623,7 @@ public class DisguiseUtilities {
try { try {
PacketContainer destroyPacket = getDestroyPacket(disguise.getEntity().getEntityId()); PacketContainer destroyPacket = getDestroyPacket(disguise.getEntity().getEntityId());
if (disguise.isDisguiseInUse() && disguise.getEntity() instanceof Player if (disguise.isDisguiseInUse() && disguise.getEntity() instanceof Player
&& ((Player) disguise.getEntity()).getName().equalsIgnoreCase(player)) { && disguise.getEntity().getName().equalsIgnoreCase(player)) {
removeSelfDisguise((Player) disguise.getEntity()); removeSelfDisguise((Player) disguise.getEntity());
if (disguise.isSelfDisguiseVisible()) { if (disguise.isSelfDisguiseVisible()) {
selfDisguised.add(disguise.getEntity().getUniqueId()); selfDisguised.add(disguise.getEntity().getUniqueId());
@ -639,14 +642,16 @@ public class DisguiseUtilities {
} else { } else {
final Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity()); final Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity());
if (entityTrackerEntry != null) { if (entityTrackerEntry != null) {
HashSet trackedPlayers = (HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers") Set trackedPlayers = (Set) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers")
.get(entityTrackerEntry); .get(entityTrackerEntry);
Method clear = ReflectionManager.getNmsMethod("EntityTrackerEntry", "clear", Method clear = ReflectionManager.getNmsMethod("EntityTrackerEntry", "clear",
ReflectionManager.getNmsClass("EntityPlayer")); ReflectionManager.getNmsClass("EntityPlayer"));
final Method updatePlayer = ReflectionManager.getNmsMethod("EntityTrackerEntry", "updatePlayer", final Method updatePlayer = ReflectionManager.getNmsMethod("EntityTrackerEntry", "updatePlayer",
ReflectionManager.getNmsClass("EntityPlayer")); ReflectionManager.getNmsClass("EntityPlayer"));
HashSet cloned = (HashSet) trackedPlayers.clone(); if (isHashSet(trackedPlayers)) {
for (final Object p : cloned) { trackedPlayers = (Set) ((HashSet<Object>)trackedPlayers).clone();
}
for (final Object p : trackedPlayers) {
Player pl = (Player) ReflectionManager.getBukkitEntity(p); Player pl = (Player) ReflectionManager.getBukkitEntity(p);
if (player.equalsIgnoreCase((pl).getName())) { if (player.equalsIgnoreCase((pl).getName())) {
clear.invoke(entityTrackerEntry, p); clear.invoke(entityTrackerEntry, p);
@ -681,14 +686,16 @@ public class DisguiseUtilities {
PacketContainer destroyPacket = getDestroyPacket(entity.getEntityId()); PacketContainer destroyPacket = getDestroyPacket(entity.getEntityId());
final Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(entity); final Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(entity);
if (entityTrackerEntry != null) { if (entityTrackerEntry != null) {
HashSet trackedPlayers = (HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get( Set trackedPlayers = (Set) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(
entityTrackerEntry); entityTrackerEntry);
Method clear = ReflectionManager.getNmsMethod("EntityTrackerEntry", "clear", Method clear = ReflectionManager.getNmsMethod("EntityTrackerEntry", "clear",
ReflectionManager.getNmsClass("EntityPlayer")); ReflectionManager.getNmsClass("EntityPlayer"));
final Method updatePlayer = ReflectionManager.getNmsMethod("EntityTrackerEntry", "updatePlayer", final Method updatePlayer = ReflectionManager.getNmsMethod("EntityTrackerEntry", "updatePlayer",
ReflectionManager.getNmsClass("EntityPlayer")); ReflectionManager.getNmsClass("EntityPlayer"));
HashSet cloned = (HashSet) trackedPlayers.clone(); if (isHashSet(trackedPlayers)) {
for (final Object p : cloned) { trackedPlayers = (Set) ((HashSet<Object>)trackedPlayers).clone();
}
for (final Object p : trackedPlayers) {
Player player = (Player) ReflectionManager.getBukkitEntity(p); Player player = (Player) ReflectionManager.getBukkitEntity(p);
if (player != entity) { if (player != entity) {
clear.invoke(entityTrackerEntry, p); clear.invoke(entityTrackerEntry, p);
@ -736,14 +743,15 @@ public class DisguiseUtilities {
} }
final Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity()); final Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(disguise.getEntity());
if (entityTrackerEntry != null) { if (entityTrackerEntry != null) {
HashSet trackedPlayers = (HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get( Set trackedPlayers = (Set) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry);
entityTrackerEntry);
Method clear = ReflectionManager.getNmsMethod("EntityTrackerEntry", "clear", Method clear = ReflectionManager.getNmsMethod("EntityTrackerEntry", "clear",
ReflectionManager.getNmsClass("EntityPlayer")); ReflectionManager.getNmsClass("EntityPlayer"));
final Method updatePlayer = ReflectionManager.getNmsMethod("EntityTrackerEntry", "updatePlayer", final Method updatePlayer = ReflectionManager.getNmsMethod("EntityTrackerEntry", "updatePlayer",
ReflectionManager.getNmsClass("EntityPlayer")); ReflectionManager.getNmsClass("EntityPlayer"));
HashSet cloned = (HashSet) trackedPlayers.clone(); if (isHashSet(trackedPlayers)) {
for (final Object p : cloned) { trackedPlayers = (Set) ((HashSet<Object>)trackedPlayers).clone();
}
for (final Object p : trackedPlayers) {
Player player = (Player) ReflectionManager.getBukkitEntity(p); Player player = (Player) ReflectionManager.getBukkitEntity(p);
if (disguise.getEntity() != player && disguise.canSee(player)) { if (disguise.getEntity() != player && disguise.canSee(player)) {
clear.invoke(entityTrackerEntry, p); clear.invoke(entityTrackerEntry, p);
@ -767,6 +775,19 @@ public class DisguiseUtilities {
} }
} }
/**
* Pass in a set, check if it's a hashset.
* If it's not, return false.
* If you pass in something else, you failed.
* @param obj
* @return
*/
private static boolean isHashSet(Object obj) {
if (obj instanceof HashSet) return true; //It's Spigot/Bukkit
if (obj instanceof Set) return false; //It's PaperSpigot/SportsBukkit
throw new IllegalArgumentException("Object passed was not either a hashset or set!");
}
public static boolean removeDisguise(TargetedDisguise disguise) { public static boolean removeDisguise(TargetedDisguise disguise) {
UUID entityId = disguise.getEntity().getUniqueId(); UUID entityId = disguise.getEntity().getUniqueId();
if (getDisguises().containsKey(entityId) && getDisguises().get(entityId).remove(disguise)) { if (getDisguises().containsKey(entityId) && getDisguises().get(entityId).remove(disguise)) {
@ -805,10 +826,13 @@ public class DisguiseUtilities {
try { try {
Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(player); Object entityTrackerEntry = ReflectionManager.getEntityTrackerEntry(player);
if (entityTrackerEntry != null) { if (entityTrackerEntry != null) {
HashSet trackedPlayers = (HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get( Object trackedPlayersObj = ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry);
entityTrackerEntry);
// If the tracker exists. Remove himself from his tracker // If the tracker exists. Remove himself from his tracker
trackedPlayers.remove(ReflectionManager.getNmsEntity(player)); if (isHashSet(trackedPlayersObj)) {
((Set<Object>) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry)).remove(ReflectionManager.getNmsEntity(player));
} else {
((Map<Object, Object>) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayerMap").get(entityTrackerEntry)).remove(ReflectionManager.getNmsEntity(player));
}
} }
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(System.out); ex.printStackTrace(System.out);
@ -855,8 +879,14 @@ public class DisguiseUtilities {
return; return;
} }
// Add himself to his own entity tracker // Add himself to his own entity tracker
((HashSet<Object>) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry)) Object trackedPlayersObj = ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry);
.add(ReflectionManager.getNmsEntity(player)); //Check for code differences in PaperSpigot vs Spigot
if (isHashSet(trackedPlayersObj)) {
((Set<Object>) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry)).add(ReflectionManager.getNmsEntity(player));
} else {
((Map<Object, Object>) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayerMap").get(entityTrackerEntry)).put(ReflectionManager.getNmsEntity(player), true);
}
ProtocolManager manager = ProtocolLibrary.getProtocolManager(); ProtocolManager manager = ProtocolLibrary.getProtocolManager();
// Send the player a packet with himself being spawned // Send the player a packet with himself being spawned
manager.sendServerPacket(player, manager.createPacketConstructor(PacketType.Play.Server.NAMED_ENTITY_SPAWN, player) manager.sendServerPacket(player, manager.createPacketConstructor(PacketType.Play.Server.NAMED_ENTITY_SPAWN, player)

View File

@ -1,12 +1,18 @@
package me.libraryaddict.disguise.utilities; package me.libraryaddict.disguise.utilities;
import java.lang.reflect.InvocationTargetException; import com.comphenix.protocol.PacketType;
import java.util.ArrayList; import com.comphenix.protocol.ProtocolLibrary;
import java.util.Arrays; import com.comphenix.protocol.events.ListenerPriority;
import java.util.List; import com.comphenix.protocol.events.PacketAdapter;
import java.util.Random; import com.comphenix.protocol.events.PacketContainer;
import java.util.UUID; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedAttribute.Builder;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import me.libraryaddict.disguise.DisguiseAPI; import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.DisguiseConfig; import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.LibsDisguises;
@ -22,7 +28,7 @@ import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.SheepWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.SheepWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.WolfWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.WolfWatcher;
import me.libraryaddict.disguise.utilities.DisguiseSound.SoundType; import me.libraryaddict.disguise.utilities.DisguiseSound.SoundType;
import net.minecraft.server.v1_8_R3.DamageSource;
import org.bukkit.Art; import org.bukkit.Art;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -41,21 +47,12 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import com.comphenix.protocol.PacketType; import java.lang.reflect.InvocationTargetException;
import com.comphenix.protocol.ProtocolLibrary; import java.util.ArrayList;
import com.comphenix.protocol.events.ListenerPriority; import java.util.Arrays;
import com.comphenix.protocol.events.PacketAdapter; import java.util.List;
import com.comphenix.protocol.events.PacketContainer; import java.util.Random;
import com.comphenix.protocol.events.PacketEvent; import java.util.UUID;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedAttribute.Builder;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import net.minecraft.server.v1_8_R3.DamageSource;
public class PacketsManager { public class PacketsManager {

View File

@ -1,6 +1,6 @@
name: LibsDisguises name: LibsDisguises
main: me.libraryaddict.disguise.LibsDisguises main: me.libraryaddict.disguise.LibsDisguises
version: 8.6.5 version: 8.6.6
author: libraryaddict author: libraryaddict
authors: [Byteflux, Navid K.] authors: [Byteflux, Navid K.]
depend: [ProtocolLib] depend: [ProtocolLib]