AsmClassTransformer

This commit is contained in:
Izzel_Aliz 2018-04-29 15:44:01 +08:00
parent 250d168c2e
commit da8890b30a
12 changed files with 285 additions and 92 deletions

View File

@ -2,6 +2,7 @@
<dictionary name="csh20">
<words>
<w>autoload</w>
<w>craftbukkit</w>
<w>mvdw</w>
<w>papi</w>
<w>sendable</w>

View File

@ -66,6 +66,21 @@ public class TConfigInjector {
return null;
}
public static void reloadConfig(Plugin plugin, Object object) {
try {
Config config = object.getClass().getAnnotation(Config.class);
Validate.notNull(config);
File file = new File(plugin.getDataFolder(), config.name());
Map<String, Object> map = ConfigUtils.confToMap(ConfigUtils.loadYaml(plugin, file));
Object obj = ConfigUtils.mapToObj(map, object);
if (!config.readOnly()) saveConfig(plugin, obj);
} catch (NullPointerException e) {
TLocale.Logger.warn("CONFIG.LOAD-FAIL-NO-ANNOTATION", plugin.toString(), object.getClass().getSimpleName());
} catch (Exception e) {
TLocale.Logger.warn("CONFIG.LOAD-FAIL", plugin.toString(), object.getClass().getSimpleName());
}
}
public static Object unserialize(Plugin plugin, Class<?> clazz) {
try {
Config config = clazz.getAnnotation(Config.class);

View File

@ -72,11 +72,7 @@ public class TDependencyInjector {
obj,
object -> {
try {
Object newObj = TConfigInjector.loadConfig(plugin, object.getClass());
for (Field f : newObj.getClass().getDeclaredFields()) {
f.setAccessible(true);
f.set(obj, f.get(newObj));
}
TConfigInjector.reloadConfig(plugin, object);
TLocale.Logger.info("CONFIG.RELOAD-SUCCESS", plugin.toString(), config.name());
} catch (Exception ignored) {
TLocale.Logger.warn("CONFIG.RELOAD-FAIL", plugin.toString(), config.name());

View File

@ -0,0 +1,50 @@
package com.ilummc.tlib.nms;
import com.ilummc.tlib.util.asm.AsmClassTransformer;
import me.skymc.taboolib.TabooLib;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public abstract class ActionBar {
private static ActionBar instance;
static {
if (TabooLib.getVerint() > 11100) {
instance = (ActionBar) AsmClassTransformer.builder().from(Impl_1_12.class).fromVersion("v1_12_R1")
.toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]).build().transform();
} else {
instance = (ActionBar) AsmClassTransformer.builder().from(Impl_1_8.class).fromVersion("v1_8_R3")
.toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]).build().transform();
}
System.out.println(instance.getClass());
}
public static void sendActionBar(Player player, String text) {
instance.send(player, text);
}
public abstract void send(Player player, String text);
public static class Impl_1_8 extends ActionBar {
@Override
public void send(Player player, String text) {
net.minecraft.server.v1_8_R3.ChatComponentText component = new net.minecraft.server.v1_8_R3.ChatComponentText(text);
net.minecraft.server.v1_8_R3.PacketPlayOutChat packet = new net.minecraft.server.v1_8_R3.PacketPlayOutChat(component, (byte) 2);
((org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
}
public static class Impl_1_12 extends ActionBar {
@Override
public void send(Player player, String text) {
net.minecraft.server.v1_12_R1.ChatComponentText component = new net.minecraft.server.v1_12_R1.ChatComponentText(text);
net.minecraft.server.v1_12_R1.PacketPlayOutChat packet = new net.minecraft.server.v1_12_R1.PacketPlayOutChat(component,
net.minecraft.server.v1_12_R1.ChatMessageType.a((byte) 2));
((org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
}
}

View File

@ -72,6 +72,8 @@ public class TLocaleLoader {
TLocaleInstance localeInstance = new TLocaleInstance(plugin);
localeInstance.load(configuration);
map.put(plugin.getName(), localeInstance);
TLib.getTLib().getLogger().info(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("SUCCESS-LOADING-LANG"),
plugin.getName(), lang, String.valueOf(localeInstance.size())));
}
File finalFile = file;
String finalLang = lang;
@ -84,7 +86,6 @@ public class TLocaleLoader {
TLib.getTLib().getLogger().info(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("SUCCESS-LOADING-LANG"),
plugin.getName(), finalLang, String.valueOf(localeInstance.size())));
});
TLib.getTLib().getLogger().info(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("SUCCESS-LOADING-LANG"), plugin.getName(), lang));
}
} catch (Exception e) {
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("ERROR-LOADING-LANG"),

View File

@ -0,0 +1,13 @@
package com.ilummc.tlib.util.asm;
public class AsmClassLoader extends ClassLoader {
public AsmClassLoader() {
super(AsmClassLoader.class.getClassLoader());
}
public Class<?> createNewClass(String name, byte[] arr) {
return defineClass(name, arr, 0, arr.length, AsmClassLoader.class.getProtectionDomain());
}
}

View File

@ -1,4 +1,153 @@
package com.ilummc.tlib.util.asm;
public class AsmClassTransformer {
import org.bukkit.Bukkit;
import org.objectweb.asm.*;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
public class AsmClassTransformer extends ClassVisitor implements Opcodes {
private final Class<?> from;
private final String fromVer, toVer;
private final ClassWriter writer;
private String newClassName, prevName;
private AsmClassTransformer(Class<?> from, String fromVer, String toVer, ClassWriter classWriter) {
super(Opcodes.ASM6, classWriter);
writer = classWriter;
this.from = from;
this.fromVer = fromVer;
this.toVer = toVer;
}
public static Builder builder() {
return new Builder().toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]);
}
public Object transform() {
try {
ClassReader classReader = new ClassReader(from.getResourceAsStream("/" + from.getName().replace('.', '/') + ".class"));
newClassName = from.getName() + "_TabooLibRemap_" + this.hashCode() + "_" + toVer;
prevName = from.getName().replace('.', '/');
classReader.accept(this, ClassReader.SKIP_DEBUG);
Class<?> clazz = new AsmClassLoader().createNewClass(newClassName, writer.toByteArray());
Field field = from.getClassLoader().getClass().getDeclaredField("classes");
field.setAccessible(true);
((Map<String, Class<?>>) field.get(from.getClassLoader())).put(newClassName, clazz);
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (IOException | NoSuchFieldException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, newClassName.replace('.', '/'), replace(signature),
prevName, replace(interfaces));
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor visitor = super.visitMethod(access, name, replace(descriptor), replace(signature), replace(exceptions));
return new AsmMethodTransformer(visitor);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
return super.visitField(access, name, replace(descriptor), replace(signature), value);
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
super.visitInnerClass(replace(name), outerName, replace(name).substring(outerName.length() + 1), access);
}
private String replace(String text) {
if (text != null)
return text.replace("net/minecraft/server/" + fromVer, "net/minecraft/server/" + toVer)
.replace("org/bukkit/craftbukkit/" + fromVer, "org/bukkit/craftbukkit/" + toVer)
.replace(prevName, newClassName.replace('.', '/'));
else return null;
}
private String[] replace(String[] text) {
if (text != null) {
for (int i = 0; i < text.length; i++) {
text[i] = replace(text[i]);
}
return text;
} else return null;
}
public static class Builder {
private Class<?> from;
private String fromVersion, toVersion;
public Builder from(Class<?> clazz) {
this.from = clazz;
return this;
}
public Builder fromVersion(String ver) {
fromVersion = ver;
return this;
}
public Builder toVersion(String ver) {
toVersion = ver;
return this;
}
public AsmClassTransformer build() {
return new AsmClassTransformer(from, fromVersion, toVersion, new ClassWriter(ClassWriter.COMPUTE_MAXS));
}
}
private class AsmMethodTransformer extends MethodVisitor {
AsmMethodTransformer(MethodVisitor visitor) {
super(Opcodes.ASM6, visitor);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
super.visitMethodInsn(opcode, replace(owner), name, replace(descriptor), isInterface);
}
@Override
public void visitLdcInsn(Object value) {
if (value instanceof String)
super.visitLdcInsn(replace((String) value));
else super.visitLdcInsn(value);
}
@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, replace(type));
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
super.visitFieldInsn(opcode, replace(owner), name, replace(descriptor));
}
@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
super.visitLocalVariable(name, replace(descriptor), replace(signature), start, end, index);
}
}
}

