Java 16 support, some dodgy code hmm. Has errors with "class loaded by another plugin" which needs to be resolved

This commit is contained in:
libraryaddict 2021-05-20 03:43:26 +12:00
parent 269363753c
commit f0448e6afc
11 changed files with 444 additions and 308 deletions

View File

@ -67,6 +67,8 @@ public class LibsDisguises extends JavaPlugin {
instance = this; instance = this;
WatcherSanitizer.init();
Plugin plugin = Bukkit.getPluginManager().getPlugin("ProtocolLib"); Plugin plugin = Bukkit.getPluginManager().getPlugin("ProtocolLib");
if (plugin == null || DisguiseUtilities.isOlderThan(DisguiseUtilities.getProtocolLibRequiredVersion(), plugin.getDescription().getVersion())) { if (plugin == null || DisguiseUtilities.isOlderThan(DisguiseUtilities.getProtocolLibRequiredVersion(), plugin.getDescription().getVersion())) {
@ -105,8 +107,6 @@ public class LibsDisguises extends JavaPlugin {
if (!reloaded) { if (!reloaded) {
commandConfig.load(); commandConfig.load();
} }
WatcherSanitizer.init();
} catch (Throwable throwable) { } catch (Throwable throwable) {
getUpdateChecker().doUpdate(); getUpdateChecker().doUpdate();

View File

@ -4,7 +4,6 @@ import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Play.Server; import com.comphenix.protocol.PacketType.Play.Server;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode; import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction; import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
import com.comphenix.protocol.wrappers.PlayerInfoData; import com.comphenix.protocol.wrappers.PlayerInfoData;
@ -16,7 +15,10 @@ 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;
import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType; import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType;
import me.libraryaddict.disguise.disguisetypes.watchers.*; import me.libraryaddict.disguise.disguisetypes.watchers.AbstractHorseWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.AgeableWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.BoatWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.ZombieWatcher;
import me.libraryaddict.disguise.events.DisguiseEvent; import me.libraryaddict.disguise.events.DisguiseEvent;
import me.libraryaddict.disguise.events.UndisguiseEvent; import me.libraryaddict.disguise.events.UndisguiseEvent;
import me.libraryaddict.disguise.utilities.DisguiseUtilities; import me.libraryaddict.disguise.utilities.DisguiseUtilities;
@ -29,7 +31,6 @@ import me.libraryaddict.disguise.utilities.reflection.ReflectionManager;
import me.libraryaddict.disguise.utilities.translations.LibsMsg; import me.libraryaddict.disguise.utilities.translations.LibsMsg;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.boss.BarColor; import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle; import org.bukkit.boss.BarStyle;
@ -38,7 +39,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.*; import org.bukkit.entity.*;
import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
@ -71,7 +71,7 @@ public abstract class Disguise {
/** /**
* If set, how long before disguise expires * If set, how long before disguise expires
*/ */
private long disguiseExpires; protected long disguiseExpires;
/** /**
* For when plugins may want to assign custom data to a disguise, such as who owns it * For when plugins may want to assign custom data to a disguise, such as who owns it
*/ */
@ -370,7 +370,7 @@ public abstract class Disguise {
return this; return this;
} }
private void doActionBar() { protected void doActionBar() {
if (getNotifyBar() == DisguiseConfig.NotifyBar.ACTION_BAR && getEntity() instanceof Player && !getEntity().hasPermission("libsdisguises.noactionbar") && if (getNotifyBar() == DisguiseConfig.NotifyBar.ACTION_BAR && getEntity() instanceof Player && !getEntity().hasPermission("libsdisguises.noactionbar") &&
DisguiseAPI.getDisguise(getEntity()) == Disguise.this) { DisguiseAPI.getDisguise(getEntity()) == Disguise.this) {
((Player) getEntity()).spigot().sendMessage(ChatMessageType.ACTION_BAR, LibsMsg.ACTION_BAR_MESSAGE.getChat(getDisguiseName())); ((Player) getEntity()).spigot().sendMessage(ChatMessageType.ACTION_BAR, LibsMsg.ACTION_BAR_MESSAGE.getChat(getDisguiseName()));
@ -394,227 +394,18 @@ public abstract class Disguise {
} }
private void createRunnable() { private void createRunnable() {
if (runnable != null) { if (runnable != null && !runnable.isCancelled()) {
runnable.cancel(); runnable.cancel();
} }
final boolean alwaysSendVelocity;
switch (getType()) {
case EXPERIENCE_ORB:
case WITHER_SKULL:
case FIREWORK:
alwaysSendVelocity = true;
break;
default:
alwaysSendVelocity = false;
break;
}
final Double vectorY;
switch (getType()) {
case FIREWORK:
case WITHER_SKULL:
vectorY = 0.000001D;
break;
case EXPERIENCE_ORB:
vectorY = 0.0221;
break;
default:
vectorY = null;
break;
}
final TargetedDisguise disguise = (TargetedDisguise) this; final TargetedDisguise disguise = (TargetedDisguise) this;
// A scheduler to clean up any unused disguises. // A scheduler to clean up any unused disguises.
runnable = new BukkitRunnable() { runnable = new DisguiseRunnable(this);
private int blockX, blockY, blockZ, facing;
private int deadTicks = 0;
private int actionBarTicks = -1;
private long lastRefreshed = System.currentTimeMillis();
@Override
public void run() {
if (!isDisguiseInUse() || getEntity() == null) {
cancel();
runnable = null;
return;
}
if (++actionBarTicks % 15 == 0) {
actionBarTicks = 0;
doActionBar();
}
// If entity is no longer valid. Remove it.
if (getEntity() instanceof Player && !((Player) getEntity()).isOnline()) {
removeDisguise();
} else if (disguiseExpires > 0 &&
(DisguiseConfig.isDynamicExpiry() ? disguiseExpires-- == 1 : disguiseExpires < System.currentTimeMillis())) { // If disguise expired
removeDisguise();
if (getEntity() instanceof Player) {
LibsMsg.EXPIRED_DISGUISE.send(getEntity());
}
return;
} else if (!getEntity().isValid()) {
// If it has been dead for 30+ ticks
// This is to ensure that this disguise isn't removed while clients think its the real entity
// The delay is because if it sends the destroy entity packets straight away, then it means no
// death animation
// This is probably still a problem for wither and enderdragon deaths.
if (deadTicks++ > (getType() == DisguiseType.ENDER_DRAGON ? 200 : 20)) {
if (isRemoveDisguiseOnDeath()) {
removeDisguise();
}
}
return;
}
deadTicks = 0;
// If the disguise type is tnt, we need to resend the entity packet else it will turn invisible
if (getType() == DisguiseType.FIREWORK || getType() == DisguiseType.EVOKER_FANGS) {
if (lastRefreshed + ((getType() == DisguiseType.FIREWORK ? 40 : 23) * 50) < System.currentTimeMillis()) {
lastRefreshed = System.currentTimeMillis();
DisguiseUtilities.refreshTrackers(disguise);
}
}
if (isModifyBoundingBox()) {
DisguiseUtilities.doBoundingBox(disguise);
}
if (getType() == DisguiseType.BAT && !((BatWatcher) getWatcher()).isHanging()) {
return;
}
doVelocity(vectorY, alwaysSendVelocity);
if (getType() == DisguiseType.EXPERIENCE_ORB) {
PacketContainer packet = new PacketContainer(Server.REL_ENTITY_MOVE);
packet.getIntegers().write(0, getEntity().getEntityId());
try {
for (Player player : DisguiseUtilities.getPerverts(disguise)) {
if (getEntity() != player) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet, false);
continue;
} else if (!isSelfDisguiseVisible() || !(getEntity() instanceof Player)) {
continue;
}
PacketContainer selfPacket = packet.shallowClone();
selfPacket.getModifier().write(0, DisguiseAPI.getSelfDisguiseId());
try {
ProtocolLibrary.getProtocolManager().sendServerPacket((Player) getEntity(), selfPacket, false);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
};
runnable.runTaskTimer(LibsDisguises.getInstance(), 1, 1); runnable.runTaskTimer(LibsDisguises.getInstance(), 1, 1);
} }
private void doVelocity(Double vectorY, boolean alwaysSendVelocity) {
// If the vectorY isn't 0. Cos if it is. Then it doesn't want to send any vectors.
// If this disguise has velocity sending enabled and the entity is flying.
if (isVelocitySent() && vectorY != null && (alwaysSendVelocity || !getEntity().isOnGround())) {
Vector vector = getEntity().getVelocity();
// If the entity doesn't have velocity changes already - You know. I really can't wrap my
// head about the
// if statement.
// But it doesn't seem to do anything wrong..
if (vector.getY() != 0 && !(vector.getY() < 0 && alwaysSendVelocity && getEntity().isOnGround())) {
return;
}
// If disguise isn't a experience orb, or the entity isn't standing on the ground
if (getType() != DisguiseType.EXPERIENCE_ORB || !getEntity().isOnGround()) {
PacketContainer lookPacket = null;
if (getType() == DisguiseType.WITHER_SKULL && DisguiseConfig.isWitherSkullPacketsEnabled()) {
lookPacket = new PacketContainer(Server.ENTITY_LOOK);
StructureModifier<Object> mods = lookPacket.getModifier();
lookPacket.getIntegers().write(0, getEntity().getEntityId());
Location loc = getEntity().getLocation();
mods.write(4, DisguiseUtilities.getYaw(getType(), getEntity().getType(), (byte) Math.floor(loc.getYaw() * 256.0F / 360.0F)));
mods.write(5, DisguiseUtilities.getPitch(getType(), getEntity().getType(), (byte) Math.floor(loc.getPitch() * 256.0F / 360.0F)));
if (isSelfDisguiseVisible() && getEntity() instanceof Player) {
PacketContainer selfLookPacket = lookPacket.shallowClone();
selfLookPacket.getIntegers().write(0, DisguiseAPI.getSelfDisguiseId());
try {
ProtocolLibrary.getProtocolManager().sendServerPacket((Player) getEntity(), selfLookPacket, false);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
try {
PacketContainer velocityPacket = new PacketContainer(Server.ENTITY_VELOCITY);
StructureModifier<Integer> mods = velocityPacket.getIntegers();
// Write entity ID
mods.write(0, getEntity().getEntityId());
mods.write(1, (int) (vector.getX() * 8000));
mods.write(3, (int) (vector.getZ() * 8000));
for (Player player : DisguiseUtilities.getPerverts(this)) {
PacketContainer tempVelocityPacket = velocityPacket.shallowClone();
mods = tempVelocityPacket.getIntegers();
// If the viewing player is the disguised player
if (getEntity() == player) {
// If not using self disguise, continue
if (!isSelfDisguiseVisible()) {
continue;
}
// Write self disguise ID
mods.write(0, DisguiseAPI.getSelfDisguiseId());
}
mods.write(2, (int) (8000D * (vectorY * ReflectionManager.getPing(player)) * 0.069D));
if (lookPacket != null && player != getEntity()) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, lookPacket, false);
}
ProtocolLibrary.getProtocolManager().sendServerPacket(player, tempVelocityPacket, false);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// If we need to send a packet to update the exp position as it likes to gravitate client
// sided to
// players.
}
}
/** /**
* Get the disguised entity * Get the disguised entity
* *
@ -1189,8 +980,7 @@ public abstract class Disguise {
DisguiseUtilities.refreshTrackers((TargetedDisguise) this); DisguiseUtilities.refreshTrackers((TargetedDisguise) this);
// If he is a player, then self disguise himself // If he is a player, then self disguise himself
Bukkit.getScheduler(). Bukkit.getScheduler().scheduleSyncDelayedTask(LibsDisguises.getInstance(), new Runnable() {
scheduleSyncDelayedTask(LibsDisguises.getInstance(), new Runnable() {
@Override @Override
public void run() { public void run() {
DisguiseUtilities.setupFakeDisguise(Disguise.this); DisguiseUtilities.setupFakeDisguise(Disguise.this);

View File

@ -0,0 +1,227 @@
package me.libraryaddict.disguise.disguisetypes;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.disguisetypes.watchers.BatWatcher;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.reflection.ReflectionManager;
import me.libraryaddict.disguise.utilities.translations.LibsMsg;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.lang.reflect.InvocationTargetException;
/**
* Created by libraryaddict on 20/05/2021.
*/
class DisguiseRunnable extends BukkitRunnable {
private int blockX, blockY, blockZ, facing;
private int deadTicks = 0;
private int actionBarTicks = -1;
private long lastRefreshed = System.currentTimeMillis();
private Disguise disguise;
final Double vectorY;
final boolean alwaysSendVelocity;
public DisguiseRunnable(Disguise disguise) {
switch (disguise.getType()) {
case FIREWORK:
case WITHER_SKULL:
vectorY = 0.000001D;
alwaysSendVelocity = true;
break;
case EXPERIENCE_ORB:
vectorY = 0.0221;
alwaysSendVelocity = true;
break;
default:
vectorY = null;
alwaysSendVelocity = false;
break;
}
}
@Override
public void run() {
if (!disguise.isDisguiseInUse() || disguise.getEntity() == null) {
cancel();
return;
}
if (++actionBarTicks % 15 == 0) {
actionBarTicks = 0;
disguise.doActionBar();
}
// If entity is no longer valid. Remove it.
if (disguise.getEntity() instanceof Player && !((Player) disguise.getEntity()).isOnline()) {
disguise.removeDisguise();
} else if (disguise.disguiseExpires > 0 && (DisguiseConfig.isDynamicExpiry() ? disguise.disguiseExpires-- == 1 :
disguise.disguiseExpires < System.currentTimeMillis())) { // If disguise expired
disguise.removeDisguise();
if (disguise.getEntity() instanceof Player) {
LibsMsg.EXPIRED_DISGUISE.send(disguise.getEntity());
}
return;
} else if (!disguise.getEntity().isValid()) {
// If it has been dead for 30+ ticks
// This is to ensure that this disguise isn't removed while clients think its the real entity
// The delay is because if it sends the destroy entity packets straight away, then it means no
// death animation
// This is probably still a problem for wither and enderdragon deaths.
if (deadTicks++ > (disguise.getType() == DisguiseType.ENDER_DRAGON ? 200 : 20)) {
if (disguise.isRemoveDisguiseOnDeath()) {
disguise.removeDisguise();
}
}
return;
}
deadTicks = 0;
// If the disguise type is tnt, we need to resend the entity packet else it will turn invisible
if (disguise.getType() == DisguiseType.FIREWORK || disguise.getType() == DisguiseType.EVOKER_FANGS) {
if (lastRefreshed + ((disguise.getType() == DisguiseType.FIREWORK ? 40 : 23) * 50) < System.currentTimeMillis()) {
lastRefreshed = System.currentTimeMillis();
DisguiseUtilities.refreshTrackers((TargetedDisguise) disguise);
}
}
if (disguise.isModifyBoundingBox()) {
DisguiseUtilities.doBoundingBox((TargetedDisguise) disguise);
}
if (disguise.getType() == DisguiseType.BAT && !((BatWatcher) disguise.getWatcher()).isHanging()) {
return;
}
doVelocity(vectorY, alwaysSendVelocity);
if (disguise.getType() == DisguiseType.EXPERIENCE_ORB) {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.REL_ENTITY_MOVE);
packet.getIntegers().write(0, disguise.getEntity().getEntityId());
try {
for (Player player : DisguiseUtilities.getPerverts(disguise)) {
if (disguise.getEntity() != player) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet, false);
continue;
} else if (!disguise.isSelfDisguiseVisible() || !(disguise.getEntity() instanceof Player)) {
continue;
}
PacketContainer selfPacket = packet.shallowClone();
selfPacket.getModifier().write(0, DisguiseAPI.getSelfDisguiseId());
try {
ProtocolLibrary.getProtocolManager().sendServerPacket((Player) disguise.getEntity(), selfPacket, false);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
private void doVelocity(Double vectorY, boolean alwaysSendVelocity) {
// If the vectorY isn't 0. Cos if it is. Then it doesn't want to send any vectors.
// If this disguise has velocity sending enabled and the entity is flying.
if (disguise.isVelocitySent() && vectorY != null && (alwaysSendVelocity || !disguise.getEntity().isOnGround())) {
Vector vector = disguise.getEntity().getVelocity();
// If the entity doesn't have velocity changes already - You know. I really can't wrap my
// head about the
// if statement.
// But it doesn't seem to do anything wrong..
if (vector.getY() != 0 && !(vector.getY() < 0 && alwaysSendVelocity && disguise.getEntity().isOnGround())) {
return;
}
// If disguise isn't a experience orb, or the entity isn't standing on the ground
if (disguise.getType() != DisguiseType.EXPERIENCE_ORB || !disguise.getEntity().isOnGround()) {
PacketContainer lookPacket = null;
if (disguise.getType() == DisguiseType.WITHER_SKULL && DisguiseConfig.isWitherSkullPacketsEnabled()) {
lookPacket = new PacketContainer(PacketType.Play.Server.ENTITY_LOOK);
StructureModifier<Object> mods = lookPacket.getModifier();
lookPacket.getIntegers().write(0, disguise.getEntity().getEntityId());
Location loc = disguise.getEntity().getLocation();
mods.write(4,
DisguiseUtilities.getYaw(disguise.getType(), disguise.getEntity().getType(), (byte) Math.floor(loc.getYaw() * 256.0F / 360.0F)));
mods.write(5, DisguiseUtilities
.getPitch(disguise.getType(), disguise.getEntity().getType(), (byte) Math.floor(loc.getPitch() * 256.0F / 360.0F)));
if (disguise.isSelfDisguiseVisible() && disguise.getEntity() instanceof Player) {
PacketContainer selfLookPacket = lookPacket.shallowClone();
selfLookPacket.getIntegers().write(0, DisguiseAPI.getSelfDisguiseId());
try {
ProtocolLibrary.getProtocolManager().sendServerPacket((Player) disguise.getEntity(), selfLookPacket, false);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
try {
PacketContainer velocityPacket = new PacketContainer(PacketType.Play.Server.ENTITY_VELOCITY);
StructureModifier<Integer> mods = velocityPacket.getIntegers();
// Write entity ID
mods.write(0, disguise.getEntity().getEntityId());
mods.write(1, (int) (vector.getX() * 8000));
mods.write(3, (int) (vector.getZ() * 8000));
for (Player player : DisguiseUtilities.getPerverts(disguise)) {
PacketContainer tempVelocityPacket = velocityPacket.shallowClone();
mods = tempVelocityPacket.getIntegers();
// If the viewing player is the disguised player
if (disguise.getEntity() == player) {
// If not using self disguise, continue
if (!disguise.isSelfDisguiseVisible()) {
continue;
}
// Write self disguise ID
mods.write(0, DisguiseAPI.getSelfDisguiseId());
}
mods.write(2, (int) (8000D * (vectorY * ReflectionManager.getPing(player)) * 0.069D));
if (lookPacket != null && player != disguise.getEntity()) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, lookPacket, false);
}
ProtocolLibrary.getProtocolManager().sendServerPacket(player, tempVelocityPacket, false);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// If we need to send a packet to update the exp position as it likes to gravitate client
// sided to
// players.
}
}
}

View File

@ -3,7 +3,6 @@ package me.libraryaddict.disguise.utilities.reflection.asm;
import org.objectweb.asm.*; import org.objectweb.asm.*;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
@ -11,8 +10,7 @@ import java.util.Map;
* Created by libraryaddict on 17/02/2020. * Created by libraryaddict on 17/02/2020.
*/ */
public class Asm13 { public class Asm13 {
public byte[] createClassWithoutMethods(String className, ArrayList<Map.Entry<String, String>> illegalMethods) public byte[] createClassWithoutMethods(String className, ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException {
throws IOException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
className = className.replace(".", "/") + ".class"; className = className.replace(".", "/") + ".class";
ClassReader cr = new ClassReader(getClass().getClassLoader().getResourceAsStream(className)); ClassReader cr = new ClassReader(getClass().getClassLoader().getResourceAsStream(className));
@ -20,7 +18,6 @@ public class Asm13 {
cr.accept(new ClassVisitor(Opcodes.ASM5, writer) { cr.accept(new ClassVisitor(Opcodes.ASM5, writer) {
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
Map.Entry<String, String> entry = Map.Entry<String, String> entry =
illegalMethods.stream().filter(e -> e.getKey().equals(name) && e.getValue().equals(desc)).findFirst().orElse(null); illegalMethods.stream().filter(e -> e.getKey().equals(name) && e.getValue().equals(desc)).findFirst().orElse(null);

View File

@ -7,8 +7,6 @@ import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
@ -28,7 +26,6 @@ public class AsmLoader {
private final File filePath = new File(LibsDisguises.getInstance().getDataFolder(), "libs/org-ow2-asm-9.1.jar"); private final File filePath = new File(LibsDisguises.getInstance().getDataFolder(), "libs/org-ow2-asm-9.1.jar");
private boolean asmExists; private boolean asmExists;
private URLClassLoader classLoader; private URLClassLoader classLoader;
private LibsJarFile libsJarFile;
public AsmLoader() { public AsmLoader() {
try { try {
@ -47,28 +44,6 @@ public class AsmLoader {
} }
} }
public void load() {
try {
ClassLoader pluginClassLoader = getClass().getClassLoader();
Class c = Class.forName("org.bukkit.plugin.java.PluginClassLoader");
Field file = c.getDeclaredField("file");
file.setAccessible(true);
libsJarFile = new LibsJarFile((File) file.get(pluginClassLoader));
Field field = c.getDeclaredField("jar");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(pluginClassLoader, libsJarFile);
} catch (Throwable e) {
e.printStackTrace();
}
}
public void unload() { public void unload() {
try { try {
classLoader.close(); classLoader.close();

View File

@ -0,0 +1,120 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.reflection.ReflectionManager;
import org.apache.commons.lang.StringUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
/**
* Created by libraryaddict on 20/05/2021.
*/
public class FakePluginCreator {
public String getPluginYAML() {
return "name: LibsDisguisesVersioning\nmain: " + getPluginClassPath().replace(".class", "").replace("/", ".") +
"\ndescription: Plugin created by Libs Disguises for " +
"compatibility with different versions\nversion: 1.0.0\nauthor: libraryaddict\napi-version: '1.13'\nsoftdepend: [ProtocolLib, LibsDisguises]";
}
public File getDestination() {
return new File(LibsDisguises.getInstance().getDataFolder(), "libs/LibsDisguisesCompat.jar");
}
public String getPluginClassPath() {
return "me/libraryaddict/disguise/utilities/reflection/asm/LibsDisguisesCompat.class";
}
public String getVersion() throws Exception {
File dest = getDestination();
if (!dest.exists()) {
return null;
}
JarFile jarFile = new JarFile(dest);
try (InputStream stream = jarFile.getInputStream(jarFile.getEntry("version.txt"))) {
return new BufferedReader(new InputStreamReader(stream)).lines().collect(Collectors.joining("\n"));
}
}
public String getOurVersion() {
YamlConfiguration pluginYml = ReflectionManager.getPluginYAML(LibsDisguises.getInstance().getFile());
String buildNo = StringUtils.stripToNull(pluginYml.getString("build-number"));
return buildNo != null && buildNo.matches("[0-9]+") ? ReflectionManager.getBukkitVersion() + " " + Integer.parseInt(buildNo) :
ReflectionManager.getBukkitVersion() + " CUSTOM";
}
public void createJar(String ourVersion, Map<String, byte[]> classes) throws Exception {
File dest = getDestination();
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
if (dest.exists()) {
dest.delete();
}
JarOutputStream out = new JarOutputStream(new FileOutputStream(dest));
out.putNextEntry(new ZipEntry("plugin.yml"));
out.write(getPluginYAML().getBytes(StandardCharsets.UTF_8));
out.closeEntry();
// Write our main plugin class
try (JarFile jar = new JarFile(LibsDisguises.getInstance().getFile())) {
Enumeration<JarEntry> entries = jar.entries();
String mainPath = getPluginClassPath().replace(".class", "");
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.getName().equals(mainPath) && !entry.getName().startsWith("me/libraryaddict/disguise/disguisetypes/")) {
continue;
}
if (classes.containsKey(entry.getName())) {
continue;
}
out.putNextEntry(new ZipEntry(entry.getName().equals(mainPath) ? getPluginClassPath() : entry.getName()));
try (InputStream stream = jar.getInputStream(entry)) {
int nRead;
byte[] data = new byte[1024];
while ((nRead = stream.read(data, 0, data.length)) != -1) {
out.write(data, 0, nRead);
}
}
out.closeEntry();
}
} catch (Exception ex) {
ex.printStackTrace();
}
for (Map.Entry<String, byte[]> entry : classes.entrySet()) {
out.putNextEntry(new ZipEntry(entry.getKey()));
out.write(entry.getValue());
out.closeEntry();
}
out.putNextEntry(new ZipEntry("version.txt"));
out.write(ourVersion.getBytes(StandardCharsets.UTF_8));
out.closeEntry();
out.close();
}
}

View File

@ -0,0 +1,9 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import org.bukkit.plugin.java.JavaPlugin;
/**
* Created by libraryaddict on 20/05/2021.
*/
public class LibsDisguisesCompat extends JavaPlugin {
}

View File

@ -1,39 +0,0 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
/**
* Created by libraryaddict on 15/05/2021.
*/
public class LibsJarFile extends JarFile {
private final HashMap<String, byte[]> customFiles = new HashMap<>();
public LibsJarFile(File file) throws IOException {
super(file);
}
@Override
public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
if (customFiles.containsKey(ze.getName())) {
return new ByteArrayInputStream(customFiles.get(ze.getName()));
}
return super.getInputStream(ze);
}
public void addClass(String name, byte[] bytes) {
customFiles.put(name, bytes);
}
@Override
public void close() throws IOException {
customFiles.clear();
super.close();
}
}

View File

@ -3,9 +3,12 @@ package me.libraryaddict.disguise.utilities.reflection.asm;
import com.google.gson.Gson; import com.google.gson.Gson;
import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.reflection.ReflectionManager; import me.libraryaddict.disguise.utilities.reflection.ReflectionManager;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.plugin.java.JavaPluginLoader;
import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -69,11 +72,29 @@ public class WatcherSanitizer {
} catch (NoSuchFieldException | IllegalAccessException ignored) { } catch (NoSuchFieldException | IllegalAccessException ignored) {
} }
if (Bukkit.getPluginManager().getPlugin("LibsDisguisesVersioning") != null) {
throw new IllegalStateException("Why is LibsDisguisesVersioning already active? Did the server owner do something.. Weird?");
}
FakePluginCreator fakePluginCreator = new FakePluginCreator();
String ourVers = fakePluginCreator.getOurVersion();
try {
if (ourVers != null && ourVers.equals(fakePluginCreator.getVersion()) && !ourVers.contains(" CUSTOM")) {
loadPlugin(fakePluginCreator);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
LibsDisguises.getInstance().getLogger().info("Creating a new version compatibility jar");
ArrayList<String> mapped = new ArrayList<>(); ArrayList<String> mapped = new ArrayList<>();
try (InputStream stream = LibsDisguises.getInstance().getResource("ANTI_PIRACY_ENCRYPTION")) { try (InputStream stream = LibsDisguises.getInstance().getResource("ANTI_PIRACY_ENCRYPTION")) {
AsmLoader loader = new AsmLoader(); AsmLoader loader = new AsmLoader();
loader.load();
Object obj; Object obj;
Method getBytes; Method getBytes;
@ -109,15 +130,18 @@ public class WatcherSanitizer {
list.add(new HashMap.SimpleEntry(info.getMethod(), info.getDescriptor())); list.add(new HashMap.SimpleEntry(info.getMethod(), info.getDescriptor()));
} }
Map<String, byte[]> classes = new HashMap<>();
for (Map.Entry<String, ArrayList<Map.Entry<String, String>>> entry : toRemove.entrySet()) { for (Map.Entry<String, ArrayList<Map.Entry<String, String>>> entry : toRemove.entrySet()) {
byte[] bytes = (byte[]) getBytes.invoke(obj, entry.getKey(), entry.getValue()); byte[] bytes = (byte[]) getBytes.invoke(obj, entry.getKey(), entry.getValue());
mapped.add(entry.getKey()); mapped.add(entry.getKey());
String name = entry.getKey().replace(".", "/") + ".class"; classes.put(entry.getKey().replace(".", "/") + ".class", bytes);
loader.getLibsJarFile().addClass(name, bytes);
} }
fakePluginCreator.createJar(ourVers, classes);
loadPlugin(fakePluginCreator);
if (!loader.isAsmExists()) { if (!loader.isAsmExists()) {
loader.unload(); loader.unload();
} }
@ -126,4 +150,32 @@ public class WatcherSanitizer {
LibsDisguises.getInstance().getLogger().severe("Registered: " + new Gson().toJson(mapped)); LibsDisguises.getInstance().getLogger().severe("Registered: " + new Gson().toJson(mapped));
} }
} }
private static void loadPlugin(FakePluginCreator fakePluginCreator) throws Exception {
LibsDisguises.getInstance().getLogger().info("Starting version support plugin: LibsDisguisesVersioning");
Method method = Class.forName("org.bukkit.plugin.PluginManager", false, WatcherSanitizer.class.getClassLoader().getParent())
.getMethod("loadPlugin", File.class);
Plugin plugin = (Plugin) method.invoke(Bukkit.getPluginManager(), fakePluginCreator.getDestination());
Class pluginClassLoader = Class.forName("org.bukkit.plugin.java.PluginClassLoader");
Field loaderField = JavaPluginLoader.class.getDeclaredField("loaders");
loaderField.setAccessible(true);
List loaderList = (List) loaderField.get(LibsDisguises.getInstance().getPluginLoader());
Field pluginOwner = pluginClassLoader.getDeclaredField("plugin");
pluginOwner.setAccessible(true);
// Move Lib's Disguises to load its classes after the new plugin
for (Object o : loaderList) {
if (pluginOwner.get(o) != LibsDisguises.getInstance()) {
continue;
}
loaderList.remove(o);
loaderList.add(o);
break;
}
}
} }

View File

@ -5,6 +5,7 @@ import me.libraryaddict.disguise.utilities.LibsPremium;
import me.libraryaddict.disguise.utilities.reflection.ClassGetter; import me.libraryaddict.disguise.utilities.reflection.ClassGetter;
import me.libraryaddict.disguise.utilities.reflection.NmsAddedIn; import me.libraryaddict.disguise.utilities.reflection.NmsAddedIn;
import me.libraryaddict.disguise.utilities.reflection.NmsRemovedIn; import me.libraryaddict.disguise.utilities.reflection.NmsRemovedIn;
import me.libraryaddict.disguise.utilities.reflection.asm.FakePluginCreator;
import me.libraryaddict.disguise.utilities.sounds.DisguiseSoundEnums; import me.libraryaddict.disguise.utilities.sounds.DisguiseSoundEnums;
import me.libraryaddict.disguise.utilities.sounds.SoundGroup; import me.libraryaddict.disguise.utilities.sounds.SoundGroup;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -34,6 +35,14 @@ public class CompileMethods {
public static void main(String[] args) { public static void main(String[] args) {
doMethods(); doMethods();
doSounds(); doSounds();
moveCompat();
}
private static void moveCompat() {
FakePluginCreator creator = new FakePluginCreator();
File compatFile = new File("target/classes/" + creator.getPluginClassPath());
compatFile.renameTo(new File(compatFile.getParentFile(), compatFile.getName().replace(".class", "")));
} }
private static void doSounds() { private static void doSounds() {
@ -91,8 +100,7 @@ public class CompileMethods {
} }
private static void doMethods() { private static void doMethods() {
ArrayList<Class<?>> classes = ArrayList<Class<?>> classes = ClassGetter.getClassesForPackage(FlagWatcher.class, "me.libraryaddict.disguise.disguisetypes.watchers");
ClassGetter.getClassesForPackage(FlagWatcher.class, "me.libraryaddict.disguise.disguisetypes.watchers");
ArrayList<Class> sorted = new ArrayList<>(); ArrayList<Class> sorted = new ArrayList<>();
@ -106,12 +114,10 @@ public class CompileMethods {
for (Method method : c.getMethods()) { for (Method method : c.getMethods()) {
if (!FlagWatcher.class.isAssignableFrom(method.getDeclaringClass())) { if (!FlagWatcher.class.isAssignableFrom(method.getDeclaringClass())) {
continue; continue;
} else if (method.getParameterCount() > 1 && !method.isAnnotationPresent(NmsAddedIn.class) && } else if (method.getParameterCount() > 1 && !method.isAnnotationPresent(NmsAddedIn.class) && !method.isAnnotationPresent(NmsRemovedIn.class)) {
!method.isAnnotationPresent(NmsRemovedIn.class)) {
continue; continue;
} else if (!(method.getName().startsWith("set") && method.getParameterCount() == 1) && } else if (!(method.getName().startsWith("set") && method.getParameterCount() == 1) && !method.getName().startsWith("get") &&
!method.getName().startsWith("get") && !method.getName().startsWith("has") && !method.getName().startsWith("has") && !method.getName().startsWith("is")) {
!method.getName().startsWith("is")) {
continue; continue;
} else if (method.getName().equals("removePotionEffect")) { } else if (method.getName().equals("removePotionEffect")) {
continue; continue;
@ -141,8 +147,7 @@ public class CompileMethods {
descriptor = ":" + getMethodDescriptor(method) + ":" + added + ":" + removed; descriptor = ":" + getMethodDescriptor(method) + ":" + added + ":" + removed;
} }
String s = String s = method.getDeclaringClass().getSimpleName() + ":" + method.getName() + ":" + param + descriptor;
method.getDeclaringClass().getSimpleName() + ":" + method.getName() + ":" + param + descriptor;
if (methods.contains(s)) { if (methods.contains(s)) {
continue; continue;

View File

@ -6,7 +6,7 @@ build-date: ${timestamp}
build-number: ${build.number} build-number: ${build.number}
author: libraryaddict author: libraryaddict
authors: [Byteflux, Navid K.] authors: [Byteflux, Navid K.]
softdepend: [ProtocolLib] softdepend: [ProtocolLib, LibsDisguisesVersioning]
api-version: '1.13' api-version: '1.13'
commands: commands:
libsdisguises: libsdisguises: