feat: 独立Bukkit插件部分 完善console输出 完善format格式化
This commit is contained in:
parent
e387863061
commit
c7293370ea
@ -29,7 +29,6 @@ public class MiaoScript extends JavaPlugin implements Executor {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
new CommandSub("ms", this);
|
new CommandSub("ms", this);
|
||||||
saveScript();
|
|
||||||
engine = new ScriptEngine(getDataFolder().getCanonicalPath(), getClassLoader(), getLogger());
|
engine = new ScriptEngine(getDataFolder().getCanonicalPath(), getClassLoader(), getLogger());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,11 +72,6 @@ public class MiaoScript extends JavaPlugin implements Executor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveScript() {
|
|
||||||
P.saveFile(true, "core", "modules");
|
|
||||||
P.saveFile("plugins");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
engine.disableEngine();
|
engine.disableEngine();
|
||||||
|
@ -17,14 +17,17 @@ var disable;
|
|||||||
try {
|
try {
|
||||||
init(root);
|
init(root);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
if(console.ex){
|
||||||
|
console.ex(ex);
|
||||||
|
}else{
|
||||||
throw ex;
|
throw ex;
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
disable = disablePlugins
|
disable = disablePlugins
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function release(root, regex, replace) {
|
function release(root, regex, replace) {
|
||||||
print(Array.prototype.join.call(arguments, ' '));
|
|
||||||
var Files = Java.type("java.nio.file.Files");
|
var Files = Java.type("java.nio.file.Files");
|
||||||
var Paths = Java.type("java.nio.file.Paths");
|
var Paths = Java.type("java.nio.file.Paths");
|
||||||
var StandardCopyOption = Java.type("java.nio.file.StandardCopyOption");
|
var StandardCopyOption = Java.type("java.nio.file.StandardCopyOption");
|
||||||
@ -55,7 +58,6 @@ var disable;
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
this.log = function () {
|
this.log = function () {
|
||||||
log.info(this.name + Array.prototype.join.call(arguments, ' '));
|
log.info(this.name + Array.prototype.join.call(arguments, ' '));
|
||||||
};
|
};
|
||||||
|
this.info = this.log;
|
||||||
this.warn = function () {
|
this.warn = function () {
|
||||||
log.warning(this.name + Array.prototype.join.call(arguments, ' '));
|
log.warning(this.name + Array.prototype.join.call(arguments, ' '));
|
||||||
};
|
};
|
||||||
@ -71,9 +72,9 @@
|
|||||||
}
|
}
|
||||||
track.forEach(function (stack) {
|
track.forEach(function (stack) {
|
||||||
if (stack.className.startsWith('<')) {
|
if (stack.className.startsWith('<')) {
|
||||||
this.console(String.format(' §e位于 §c%s => §c%s §4行%s', stack.fileName, stack.methodName, stack.lineNumber));
|
this.console(' §e位于 §c%s => §c%s §4行%s'.format(stack.fileName, stack.methodName, stack.lineNumber));
|
||||||
} else {
|
} else {
|
||||||
this.console(String.format(' §e位于 §c%s.%s(§4%s:%s§c)', stack.className, stack.methodName, stack.fileName, stack.lineNumber));
|
this.console(' §e位于 §c%s.%s(§4%s:%s§c)'.format(stack.className, stack.methodName, stack.fileName, stack.lineNumber));
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ exports.concat = function () {
|
|||||||
*/
|
*/
|
||||||
exports.file = function () {
|
exports.file = function () {
|
||||||
if (!arguments[0]) {
|
if (!arguments[0]) {
|
||||||
log.w("文件名称不得为 undefined 或者 null !");
|
console.warn("文件名称不得为 undefined 或者 null !");
|
||||||
}
|
}
|
||||||
switch (arguments.length) {
|
switch (arguments.length) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -77,7 +77,7 @@ exports.copy = function (inputStream, target, override) {
|
|||||||
exports.read = function (file) {
|
exports.read = function (file) {
|
||||||
file = exports.file(file);
|
file = exports.file(file);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
log.w("读取文件 %s 错误 文件不存在!", file);
|
console.warn("读取文件 %s 错误 文件不存在!".format(file));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// noinspection JSPrimitiveTypeWrapperUsage
|
// noinspection JSPrimitiveTypeWrapperUsage
|
||||||
@ -103,7 +103,7 @@ exports.list = function (path) {
|
|||||||
if (dir.isDirectory()) {
|
if (dir.isDirectory()) {
|
||||||
return Files.list(dir.toPath());
|
return Files.list(dir.toPath());
|
||||||
}
|
}
|
||||||
log.w("路径 %s 不是一个目录 返回空数组!", path);
|
console.warn("路径 %s 不是一个目录 返回空数组!".format(path));
|
||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,7 @@ function loadLib4Bukkit() {
|
|||||||
function loadPlugins() {
|
function loadPlugins() {
|
||||||
// 初始化本体插件
|
// 初始化本体插件
|
||||||
global.manager = require('modules/plugin');
|
global.manager = require('modules/plugin');
|
||||||
manager.init(base.plugin, 'plugins');
|
manager.init('plugins');
|
||||||
// 只有当在正式环境运行的时候才加载
|
// 只有当在正式环境运行的时候才加载
|
||||||
if (manager.$) {
|
if (manager.$) {
|
||||||
manager.load();
|
manager.load();
|
||||||
@ -77,7 +77,7 @@ function loadPlugins() {
|
|||||||
* 关闭插件Hook
|
* 关闭插件Hook
|
||||||
*/
|
*/
|
||||||
function disablePlugins() {
|
function disablePlugins() {
|
||||||
if (manager.$) {
|
if (manager && manager.$) {
|
||||||
manager.disable();
|
manager.disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,6 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function(){
|
(function(){
|
||||||
|
// Java格式化方法
|
||||||
|
var str = Java.type('java.lang.String');
|
||||||
|
String.prototype.format = function(){ return str.format(this, Array.prototype.slice.call(arguments, 0))}
|
||||||
|
|
||||||
// JSON快捷方法
|
// JSON快捷方法
|
||||||
Object.prototype.toJson = function(){ return JSON.stringify(this); }
|
Object.prototype.toJson = function(){ return JSON.stringify(this); }
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
function compileModule(id, name, file, optional) {
|
function compileModule(id, name, file, optional) {
|
||||||
console.debug('加载模块 %s 位于 %s Optional %s', name, id, optional.toJson());
|
console.debug('加载模块 %s 位于 %s Optional %s'.format(name, id, optional.toJson()));
|
||||||
// noinspection JSUnresolvedVariable
|
// noinspection JSUnresolvedVariable
|
||||||
var module = {
|
var module = {
|
||||||
id: id,
|
id: id,
|
||||||
@ -120,10 +120,10 @@
|
|||||||
compiledWrapper.apply(module.exports, [
|
compiledWrapper.apply(module.exports, [
|
||||||
module, module.exports, module.require, file.parentFile, file
|
module, module.exports, module.require, file.parentFile, file
|
||||||
]);
|
]);
|
||||||
console.debug('模块 %s 编译成功!', name);
|
console.debug('模块 %s 编译成功!'.format(name));
|
||||||
module.loaded = true;
|
module.loaded = true;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.console("§4警告! §b模块 §a%s §4编译失败! ERR: %s", name, ex);
|
console.console("§4警告! §c模块 §a%s §c编译失败! §4ERR: %s".format(name, ex));
|
||||||
console.ex(ex);
|
console.ex(ex);
|
||||||
}
|
}
|
||||||
return module;
|
return module;
|
||||||
@ -155,7 +155,7 @@
|
|||||||
function _require(name, path, optional) {
|
function _require(name, path, optional) {
|
||||||
var file = _canonical(name) ? name : resolve(name, path);
|
var file = _canonical(name) ? name : resolve(name, path);
|
||||||
if (file === undefined) {
|
if (file === undefined) {
|
||||||
console.console("§c模块 §a%s §c加载失败! §4未找到该模块!", name);
|
console.console("§c模块 §a%s §c加载失败! §4未找到该模块!".format(name));
|
||||||
return {exports: {}};
|
return {exports: {}};
|
||||||
}
|
}
|
||||||
if (!optional) optional = {cache: true};
|
if (!optional) optional = {cache: true};
|
||||||
@ -189,6 +189,6 @@
|
|||||||
parent = new File(parent);
|
parent = new File(parent);
|
||||||
}
|
}
|
||||||
var cacheModules = [];
|
var cacheModules = [];
|
||||||
console.debug("初始化 require 模块组件 父目录 %s", _canonical(parent));
|
console.debug("初始化 require 模块组件 父目录 ", _canonical(parent));
|
||||||
return exports(parent);
|
return exports(parent);
|
||||||
});
|
});
|
@ -12,7 +12,7 @@ var fs = require('fs');
|
|||||||
*/
|
*/
|
||||||
function unzip(zipFile, target) {
|
function unzip(zipFile, target) {
|
||||||
if (!zipFile.exists()) {
|
if (!zipFile.exists()) {
|
||||||
log.w("解压文件 %s 错误 文件不存在!", zipFile);
|
console.warn("解压文件 %s 错误 文件不存在!".format(zipFile));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (target === undefined) {
|
if (target === undefined) {
|
||||||
@ -20,7 +20,7 @@ function unzip(zipFile, target) {
|
|||||||
// noinspection JSUnresolvedVariable
|
// noinspection JSUnresolvedVariable
|
||||||
target = fs.file(zipFile.parentFile.canonicalPath, fname.substring(0, fname.length() - 4));
|
target = fs.file(zipFile.parentFile.canonicalPath, fname.substring(0, fname.length() - 4));
|
||||||
}
|
}
|
||||||
log.d("解压文件 %s 到目录 %s", zipFile.canonicalPath, target);
|
console.debug("解压文件 %s 到目录 %s".format(zipFile.canonicalPath, target));
|
||||||
var zipObj = new ZipFile(zipFile);
|
var zipObj = new ZipFile(zipFile);
|
||||||
var e = zipObj.entries();
|
var e = zipObj.entries();
|
||||||
while (e.hasMoreElements()) {
|
while (e.hasMoreElements()) {
|
||||||
|
93
src/main/resources/modules/bukkit/command.js
Normal file
93
src/main/resources/modules/bukkit/command.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
'use strict';
|
||||||
|
/**
|
||||||
|
* Bukkit 命令相关类
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global Java, base, module, exports, require, __FILE__*/
|
||||||
|
var ref = require('core/reflect');
|
||||||
|
var bukkit = require('./server');
|
||||||
|
var plugin = bukkit.plugin.self;
|
||||||
|
var commandMap = ref.on(bukkit.plugin.manager).get('commandMap').get();
|
||||||
|
var PluginCommand = Java.type('org.bukkit.command.PluginCommand');
|
||||||
|
|
||||||
|
var StringUtil = Java.type('org.bukkit.util.StringUtil');
|
||||||
|
|
||||||
|
var ArrayList = Java.type('java.util.ArrayList');
|
||||||
|
var Arrays = Java.type('java.util.Arrays');
|
||||||
|
|
||||||
|
function enable(jsp) {
|
||||||
|
var commands = jsp.description.commands;
|
||||||
|
if (commands) {
|
||||||
|
var pluginCmds = [];
|
||||||
|
for (var name in commands) {
|
||||||
|
var command = commands[name];
|
||||||
|
if (typeof command !== 'object') continue;
|
||||||
|
var newCmd = create(jsp, name);
|
||||||
|
if (command.description) newCmd.setDescription(command.description);
|
||||||
|
if (command.usage) newCmd.setUsage(command.usage);
|
||||||
|
if (command.aliases) newCmd.setAliases(Arrays.asList(command.aliases));
|
||||||
|
if (command.permission) newCmd.setPermission(command.permission);
|
||||||
|
if (command['permission-message']) newCmd.setPermissionMessage(command['permission-message']);
|
||||||
|
pluginCmds.push(newCmd);
|
||||||
|
console.debug('插件 %s 注册命令 %s ...'.format(jsp.description.name, name));
|
||||||
|
}
|
||||||
|
commandMap.registerAll(jsp.description.name, Arrays.asList(pluginCmds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(name) {
|
||||||
|
return commandMap.getCommand(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(jsp, name) {
|
||||||
|
return register(jsp, ref.on(PluginCommand).create(name, plugin).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
function register(jsp, cmd) {
|
||||||
|
commandMap.register(jsp.description.name, cmd);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// var exec = {
|
||||||
|
// cmd: function (sender, command, args) {
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// tab: function (sender, command, args) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
function on(jsp, name, exec) {
|
||||||
|
var c = get(name) || create(jsp, name);
|
||||||
|
console.debug('插件 %s 设置命令 %s(%s) 执行器 ...'.fotmat(jsp.description.name, name, c));
|
||||||
|
if (exec.cmd) {
|
||||||
|
c.setExecutor(function (sender, cmd, command, args) {
|
||||||
|
try {
|
||||||
|
return exec.cmd(sender, command, args);
|
||||||
|
} catch (ex) {
|
||||||
|
console.console('§6玩家 §a%s §6执行 §b%s §6插件 §d%s %s §6命令时发生异常 §4%s'.fotmat(sender.name, jsp.description.name, command, Java.from(args).join(' '), ex));
|
||||||
|
console.ex(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (exec.tab) {
|
||||||
|
c.setTabCompleter(function (sender, cmd, command, args) {
|
||||||
|
try {
|
||||||
|
var completions = new ArrayList();
|
||||||
|
var token = args[args.length - 1];
|
||||||
|
StringUtil.copyPartialMatches(token, Arrays.asList(exec.tab(sender, command, args)), completions);
|
||||||
|
return completions;
|
||||||
|
} catch (ex) {
|
||||||
|
console.console('§6玩家 §a%s §6执行 §b%s §6插件 §d%s %s §6补全时发生异常 §4%s'.fotmat(sender.name, jsp.description.name, command, Java.from(args).join(' '), ex));
|
||||||
|
console.ex(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.enable = enable;
|
||||||
|
|
||||||
|
exports.on = on;
|
||||||
|
exports.off = function () {
|
||||||
|
|
||||||
|
};
|
158
src/main/resources/modules/bukkit/event.js
Normal file
158
src/main/resources/modules/bukkit/event.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
'use strict';
|
||||||
|
/**
|
||||||
|
* Bukkit 事件相关类
|
||||||
|
*/
|
||||||
|
/*global Java, base, module, exports, require, __FILE__*/
|
||||||
|
var Thread = Java.type("java.lang.Thread");
|
||||||
|
var Bukkit = Java.type("org.bukkit.Bukkit");
|
||||||
|
var Listener = Java.type("org.bukkit.event.Listener");
|
||||||
|
var Modifier = Java.type("java.lang.reflect.Modifier");
|
||||||
|
var BukkitEvent = Java.type("org.bukkit.event.Event");
|
||||||
|
var HandlerList = Java.type('org.bukkit.event.HandlerList');
|
||||||
|
var EventPriority = Java.type("org.bukkit.event.EventPriority");
|
||||||
|
var EventExecutor = Java.type("org.bukkit.plugin.EventExecutor");
|
||||||
|
var IllegalStateException = Java.type("java.lang.IllegalStateException");
|
||||||
|
|
||||||
|
var plugin = require('./server').plugin.self;
|
||||||
|
|
||||||
|
var ref = require('reflect');
|
||||||
|
|
||||||
|
var listenerMap = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描包 org.bukkit.event 下的所有事件
|
||||||
|
* 映射简写名称 org.bukkit.event.player.PlayerLoginEvent => playerloginevent
|
||||||
|
*/
|
||||||
|
function mapEventName() {
|
||||||
|
var eventPackageDir = "org/bukkit/event";
|
||||||
|
var count = 0;
|
||||||
|
var dirs = Thread.currentThread().getContextClassLoader().getResources(eventPackageDir);
|
||||||
|
while (dirs.hasMoreElements()) {
|
||||||
|
var url = dirs.nextElement();
|
||||||
|
var protocol = url.protocol;
|
||||||
|
if (protocol === "jar") {
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
var jar = url.openConnection().jarFile;
|
||||||
|
var entries = jar.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
var entry = entries.nextElement();
|
||||||
|
var name = entry.name;
|
||||||
|
// 以 org/bukkit/event 开头 并且以 .class 结尾
|
||||||
|
if (name.startsWith(eventPackageDir) && name.endsWith(".class")) {
|
||||||
|
var i = name.replaceAll('/', '.');
|
||||||
|
try {
|
||||||
|
var clz = base.getClass(i.substring(0, i.length - 6));
|
||||||
|
// 继承于 org.bukkit.event.Event 访问符为Public
|
||||||
|
if (isVaildEvent(clz)) {
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
var simpleName = clz.simpleName.toLowerCase();
|
||||||
|
console.debug("Mapping Event [%s] => %s".format(clz.name, simpleName));
|
||||||
|
mapEvent[simpleName] = clz;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
//ignore already loaded class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为一个有效的事件类
|
||||||
|
* @param clz
|
||||||
|
* @returns {*|boolean}
|
||||||
|
*/
|
||||||
|
function isVaildEvent(clz) {
|
||||||
|
// noinspection JSUnresolvedVariable 继承于 org.bukkit.event.Event
|
||||||
|
return BukkitEvent.class.isAssignableFrom(clz) &&
|
||||||
|
// 访问符为Public
|
||||||
|
Modifier.isPublic(clz.getModifiers()) &&
|
||||||
|
// 不是抽象类
|
||||||
|
!Modifier.isAbstract(clz.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加事件监听
|
||||||
|
* @param jsp
|
||||||
|
* @param event
|
||||||
|
* @param exec {function}
|
||||||
|
* @param priority
|
||||||
|
* @param ignoreCancel
|
||||||
|
*/
|
||||||
|
function listen(jsp, event, exec, priority, ignoreCancel) {
|
||||||
|
var name = jsp.description.name;
|
||||||
|
if (ext.isNull(name)) throw new TypeError('插件名称为空 请检查传入参数!');
|
||||||
|
var eventCls = mapEvent[event] || mapEvent[event.toLowerCase()] || mapEvent[event + 'Event'] || mapEvent[event.toLowerCase() + 'event'];
|
||||||
|
if (!eventCls) {
|
||||||
|
try {
|
||||||
|
eventCls = base.getClass(eventCls);
|
||||||
|
} catch (ex) {
|
||||||
|
console.warn("事件 %s 未找到!".format(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof priority === 'boolean') {
|
||||||
|
ignoreCancel = priority
|
||||||
|
}
|
||||||
|
priority = priority || 'NORMAL';
|
||||||
|
ignoreCancel = ignoreCancel || false;
|
||||||
|
var listener = new Listener({});
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
/**
|
||||||
|
* @param event Event type to register
|
||||||
|
* @param listener Listener to register
|
||||||
|
* @param priority Priority to register this event at
|
||||||
|
* @param executor EventExecutor to register
|
||||||
|
* @param plugin Plugin to register
|
||||||
|
* @param ignoreCancel
|
||||||
|
*/
|
||||||
|
Bukkit.getPluginManager().registerEvent(
|
||||||
|
eventCls,
|
||||||
|
listener,
|
||||||
|
EventPriority[priority],
|
||||||
|
new EventExecutor({
|
||||||
|
execute: function (listener, event) {
|
||||||
|
try {
|
||||||
|
exec(event);
|
||||||
|
} catch (ex) {
|
||||||
|
console.console('§6插件 §b%s §6处理 §d%s §6事件时发生异常 §4%s'.format(name, event.class.simpleName, ex));
|
||||||
|
console.ex(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
plugin,
|
||||||
|
ignoreCancel);
|
||||||
|
// 添加到缓存 用于关闭插件的时候关闭事件
|
||||||
|
if (!listenerMap[name]) listenerMap[name] = []
|
||||||
|
var listeners = listenerMap[name];
|
||||||
|
var off = {
|
||||||
|
event: eventCls,
|
||||||
|
listener: listener,
|
||||||
|
off: function () {
|
||||||
|
ref.on(this.event).call('getHandlerList').get().unregister(this.listener);
|
||||||
|
console.debug('插件 %s 注销事件 %s'.format(name, this.event.simpleName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listeners.push(off);
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
console.debug('插件 %s 注册事件 %s 方法 %s'.format(name, eventCls.simpleName, exec.name === '' ? '匿名方法' : exec.name));
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapEvent = [];
|
||||||
|
// 映射事件名称
|
||||||
|
console.info('Bukkit 事件映射完毕 共计 %s 个事件!'.format(mapEventName().toFixed(0)));
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
on: listen,
|
||||||
|
disable: function (jsp) {
|
||||||
|
var jspl = listenerMap[jsp.description.name];
|
||||||
|
if (jspl) {
|
||||||
|
jspl.forEach(function (t) t.off());
|
||||||
|
delete listenerMap[jsp.description.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
48
src/main/resources/modules/bukkit/permission.js
Normal file
48
src/main/resources/modules/bukkit/permission.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
/**
|
||||||
|
* Bukkit 权限相关类
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global Java, base, module, exports, require, __FILE__*/
|
||||||
|
var manager = require('./server').plugin.manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission(String name, String description)
|
||||||
|
*/
|
||||||
|
var Permission = Java.type("org.bukkit.permissions.Permission");
|
||||||
|
var PermissionDefault = Java.type('org.bukkit.permissions.PermissionDefault');
|
||||||
|
|
||||||
|
function enable(plugin) {
|
||||||
|
var permissions = plugin.description.permissions;
|
||||||
|
if (permissions) {
|
||||||
|
for (var name in permissions) {
|
||||||
|
var permission = permissions[name];
|
||||||
|
if (typeof permission !== 'object') continue;
|
||||||
|
var desc = permission.description;
|
||||||
|
var def = permission.default || 'OP';
|
||||||
|
try {
|
||||||
|
manager.addPermission(new Permission(name, desc, PermissionDefault.getByName(def)));
|
||||||
|
} catch (ex) {
|
||||||
|
// ignore eg: java.lang.IllegalArgumentException: The permission xxxxxx.default is already defined!
|
||||||
|
}
|
||||||
|
console.debug('插件 %s 注册权限 %s Default %s ...'.format(plugin.description.name, name, def));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable(plugin) {
|
||||||
|
var permissions = plugin.description.permissions;
|
||||||
|
if (permissions) {
|
||||||
|
for (var name in permissions) {
|
||||||
|
try {
|
||||||
|
manager.removePermission(name);
|
||||||
|
} catch (ex) {
|
||||||
|
// ignore eg: java.lang.IllegalArgumentException: The permission xxxxxx.default is already defined!
|
||||||
|
}
|
||||||
|
console.debug('插件 %s 注销权限 %s ...'.format(plugin.description.name, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.enable = enable;
|
||||||
|
exports.disable = disable;
|
259
src/main/resources/modules/bukkit/plugin.js
Normal file
259
src/main/resources/modules/bukkit/plugin.js
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
'use strict';
|
||||||
|
/**
|
||||||
|
* MiaoScript脚本插件加载类
|
||||||
|
*/
|
||||||
|
/*global Java, base, module, exports, require, __FILE__*/
|
||||||
|
// var zip = require("core/zip");
|
||||||
|
var fs = require('core/fs');
|
||||||
|
var yaml = require('modules/yaml');
|
||||||
|
var event = require('modules/event');
|
||||||
|
var bukkit = require('./server');
|
||||||
|
var command = require('./command');
|
||||||
|
var permission = require('./permission');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 载入插件
|
||||||
|
* @param path
|
||||||
|
*/
|
||||||
|
function loadPlugins(dir) {
|
||||||
|
var plugin = fs.file(root, dir);
|
||||||
|
if (!plugin) {
|
||||||
|
console.info("首次加载 创建文件夹 %s ...".format(plugin));
|
||||||
|
} else {
|
||||||
|
console.info("开始扫描 %s 下的插件 ...".format(plugin));
|
||||||
|
createUpdate(plugin);
|
||||||
|
var files = [];
|
||||||
|
fs.list(plugin).forEach(function (file) {
|
||||||
|
files.push(file.toFile());
|
||||||
|
});
|
||||||
|
loadZipPlugins(files);
|
||||||
|
loadJsPlugins(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新插件
|
||||||
|
* @param path
|
||||||
|
*/
|
||||||
|
function createUpdate(path) {
|
||||||
|
var update = fs.file(path, "update");
|
||||||
|
if (!update.exists()) {
|
||||||
|
update.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ZIP类型插件预加载
|
||||||
|
* @param files
|
||||||
|
*/
|
||||||
|
function loadZipPlugins(files) {
|
||||||
|
// // TODO ZIP类型插件加载
|
||||||
|
// files.filter(function (file) {
|
||||||
|
// return file.name.endsWith(".zip");
|
||||||
|
// }).forEach(function (file) {
|
||||||
|
// zip.unzip(fs.file(plugins_dir, file));
|
||||||
|
// var dir = new File(plugins_dir, file.name.split(".")[0]);
|
||||||
|
// // TODO 添加文件夹类型的插件兼容
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS类型插件预加载
|
||||||
|
*/
|
||||||
|
function loadJsPlugins(files) {
|
||||||
|
files.filter(function (file) {
|
||||||
|
return file.name.endsWith(".js")
|
||||||
|
}).forEach(function (file) {
|
||||||
|
loadPlugin(file);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPlugin(file) {
|
||||||
|
var update = fs.file(fs.file(file.parentFile, 'update'), file.name);
|
||||||
|
if (update.exists()) {
|
||||||
|
console.info('自动升级插件 %s'.format(file.name));
|
||||||
|
fs.move(update, file, true);
|
||||||
|
}
|
||||||
|
var plugin = require(file, {
|
||||||
|
cache: false,
|
||||||
|
hook: function (origin) {
|
||||||
|
return beforeLoadHook(origin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.debug("插件编译结果: %s".format(plugin.toJson()));
|
||||||
|
var desc = plugin.description;
|
||||||
|
if (!desc || !desc.name) {
|
||||||
|
console.warn("文件 %s 不存在 description 描述信息 无法加载插件!".format(file));
|
||||||
|
} else {
|
||||||
|
initPlugin(file, plugin);
|
||||||
|
afterLoadHook(plugin);
|
||||||
|
plugins.push(plugin);
|
||||||
|
plugins[plugin.description.name] = plugin;
|
||||||
|
console.info('载入插件 %s 版本 %s By %s'.format(desc.name, desc.version || '未知', desc.author || '未知'));
|
||||||
|
}
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeLoadHook(origin) {
|
||||||
|
var result = origin;
|
||||||
|
// 处理 event 为了不影响 正常逻辑 event 还是手动require吧
|
||||||
|
// result = result + 'var event = {}; module.exports.event = event;';
|
||||||
|
// 注入 console 对象 // 给插件注入单独的 console
|
||||||
|
result = result + 'var console = new Console(); module.exports.console = console;';
|
||||||
|
// 插件注入 self 对象
|
||||||
|
result = result + 'var self = {}; module.exports.self = self;';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterLoadHook(plugin) {
|
||||||
|
// plugin.event.on = event.on.bind(plugin);
|
||||||
|
// 给 console 添加插件名称
|
||||||
|
plugin.console.name = plugin.description.name;
|
||||||
|
// 赋值 self
|
||||||
|
for (var i in plugin) {
|
||||||
|
plugin.self[i] = plugin[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化插件内容(提供config,__DATA__等参数)
|
||||||
|
*/
|
||||||
|
function initPlugin(file, plugin) {
|
||||||
|
// 初始化 __FILE__
|
||||||
|
plugin.__FILE__ = file;
|
||||||
|
// 初始化 __DATA__
|
||||||
|
plugin.__DATA__ = fs.file(file.parentFile, plugin.description.name);
|
||||||
|
// 初始化 getDataFolder()
|
||||||
|
plugin.getDataFolder = function () {
|
||||||
|
return plugin.__DATA__;
|
||||||
|
};
|
||||||
|
// 初始化 getFile()
|
||||||
|
plugin.getFile = function (name) {
|
||||||
|
return fs.file(plugin.getDataFolder(), name);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化插件配置相关方法
|
||||||
|
initPluginConfig(plugin);
|
||||||
|
|
||||||
|
command.enable(plugin);
|
||||||
|
permission.enable(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化插件配置
|
||||||
|
*/
|
||||||
|
function initPluginConfig(plugin) {
|
||||||
|
// 初始化 config
|
||||||
|
plugin.configFile = plugin.getFile('config.yml');
|
||||||
|
/**
|
||||||
|
* 获取配置文件
|
||||||
|
* @constructor
|
||||||
|
* @constructor (file|string)
|
||||||
|
*/
|
||||||
|
plugin.getConfig = function () {
|
||||||
|
switch (arguments.length) {
|
||||||
|
case 0:
|
||||||
|
return plugin.config;
|
||||||
|
case 1:
|
||||||
|
var file = arguments[0];
|
||||||
|
if (!file.isFile) {
|
||||||
|
file = plugin.getFile(file);
|
||||||
|
}
|
||||||
|
return yaml.safeLoad(base.read(file));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 重载配置文件
|
||||||
|
* @constructor
|
||||||
|
* @constructor (file|string)
|
||||||
|
*/
|
||||||
|
plugin.reloadConfig = function () {
|
||||||
|
plugin.config = plugin.getConfig(plugin.configFile);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 保存配置文件
|
||||||
|
* @constructor
|
||||||
|
* @constructor (file, content)
|
||||||
|
*/
|
||||||
|
plugin.saveConfig = function () {
|
||||||
|
switch (arguments.length) {
|
||||||
|
case 0:
|
||||||
|
plugin.configFile.parentFile.mkdirs();
|
||||||
|
base.save(plugin.configFile, plugin.config.toYaml());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
base.save(arguments[0], arguments[1].toYaml());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (plugin.configFile.isFile()) {
|
||||||
|
plugin.config = plugin.getConfig('config.yml');
|
||||||
|
} else if (plugin.description.config) {
|
||||||
|
plugin.config = plugin.description.config;
|
||||||
|
plugin.saveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runAndCatch(jsp, exec, ext) {
|
||||||
|
if (exec) {
|
||||||
|
try {
|
||||||
|
// 绑定方法的this到插件自身
|
||||||
|
exec.bind(jsp)();
|
||||||
|
if (ext) {
|
||||||
|
ext();
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
console.console('§6插件 §b%s §6执行 §d%s §6方法时发生错误 §4%s'.format(jsp.description.name, exec.name, ex.message));
|
||||||
|
console.ex(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndGet(args) {
|
||||||
|
if (args.length === 0) {
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
|
var name = args[0];
|
||||||
|
// 如果是插件 则直接返回
|
||||||
|
if (name.description) {
|
||||||
|
return [name];
|
||||||
|
}
|
||||||
|
if (!exports.plugins[name]) {
|
||||||
|
throw new Error("插件 " + name + " 不存在!");
|
||||||
|
}
|
||||||
|
return [exports.plugins[name]];
|
||||||
|
}
|
||||||
|
|
||||||
|
var plugins = [];
|
||||||
|
|
||||||
|
exports.$ = undefined;
|
||||||
|
exports.plugins = plugins;
|
||||||
|
exports.init = function (path) {
|
||||||
|
var plugin = bukkit.plugin.self;
|
||||||
|
if (plugin !== null) {
|
||||||
|
// 如果过plugin不等于null 则代表是正式环境
|
||||||
|
exports.$ = plugin;
|
||||||
|
console.info("初始化 MiaoScript 插件系统 版本: %s".format(plugin.description.version));
|
||||||
|
}
|
||||||
|
loadPlugins(path);
|
||||||
|
};
|
||||||
|
exports.load = function () {
|
||||||
|
checkAndGet(arguments).forEach(function (p) runAndCatch(p, p.load));
|
||||||
|
};
|
||||||
|
exports.enable = function () {
|
||||||
|
checkAndGet(arguments).forEach(function (p) runAndCatch(p, p.enable));
|
||||||
|
};
|
||||||
|
exports.disable = function () {
|
||||||
|
checkAndGet(arguments).forEach(function (p) runAndCatch(p, p.disable, function () {
|
||||||
|
event.disable(p);
|
||||||
|
// task.cancel();
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
exports.reload = function () {
|
||||||
|
checkAndGet(arguments).forEach(function (p) {
|
||||||
|
exports.disable(p);
|
||||||
|
p = loadPlugin(p.__FILE__);
|
||||||
|
exports.load(p);
|
||||||
|
exports.enable(p);
|
||||||
|
});
|
||||||
|
};
|
@ -75,7 +75,8 @@ exports.plugin = {
|
|||||||
PluginManager.enablePlugin(plugin);
|
PluginManager.enablePlugin(plugin);
|
||||||
}
|
}
|
||||||
return PluginManager.isPluginEnabled(name);
|
return PluginManager.isPluginEnabled(name);
|
||||||
}
|
},
|
||||||
|
self: PluginManager.getPlugin('MiaoScript')
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* 公告
|
* 公告
|
71
src/main/resources/modules/bukkit/task.js
Normal file
71
src/main/resources/modules/bukkit/task.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
'use strict';
|
||||||
|
/*global Java, base, module, exports, require, __FILE__*/
|
||||||
|
/**
|
||||||
|
* 任务计划
|
||||||
|
* Created by 蒋天蓓 on 2017/2/9 0009.
|
||||||
|
*/
|
||||||
|
var plugin = require('./server').plugin.self;
|
||||||
|
var BukkitRunnable = Java.type("org.bukkit.scheduler.BukkitRunnable");
|
||||||
|
/**
|
||||||
|
* 创建任务对象
|
||||||
|
* @param func 任务
|
||||||
|
*/
|
||||||
|
exports.create = function (func) {
|
||||||
|
return new BukkitRunnable(func);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 运行任务
|
||||||
|
* @param func 任务
|
||||||
|
*/
|
||||||
|
exports.run = function (func) {
|
||||||
|
return exports.create(func).runTask(plugin);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 延时运行任务
|
||||||
|
* @param func 任务
|
||||||
|
* @param time 延时时间
|
||||||
|
*/
|
||||||
|
exports.later = function (func, time) {
|
||||||
|
return exports.create(func).runTaskLater(plugin, time);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 运行循环任务
|
||||||
|
* @constructor (任务,执行间隔).
|
||||||
|
* @constructor (任务,首次延时,执行间隔)
|
||||||
|
*/
|
||||||
|
exports.timer = function () {
|
||||||
|
switch (arguments.length) {
|
||||||
|
case 2:
|
||||||
|
return exports.create(arguments[0]).runTaskTimer(plugin, 0, arguments[1]);
|
||||||
|
case 3:
|
||||||
|
return exports.create(arguments[0]).runTaskTimer(plugin, arguments[1], arguments[2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 运行异步任务
|
||||||
|
* @param func function 任务
|
||||||
|
*/
|
||||||
|
exports.async = function (func) {
|
||||||
|
return exports.create(func).runTaskAsynchronously(plugin);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 延时运行异步任务
|
||||||
|
* @param func 任务
|
||||||
|
* @param time 延时时间
|
||||||
|
*/
|
||||||
|
exports.laterAsync = function (func, time) {
|
||||||
|
return exports.create(func).runTaskLaterAsynchronously(plugin, time);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 运行异步循环任务
|
||||||
|
* @constructor (任务,执行间隔).
|
||||||
|
* @constructor (任务,首次延时,执行间隔)
|
||||||
|
*/
|
||||||
|
exports.timerAsync = function () {
|
||||||
|
switch (arguments.length) {
|
||||||
|
case 2:
|
||||||
|
return exports.create(arguments[0]).runTaskTimerAsynchronously(plugin, 0, arguments[1]);
|
||||||
|
case 3:
|
||||||
|
return exports.create(arguments[0]).runTaskTimerAsynchronously(plugin, arguments[1], arguments[2]);
|
||||||
|
}
|
||||||
|
};
|
@ -1,93 +1 @@
|
|||||||
'use strict';
|
module.exports = require('./bukkit/command');
|
||||||
/**
|
|
||||||
* Bukkit 命令相关类
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*global Java, base, module, exports, require, __FILE__*/
|
|
||||||
var plugin = base.plugin;
|
|
||||||
var ref = require('core/reflect');
|
|
||||||
var bukkit = require('./bukkit');
|
|
||||||
var commandMap = ref.on(bukkit.plugin.manager).get('commandMap').get();
|
|
||||||
var PluginCommand = Java.type('org.bukkit.command.PluginCommand');
|
|
||||||
|
|
||||||
var StringUtil = Java.type('org.bukkit.util.StringUtil');
|
|
||||||
|
|
||||||
var ArrayList = Java.type('java.util.ArrayList');
|
|
||||||
var Arrays = Java.type('java.util.Arrays');
|
|
||||||
|
|
||||||
function enable(jsp) {
|
|
||||||
var commands = jsp.description.commands;
|
|
||||||
if (commands) {
|
|
||||||
var pluginCmds = [];
|
|
||||||
for (var name in commands) {
|
|
||||||
var command = commands[name];
|
|
||||||
if (typeof command !== 'object') continue;
|
|
||||||
var newCmd = create(jsp, name);
|
|
||||||
if (command.description) newCmd.setDescription(command.description);
|
|
||||||
if (command.usage) newCmd.setUsage(command.usage);
|
|
||||||
if (command.aliases) newCmd.setAliases(Arrays.asList(command.aliases));
|
|
||||||
if (command.permission) newCmd.setPermission(command.permission);
|
|
||||||
if (command['permission-message']) newCmd.setPermissionMessage(command['permission-message']);
|
|
||||||
pluginCmds.push(newCmd);
|
|
||||||
console.debug('插件 %s 注册命令 %s ...', jsp.description.name, name);
|
|
||||||
}
|
|
||||||
commandMap.registerAll(jsp.description.name, Arrays.asList(pluginCmds));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function get(name) {
|
|
||||||
return commandMap.getCommand(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
function create(jsp, name) {
|
|
||||||
return register(jsp, ref.on(PluginCommand).create(name, plugin).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
function register(jsp, cmd) {
|
|
||||||
commandMap.register(jsp.description.name, cmd);
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// var exec = {
|
|
||||||
// cmd: function (sender, command, args) {
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// tab: function (sender, command, args) {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
function on(jsp, name, exec) {
|
|
||||||
var c = get(name) || create(jsp, name);
|
|
||||||
console.debug('插件 %s 设置命令 %s(%s) 执行器 ...', jsp.description.name, name, c);
|
|
||||||
if (exec.cmd) {
|
|
||||||
c.setExecutor(function (sender, cmd, command, args) {
|
|
||||||
try {
|
|
||||||
return exec.cmd(sender, command, args);
|
|
||||||
} catch (ex) {
|
|
||||||
console.console('§6玩家 §a%s §6执行 §b%s §6插件 §d%s %s §6命令时发生异常 §4%s', sender.name, jsp.description.name, command, Java.from(args).join(' '), ex);
|
|
||||||
console.ex(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (exec.tab) {
|
|
||||||
c.setTabCompleter(function (sender, cmd, command, args) {
|
|
||||||
try {
|
|
||||||
var completions = new ArrayList();
|
|
||||||
var token = args[args.length - 1];
|
|
||||||
StringUtil.copyPartialMatches(token, Arrays.asList(exec.tab(sender, command, args)), completions);
|
|
||||||
return completions;
|
|
||||||
} catch (ex) {
|
|
||||||
console.console('§6玩家 §a%s §6执行 §b%s §6插件 §d%s %s §6补全时发生异常 §4%s', sender.name, jsp.description.name, command, Java.from(args).join(' '), ex);
|
|
||||||
console.ex(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.enable = enable;
|
|
||||||
|
|
||||||
exports.on = on;
|
|
||||||
exports.off = function () {
|
|
||||||
|
|
||||||
};
|
|
@ -1,158 +1 @@
|
|||||||
'use strict';
|
module.exports = require('./bukkit/event');
|
||||||
/**
|
|
||||||
* Bukkit 事件相关类
|
|
||||||
*/
|
|
||||||
/*global Java, base, module, exports, require, __FILE__*/
|
|
||||||
var Thread = Java.type("java.lang.Thread");
|
|
||||||
var Bukkit = Java.type("org.bukkit.Bukkit");
|
|
||||||
var Listener = Java.type("org.bukkit.event.Listener");
|
|
||||||
var Modifier = Java.type("java.lang.reflect.Modifier");
|
|
||||||
var BukkitEvent = Java.type("org.bukkit.event.Event");
|
|
||||||
var HandlerList = Java.type('org.bukkit.event.HandlerList');
|
|
||||||
var EventPriority = Java.type("org.bukkit.event.EventPriority");
|
|
||||||
var EventExecutor = Java.type("org.bukkit.plugin.EventExecutor");
|
|
||||||
var IllegalStateException = Java.type("java.lang.IllegalStateException");
|
|
||||||
|
|
||||||
var plugin = base.plugin;
|
|
||||||
|
|
||||||
var ref = require('reflect');
|
|
||||||
|
|
||||||
var listenerMap = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扫描包 org.bukkit.event 下的所有事件
|
|
||||||
* 映射简写名称 org.bukkit.event.player.PlayerLoginEvent => playerloginevent
|
|
||||||
*/
|
|
||||||
function mapEventName() {
|
|
||||||
var eventPackageDir = "org/bukkit/event";
|
|
||||||
var count = 0;
|
|
||||||
var dirs = Thread.currentThread().getContextClassLoader().getResources(eventPackageDir);
|
|
||||||
while (dirs.hasMoreElements()) {
|
|
||||||
var url = dirs.nextElement();
|
|
||||||
var protocol = url.protocol;
|
|
||||||
if (protocol === "jar") {
|
|
||||||
// noinspection JSUnresolvedVariable
|
|
||||||
var jar = url.openConnection().jarFile;
|
|
||||||
var entries = jar.entries();
|
|
||||||
while (entries.hasMoreElements()) {
|
|
||||||
var entry = entries.nextElement();
|
|
||||||
var name = entry.name;
|
|
||||||
// 以 org/bukkit/event 开头 并且以 .class 结尾
|
|
||||||
if (name.startsWith(eventPackageDir) && name.endsWith(".class")) {
|
|
||||||
var i = name.replaceAll('/', '.');
|
|
||||||
try {
|
|
||||||
var clz = base.getClass(i.substring(0, i.length - 6));
|
|
||||||
// 继承于 org.bukkit.event.Event 访问符为Public
|
|
||||||
if (isVaildEvent(clz)) {
|
|
||||||
// noinspection JSUnresolvedVariable
|
|
||||||
var simpleName = clz.simpleName.toLowerCase();
|
|
||||||
console.debug("Mapping Event [%s] => %s", clz.name, simpleName);
|
|
||||||
mapEvent[simpleName] = clz;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
//ignore already loaded class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否为一个有效的事件类
|
|
||||||
* @param clz
|
|
||||||
* @returns {*|boolean}
|
|
||||||
*/
|
|
||||||
function isVaildEvent(clz) {
|
|
||||||
// noinspection JSUnresolvedVariable 继承于 org.bukkit.event.Event
|
|
||||||
return BukkitEvent.class.isAssignableFrom(clz) &&
|
|
||||||
// 访问符为Public
|
|
||||||
Modifier.isPublic(clz.getModifiers()) &&
|
|
||||||
// 不是抽象类
|
|
||||||
!Modifier.isAbstract(clz.getModifiers());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加事件监听
|
|
||||||
* @param jsp
|
|
||||||
* @param event
|
|
||||||
* @param exec {function}
|
|
||||||
* @param priority
|
|
||||||
* @param ignoreCancel
|
|
||||||
*/
|
|
||||||
function listen(jsp, event, exec, priority, ignoreCancel) {
|
|
||||||
var name = jsp.description.name;
|
|
||||||
if (ext.isNull(name)) throw new TypeError('插件名称为空 请检查传入参数!');
|
|
||||||
var eventCls = mapEvent[event] || mapEvent[event.toLowerCase()] || mapEvent[event + 'Event'] || mapEvent[event.toLowerCase() + 'event'];
|
|
||||||
if (!eventCls) {
|
|
||||||
try {
|
|
||||||
eventCls = base.getClass(eventCls);
|
|
||||||
} catch (ex) {
|
|
||||||
console.warn("事件 %s 未找到!", event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof priority === 'boolean') {
|
|
||||||
ignoreCancel = priority
|
|
||||||
}
|
|
||||||
priority = priority || 'NORMAL';
|
|
||||||
ignoreCancel = ignoreCancel || false;
|
|
||||||
var listener = new Listener({});
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
/**
|
|
||||||
* @param event Event type to register
|
|
||||||
* @param listener Listener to register
|
|
||||||
* @param priority Priority to register this event at
|
|
||||||
* @param executor EventExecutor to register
|
|
||||||
* @param plugin Plugin to register
|
|
||||||
* @param ignoreCancel
|
|
||||||
*/
|
|
||||||
Bukkit.getPluginManager().registerEvent(
|
|
||||||
eventCls,
|
|
||||||
listener,
|
|
||||||
EventPriority[priority],
|
|
||||||
new EventExecutor({
|
|
||||||
execute: function (listener, event) {
|
|
||||||
try {
|
|
||||||
exec(event);
|
|
||||||
} catch (ex) {
|
|
||||||
console.console('§6插件 §b%s §6处理 §d%s §6事件时发生异常 §4%s', name, event.class.simpleName, ex);
|
|
||||||
console.ex(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
plugin,
|
|
||||||
ignoreCancel);
|
|
||||||
// 添加到缓存 用于关闭插件的时候关闭事件
|
|
||||||
if (!listenerMap[name]) listenerMap[name] = []
|
|
||||||
var listeners = listenerMap[name];
|
|
||||||
var off = {
|
|
||||||
event: eventCls,
|
|
||||||
listener: listener,
|
|
||||||
off: function () {
|
|
||||||
ref.on(this.event).call('getHandlerList').get().unregister(this.listener);
|
|
||||||
console.debug('插件 %s 注销事件 %s', name, this.event.simpleName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
listeners.push(off);
|
|
||||||
// noinspection JSUnresolvedVariable
|
|
||||||
console.debug('插件 %s 注册事件 %s 方法 %s', name, eventCls.simpleName, exec.name === '' ? '匿名方法' : exec.name);
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mapEvent = [];
|
|
||||||
// 映射事件名称
|
|
||||||
console.info('Bukkit 事件映射完毕 共计 %s 个事件!', mapEventName().toFixed(0));
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
on: listen,
|
|
||||||
disable: function (jsp) {
|
|
||||||
var jspl = listenerMap[jsp.description.name];
|
|
||||||
if (jspl) {
|
|
||||||
jspl.forEach(function (t) t.off());
|
|
||||||
delete listenerMap[jsp.description.name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,48 +1 @@
|
|||||||
'use strict';
|
module.exports = require('./bukkit/permission');
|
||||||
/**
|
|
||||||
* Bukkit 权限相关类
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*global Java, base, module, exports, require, __FILE__*/
|
|
||||||
var manager = require('./bukkit').plugin.manager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permission(String name, String description)
|
|
||||||
*/
|
|
||||||
var Permission = Java.type("org.bukkit.permissions.Permission");
|
|
||||||
var PermissionDefault = Java.type('org.bukkit.permissions.PermissionDefault');
|
|
||||||
|
|
||||||
function enable(plugin) {
|
|
||||||
var permissions = plugin.description.permissions;
|
|
||||||
if (permissions) {
|
|
||||||
for (var name in permissions) {
|
|
||||||
var permission = permissions[name];
|
|
||||||
if (typeof permission !== 'object') continue;
|
|
||||||
var desc = permission.description;
|
|
||||||
var def = permission.default || 'OP';
|
|
||||||
try {
|
|
||||||
manager.addPermission(new Permission(name, desc, PermissionDefault.getByName(def)));
|
|
||||||
} catch (ex) {
|
|
||||||
// ignore eg: java.lang.IllegalArgumentException: The permission xxxxxx.default is already defined!
|
|
||||||
}
|
|
||||||
console.debug('插件 %s 注册权限 %s Default %s ...', plugin.description.name, name, def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function disable(plugin) {
|
|
||||||
var permissions = plugin.description.permissions;
|
|
||||||
if (permissions) {
|
|
||||||
for (var name in permissions) {
|
|
||||||
try {
|
|
||||||
manager.removePermission(name);
|
|
||||||
} catch (ex) {
|
|
||||||
// ignore eg: java.lang.IllegalArgumentException: The permission xxxxxx.default is already defined!
|
|
||||||
}
|
|
||||||
console.debug('插件 %s 注销权限 %s ...', plugin.description.name, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.enable = enable;
|
|
||||||
exports.disable = disable;
|
|
@ -1,258 +1 @@
|
|||||||
'use strict';
|
module.exports = require('./bukkit/plugin');
|
||||||
/**
|
|
||||||
* MiaoScript脚本插件加载类
|
|
||||||
*/
|
|
||||||
/*global Java, base, module, exports, require, __FILE__*/
|
|
||||||
// var zip = require("core/zip");
|
|
||||||
var fs = require('core/fs');
|
|
||||||
var yaml = require('modules/yaml');
|
|
||||||
var event = require('modules/event');
|
|
||||||
var bukkit = require('./bukkit');
|
|
||||||
var command = require('./command');
|
|
||||||
var permission = require('./permission');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 载入插件
|
|
||||||
* @param path
|
|
||||||
*/
|
|
||||||
function loadPlugins(dir) {
|
|
||||||
var plugin = fs.file(root, dir);
|
|
||||||
if (!plugin) {
|
|
||||||
console.info("首次加载 创建文件夹 %s ...", plugin);
|
|
||||||
} else {
|
|
||||||
console.info("开始扫描 %s 下的插件 ...", plugin);
|
|
||||||
createUpdate(plugin);
|
|
||||||
var files = [];
|
|
||||||
fs.list(plugin).forEach(function (file) {
|
|
||||||
files.push(file.toFile());
|
|
||||||
});
|
|
||||||
loadZipPlugins(files);
|
|
||||||
loadJsPlugins(files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新插件
|
|
||||||
* @param path
|
|
||||||
*/
|
|
||||||
function createUpdate(path) {
|
|
||||||
var update = fs.file(path, "update");
|
|
||||||
if (!update.exists()) {
|
|
||||||
update.mkdirs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ZIP类型插件预加载
|
|
||||||
* @param files
|
|
||||||
*/
|
|
||||||
function loadZipPlugins(files) {
|
|
||||||
// // TODO ZIP类型插件加载
|
|
||||||
// files.filter(function (file) {
|
|
||||||
// return file.name.endsWith(".zip");
|
|
||||||
// }).forEach(function (file) {
|
|
||||||
// zip.unzip(fs.file(plugins_dir, file));
|
|
||||||
// var dir = new File(plugins_dir, file.name.split(".")[0]);
|
|
||||||
// // TODO 添加文件夹类型的插件兼容
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JS类型插件预加载
|
|
||||||
*/
|
|
||||||
function loadJsPlugins(files) {
|
|
||||||
files.filter(function (file) {
|
|
||||||
return file.name.endsWith(".js")
|
|
||||||
}).forEach(function (file) {
|
|
||||||
loadPlugin(file);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadPlugin(file) {
|
|
||||||
var update = fs.file(fs.file(file.parentFile, 'update'), file.name);
|
|
||||||
if (update.exists()) {
|
|
||||||
console.info('自动升级插件 %s', file.name);
|
|
||||||
fs.move(update, file, true);
|
|
||||||
}
|
|
||||||
var plugin = require(file, {
|
|
||||||
cache: false,
|
|
||||||
hook: function (origin) {
|
|
||||||
return beforeLoadHook(origin);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.debug("插件编译结果: %s", plugin.toJson());
|
|
||||||
var desc = plugin.description;
|
|
||||||
if (!desc || !desc.name) {
|
|
||||||
console.warn("文件 %s 不存在 description 描述信息 无法加载插件!", file);
|
|
||||||
} else {
|
|
||||||
initPlugin(file, plugin);
|
|
||||||
afterLoadHook(plugin);
|
|
||||||
plugins.push(plugin);
|
|
||||||
plugins[plugin.description.name] = plugin;
|
|
||||||
console.info('载入插件 %s 版本 %s By %s', desc.name, desc.version || '未知', desc.author || '未知');
|
|
||||||
}
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
function beforeLoadHook(origin) {
|
|
||||||
var result = origin;
|
|
||||||
// 处理 event 为了不影响 正常逻辑 event 还是手动require吧
|
|
||||||
// result = result + 'var event = {}; module.exports.event = event;';
|
|
||||||
// 注入 console 对象 // 给插件注入单独的 console
|
|
||||||
result = result + 'var console = new Console(); module.exports.console = console;';
|
|
||||||
// 插件注入 self 对象
|
|
||||||
result = result + 'var self = {}; module.exports.self = self;';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function afterLoadHook(plugin) {
|
|
||||||
// plugin.event.on = event.on.bind(plugin);
|
|
||||||
// 给 console 添加插件名称
|
|
||||||
plugin.console.name = plugin.description.name;
|
|
||||||
// 赋值 self
|
|
||||||
for (var i in plugin) {
|
|
||||||
plugin.self[i] = plugin[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化插件内容(提供config,__DATA__等参数)
|
|
||||||
*/
|
|
||||||
function initPlugin(file, plugin) {
|
|
||||||
// 初始化 __FILE__
|
|
||||||
plugin.__FILE__ = file;
|
|
||||||
// 初始化 __DATA__
|
|
||||||
plugin.__DATA__ = fs.file(file.parentFile, plugin.description.name);
|
|
||||||
// 初始化 getDataFolder()
|
|
||||||
plugin.getDataFolder = function () {
|
|
||||||
return plugin.__DATA__;
|
|
||||||
};
|
|
||||||
// 初始化 getFile()
|
|
||||||
plugin.getFile = function (name) {
|
|
||||||
return fs.file(plugin.getDataFolder(), name);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化插件配置相关方法
|
|
||||||
initPluginConfig(plugin);
|
|
||||||
|
|
||||||
command.enable(plugin);
|
|
||||||
permission.enable(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化插件配置
|
|
||||||
*/
|
|
||||||
function initPluginConfig(plugin) {
|
|
||||||
// 初始化 config
|
|
||||||
plugin.configFile = plugin.getFile('config.yml');
|
|
||||||
/**
|
|
||||||
* 获取配置文件
|
|
||||||
* @constructor
|
|
||||||
* @constructor (file|string)
|
|
||||||
*/
|
|
||||||
plugin.getConfig = function () {
|
|
||||||
switch (arguments.length) {
|
|
||||||
case 0:
|
|
||||||
return plugin.config;
|
|
||||||
case 1:
|
|
||||||
var file = arguments[0];
|
|
||||||
if (!file.isFile) {
|
|
||||||
file = plugin.getFile(file);
|
|
||||||
}
|
|
||||||
return yaml.safeLoad(base.read(file));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 重载配置文件
|
|
||||||
* @constructor
|
|
||||||
* @constructor (file|string)
|
|
||||||
*/
|
|
||||||
plugin.reloadConfig = function () {
|
|
||||||
plugin.config = plugin.getConfig(plugin.configFile);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 保存配置文件
|
|
||||||
* @constructor
|
|
||||||
* @constructor (file, content)
|
|
||||||
*/
|
|
||||||
plugin.saveConfig = function () {
|
|
||||||
switch (arguments.length) {
|
|
||||||
case 0:
|
|
||||||
plugin.configFile.parentFile.mkdirs();
|
|
||||||
base.save(plugin.configFile, plugin.config.toYaml());
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
base.save(arguments[0], arguments[1].toYaml());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (plugin.configFile.isFile()) {
|
|
||||||
plugin.config = plugin.getConfig('config.yml');
|
|
||||||
} else if (plugin.description.config) {
|
|
||||||
plugin.config = plugin.description.config;
|
|
||||||
plugin.saveConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function runAndCatch(jsp, exec, ext) {
|
|
||||||
if (exec) {
|
|
||||||
try {
|
|
||||||
// 绑定方法的this到插件自身
|
|
||||||
exec.bind(jsp)();
|
|
||||||
if (ext) {
|
|
||||||
ext();
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
console.console('§6插件 §b%s §6执行 §d%s §6方法时发生错误 §4%s', jsp.description.name, exec.name, ex.message);
|
|
||||||
console.ex(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkAndGet(args) {
|
|
||||||
if (args.length === 0) {
|
|
||||||
return plugins;
|
|
||||||
}
|
|
||||||
var name = args[0];
|
|
||||||
// 如果是插件 则直接返回
|
|
||||||
if (name.description) {
|
|
||||||
return [name];
|
|
||||||
}
|
|
||||||
if (!exports.plugins[name]) {
|
|
||||||
throw new Error("插件 " + name + " 不存在!");
|
|
||||||
}
|
|
||||||
return [exports.plugins[name]];
|
|
||||||
}
|
|
||||||
|
|
||||||
var plugins = [];
|
|
||||||
|
|
||||||
exports.$ = undefined;
|
|
||||||
exports.plugins = plugins;
|
|
||||||
exports.init = function (plugin, path) {
|
|
||||||
if (plugin !== null) {
|
|
||||||
// 如果过plugin不等于null 则代表是正式环境
|
|
||||||
exports.$ = plugin;
|
|
||||||
console.info("初始化 MiaoScript 插件系统 版本: %s", plugin.description.version);
|
|
||||||
}
|
|
||||||
loadPlugins(path);
|
|
||||||
};
|
|
||||||
exports.load = function () {
|
|
||||||
checkAndGet(arguments).forEach(function (p) runAndCatch(p, p.load));
|
|
||||||
};
|
|
||||||
exports.enable = function () {
|
|
||||||
checkAndGet(arguments).forEach(function (p) runAndCatch(p, p.enable));
|
|
||||||
};
|
|
||||||
exports.disable = function () {
|
|
||||||
checkAndGet(arguments).forEach(function (p) runAndCatch(p, p.disable, function () {
|
|
||||||
event.disable(p);
|
|
||||||
// task.cancel();
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
exports.reload = function () {
|
|
||||||
checkAndGet(arguments).forEach(function (p) {
|
|
||||||
exports.disable(p);
|
|
||||||
p = loadPlugin(p.__FILE__);
|
|
||||||
exports.load(p);
|
|
||||||
exports.enable(p);
|
|
||||||
});
|
|
||||||
};
|
|
1
src/main/resources/modules/server.js
Normal file
1
src/main/resources/modules/server.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./bukkit/server');
|
@ -1,71 +1 @@
|
|||||||
'use strict';
|
module.exports = require('./bukkit/task');
|
||||||
/*global Java, base, module, exports, require, __FILE__*/
|
|
||||||
/**
|
|
||||||
* 任务计划
|
|
||||||
* Created by 蒋天蓓 on 2017/2/9 0009.
|
|
||||||
*/
|
|
||||||
var plugin = base.plugin;
|
|
||||||
var BukkitRunnable = Java.type("org.bukkit.scheduler.BukkitRunnable");
|
|
||||||
/**
|
|
||||||
* 创建任务对象
|
|
||||||
* @param func 任务
|
|
||||||
*/
|
|
||||||
exports.create = function (func) {
|
|
||||||
return new BukkitRunnable(func);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 运行任务
|
|
||||||
* @param func 任务
|
|
||||||
*/
|
|
||||||
exports.run = function (func) {
|
|
||||||
return exports.create(func).runTask(plugin);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 延时运行任务
|
|
||||||
* @param func 任务
|
|
||||||
* @param time 延时时间
|
|
||||||
*/
|
|
||||||
exports.later = function (func, time) {
|
|
||||||
return exports.create(func).runTaskLater(plugin, time);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 运行循环任务
|
|
||||||
* @constructor (任务,执行间隔).
|
|
||||||
* @constructor (任务,首次延时,执行间隔)
|
|
||||||
*/
|
|
||||||
exports.timer = function () {
|
|
||||||
switch (arguments.length) {
|
|
||||||
case 2:
|
|
||||||
return exports.create(arguments[0]).runTaskTimer(plugin, 0, arguments[1]);
|
|
||||||
case 3:
|
|
||||||
return exports.create(arguments[0]).runTaskTimer(plugin, arguments[1], arguments[2]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 运行异步任务
|
|
||||||
* @param func function 任务
|
|
||||||
*/
|
|
||||||
exports.async = function (func) {
|
|
||||||
return exports.create(func).runTaskAsynchronously(plugin);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 延时运行异步任务
|
|
||||||
* @param func 任务
|
|
||||||
* @param time 延时时间
|
|
||||||
*/
|
|
||||||
exports.laterAsync = function (func, time) {
|
|
||||||
return exports.create(func).runTaskLaterAsynchronously(plugin, time);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 运行异步循环任务
|
|
||||||
* @constructor (任务,执行间隔).
|
|
||||||
* @constructor (任务,首次延时,执行间隔)
|
|
||||||
*/
|
|
||||||
exports.timerAsync = function () {
|
|
||||||
switch (arguments.length) {
|
|
||||||
case 2:
|
|
||||||
return exports.create(arguments[0]).runTaskTimerAsynchronously(plugin, 0, arguments[1]);
|
|
||||||
case 3:
|
|
||||||
return exports.create(arguments[0]).runTaskTimerAsynchronously(plugin, arguments[1], arguments[2]);
|
|
||||||
}
|
|
||||||
};
|
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
var fs = require('core/fs');
|
var fs = require('core/fs');
|
||||||
var event = require('modules/event');
|
var event = require('modules/event');
|
||||||
var bukkit = require('modules/bukkit');
|
var bukkit = require('modules/server');
|
||||||
var command = require('modules/command');
|
var command = require('modules/command');
|
||||||
|
|
||||||
var papi = require('./ext/papi');
|
var papi = require('./ext/papi');
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
/*global Java, base, module, exports, require, __FILE__*/
|
/*global Java, base, module, exports, require, __FILE__*/
|
||||||
var PlaceholderAPI;
|
var PlaceholderAPI;
|
||||||
var bukkit = require('modules/bukkit');
|
var bukkit = require('modules/server');
|
||||||
if (bukkit.plugin.load("PlaceholderAPI")) {
|
if (bukkit.plugin.load("PlaceholderAPI")) {
|
||||||
PlaceholderAPI = ext.getStatic("me.clip.placeholderapi.PlaceholderAPI");
|
PlaceholderAPI = ext.getStatic("me.clip.placeholderapi.PlaceholderAPI");
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user