View File

@ -1,52 +1,20 @@
package me.skymc.taboolib.display;
import java.lang.reflect.Constructor;
import com.ilummc.tlib.nms.ActionBar;
import org.bukkit.entity.Player;
import me.skymc.taboolib.TabooLib;
import me.skymc.taboolib.nms.NMSUtils;
/**
* @author Bkm016
* @since 2018-04-26
*/
public class ActionUtils {
private static Class<?> Packet = NMSUtils.getNMSClass("Packet");
private static Class<?> ChatComponentText = NMSUtils.getNMSClass("ChatComponentText");
private static Class<?> ChatMessageType = NMSUtils.getNMSClass("ChatMessageType");
private static Class<?> PacketPlayOutChat = NMSUtils.getNMSClass("PacketPlayOutChat");
private static Class<?> IChatBaseComponent = NMSUtils.getNMSClass("IChatBaseComponent");
public static void send(Player player, String action) {
if (player == null) {
if (player == null)
return;
}
try {
Object ab = ChatComponentText.getConstructor(String.class).newInstance(action);
Constructor<?> ac = null;
Object abPacket = null;
if (TabooLib.getVerint() > 11100) {
ac = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType);
abPacket = ac.newInstance(ab, ChatMessageType.getMethod("a", Byte.TYPE).invoke(null, (byte) 2));
} else {
ac = PacketPlayOutChat.getConstructor(IChatBaseComponent, Byte.TYPE);
abPacket = ac.newInstance(ab, (byte) 2);
}
sendPacket(player, abPacket);
}
catch (Exception ignored) {
}
}
private static void sendPacket(Player player, Object packet) {
try {
Object handle = player.getClass().getMethod("getHandle", new Class[0]).invoke(player);
Object playerConnection = handle.getClass().getField("playerConnection").get(handle);
playerConnection.getClass().getMethod("sendPacket", Packet).invoke(playerConnection, packet);
}
catch (Exception ignored) {
ActionBar.sendActionBar(player, action);
} catch (Throwable ignored) {
}
}
}

