feat: add MavenDependLoader

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
MiaoWoo 2022-05-14 18:44:24 +08:00
parent e2f9bbf587
commit 56152657c8
28 changed files with 267 additions and 1317 deletions

12
pom.xml
View File

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>pw.yumc</groupId>
<artifactId>MiaoScript</artifactId>
<version>0.20.0</version>
<version>0.21.1</version>
<developers>
<developer>
<id>502647092</id>
@ -16,9 +16,6 @@
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>dev-plugins/**</exclude>
</excludes>
<filtering>true</filtering>
</resource>
</resources>
@ -230,7 +227,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.3.13</version>
<version>5.3.18</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -239,5 +236,10 @@
<version>9.0.35</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
</dependencies>
</project>

View File

@ -2,6 +2,7 @@ package pw.yumc.MiaoScript;
import lombok.SneakyThrows;
import org.bukkit.plugin.java.JavaPlugin;
import pw.yumc.MiaoScript.api.ScriptEngine;
/**
* 喵式脚本

View File

@ -2,6 +2,7 @@ package pw.yumc.MiaoScript;
import lombok.SneakyThrows;
import net.md_5.bungee.api.plugin.Plugin;
import pw.yumc.MiaoScript.api.ScriptEngine;
/**
* Created with IntelliJ IDEA

View File

@ -2,6 +2,7 @@ package pw.yumc.MiaoScript;
import cn.nukkit.plugin.PluginBase;
import lombok.SneakyThrows;
import pw.yumc.MiaoScript.api.ScriptEngine;
/**
* @author MiaoWoo

View File

@ -11,6 +11,7 @@ import org.spongepowered.api.event.game.state.GameStartingServerEvent;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
import pw.yumc.MiaoScript.api.ScriptEngine;
import java.io.File;

View File

@ -6,6 +6,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import pw.yumc.MiaoScript.api.ScriptEngine;
import java.io.File;

View File

@ -1,6 +1,4 @@
package pw.yumc.MiaoScript;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
package pw.yumc.MiaoScript.api;
import java.io.File;
import java.io.IOException;
@ -16,7 +14,7 @@ import java.nio.file.Paths;
* Created on 2017/10/9 12:40.
*/
public class Base {
private Object instance;
private final Object instance;
Base(Object instance) {
this.instance = instance;
@ -42,6 +40,10 @@ public class Base {
return JavaScriptTask.class;
}
public File[] loadMavenDepend(String groupId, String artifactId, String version) {
return MiaoScriptAPI.loadMavenDepend(groupId, artifactId, version);
}
public String read(String path) throws IOException {
return read(Paths.get(path));
}

View File

@ -1,4 +1,4 @@
package pw.yumc.MiaoScript;
package pw.yumc.MiaoScript.api;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

View File

@ -1,17 +1,48 @@
package pw.yumc.MiaoScript.api;
import pw.yumc.MiaoScript.MiaoScriptEngine;
import pw.yumc.MiaoScript.ScriptEngine;
import pw.yumc.MiaoScript.api.loader.MavenDependLoader;
import pw.yumc.MiaoScript.api.plugin.PluginManager;
import pw.yumc.MiaoScript.engine.MiaoScriptEngine;
import java.io.File;
import java.nio.file.Paths;
public class MiaoScriptAPI {
public static final String VERSION = "0.20.0";
public static final String VERSION = "0.21.1";
private static String root;
private static String libPath;
private static ScriptEngine scriptEngine;
private static PluginManager pluginManager;
public static void setEngine(ScriptEngine scriptEngine) {
MiaoScriptAPI.scriptEngine = scriptEngine;
public static String getRoot() {
return root;
}
public static void setRoot(String root) {
MiaoScriptAPI.root = root;
MiaoScriptAPI.libPath = Paths.get(root, "libs").toString();
}
public static MiaoScriptEngine getEngine() {
return MiaoScriptAPI.scriptEngine.getEngine();
}
public static void setEngine(ScriptEngine scriptEngine) {
MiaoScriptAPI.scriptEngine = scriptEngine;
}
public static PluginManager getPluginManager() {
return pluginManager;
}
public static void setPluginManager(Object pluginManager) {
MiaoScriptAPI.pluginManager = getEngine().getInterface(pluginManager, PluginManager.class);
}
public static File[] loadMavenDepend(String groupId, String artifactId, String version) {
if (root == null || scriptEngine == null) {
throw new IllegalStateException("root can't be null before loadMavenDepend.");
}
return MavenDependLoader.load(MiaoScriptAPI.libPath, groupId, artifactId, version);
}
}

View File

@ -1,4 +1,4 @@
package pw.yumc.MiaoScript;
package pw.yumc.MiaoScript.api;
import javax.script.Bindings;
import javax.script.ScriptEngine;

View File

@ -1,7 +1,7 @@
package pw.yumc.MiaoScript;
package pw.yumc.MiaoScript.api;
import lombok.SneakyThrows;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
import pw.yumc.MiaoScript.engine.MiaoScriptEngine;
import java.nio.file.Files;
import java.nio.file.Path;
@ -25,6 +25,7 @@ public class ScriptEngine {
this.root = root;
this.logger = logger;
this.base = new Base(instance);
MiaoScriptAPI.setRoot(root);
MiaoScriptAPI.setEngine(this);
}

View File

@ -7,12 +7,12 @@ import org.bukkit.event.HandlerList;
import javax.script.Bindings;
public class ScriptEvent extends Event implements Cancellable {
private final String plugin;
private final Bindings plugin;
private final String event;
private final Bindings data;
private boolean cancelled = false;
public ScriptEvent(String plugin, String event, Bindings data) {
public ScriptEvent(Bindings plugin, String event, Bindings data) {
this.plugin = plugin;
this.event = event;
this.data = data;
@ -23,7 +23,7 @@ public class ScriptEvent extends Event implements Cancellable {
*
* @return PluginName
*/
public String getPlugin() {
public Bindings getPlugin() {
return plugin;
}

View File

@ -0,0 +1,48 @@
package pw.yumc.MiaoScript.api.loader;
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 Object ucp;
private static MethodHandle addURLMethodHandle;
static {
initReflect();
}
@SneakyThrows
public static File load(File file) {
addURLMethodHandle.invoke(ucp, file.toURI().toURL());
return file;
}
private static 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);
}
}
}

View File

@ -0,0 +1,80 @@
package pw.yumc.MiaoScript.api.loader;
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 {
private 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"))
};
}
@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(30000);
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();
}
}

View File

@ -0,0 +1,10 @@
package pw.yumc.MiaoScript.api.plugin;
import javax.script.Bindings;
import java.util.Map;
public interface PluginManager {
Map<String, Bindings> getPlugins();
Bindings getPlugin(String name);
}

View File

@ -1,19 +1,15 @@
package pw.yumc.MiaoScript;
package pw.yumc.MiaoScript.engine;
import lombok.SneakyThrows;
import lombok.val;
import pw.yumc.MiaoScript.api.loader.JarLoader;
import pw.yumc.MiaoScript.api.loader.MavenDependLoader;
import javax.script.ScriptEngine;
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;
/**
@ -23,12 +19,9 @@ 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 {
@ -88,71 +81,26 @@ public class MiaoScriptEngine implements ScriptEngine, Invocable {
for (String dir : dirs) {
File nashorn = new File(dir, "nashorn.jar");
if (nashorn.exists()) {
this.loadJar(nashorn);
JarLoader.load(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");
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");
MavenDependLoader.load(libRoot, "org.openjdk.nashorn", "nashorn-core", "15.3");
MavenDependLoader.load(libRoot, "org.ow2.asm", "asm", "9.2");
MavenDependLoader.load(libRoot, "org.ow2.asm", "asm-commons", "9.2");
MavenDependLoader.load(libRoot, "org.ow2.asm", "asm-tree", "9.2");
MavenDependLoader.load(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);
Method getScriptEngine = NashornScriptEngineFactory.getMethod("getScriptEngine");
Object factory = NashornScriptEngineFactory.newInstance();
engine = (ScriptEngine) getScriptEngine.invoke(factory, ClassLoader.getSystemClassLoader());
}
@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);
engine = (ScriptEngine) getScriptEngine.invoke(factory);
}
private void createEngineByName(String engineType) {

View File

@ -16,7 +16,7 @@ var global = this;
global.logger = logger
// Development Env Detect
global.root = root || "src/main/resources"
checkDebug()
readEnvironment()
if (!global.debug) {
checkUpgrade()
}
@ -50,24 +50,24 @@ var global = this;
global.engineDisableImpl && global.engineDisableImpl()
}
function checkDebug() {
function readEnvironment() {
if (__FILE__.indexOf('!') === -1) {
logger.info('Loading custom BIOS file ' + __FILE__)
logger.info('loading custom BIOS file ' + __FILE__)
global.debug = true
}
if (Files.exists(Paths.get(root, "debug"))) {
logger.info('Running in debug mode...')
logger.info('running in debug mode...')
global.debug = true
}
if (Files.exists(Paths.get(root, "level"))) {
global.level = base.read(Paths.get(root, "level"))
logger.info('Set system level to [' + global.level + ']...')
logger.info('set system level to [' + global.level + ']...')
}
}
function checkUpgrade() {
if (Files.exists(Paths.get(root, "upgrade"))) {
logger.info('Found upgrade file starting upgrade...')
logger.info('found upgrade file starting upgrade...')
base.move(Paths.get(root, "node_modules"), Paths.get(root, "old_node_modules"))
base.delete(Paths.get(root, "upgrade"))
}

View File

@ -2,12 +2,30 @@
// @ts-check
(
/**
* @param {{ info: (arg0: string) => void; }} logger
* @param {{
* info: (arg0: string) => void;
* warn: (arg0: string) => void;
* debug: (arg0: string) => void;
* error: (arg0: string) => void;
* warning: (arg0: string) => void;
* }} logger
*/
function (logger) {
function log() {
logger.info(Array.prototype.join.call(arguments, ' '))
}
function warn() {
logger.warn(Array.prototype.join.call(arguments, ' '))
}
function debug() {
logger.debug(Array.prototype.join.call(arguments, ' '))
}
function error() {
logger.error(Array.prototype.join.call(arguments, ' '))
}
function warning() {
logger.warning(Array.prototype.join.call(arguments, ' '))
}
/**
* @param {string} prefix
*/
@ -16,13 +34,23 @@
log('[' + prefix + ']', Array.prototype.join.call(arguments, ' '))
}
}
return {
var logProxy = {
log: log,
info: log,
ex: log,
trace: global.level === "trace" ? _proxy('TRACE') : global.noop,
debug: global.debug ? _proxy('DEBUG') : global.noop,
debug: global.debug ? logger.debug ? debug : _proxy('DEBUG') : global.noop,
warn: _proxy('WARN'),
error: _proxy('ERROR')
}
if (logger.warn) {
logProxy.warn = warn
}
if (logger.warning) {
logProxy.warn = warning
}
if (logger.error) {
logProxy.error = error
}
return logProxy
})

