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"> <dictionary name="csh20">
<words> <words>
<w>autoload</w> <w>autoload</w>
<w>craftbukkit</w>
<w>mvdw</w> <w>mvdw</w>
<w>papi</w> <w>papi</w>
<w>sendable</w> <w>sendable</w>

View File

@ -66,6 +66,21 @@ public class TConfigInjector {
return null; 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) { public static Object unserialize(Plugin plugin, Class<?> clazz) {
try { try {
Config config = clazz.getAnnotation(Config.class); Config config = clazz.getAnnotation(Config.class);

View File

@ -72,11 +72,7 @@ public class TDependencyInjector {
obj, obj,
object -> { object -> {
try { try {
Object newObj = TConfigInjector.loadConfig(plugin, object.getClass()); TConfigInjector.reloadConfig(plugin, object);
for (Field f : newObj.getClass().getDeclaredFields()) {
f.setAccessible(true);
f.set(obj, f.get(newObj));
}
TLocale.Logger.info("CONFIG.RELOAD-SUCCESS", plugin.toString(), config.name()); TLocale.Logger.info("CONFIG.RELOAD-SUCCESS", plugin.toString(), config.name());
} catch (Exception ignored) { } catch (Exception ignored) {
TLocale.Logger.warn("CONFIG.RELOAD-FAIL", plugin.toString(), config.name()); 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); TLocaleInstance localeInstance = new TLocaleInstance(plugin);
localeInstance.load(configuration); localeInstance.load(configuration);
map.put(plugin.getName(), localeInstance); 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; File finalFile = file;
String finalLang = lang; String finalLang = lang;
@ -84,7 +86,6 @@ public class TLocaleLoader {
TLib.getTLib().getLogger().info(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("SUCCESS-LOADING-LANG"), TLib.getTLib().getLogger().info(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("SUCCESS-LOADING-LANG"),
plugin.getName(), finalLang, String.valueOf(localeInstance.size()))); 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) { } catch (Exception e) {
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getTLib().getInternalLang().getString("ERROR-LOADING-LANG"), 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; 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; package me.skymc.taboolib.display;
import java.lang.reflect.Constructor; import com.ilummc.tlib.nms.ActionBar;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import me.skymc.taboolib.TabooLib;
import me.skymc.taboolib.nms.NMSUtils;
/** /**
* @author Bkm016 * @author Bkm016
* @since 2018-04-26 * @since 2018-04-26
*/ */
public class ActionUtils { 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) { public static void send(Player player, String action) {
if (player == null) { if (player == null)
return; return;
}
try { try {
Object ab = ChatComponentText.getConstructor(String.class).newInstance(action); ActionBar.sendActionBar(player, action);
Constructor<?> ac = null; } catch (Throwable ignored) {
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) {
} }
} }
} }

View File

@ -1,11 +1,9 @@
package me.skymc.taboolib.display; package me.skymc.taboolib.display;
import java.lang.reflect.Constructor; import me.skymc.taboolib.nms.NMSUtils;
import java.lang.reflect.Method;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import me.skymc.taboolib.nms.NMSUtils; import java.lang.reflect.Constructor;
/** /**
* @author Bkm016 * @author Bkm016
@ -13,20 +11,20 @@ import me.skymc.taboolib.nms.NMSUtils;
*/ */
public class TitleUtils { public class TitleUtils {
private static Class<?> Packet = NMSUtils.getNMSClass("Packet"); private static Class<?> Packet = NMSUtils.getNMSClass("Packet");
private static Class<?> PacketPlayOutTitle = NMSUtils.getNMSClass("PacketPlayOutTitle"); private static Class<?> PacketPlayOutTitle = NMSUtils.getNMSClass("PacketPlayOutTitle");
private static Class<?> IChatBaseComponent = NMSUtils.getNMSClass("IChatBaseComponent"); private static Class<?> IChatBaseComponent = NMSUtils.getNMSClass("IChatBaseComponent");
private static Class<?> EnumTitleAction = PacketPlayOutTitle.getDeclaredClasses()[0]; private static Class<?> EnumTitleAction = PacketPlayOutTitle.getDeclaredClasses()[0];
public static void sendTitle(Player p, String title, String subtitle, int fadein, int stay, int fadeout) { public static void sendTitle(Player p, String title, String subtitle, int fadein, int stay, int fadeout) {
sendTitle(p, title, fadein, stay, fadeout, subtitle, fadein, stay, fadeout); sendTitle(p, title, fadein, stay, fadeout, subtitle, fadein, stay, fadeout);
} }
public static void sendTitle(Player p, String title, int fadeint, int stayt, int fadeoutt, String subtitle, int fadeinst, int stayst, int fadeoutst) { public static void sendTitle(Player p, String title, int fadeint, int stayt, int fadeoutt, String subtitle, int fadeinst, int stayst, int fadeoutst) {
if (p == null) { if (p == null) {
return; return;
} }
try { try {
if (title != null) { if (title != null) {
Object times = EnumTitleAction.getField("TIMES").get(null); Object times = EnumTitleAction.getField("TIMES").get(null);
Object chatTitle = IChatBaseComponent.getDeclaredClasses()[0].getMethod("a", String.class).invoke(null, "{\"text\":\"" + title + "\"}"); Object chatTitle = IChatBaseComponent.getDeclaredClasses()[0].getMethod("a", String.class).invoke(null, "{\"text\":\"" + title + "\"}");
@ -53,8 +51,7 @@ public class TitleUtils {
subtitlePacket = subtitleConstructor.newInstance(times, chatSubtitle, fadeinst, stayst, fadeoutst); subtitlePacket = subtitleConstructor.newInstance(times, chatSubtitle, fadeinst, stayst, fadeoutst);
sendPacket(p, subtitlePacket); 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.collect.Maps;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.ilummc.tlib.TLib; import com.ilummc.tlib.TLib;
import com.ilummc.tlib.bean.Property;
import com.ilummc.tlib.util.Ref; import com.ilummc.tlib.util.Ref;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration; 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) { public static <T> T mapToObj(Map<String, Object> map, T obj) {
Class<?> clazz = obj.getClass(); Class<?> clazz = obj.getClass();
map.forEach((string, value) -> Ref.getFieldBySerializedName(clazz, string).ifPresent(field -> { map.forEach((string, value) -> Ref.getFieldBySerializedName(clazz, string).ifPresent(field -> {
if (!field.isAccessible()) if (!field.isAccessible())
field.setAccessible(true); field.setAccessible(true);
try { try {
field.set(obj, value); 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) { } catch (IllegalAccessException ignored) {
} }
})); }));
@ -108,7 +116,9 @@ public class ConfigUtils {
for (Field field : Ref.getDeclaredFields(object.getClass(), excludedModifiers, false)) { for (Field field : Ref.getDeclaredFields(object.getClass(), excludedModifiers, false)) {
try { try {
if (!field.isAccessible()) field.setAccessible(true); 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) { } catch (IllegalAccessException ignored) {
} }
} }

View File

@ -1,22 +1,14 @@
package me.skymc.taboolib.fileutils; package me.skymc.taboolib.fileutils;
import java.io.BufferedInputStream; import ch.njol.util.Closeable;
import java.io.BufferedReader; import me.skymc.taboolib.message.MsgUtils;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import ch.njol.util.Closeable;
import me.skymc.taboolib.message.MsgUtils;
public class FileUtils { public class FileUtils {
public static String ip() { public static String ip() {
@ -248,6 +240,11 @@ public class FileUtils {
return null; 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; package me.skymc.taboolib.update;
import java.util.regex.Matcher; import com.google.gson.JsonObject;
import java.util.regex.Pattern; import com.google.gson.JsonParser;
import org.bukkit.scheduler.BukkitRunnable;
import me.skymc.taboolib.Main; import me.skymc.taboolib.Main;
import me.skymc.taboolib.TabooLib; import me.skymc.taboolib.TabooLib;
import me.skymc.taboolib.fileutils.FileUtils; import me.skymc.taboolib.fileutils.FileUtils;
import me.skymc.taboolib.message.MsgUtils; import me.skymc.taboolib.message.MsgUtils;
import org.bukkit.scheduler.BukkitRunnable;
/** /**
* @author sky * @author sky
@ -16,6 +14,8 @@ import me.skymc.taboolib.message.MsgUtils;
*/ */
public class UpdateTask { 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")) { if (!Main.getInst().getConfig().getBoolean("UPDATE-CHECK")) {
return; return;
} }
String value = FileUtils.getStringFromURL("https://github.com/Bkm016/TabooLib/releases", 1024); String value = FileUtils.getStringFromURL(API, "{}");
if (value == null) { JsonObject json = new JsonParser().parse(value).getAsJsonObject();
return; if (json.entrySet().size() > 0) {
} double newVersion = Double.parseDouble(json.get("tag_name").getAsString());
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));
if (TabooLib.getPluginVersion() >= newVersion) { if (TabooLib.getPluginVersion() >= newVersion) {
MsgUtils.send("插件已是最新版, 无需更新!"); MsgUtils.send("插件已是最新版, 无需更新!");
} }