View File

@ -1,11 +1,9 @@
package me.skymc.taboolib.display;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import me.skymc.taboolib.nms.NMSUtils;
import org.bukkit.entity.Player;
import me.skymc.taboolib.nms.NMSUtils;
import java.lang.reflect.Constructor;
/**
* @author Bkm016
@ -53,8 +51,7 @@ public class TitleUtils {
subtitlePacket = subtitleConstructor.newInstance(times, chatSubtitle, fadeinst, stayst, fadeoutst);
sendPacket(p, subtitlePacket);
}
}
catch (Exception ignored) {
} catch (Exception ignored) {
}
}

View File

@ -3,6 +3,7 @@ package me.skymc.taboolib.fileutils;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.ilummc.tlib.TLib;
import com.ilummc.tlib.bean.Property;
import com.ilummc.tlib.util.Ref;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
@ -86,13 +87,20 @@ public class ConfigUtils {
}
}
@SuppressWarnings("unchecked")
public static <T> T mapToObj(Map<String, Object> map, T obj) {
Class<?> clazz = obj.getClass();
map.forEach((string, value) -> Ref.getFieldBySerializedName(clazz, string).ifPresent(field -> {
if (!field.isAccessible())
field.setAccessible(true);
try {
if (Property.class.isAssignableFrom(field.getType())) {
Property<Object> property = (Property) field.get(obj);
if (property != null) property.set(value);
else field.set(obj, Property.of(value));
} else {
field.set(obj, value);
}
} catch (IllegalAccessException ignored) {
}
}));
@ -108,7 +116,9 @@ public class ConfigUtils {
for (Field field : Ref.getDeclaredFields(object.getClass(), excludedModifiers, false)) {
try {
if (!field.isAccessible()) field.setAccessible(true);
map.put(Ref.getSerializedName(field), field.get(object));
Object obj = field.get(object);
if (obj instanceof Property) obj = ((Property) obj).get();
map.put(Ref.getSerializedName(field), obj);
} catch (IllegalAccessException ignored) {
}
}

View File

@ -1,22 +1,14 @@
package me.skymc.taboolib.fileutils;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import ch.njol.util.Closeable;
import me.skymc.taboolib.message.MsgUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import ch.njol.util.Closeable;
import me.skymc.taboolib.message.MsgUtils;
public class FileUtils {
public static String ip() {
@ -248,6 +240,11 @@ public class FileUtils {
return null;
}
public static String getStringFromURL(String url, String def) {
String s = getStringFromURL(url, 1024);
return s == null ? def : s;
}
/**
* 下载文件
*

View File

@ -1,14 +1,12 @@
package me.skymc.taboolib.update;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.scheduler.BukkitRunnable;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.skymc.taboolib.Main;
import me.skymc.taboolib.TabooLib;
import me.skymc.taboolib.fileutils.FileUtils;
import me.skymc.taboolib.message.MsgUtils;
import org.bukkit.scheduler.BukkitRunnable;
/**
* @author sky
@ -16,6 +14,8 @@ import me.skymc.taboolib.message.MsgUtils;
*/
public class UpdateTask {
private static final String API = "https://api.github.com/repos/Bkm016/TabooLib/releases/latest";
/**
* 检测更新
*/
@ -27,14 +27,10 @@ public class UpdateTask {
if (!Main.getInst().getConfig().getBoolean("UPDATE-CHECK")) {
return;
}
String value = FileUtils.getStringFromURL("https://github.com/Bkm016/TabooLib/releases", 1024);
if (value == null) {
return;
}
Pattern pattern = Pattern.compile("<a href=\"/Bkm016/TabooLib/releases/tag/(\\S+)\">");
Matcher matcher = pattern.matcher(value);
if (matcher.find()) {
double newVersion = Double.valueOf(matcher.group(1));
String value = FileUtils.getStringFromURL(API, "{}");
JsonObject json = new JsonParser().parse(value).getAsJsonObject();
if (json.entrySet().size() > 0) {
double newVersion = Double.parseDouble(json.get("tag_name").getAsString());
if (TabooLib.getPluginVersion() >= newVersion) {
MsgUtils.send("插件已是最新版, 无需更新!");
}