View File

@ -217,7 +217,7 @@
var filename = file.name
var lastDotIndexOf = filename.lastIndexOf('.')
if (lastDotIndexOf == -1) {
throw Error('require module must include file ext.')
throw Error('require ' + file + ' error: module must include file ext.')
}
var name = filename.substring(0, lastDotIndexOf)
var ext = filename.substring(lastDotIndexOf + 1)
@ -268,8 +268,9 @@
}
// 2019-09-19 使用 扩展函数直接 load 无需保存/删除文件
// 2020-02-16 结尾新增换行 防止有注释导致加载失败
var wrapperScript = '(function (module, exports, require, __dirname, __filename) {' + script + '\n});'
var compiledWrapper = engineLoad({
script: '(function (module, exports, require, __dirname, __filename) {' + script + '\n});',
script: wrapperScript,
name: optional.id
})
compiledWrapper.apply(module.exports, [
@ -306,19 +307,24 @@
var module_name = name.startsWith('@') ? name_arr[0] + '/' + name_arr[1] : name_arr[0]
var target = MS_NODE_PATH + separatorChar + module_name
var _package = new File(target, 'package.json')
if (_package.exists()) {
return
}
if (_package.exists()) { return name }
// at windows need replace file name java.lang.IllegalArgumentException: Invalid prefix or suffix
var info = fetchPackageInfo(module_name)
var url = info.versions[ModulesVersionLock[module_name] || info['dist-tags']['latest']].dist.tarball
console.log('fetch node_module ' + module_name + ' from ' + url + ' waiting...')
var latest_version = info['dist-tags']['latest']
var version = ModulesVersionLock[module_name] || latest_version
var _version = info.versions[version] || info.versions[latest_version]
var url = _version.dist.tarball
console.log('fetch node_module ' + module_name + ' version ' + version + ' waiting...')
return executor.submit(new Callable(function () {
var tis = new TarInputStream(new BufferedInputStream(new GZIPInputStream(new URL(url).openStream())))
var entry
while ((entry = tis.getNextEntry()) != null) {
var targetPath = Paths.get(target + separatorChar + entry.getName().substring(8))
targetPath.toFile().getParentFile().mkdirs()
var parentFile = targetPath.toFile().getParentFile()
if (!parentFile.isDirectory()) {
parentFile.delete()
parentFile.mkdirs()
}
Files.copy(tis, targetPath, StandardCopyOption.REPLACE_EXISTING)
}
return name
@ -418,13 +424,8 @@
if (optional.local || optional.recursive || notFoundModules[name]) {
throw new Error("Can't found module " + name + '(' + JSON.stringify(optional) + ') at local ' + path + ' or network!')
}
try {
optional.recursive = true
return _require(download(name), path, optional)
} catch (ex) {
notFoundModules[name] = true
throw new Error("Can't found module " + name + ' in directory ' + path + ' ERROR: ' + ex)
}
}
setCacheModule(file, optional)
return _requireFile(file, optional)
@ -592,7 +593,8 @@
function initVersionLock() {
try {
ModulesVersionLock = JSON.parse(fetchContent('https://ms.yumc.pw/api/plugin/download/name/version_lock', 5))
var version_lock_url = 'https://ms.yumc.pw/api/plugin/download/name/version_lock' + (global.debug ? '-debug' : '')
ModulesVersionLock = JSON.parse(fetchContent(version_lock_url, 5))
try {
ModulesVersionLock = __assign(ModulesVersionLock, JSON.parse(base.read(localVersionLockFile)))
} catch (e) {

View File

@ -1,69 +0,0 @@
'use strict';
/**
* Hello Wrold 测试插件
*/
/*global Java, base, module, exports, require*/
var event = require('api/event');
var wrapper = require('api/wrapper');
var command = require('api/command');
var server = require('api/server');
var fs = require('fs');
var description = {
name: 'HelloWorld',
version: '1.0',
author: 'MiaoWoo',
commands: {
'hello': {
description: 'HelloWorld主命令'
}
}
};
function load() {
console.log('载入 Hello Wrold 测试插件!');
}
function enable() {
// noinspection JSUnusedLocalSymbols
command.on(this, 'hello', {
cmd: function(sender, command, args) {
engineLoad(fs.file(root, 'test.js'));
return true;
}
});
console.log('启用 Hello World 测试插件!');
switch (DetectServerType) {
case ServerType.Bukkit:
event.on(this, 'PlayerLoginEvent', function join(event) {
send(event, wrapper.player(event.player));
});
break;
case ServerType.Sponge:
event.on(this, 'ClientConnectionEvent.Join', function join(event) {
send(event, wrapper.player(event.targetEntity));
});
break;
}
}
function send(event, player) {
// noinspection JSUnresolvedVariable
console.debug('玩家', player.getName(), "触发事件", event.class.simpleName);
setTimeout(function() {
// noinspection JSUnresolvedVariable
player.sendMessage("§a欢迎来到 §bMiaoScript §a的世界! 当前在线: " + server.players().length)
}, 10);
}
function disable() {
console.log('卸载 Hello World 测试插件!');
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};

View File

@ -1,88 +0,0 @@
'use strict';
/*global Java, base, module, exports, require*/
var event = require('api/event');
var task = require('api/task');
var http = require('http');
var fs = require('fs');
var Keys;
var description = {
name: 'ItemTag',
version: '1.0',
author: 'MiaoWoo'
};
var itemConfig;
function load() {
var itemFile = self.file('item.yml');
task.async(function() {
if (!itemFile.exists()) {
fs.save(itemFile, http.get('https://data.yumc.pw/config/Item_zh_CN.yml'))
}
itemConfig = self.getConfig('item.yml')
})
}
function enable() {
switch (DetectServerType) {
case ServerType.Bukkit:
var i = require('api/item').create('STONE')
if (!i.setCustomName) { return }
event.on(self, 'ItemMergeEvent', function(event) {
bukkit(event.target, event.entity.itemStack.amount + event.target.itemStack.amount);
});
event.on(self, 'ItemSpawnEvent', function(event) {
if (event.entity.itemStack) {
bukkit(event.entity, event.entity.itemStack.amount);
}
});
break;
case ServerType.Sponge:
Keys = Java.type('org.spongepowered.api.data.key.Keys');
event.on(self, 'ItemMergeItemEvent', function(event) {
// Sponge 暂未实现当前事件
});
event.on(self, 'SpawnEntityEvent', function(event) {
event.entities.forEach(function(entity) {
if (entity.type.name === "item") sponge(entity);
})
});
break;
}
}
function bukkit(item, amount) {
item.setCustomName('§b' + getItemName(item.itemStack.type) + getItemCount(amount));
item.setCustomNameVisible(true);
}
function sponge(entity) {
var itemOptional = entity.get(Keys['REPRESENTED_ITEM']);
if (itemOptional.isPresent()) {
var item = itemOptional.get();
var itemName = '§b' + getItemName(item.type.name.split(':')[1]) + getItemCount(item.count);
entity.offer(Keys['DISPLAY_NAME'], org.spongepowered.api.text.Text.of(itemName));
entity.offer(Keys['CUSTOM_NAME_VISIBLE'], true);
}
}
function getItemName(name) {
return itemConfig[(name + '').toUpperCase()] || name;
}
function getItemCount(amount) {
return amount === 1 ? "" : "*" + amount;
}
function disable() {
console.log('卸载', description.name, '插件!');
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};

View File

@ -1,70 +0,0 @@
'use strict';
/**
* MiaoAuth简易登录系统
*/
/*global Java, base, module, exports, require*/
var event = require('api/event');
var wrapper = require('api/wrapper');
var command = require('api/command');
var description = {
name: 'MiaoAuth',
version: '1.0',
author: 'MiaoWoo',
commands: {
'l': {
description: 'MiaoAuth登录命令'
},
'r': {
description: 'MiaoAuth注册命令'
}
}
};
function load() {
console.log('载入 MiaoAuth 插件!');
}
function enable() {
command.on(this, 'l', {
cmd: function(sender, command, args) {
return true;
}
});
command.on(this, 'r', {
cmd: function(sender, command, args) {
return true;
}
});
console.log('启用 MiaoAuth 测试插件!');
switch (DetectServerType) {
case ServerType.Bukkit:
event.on(this, 'playerloginevent', function join(event) {
send(wrapper.player(event.player));
});
break;
case ServerType.Sponge:
event.on(this, 'clientconnectionevent.join', function join(event) {
send(wrapper.player(event.targetEntity));
});
break;
}
}
function send(player) {
setTimeout(function sendMessage() {
player.sendMessage('§a输入 /l <密码> 以登录!');
}, 1000);
}
function disable() {
console.log('卸载 MiaoAuth 测试插件!');
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};

View File

@ -1,261 +0,0 @@
'use strict';
/**
* MiaoChat 喵式聊天插件
*/
/*global Java, base, module, exports, require*/
var event = require('api/event');
var command = require('api/command');
var tellraw = require('tellraw');
var papi = require('papi');
var utils = require('utils');
var Player;
var description = {
name: 'MiaoChat',
version: '1.0',
author: 'MiaoWoo',
commands: {
'mchat': {
description: 'MiaoChat登录命令'
}
},
permissions: {
'MiaoChat.default': {
default: true,
description: '默认权限 赋予玩家'
},
'MiaoChat.admin': {
default: false,
description: '管理权限'
}
},
config: {
Version: "1.8.5",
BungeeCord: true,
Server: "生存服",
ChatFormats: {
"default": {
"index": 50,
"permission": "MiaoChat.default",
"range": 0,
"format": "[world][player]&7: ",
"item": true,
"itemformat": "&6[&b%s&6]&r"
},
"admin": {
"index": 49,
"permission": "MiaoChat.admin",
"format": "[admin][world][player][help]&7: ",
"range": 0,
"item": true,
"itemformat": "&6[&b%s&6]&r"
}
},
StyleFormats: {
"world": {
"text": "&6[&a%player_world%&6]",
"hover": [
"&6当前所在位置:",
"&6世界: &d%player_world%",
"&6坐标: &aX:%player_x% Y: %player_y% Z: %player_z%",
"",
"&c点击即可TP我!"
],
"click": {
"type": "COMMAND",
"command": "/tpa %player_name%"
}
},
"player": {
"text": "&b%player_name%",
"hover": [
"&6玩家名称: &b%player_name%",
"&6玩家等级: &a%player_level%",
"&6玩家血量: &c%player_health%",
"&6玩家饥饿: &d%player_food_level%",
"&6游戏模式: &4%player_gamemode%",
"",
"&c点击与我聊天"
],
"click": {
"type": "SUGGEST",
"command": "/tell %player_name%"
}
},
"admin": {
"text": "&6[&c管理员&6]"
},
"help": {
"text": "&4[求助]",
"hover": [
"点击求助OP"
],
"click": {
"type": "COMMAND",
"command": "管理员@%player_name% 我需要你的帮助!"
}
}
}
}
};
var chat_formats;
var style_formats;
function load() {
chat_formats = Object.values(self.config.ChatFormats);
chat_formats.sort(utils.compare('index'));
initFormat(chat_formats);
style_formats = self.config.StyleFormats;
}
// 用于匹配 '[xx]' 聊天格式
var FORMAT_PATTERN = /[\[]([^\[\]]+)[\]]/ig;
function initFormat(chat_formats) {
chat_formats.forEach(function(chat_format) {
var chat_format_str = chat_format.format;
var temp = [];
var r;
while (r = FORMAT_PATTERN.exec(chat_format_str)) {
temp.push(r[1]);
}
var format_list = [];
temp.forEach(function splitStyle(t) {
var arr = chat_format_str.split('[' + t + ']', 2);
if (arr[0]) {
format_list.push(arr[0]);
}
format_list.push(t);
chat_format_str = arr[1];
});
if (chat_format_str) {
format_list.push(chat_format_str);
}
chat_format.format_list = format_list;
})
}
function enable() {
registerCommand();
registerEvent();
}
function registerCommand() {
command.on(self, 'mchat', {
cmd: mainCommand
});
}
// noinspection JSUnusedLocalSymbols
function mainCommand(sender, command, args) {
return true;
}
function registerEvent() {
switch (DetectServerType) {
case ServerType.Bukkit:
event.on(self, 'AsyncPlayerChatEvent', handlerBukkitChat);
break;
case ServerType.Sponge:
Player = org.spongepowered.api.entity.living.player.Player;
event.on(self, 'MessageChannelEvent.Chat', handlerSpongeChat);
break;
}
}
function handlerBukkitChat(event) {
sendChat(event.player, event.message, function() {
event.setCancelled(true);
});
}
function handlerSpongeChat(event) {
var player = event.getCause().first(Player.class).orElse(null);
if (player == null) {
return;
}
var plain = event.getRawMessage().toPlain();
if (plain.startsWith(tellraw.duplicateChar)) {
return;
}
sendChat(player, plain, function() {
event.setMessageCancelled(true)
});
}
function sendChat(player, plain, callback) {
var chat_format = getChatFormat(player);
if (!chat_format) {
console.debug('未获得用户', player.name, '的 ChatRule 跳过执行...');
return;
}
callback();
var tr = tellraw.create();
chat_format.format_list.forEach(function setStyle(format) {
var style = style_formats[format];
if (style) {
tr.then(replace(player, style.text));
if (style.hover) {
tr.tip(replace(player, style.hover));
}
if (style.click && style.click.type && style.click.command) {
var command = replace(player, style.click.command);
switch (style.click.type) {
case "COMMAND":
tr.command(command);
break;
case "OPENURL":
tr.link(command);
break;
case "SUGGEST":
tr.suggest(command);
break;
default:
}
}
} else {
tr.then(replace(player, format));
}
});
tr.then(replace(player, plain)).sendAll();
}
function getChatFormat(player) {
for (var i in chat_formats) {
var format = chat_formats[i];
if (player.hasPermission(format.permission)) {
return format;
}
}
return null;
}
function replace(player, target) {
if (toString.call(target) === "[object Array]") {
for (var i in target) {
target[i] = replaceStr(player, target[i]);
}
} else {
target = replaceStr(player, target);
}
return target;
}
function replaceStr(player, target) {
return papi.$(player, target);
}
function disable() {
console.log('卸载', description.name, '插件!');
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};

View File

@ -1,231 +0,0 @@
'use strict';
/**
* Hello Wrold 测试插件
*/
/*global Java, base, module, exports, require*/
var event = require('api/event');
var command = require('api/command');
var bukkit = require('api/server');
var item = require('api/item');
var Arrays = Java.type('java.util.Arrays');
var ItemStackArray = Java.type('org.bukkit.inventory.ItemStack[]');
var PANE = 'STAINED_GLASS_PANE'
var description = {
name: 'Lottery',
version: '1.0',
author: 'MiaoWoo',
commands: {
'lottery': {
description: 'Lottery主命令'
}
},
config: {
title: '§m§s§a幸运抽奖',
control: {
panel: PANE + ':13',
ok: PANE + ':14',
no: PANE + ':15',
},
list: [
{
box: {
id: PANE,
damage: 1,
name: '§a箱子',
lore: [
'这是箱子的Lore'
]
},
key: {
id: PANE,
damage: 2,
name: '§b钥匙',
lore: [
'这是钥匙的Lore'
]
},
result: [
{
percent: 10,
command: 'money give %player% 100',
item: {
id: PANE,
damage: 3,
name: '§c奖品1',
lore: [
'这是奖品1的Lore'
]
}
},
{
percent: 20,
command: 'money give %player% 200',
item: {
id: PANE,
damage: 4,
name: '§c奖品2',
lore: [
'这是奖品2的Lore'
]
}
}
]
}
]
}
};
var panel;
var config;
var items;
function load() {
config = this.config;
panel = newItemFromString(config.control.panel || PANE + ':13')
items = new ItemStackArray(54);
item.setName(panel, '');
var ok = newItemFromString(config.control.ok || PANE + ':14')
item.setName(ok, '§a确定抽奖');
var no = newItemFromString(config.control.no || PANE + ':15')
item.setName(no, '§c取消抽奖');
Arrays.fill(items, 0, 10, panel);
Arrays.fill(items, 11, 16, panel);
Arrays.fill(items, 17, 29, panel);
items[29] = no;
Arrays.fill(items, 30, 33, panel);
items[33] = ok;
Arrays.fill(items, 34, 40, panel);
Arrays.fill(items, 41, 54, panel);
}
function isTargetItem(item, config) {
return item.typeId === config.id &&
item.itemMeta &&
item.itemMeta.displayName === config.name &&
item.itemMeta.lore && Java.from(item.itemMeta.lore).toJson() === config.lore.toJson()
}
function newItem(name, sub) {
return item.create(name, 1, sub || 0);
}
function newItemFromString(str) {
var arr = str.split(':');
if (arr.length === 2) {
return newItem(arr[0], arr[1]);
} else {
return newItem(arr[0]);
}
}
function newItemFromConfig(config) {
var i = newItem(config.id, config.damage);
if (config.name) item.setName(i, config.name);
if (config.lore) item.setLore(i, config.lore);
return i;
}
function enable() {
// noinspection JSUnusedLocalSymbols
command.on(this, 'l', {
cmd: function(sender, command, args) {
sender.inventory.addItem(newItemFromConfig(config.list[0].box))
sender.inventory.addItem(newItemFromConfig(config.list[0].key))
if (!sender.openInventory) {
console.sender(sender, "§4当前用户无法使用该命令!");
}
var inv = MServer.createInventory(null, 54, config.title);
inv.setContents(items);
sender.openInventory(inv);
return true;
}
});
event.on(this, 'InventoryClick', function click(event) {
var inv = event.inventory;
if (inv && inv.title !== config.title) return;
var player = event.whoClicked;
var slot = event.rawSlot;
if (slot > 53 || slot < 0) {
return;
}
event.cancelled = true;
switch (slot) {
case 10:
case 16:
case 40:
event.cancelled = false;
break;
case 29:
// TODO 关闭界面
player.closeInventory();
break;
case 33:
var temp = inv.getItem(40);
if (temp && temp.typeId !== 0) {
console.sender(player, '§c请先取走奖品!');
return;
}
var litem;
var box = inv.getItem(10);
if (!box) {
console.sender(player, '§c请先放入抽奖物品和钥匙!');
return;
}
var key = inv.getItem(16);
if (box && box.typeId !== 0 && key && key.typeId !== 0) {
for (var i = 0; i < config.list.length; i++) {
var r = config.list[i];
if (isTargetItem(box, r.box)) {
litem = r;
break;
}
}
}
// TODO 抽奖
if (!litem) {
console.sender(player, '§c请先放入抽奖物品和钥匙!');
return;
}
if (!isTargetItem(key, litem.key)) {
console.sender(player, '§c抽奖物品和钥匙不匹配!');
return;
}
var resultList = [];
litem.result.forEach(function(t) {
for (var i = 0; i < t.percent; i++) {
resultList.push(t);
}
});
var ri = random(resultList.length);
var result = resultList[ri];
box.amount = box.amount - 1;
key.amount = key.amount - 1;
inv.setItem(10, box);
inv.setItem(16, key);
inv.setItem(40, newItemFromConfig(result.item));
bukkit.console(result.command.replace('%player%', player.name));
break;
default:
event.cancelled = true;
}
});
}
function random(max, min) {
min = min === undefined ? 0 : min;
return Math.floor(Math.random() * (max - min) + min);
};
function disable() {
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};

View File

@ -1,188 +0,0 @@
'use strict';
/**
* MiaoTag
* 可兼容任何记分板
*/
/*global Java, base, module, exports, require*/
var event = require('api/event');
var bukkit = require('api/server');
var command = require('api/command');
var fakeTag;
var description = {
name: 'MiaoTag',
version: '1.1',
author: 'MiaoWoo',
config: {
format: '§4§l❤'
},
commands: {
'mtag': {
description: 'MiaoTag主命令',
usage: '',
permission: 'MiaoTag.admin'
}
},
permissions: {
'MiaoTag.default': {
default: true,
description: '默认权限 赋予玩家'
},
'MiaoTag.admin': {
default: false,
description: '管理权限'
}
}
};
var config;
function load() {
config = self.getConfig();
}
function enable() {
registryCommand()
fakeTag = new FakeTag(config.format);
registryEvent()
}
function registryCommand() {
command.on(self, 'mtag', {
cmd: function cmd(sender, command, args) {
var subCommand = args[0];
switch (subCommand) {
case 'reload':
self.reloadConfig();
fakeTag = new FakeTag(config.format);
console.sender(sender, "§a配置文件重载完成!");
break;
}
},
tab: function tab(sender, command, args) {
return ['reload'];
}
});
}
function registryEvent() {
bukkit.players(function(p) { fakeTag.set(p) });
event.on(self, 'PlayerJoin', function(event) { fakeTag.set(event.player) });
event.on(self, 'EntityRegainHealth', entityUpdate, false);
event.on(self, 'EntityDamage', entityUpdate, false);
event.on(self, 'EntityRegainHealth', entityUpdate, false);
event.on(self, 'PlayerRespawn', entityUpdate, false);
}
function entityUpdate(event) {
var player = event.entity || event.player;
if (player instanceof org.bukkit.entity.Player) {
setTimeout(function() {
fakeTag.update(player);
}, 1);
}
};
function disable() {
if (fakeTag) { fakeTag.disable() };
}
function FakeTag(name) {
var ver1_13 = false;
// NMS CLASS
try {
var ScoreboardBaseCriteria = bukkit.nmsCls('ScoreboardBaseCriteria');
} catch (ex) {
try {
var IScoreboardCriteria = bukkit.nmsCls('IScoreboardCriteria');
var ScoreboardServer = bukkit.nmsCls("ScoreboardServer");
var ChatComponentText = bukkit.nmsCls('ChatComponentText');
ver1_13 = true;
} catch (ex) {
console.log(ex);
throw ex;
}
}
var PacketPlayOutScoreboardScore = bukkit.nmsCls('PacketPlayOutScoreboardScore');
var PacketPlayOutScoreboardObjective = bukkit.nmsCls('PacketPlayOutScoreboardObjective');
var PacketPlayOutScoreboardDisplayObjective = bukkit.nmsCls('PacketPlayOutScoreboardDisplayObjective');
var scoreboardManager = bukkit.$.scoreboardManager;
var mainScoreboard = scoreboardManager.mainScoreboard.handle;
// 注销对象
var objective = mainScoreboard.getObjective(name);
if (objective) {
mainScoreboard.unregisterObjective(objective);
}
try {
if (!ver1_13) {
// 注册tag对象
objective = mainScoreboard.registerObjective(name, new ScoreboardBaseCriteria(name));
} else {
// 注册tag对象
objective = mainScoreboard.registerObjective(name,
IScoreboardCriteria.HEALTH,
new ChatComponentText(name),
IScoreboardCriteria.EnumScoreboardHealthDisplay.HEARTS);
}
} catch (ex) {
throw ex
// ignore 忽略创建错误 eg: java.lang.IllegalArgumentException: An objective with the name 'xxxxx' already exists!
}
if (!objective) {
throw Error("Error Can't Found MainScoreboard Objective " + name)
}
// 缓存虚拟的tag包
var cache = {
objective: new PacketPlayOutScoreboardObjective(objective, 0),
display: new PacketPlayOutScoreboardDisplayObjective(2, objective)
};
function sendPacket(player, p) {
player.handle.playerConnection.sendPacket(p);
}
this.set = function(player) {
sendPacket(player, cache.objective);
sendPacket(player, cache.display);
this.update(player);
};
function createScore(player) {
if (!ver1_13) {
var score = mainScoreboard.getPlayerScoreForObjective(player.name, objective);
score.setScore(player.health);
return new PacketPlayOutScoreboardScore(score);
} else {
return new PacketPlayOutScoreboardScore(ScoreboardServer.Action.CHANGE, name, player.name, player.health)
}
}
this.update = function update(player) {
var scorePack = createScore(player);
//把其他玩家缓存的包发给这个玩家
bukkit.players(function(t) {
sendPacket(t, scorePack);
if (t.name !== player.name) {
sendPacket(player, createScore(t));
}
});
};
this.disable = function() {
// 注销tag对象
mainScoreboard.unregisterObjective(objective);
}
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};

View File

@ -1,48 +0,0 @@
'use strict';
/**
* WorldEdit 插件
*/
/*global Java, base, module, exports, require*/
var item = require('/api/item');
var command = require('api/command');
var Material = Java.type("org.bukkit.Material");
var description = {
name: 'WorldEdit',
version: '1.0',
author: 'MiaoWoo',
commands: {
'/up': {
description: 'Up Player And Set Block'
}
}
};
function load() {
}
function enable() {
command.on(this, '/up', {
cmd: function(sender, command, args) {
if (!sender.openInventory) {
return;
}
var player = sender;
var location = player.location;
var type = item.type(args[0], 'STONE');
player.velocity = player.velocity.setY(0.5);
setTimeout(function() {
location.block.type = type
}, 6);
return true;
},
tab: function(sender, command, args) {
}
});
}
module.exports = {
description: description,
enable: enable
};

View File

@ -1,32 +0,0 @@
## MiaoChat for MiaoScript
![](https://dn-coding-net-production-pp.qbox.me/f459067b-7829-45ec-9713-bb559d1e0118.png)
> 注意: MiaoScript 在Windows环境下 暂不支持 reload 所以 Windows 环境请重启服务器
> 注意: MiaoScript 将不会计划兼容 非 `Sponge` 的MOD端
- 基于 `MiaoScript` 开发 同时兼容 `Sponge``Bukkit`
- 支持PAPI
- `Bukkit``me.clip.placeholderapi.PlaceholderAPI`
- `Sponge``me.rojo8399.placeholderapi.PlaceholderService`
- 支持悬浮提示
- 支持点击执行命令
- 支持点击命令补全
### Feature(开发规划)
- 悬浮物品提示
- 兼容 `BungeeCord` 支持跨服聊天
- 兼容其他聊天插件 保护插件
### 安装教程
- 下载 MiaoChat 本体并安装
- [下载地址-论坛](http://www.mcbbs.net/thread-774401-1-1.html)
- [下载地址-备用](https://git.yumc.pw/502647092/MiaoScript/releases)
- 安装到服务端对应的目录
- Bukkit => plugins
- Sponge => mods
- 重启服务器
- 下载 安装 `MiaoScriptPackageManager`
- [安装教程](http://www.mcbbs.net/thread-774797-1-1.html)
- 使用 `MiaoScriptPackageManager` 安装插件
- 执行 `mpm install MiaoChat` 即可安装成功

View File

@ -1,220 +0,0 @@
'use strict';
/**
* MiaoBoard 喵式聊天插件
*/
/*global Java, base, module, exports, require*/
var task = require('api/task');
var event = require('api/event');
var server = require('api/server');
var command = require('api/command');
var papi = require('papi');
var Player;
var boards = [];
var description = {
name: 'MiaoBoard',
version: '1.0',
author: 'MiaoWoo',
commands: {
'mboard': {
description: '喵式记分板主命令'
}
},
permissions: {
'mb.default': {
default: true,
description: '默认权限 赋予玩家'
},
'mb.admin': {
default: false,
description: '管理权限'
}
},
config: {
"Version": 2,
"UpdateTime": 10,
"DisableWorld": [
"WorldName"
],
"Boards": {
"default": {
"index": 50,
"time": {
"start": "2016-01-01 00:00:00",
"end": "2020-01-01 00:00:00"
},
"title": "玩家信息",
"permission": "mb.default",
"lines": [
"&6名 称: &a%player_displayname%",
"&6世 界: &b%player_world%",
"&6位 置: &3%player_x%,%player_y%,%player_z%",
"&6等 级: &e%player_level%",
"&6血 量: &c%player_health%",
"&6模 式: &4%player_gamemode%"
]
},
"admin": {
"index": 49,
"title": "服务器信息",
"permission": "mb.reload",
"lines": [
"&6名 称: &aMiaoBoard",
"&6版 本: &b1.0.0",
"&6作 者: &cMiaoWoo",
"&6人 数: &c%server_online%/%server_max%",
"&6内 存: &a%server_ram_used%/%server_ram_total%/%server_ram_max%"
]
}
}
}
}
var update_task;
var board_formats;
function load() {
board_formats = self.config.Boards;
}
function enable() {
registerCommand();
registerEvent();
registerTask();
updatePlayers()
}
function registerCommand() {
command.on(self, 'mboard', {
cmd: mainCommand
});
}
function mainCommand(sender, command, args) {
if (!args[0]) {
return;
}
switch (args[0]) {
case "reload":
self.reloadConfig();
load();
break;
}
}
function registerEvent() {
switch (DetectServerType) {
case ServerType.Bukkit:
//event.on(self, 'PlayerLoginEvent', handlerPlayerJoin);
break;
case ServerType.Sponge:
Player = org.spongepowered.api.entity.living.player.Player;
event.on(self, 'ClientConnectionEvent.Join', handlerPlayerJoin);
event.on(self, 'ClientConnectionEvent.Disconnect', handlerPlayerQuit);
break;
}
}
function updatePlayers() {
switch (DetectServerType) {
case ServerType.Bukkit:
//event.on(self, 'PlayerLoginEvent', handlerPlayerJoin);
break;
case ServerType.Sponge:
server.players(function(player) {
boards[player.name] = new MiaoBoard(player);
})
break;
}
}
function handlerPlayerJoin(event) {
var player = event.player || event.targetEntity;
boards[player.name] = new MiaoBoard(player);
}
function handlerPlayerQuit(event) {
var player = event.player || event.targetEntity;
delete boards[player.name];
}
function registerTask() {
update_task = task.timerAsync(updateBoard, self.config.UpdateTime);
}
function updateBoard() {
for (var i in boards) {
var player = server.player(i);
if (player.isOnline()) {
var format = getBoardFormat(player);
if (format) {
boards[i].update(papi.$(player, format.title), papi.$(player, format.lines));
} else {
boards[i].clear();
}
} else {
delete boards[i];
}
}
}
function getBoardFormat(player) {
for (var i in board_formats) {
var format = board_formats[i];
if (player.hasPermission(format.permission)) {
return format;
}
}
return null;
}
function disable() {
if (update_task) { update_task.cancel(); }
}
function MiaoBoard(player) {
var Scoreboard = Java.type('org.spongepowered.api.scoreboard.Scoreboard');
var Objective = Java.type('org.spongepowered.api.scoreboard.objective.Objective');
var Criteria = Java.type('org.spongepowered.api.scoreboard.critieria.Criteria');
var Text = Java.type('org.spongepowered.api.text.Text');
var DisplaySlots = Java.type('org.spongepowered.api.scoreboard.displayslot.DisplaySlots');
var uuid = player.uniqueId;
var scoreboard = Scoreboard.builder().build();
var sidebar = Objective.builder().criterion(Criteria.DUMMY).displayName(Text.EMPTY).name("Sidebar").build();
var origin = [];
scoreboard.addObjective(sidebar);
player.setScoreboard(scoreboard);
this.update = function(title, lines) {
this.updateBuffer(title, lines);
}
this.updateBuffer = function(title, lines) {
sidebar.scores.values().forEach(function removeScore(score) {
sidebar.removeScore(score);
})
var i = 0;
sidebar.setDisplayName(Text.of(title));
lines.forEach(function addScore(line) {
sidebar.getOrCreateScore(Text.of(line)).setScore(lines.length - i++);
})
scoreboard.updateDisplaySlot(sidebar, DisplaySlots.SIDEBAR);
}
this.clear = function() {
player.setScoreboard(Scoreboard.builder().build());
}
}
module.exports = {
description: description,
load: load,
enable: enable,
disable: disable
};