diff --git a/src/main/java/pw/yumc/MiaoScript/MiaoScript.java b/src/main/java/pw/yumc/MiaoScript/MiaoScript.java index 1c89a72..8d35277 100644 --- a/src/main/java/pw/yumc/MiaoScript/MiaoScript.java +++ b/src/main/java/pw/yumc/MiaoScript/MiaoScript.java @@ -29,7 +29,6 @@ public class MiaoScript extends JavaPlugin implements Executor { @SneakyThrows public void onEnable() { new CommandSub("ms", this); - saveScript(); 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 public void onDisable() { engine.disableEngine(); diff --git a/src/main/resources/bios.js b/src/main/resources/bios.js index a234478..4e0bf64 100644 --- a/src/main/resources/bios.js +++ b/src/main/resources/bios.js @@ -17,14 +17,17 @@ var disable; try { init(root); } catch (ex) { - throw ex; + if(console.ex){ + console.ex(ex); + }else{ + throw ex; + } } finally { disable = disablePlugins } }; function release(root, regex, replace) { - print(Array.prototype.join.call(arguments, ' ')); var Files = Java.type("java.nio.file.Files"); var Paths = Java.type("java.nio.file.Paths"); var StandardCopyOption = Java.type("java.nio.file.StandardCopyOption"); @@ -55,7 +58,6 @@ var disable; } }) } catch (ex) { - } finally { } } diff --git a/src/main/resources/core/console.js b/src/main/resources/core/console.js index 587485d..df6ca78 100644 --- a/src/main/resources/core/console.js +++ b/src/main/resources/core/console.js @@ -20,6 +20,7 @@ this.log = function () { log.info(this.name + Array.prototype.join.call(arguments, ' ')); }; + this.info = this.log; this.warn = function () { log.warning(this.name + Array.prototype.join.call(arguments, ' ')); }; @@ -71,9 +72,9 @@ } track.forEach(function (stack) { 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 { - 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)); } diff --git a/src/main/resources/core/fs.js b/src/main/resources/core/fs.js index 063debd..125b798 100644 --- a/src/main/resources/core/fs.js +++ b/src/main/resources/core/fs.js @@ -21,7 +21,7 @@ exports.concat = function () { */ exports.file = function () { if (!arguments[0]) { - log.w("文件名称不得为 undefined 或者 null !"); + console.warn("文件名称不得为 undefined 或者 null !"); } switch (arguments.length) { case 1: @@ -77,7 +77,7 @@ exports.copy = function (inputStream, target, override) { exports.read = function (file) { file = exports.file(file); if (!file.exists()) { - log.w("读取文件 %s 错误 文件不存在!", file); + console.warn("读取文件 %s 错误 文件不存在!".format(file)); return; } // noinspection JSPrimitiveTypeWrapperUsage @@ -103,7 +103,7 @@ exports.list = function (path) { if (dir.isDirectory()) { return Files.list(dir.toPath()); } - log.w("路径 %s 不是一个目录 返回空数组!", path); + console.warn("路径 %s 不是一个目录 返回空数组!".format(path)); return []; }; /** diff --git a/src/main/resources/core/init.js b/src/main/resources/core/init.js index 21d6d4e..4b522be 100644 --- a/src/main/resources/core/init.js +++ b/src/main/resources/core/init.js @@ -64,7 +64,7 @@ function loadLib4Bukkit() { function loadPlugins() { // 初始化本体插件 global.manager = require('modules/plugin'); - manager.init(base.plugin, 'plugins'); + manager.init('plugins'); // 只有当在正式环境运行的时候才加载 if (manager.$) { manager.load(); @@ -77,7 +77,7 @@ function loadPlugins() { * 关闭插件Hook */ function disablePlugins() { - if (manager.$) { + if (manager && manager.$) { manager.disable(); } } \ No newline at end of file diff --git a/src/main/resources/core/patch.js b/src/main/resources/core/patch.js index 3bd730a..391e215 100644 --- a/src/main/resources/core/patch.js +++ b/src/main/resources/core/patch.js @@ -3,6 +3,10 @@ */ (function(){ + // Java格式化方法 + var str = Java.type('java.lang.String'); + String.prototype.format = function(){ return str.format(this, Array.prototype.slice.call(arguments, 0))} + // JSON快捷方法 Object.prototype.toJson = function(){ return JSON.stringify(this); } diff --git a/src/main/resources/core/require.js b/src/main/resources/core/require.js index 0ae54ee..31e2634 100644 --- a/src/main/resources/core/require.js +++ b/src/main/resources/core/require.js @@ -107,7 +107,7 @@ * @returns {Object} */ 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 var module = { id: id, @@ -120,10 +120,10 @@ compiledWrapper.apply(module.exports, [ module, module.exports, module.require, file.parentFile, file ]); - console.debug('模块 %s 编译成功!', name); + console.debug('模块 %s 编译成功!'.format(name)); module.loaded = true; } 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); } return module; @@ -155,7 +155,7 @@ function _require(name, path, optional) { var file = _canonical(name) ? name : resolve(name, path); if (file === undefined) { - console.console("§c模块 §a%s §c加载失败! §4未找到该模块!", name); + console.console("§c模块 §a%s §c加载失败! §4未找到该模块!".format(name)); return {exports: {}}; } if (!optional) optional = {cache: true}; @@ -189,6 +189,6 @@ parent = new File(parent); } var cacheModules = []; - console.debug("初始化 require 模块组件 父目录 %s", _canonical(parent)); + console.debug("初始化 require 模块组件 父目录 ", _canonical(parent)); return exports(parent); }); \ No newline at end of file diff --git a/src/main/resources/core/zip.js b/src/main/resources/core/zip.js index 3d1d750..c114c99 100644 --- a/src/main/resources/core/zip.js +++ b/src/main/resources/core/zip.js @@ -12,7 +12,7 @@ var fs = require('fs'); */ function unzip(zipFile, target) { if (!zipFile.exists()) { - log.w("解压文件 %s 错误 文件不存在!", zipFile); + console.warn("解压文件 %s 错误 文件不存在!".format(zipFile)); return; } if (target === undefined) { @@ -20,7 +20,7 @@ function unzip(zipFile, target) { // noinspection JSUnresolvedVariable 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 e = zipObj.entries(); while (e.hasMoreElements()) { diff --git a/src/main/resources/modules/bukkit/command.js b/src/main/resources/modules/bukkit/command.js new file mode 100644 index 0000000..c5cad95 --- /dev/null +++ b/src/main/resources/modules/bukkit/command.js @@ -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 () { + +}; \ No newline at end of file diff --git a/src/main/resources/modules/bukkit/event.js b/src/main/resources/modules/bukkit/event.js new file mode 100644 index 0000000..d9763e2 --- /dev/null +++ b/src/main/resources/modules/bukkit/event.js @@ -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]; + } + } +}; \ No newline at end of file diff --git a/src/main/resources/modules/bukkit/permission.js b/src/main/resources/modules/bukkit/permission.js new file mode 100644 index 0000000..9b85caf --- /dev/null +++ b/src/main/resources/modules/bukkit/permission.js @@ -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; \ No newline at end of file diff --git a/src/main/resources/modules/bukkit/plugin.js b/src/main/resources/modules/bukkit/plugin.js new file mode 100644 index 0000000..2724752 --- /dev/null +++ b/src/main/resources/modules/bukkit/plugin.js @@ -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); + }); +}; \ No newline at end of file diff --git a/src/main/resources/modules/bukkit.js b/src/main/resources/modules/bukkit/server.js similarity index 97% rename from src/main/resources/modules/bukkit.js rename to src/main/resources/modules/bukkit/server.js index a0c1057..1d1cc04 100644 --- a/src/main/resources/modules/bukkit.js +++ b/src/main/resources/modules/bukkit/server.js @@ -75,7 +75,8 @@ exports.plugin = { PluginManager.enablePlugin(plugin); } return PluginManager.isPluginEnabled(name); - } + }, + self: PluginManager.getPlugin('MiaoScript') }; /** * 公告 diff --git a/src/main/resources/modules/bukkit/task.js b/src/main/resources/modules/bukkit/task.js new file mode 100644 index 0000000..461b788 --- /dev/null +++ b/src/main/resources/modules/bukkit/task.js @@ -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]); + } +}; \ No newline at end of file diff --git a/src/main/resources/modules/command.js b/src/main/resources/modules/command.js index 10d6000..90faac5 100644 --- a/src/main/resources/modules/command.js +++ b/src/main/resources/modules/command.js @@ -1,93 +1 @@ -'use strict'; -/** - * 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 () { - -}; \ No newline at end of file +module.exports = require('./bukkit/command'); \ No newline at end of file diff --git a/src/main/resources/modules/event.js b/src/main/resources/modules/event.js index 06714a1..6929ac9 100644 --- a/src/main/resources/modules/event.js +++ b/src/main/resources/modules/event.js @@ -1,158 +1 @@ -'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 = 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]; - } - } -}; \ No newline at end of file +module.exports = require('./bukkit/event'); \ No newline at end of file diff --git a/src/main/resources/modules/permission.js b/src/main/resources/modules/permission.js index ed75be5..0d4bc0d 100644 --- a/src/main/resources/modules/permission.js +++ b/src/main/resources/modules/permission.js @@ -1,48 +1 @@ -'use strict'; -/** - * 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; \ No newline at end of file +module.exports = require('./bukkit/permission'); \ No newline at end of file diff --git a/src/main/resources/modules/plugin.js b/src/main/resources/modules/plugin.js index c605e58..b5c78b6 100644 --- a/src/main/resources/modules/plugin.js +++ b/src/main/resources/modules/plugin.js @@ -1,258 +1 @@ -'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('./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); - }); -}; \ No newline at end of file +module.exports = require('./bukkit/plugin'); \ No newline at end of file diff --git a/src/main/resources/modules/server.js b/src/main/resources/modules/server.js new file mode 100644 index 0000000..6026f0c --- /dev/null +++ b/src/main/resources/modules/server.js @@ -0,0 +1 @@ +module.exports = require('./bukkit/server'); \ No newline at end of file diff --git a/src/main/resources/modules/task.js b/src/main/resources/modules/task.js index 3ea34b8..0b4a7b1 100644 --- a/src/main/resources/modules/task.js +++ b/src/main/resources/modules/task.js @@ -1,71 +1 @@ -'use strict'; -/*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]); - } -}; \ No newline at end of file +module.exports = require('./bukkit/task'); \ No newline at end of file diff --git a/src/main/resources/plugins/MiaoTag.js b/src/main/resources/plugins/MiaoTag.js index 048859b..b2711d5 100644 --- a/src/main/resources/plugins/MiaoTag.js +++ b/src/main/resources/plugins/MiaoTag.js @@ -7,7 +7,7 @@ var fs = require('core/fs'); var event = require('modules/event'); -var bukkit = require('modules/bukkit'); +var bukkit = require('modules/server'); var command = require('modules/command'); var papi = require('./ext/papi'); diff --git a/src/main/resources/plugins/ext/papi.js b/src/main/resources/plugins/ext/papi.js index b98d741..9fda132 100644 --- a/src/main/resources/plugins/ext/papi.js +++ b/src/main/resources/plugins/ext/papi.js @@ -4,7 +4,7 @@ */ /*global Java, base, module, exports, require, __FILE__*/ var PlaceholderAPI; -var bukkit = require('modules/bukkit'); +var bukkit = require('modules/server'); if (bukkit.plugin.load("PlaceholderAPI")) { PlaceholderAPI = ext.getStatic("me.clip.placeholderapi.PlaceholderAPI"); } else {