42 Commits
dev ... master

Author SHA1 Message Date
6b34566489 feat: optimize require module & update asm version 2024-01-08 15:43:21 +08:00
074e6e8a10 feat: remove spring support & optimize require 2023-07-30 17:32:59 +08:00
14cc05e923 feat: support BungeeCord at JDK17
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-06-21 17:28:17 +08:00
ebc03b6164 feat: release 0.23.0
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-06-02 10:23:17 +08:00
9268ce9fee feat: 调整引擎初始化逻辑
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-06-01 17:52:55 +08:00
3e4453a197 feat: v0.22.0
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-21 15:53:03 +08:00
dc66290f97 feat: use system classloader load jar
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-15 02:55:57 +08:00
56152657c8 feat: add MavenDependLoader
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-14 18:44:24 +08:00
e2f9bbf587 feat: update to 0.20.0
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-19 17:20:43 +08:00
a9003025ee feat: support mjs.json js compile & add API
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-01 18:47:28 +08:00
91a87ab20e feat: release 0.18.0 version
1. optimize load logic
2. add root change detach
3. add common softdepends

Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-02-16 14:36:22 +08:00
ce92ae9ec6 feat: optimize upgrade logic
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-11-04 11:29:37 +08:00
cfd44a6289 feat: optimize network request
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-07-10 18:29:44 +08:00
fc7fb67023 feat: 调整启动逻辑 兼容 Arclight
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-06-25 02:44:19 +00:00
7b171f1546 feat: add local version lock
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-06-22 15:05:27 +08:00
389bfd7137 feat: support java15+
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-06-22 06:03:16 +00:00
f8ba885b8c fix: require error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-03-27 09:08:17 +08:00
48306b64c4 fix: windows load error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-03-26 18:40:57 +08:00
1cb4c05efd feat: 异步加载 polyfill 并且同步加载 @ccms/core
Signed-off-by: MiaoWoo <admin@yumc.pw>
2021-03-26 15:54:26 +08:00
54e40b6768 fix: cache module load error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-12-17 18:34:57 +08:00
51fc4549da feat: 优化路径寻找逻辑
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-12-17 16:17:16 +08:00
824c440f63 fix: window restart error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-12-07 13:49:27 +08:00
7f32062dca feat: add JavaScriptTask class
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-11-20 10:30:05 +08:00
12d07bf552 feat: add package version lock logic
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-11-11 17:27:43 +08:00
0506c9e3e6 feat: update version to 0.9.4
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-09-24 14:18:39 +08:00
40760713ff fix: engineDisable override
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-09-22 16:48:44 +08:00
7e36b6109d refactor: rename EngineStartTime
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-09-21 17:09:23 +08:00
7f85295eb1 release: update to 0.9.3
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-09-21 16:06:37 +08:00
427aa262f0 feat: update to 0.9.1
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-08-27 17:30:59 +08:00
e71bd2a0cb feat: add createEngine method
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-08 11:11:18 +08:00
9bb67410ac feat: support custom NODE env
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-23 13:39:00 +08:00
eba0e18285 feat: add web filter proxy
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-15 09:30:57 +08:00
219eb6b919 feat: add web proxy support
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-05-30 10:21:17 +08:00
17d07d6cef feat: add spring full support
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-05-28 17:05:47 +08:00
f2b8f6ff26 feat: support more core module
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-05-14 19:12:03 +08:00
9a15a0ab1f feat: 新增defineProperty全局变量
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-05-11 10:20:28 +08:00
d0de120867 feat: update to 0.6.0 rename scope to ccms
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-05-03 00:31:59 +08:00
351183ab3c feat: use classpath load core js
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-04-22 09:23:21 +08:00
87c78f7c6f feat: default require nodejs module
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-04-07 22:20:53 +08:00
28db77333e feat: clean engine after disable
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-04-03 15:07:05 +08:00
ac83acd4c8 feat: add internal node moudle punycode
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-04-01 14:41:07 +08:00
876c9b77f7 feat: add nodejs internal module support
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-03-31 16:55:18 +08:00
39 changed files with 1761 additions and 1959 deletions

3
.gitignore vendored
View File

@ -53,3 +53,6 @@ vendor/
# Minecraft Data
/world
**/node_modules/
# Gradle
.gradle

0
CHANGELOG.md Normal file
View File

View File

@ -1,87 +1,11 @@
# MiaoScript
> 排版什么的 不存在的 这辈子都不会有排版的 除非什么时候论坛支持 `MarkDown` 了
> 一个兼容 Spigot Sponge Nukkit BungeeCord 的 脚本插件运行时
### 简介
## 简介
> 这个坑是我自己刨的 但是发现坑太大 需要更多的人一起填
## 安装
#### 起源
- 诞生于 `2016年08月25日` 这是 Git 上的第一个提交 具体啥时候我也忘了
- 起初 `MiaoScript` 只是用于服务器其他插件的变量执行 并且依赖于PAPI(不知道是啥的自己百度)
- 比如 [`MiaoMenu`](http://w.yumc.pw/zc/MiaoMenu.html) 的部分复杂脚本
- 比如 [`MiaoChat`](http://mcbbs.net/thread-631240-1-1.html) 的聊天变量
- 突然有一天 圈内的大佬 `QSB` @qiu1995 过来找我 说能不能用脚本监听玩家的事件
- PS: 这货自从用过 `DeluxeMenu` 之后就喜欢上了用JS写菜单
- 当初感觉没啥问题 就出了第一个简易的 `MiaoScript` 版本 还是用 yml 做的配置文件
- 但是由于设计 BukkitAPI 等内容 对Java要求太高 后来 邱也弃坑了 我也弃坑了
#### 刨坑
- 时隔多年(也就一年) 看到了Sponge的兴起 (估摸着是MCPC系列的MOD端都弃坑了)
- 同时 这期间 收到很多腐竹的单子 但是又是非常基础的东西
- 比如 开服给玩家发一条消息啦
- 比如 修改玩家某些数据啦
- 这些东西实际上也就几行代码的事情
- 同时 很多想入坑 插件开发 但是又有一些被卡死在环境搭建上
- 比如 `Bukkit` 需要 `BukkitAPI`
- `Sponge` 需要 `SpongeAPI` 如果涉及 `MOD` 还要 `Forge` 环境
- 再或者 BungeeCord 的插件开发 我也是经常懒得搞
- 当然 最主要的是 某个 咕咕咕的群 天天有人问我 喵系插件能不能支持 Sponge
- 内心当然是拒绝的 现在要上班养老婆孩子(咳咳 不要以为我是大叔 我也才刚毕业而已) 那里还有时间免费给你们写插件
- 于是乎 我又想起了当初的 `MiaoScript`
- 突发奇想 一个插件的雏形出现在我的脑海中
- 可以兼容多种服务器
- 不需要开发环境 有记事本就可以开发
- 语法要简单 比如 JavaScript
- 能够自动搜索安装依赖(毕竟很多人天天问我为何喵系插件跑不起来 都是缺少PAPI)
- 能够不重启更新插件(当然得保证代码安全的前提下)
- 在 2017年9月14号(距离 第一个版本正式版发布(2016-09-21) 相差一年整)
- 一个全新的 `MiaoScript` 诞生了
- Java部分代码 只有一个启动类
- 核心全部由 JS 编写
- 兼容 `CommonJS` 规范
- 实时重载
- 不兼容 MOD 服 (咳咳 当然现在已经支持了)
- 基础结构如下
```txt
└─src
└─main
├─java 引导类
└─resources
├─bios.js 核心启动类 用于释放文件和初始化
├─api 全平台兼容的接口
├─core 核心代码 例如 require 模块
│ └─ext 扩展代码 例如 Object.toJson()
├─internal 内部实现 用于各个平台实现API
│ ├─bukkit BukkitAPI内部实现
│ └─sponge SpongeAPI内部实现
├─modules JS模块 例如 js-yaml, http 等
└─plugins 这里当然是插件啦
├─bukkit 只兼容bukkit的插件
├─sponge 只兼容Sponge的插件
└─ext 插件扩展类库 用于多个插件共用代码 当然最好是是用 `modules` 啦
```
- 没错 第一个版本只兼容了 BukkitAPI
- 我还用 `MiaoScript` 给某位腐竹写了一个抽奖插件
- 当时因为没解决 MOD 服兼容问题 所以就退款了 放上[源码](http://paste.yumc.pw/pknd8q6e1)
- 由于当时没有封装相关的API所以很多方法是直接调用了 `Bukkit` 原生的代码
- 所以不兼容 `Sponge`
### 进展
- [项目发布](https://git.yumc.pw/502647092/MiaoScript/releases)
- [项目代码](https://git.yumc.pw/502647092/MiaoScript)
- [项目脑图](http://naotu.baidu.com/file/293b9a0fc7cef23c69de81c55e3617d5?token=1eee8fd759198eb7)
### 规划
- 初期只会支持JS类型的插件开发
- 二期会出一个建议版本的MS脚本 可以用简单的语法实现简单的功能
- 各个层级会有依赖控制 比如 `MS脚本 => JS脚本 => 调用Java原生API`
### 填坑
- 实际上说了那么多 最终希望的就是 有大佬能一起来填坑 毕竟这个坑太大了
- 下载后放入对应服务器目录
- Bukkit Nukkit BungeeCord 以及其分支 => `plugins`
- Sponge => `mods`

113
pom.xml
View File

@ -1,15 +1,14 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pw.yumc</groupId>
<artifactId>MiaoScript</artifactId>
<version>0.3.1</version>
<version>0.28.0</version>
<developers>
<developer>
<id>502647092</id>
<name>喵♂呜</name>
<name>MiaoWoo</name>
<email>admin@yumc.pw</email>
<url>http://www.yumc.pw</url>
<url>https://www.yumc.pw</url>
</developer>
</developers>
<build>
@ -17,35 +16,9 @@
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>dev-plugins/**</exclude>
</excludes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<artifactSet>
<includes>
<include>org.kamranzafar:jtar</include>
</includes>
</artifactSet>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<ciManagement>
<system>Jenkins</system>
@ -54,15 +27,65 @@
<properties>
<env.GIT_COMMIT>DEV</env.GIT_COMMIT>
<update.changes>
§623-10-25 §afeat: 升级 asm 版本;
§623-07-30 §afeat: 优化 require 性能;
      §cfix: 修复 require 加载特殊文件异常;
§623-07-22 §afeat: 网络加载 jtar 优化包大小;
      §aremove: 移除 Spring 相关支持;
      §adeps: 更新 Nashorn 和 GraalvmJS 引擎版本
</update.changes>
<update.changelog>
§622-11-22 §afeat: 兼容 1.7.10-1.19.2 版本;
      §afeat: 新增 MiaoScriptAPI 相关方法;
§622-06-21 §afeat: 兼容 1.7.10-1.19 版本;
      §afeat: 兼容 JDK17 BungeeCord;
§622-05-25 §afeat: 兼容 1.7.10-1.18.2 版本;
§622-05-21 §afeat: 优化 框架加载逻辑;
§622-05-20 §afeat: 调整 require 主包逻辑;
§622-04-09 §afeat: 优化 引擎初始化逻辑;
      §afeat: 优化 require 网络加载;
      §afeat: 新增 JS 类型定义文件;
      §afeat: 新增 自定义类型加载逻辑;
§622-02-16 §afeat: 新增 MiaoScriptAPI;
      §afeat: 新增 ScriptEvent;
      §afeat: 新增 .mjs .json 类型加载;
§622-02-16 §afeat: 优化 初始化逻辑 加快引擎加载速度;
      §afeat: 新增 root 目录变更检测 变更后重新生成缓存;
      §afeat: 添加常用库的软依赖;
§622-01-01 §afeat: 兼容JDK17 更新Nashorn;
§621-11-04 §afeat: 优化系统模块升级逻辑;
§621-07-10 §afeat: 优化网络相关功能;
§621-06-25 §afeat: 调整启动逻辑 兼容 Arclight;
§621-06-22 §afeat: 新增本地版本锁定功能;
§621-06-19 §afeat: 兼容JDK16 反射异常;
§621-05-15 §afeat: 兼容JDK15+ 自动下载Nashorn类库;
§621-03-25 §afeat: 异步加载 polyfill 并且同步加载 @ccms/core;
§621-03-25 §cfix: 修改 ployfill 为 polyfill;
§620-12-22 §cfix: 增加 require 效验;
§620-12-17 §afeat: JavaScriptTask 新增任务ID 并通过 ID 比较优先级;
§620-12-16 §afeat: 新增 require 缓存 优化路径寻找速度;
§620-12-15 §cfix: 修复 非主线程重载时发生的异常;
§620-12-07 §cfix: 修复 Windows 环境 重载异常;
§620-11-19 §afeat: 新增 JavaScriptTask 类;
§620-11-11 §afeat: 新增 package 版本锁定逻辑;
§620-09-21 §afeat: 完善 upgrade 逻辑;
§620-08-27 §afeat: 新增 ProtocolLib 依赖;
§620-07-28 §afeat: 新增框架升级功能;
§620-06-23 §afeat: 支持自定义参数;
§620-06-22 §afeat: 优化 require 加载逻辑;
§620-05-28 §afeat: 新增 Spring 的支持;
§620-05-02 §afeat: 调整 scope 为 @ccms;
§620-04-10 §afeat: 默认从 classpath 加载内建的 js 模块;
§620-04-07 §afeat: 默认初始化 内建 nodejs 模块;
§620-04-03 §afeat: 优化 框架卸载逻辑;
§620-03-31 §afeat: require 新增 内建 nodejs 模块;
§620-03-03 §afeat: require 新增 淘宝镜像源拉取;
§620-02-27 §afeat: 异步加载脚本引擎;
§620-02-25 §afeat: 新增 Nukkit 的支持;
§620-02-16 §afeat: 新增 Source Map 支持;
§620-02-02 §afeat: 迁移 ployfill 到 @ms/ployfill;
§620-01-14 §afeat: 新增 Bungee 支持;
      §afeat: 新增 instance 实例获取
</update.changes>
<update.changelog>
       §afeat: 新增 instance 实例获取;
§619-09-24 §cremove: 移除 okhttp3 类库;
      §afeat: 新增require自动下载模块功能;
§619-09-21 §afeat: 新增 okhttp3 类库;
@ -143,10 +166,6 @@
<id>yumc-repo</id>
<url>${repo.url}/repository/maven-public/</url>
</repository>
<repository>
<id>sponge</id>
<url>https://repo.spongepowered.org/maven/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
@ -165,32 +184,32 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<dependency>
<groupId>org.kamranzafar</groupId>
<artifactId>jtar</artifactId>
<version>2.3</version>
<version>1.18.24</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version>
<version>1.18.2-R0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<version>7.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.12-SNAPSHOT</version>
<version>1.16-R0.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.nukkit</groupId>
<artifactId>nukkit</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,60 +0,0 @@
package pw.yumc.MiaoScript;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Collectors;
import lombok.val;
/**
* Created with IntelliJ IDEA
*
* @author 喵♂呜
* Created on 2017/10/9 12:40.
*/
public class Base {
private Object instance;
public Base(Object instance) {
this.instance = instance;
}
public Class getClass(String name) throws ClassNotFoundException {
return Class.forName(name);
}
public Object getInstance() {
return this.instance;
}
public Class getProxyClass() {
return ProxyClass.class;
}
public String read(String path) throws IOException {
return new String(Files.readAllBytes(new File(path).toPath()), "UTF-8");
}
public void save(String path, String content) throws IOException {
File file = new File(path);
file.getParentFile().mkdirs();
Files.write(file.toPath(), content.getBytes("UTF-8"));
}
public void delete(String path) throws IOException {
delete(new File(path).toPath());
}
public void delete(Path path) throws IOException {
val file = path.toFile();
if (!file.exists()) { return; }
if (file.isDirectory()) {
for (Path f : Files.list(file.toPath()).collect(Collectors.toList())) {
delete(f);
}
}
Files.delete(path);
}
}

View File

@ -1,28 +0,0 @@
package pw.yumc.MiaoScript;
import org.bukkit.plugin.java.JavaPlugin;
import lombok.SneakyThrows;
/**
* 喵式脚本
*
* @author 喵♂呜
* @since 2016年8月29日 上午7:50:39
*/
public class MiaoScript extends JavaPlugin {
private ScriptEngine engine;
@Override
@SneakyThrows
public void onEnable() {
Thread.currentThread().setContextClassLoader(getClassLoader());
engine = new ScriptEngine(getDataFolder().getCanonicalPath(), getLogger(), this);
engine.enableEngine();
}
@Override
public void onDisable() {
engine.disableEngine();
}
}

View File

@ -0,0 +1,40 @@
package pw.yumc.MiaoScript;
import lombok.SneakyThrows;
import org.bukkit.plugin.java.JavaPlugin;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
import pw.yumc.MiaoScript.api.ScriptEngine;
/**
* 喵式脚本
*
* @author 喵♂呜
* @since 2016年8月29日 上午7:50:39
*/
public class MiaoScriptBukkit extends JavaPlugin {
private ScriptEngine engine;
@SneakyThrows
public MiaoScriptBukkit() {
ClassLoader origin = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClassLoader());
engine = MiaoScriptAPI.createEngine(getDataFolder().getCanonicalPath(), getLogger(), this);
Thread.currentThread().setContextClassLoader(origin);
engine.loadEngine();
}
@Override
public void onLoad() {
}
@Override
public void onEnable() {
engine.enableEngine();
}
@Override
public void onDisable() {
engine.disableEngine();
engine = null;
}
}

View File

@ -2,6 +2,8 @@ package pw.yumc.MiaoScript;
import lombok.SneakyThrows;
import net.md_5.bungee.api.plugin.Plugin;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
import pw.yumc.MiaoScript.api.ScriptEngine;
/**
* Created with IntelliJ IDEA
@ -12,16 +14,25 @@ import net.md_5.bungee.api.plugin.Plugin;
public class MiaoScriptBungee extends Plugin {
private ScriptEngine engine;
@Override
@SneakyThrows
public void onEnable() {
public MiaoScriptBungee() {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
engine = new ScriptEngine(getDataFolder().getCanonicalPath(), getLogger(), this);
engine = MiaoScriptAPI.createEngine(getDataFolder().getCanonicalPath(), getLogger(), this);
engine.loadEngine();
}
@Override
public void onLoad() {
}
@Override
public void onEnable() {
engine.enableEngine();
}
@Override
public void onDisable() {
engine.disableEngine();
engine = null;
}
}

View File

@ -1,182 +0,0 @@
package pw.yumc.MiaoScript;
import java.io.File;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import lombok.val;
/**
* 喵式脚本引擎
*
* @author 喵♂呜
* @since 2016年8月29日 上午7:51:43
*/
public class MiaoScriptEngine implements ScriptEngine, Invocable {
private static MiaoScriptEngine DEFAULT;
private static ScriptEngineManager manager;
private ScriptEngine engine;
static {
manager = new ScriptEngineManager(ClassLoader.getSystemClassLoader());
}
public static void setBindings(Bindings bindings) {
manager.setBindings(bindings);
}
public static Bindings getBindings() {
return manager.getBindings();
}
public MiaoScriptEngine() {
this("js");
}
public MiaoScriptEngine(final String engineType) {
this(manager, engineType);
}
public MiaoScriptEngine(ScriptEngineManager engineManager) {
this(engineManager, "js");
}
public MiaoScriptEngine(ScriptEngineManager engineManager, final String engineType) {
try {
engine = engineManager.getEngineByName(engineType);
} catch (final NullPointerException ignored) {
}
if (engine == null) {
val dirs = System.getProperty("java.ext.dirs").split(File.pathSeparator);
for (String dir : dirs) {
File nashorn = new File(dir, "nashorn.jar");
if (nashorn.exists()) {
try {
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
// 设置方法的访问权限
method.setAccessible(true);
// 获取系统类加载器
URL url = nashorn.toURI().toURL();
method.invoke(Thread.currentThread().getContextClassLoader(), url);
engineManager = new ScriptEngineManager();
engine = engineManager.getEngineByName(engineType);
} catch (NoSuchMethodException | MalformedURLException | IllegalAccessException | InvocationTargetException | NullPointerException ignored) {
}
return;
}
}
throw new UnsupportedOperationException("当前环境不支持 " + engineType + " 脚本类型!");
}
}
public static MiaoScriptEngine getDefault() {
if (DEFAULT == null) {
DEFAULT = new MiaoScriptEngine();
}
return DEFAULT;
}
@Override
public Bindings createBindings() {
return new SimpleBindings(new HashMap<>(engine.getBindings(ScriptContext.GLOBAL_SCOPE)));
}
@Override
public Object eval(final Reader reader) throws ScriptException {
return engine.eval(reader);
}
@Override
public Object eval(final Reader reader, final Bindings n) throws ScriptException {
return engine.eval(reader, n);
}
@Override
public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
return engine.eval(reader, context);
}
@Override
public Object eval(final String script) throws ScriptException {
return engine.eval(script);
}
@Override
public Object eval(final String script, final Bindings n) throws ScriptException {
return engine.eval(script, n);
}
@Override
public Object eval(final String script, final ScriptContext context) throws ScriptException {
return engine.eval(script, context);
}
@Override
public Object get(final String key) {
return engine.get(key);
}
@Override
public Bindings getBindings(final int scope) {
return engine.getBindings(scope);
}
@Override
public ScriptContext getContext() {
return engine.getContext();
}
@Override
public ScriptEngineFactory getFactory() {
return engine.getFactory();
}
@Override
public <T> T getInterface(final Class<T> clasz) {
return ((Invocable) engine).getInterface(clasz);
}
@Override
public <T> T getInterface(final Object thiz, final Class<T> clasz) {
return ((Invocable) engine).getInterface(thiz, clasz);
}
@Override
public Object invokeFunction(final String name, final Object... args) throws ScriptException, NoSuchMethodException {
return ((Invocable) engine).invokeFunction(name, args);
}
@Override
public Object invokeMethod(final Object thiz, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
return ((Invocable) engine).invokeMethod(thiz, name, args);
}
@Override
public void put(final String key, final Object value) {
engine.put(key, value);
}
@Override
public void setBindings(final Bindings bindings, final int scope) {
engine.setBindings(bindings, scope);
}
@Override
public void setContext(final ScriptContext context) {
engine.setContext(context);
}
}

View File

@ -2,6 +2,8 @@ package pw.yumc.MiaoScript;
import cn.nukkit.plugin.PluginBase;
import lombok.SneakyThrows;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
import pw.yumc.MiaoScript.api.ScriptEngine;
/**
* @author MiaoWoo
@ -9,16 +11,25 @@ import lombok.SneakyThrows;
public class MiaoScriptNukkit extends PluginBase {
private ScriptEngine engine;
@Override
@SneakyThrows
public void onEnable() {
public MiaoScriptNukkit() {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
engine = new ScriptEngine(getDataFolder().getCanonicalPath(), getLogger(), this);
engine = MiaoScriptAPI.createEngine(getDataFolder().getCanonicalPath(), super.getLogger(), this);
engine.loadEngine();
}
@Override
public void onLoad() {
}
@Override
public void onEnable() {
engine.enableEngine();
}
@Override
public void onDisable() {
engine.disableEngine();
engine = null;
}
}

View File

@ -1,17 +1,19 @@
package pw.yumc.MiaoScript;
import java.io.File;
import com.google.inject.Inject;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.GameReloadEvent;
import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import pw.yumc.MiaoScript.api.MiaoScriptAPI;
import pw.yumc.MiaoScript.api.ScriptEngine;
import com.google.inject.Inject;
import lombok.SneakyThrows;
import java.io.File;
/**
* Created with IntelliJ IDEA
@ -19,7 +21,7 @@ import lombok.SneakyThrows;
* @author 喵♂呜
* Created on 2017/10/25 20:35.
*/
@Plugin(id = "miaoscript", name = "MiaoScript", version = "1.0", authors = "喵♂呜")
@Plugin(id = "miaoscript", name = "MiaoScript", description = "MiaoScript runtime in Sponge", version = MiaoScriptAPI.VERSION, authors = "MiaoWoo")
public class MiaoScriptSponge {
private ScriptEngine engine;
@Inject
@ -31,8 +33,14 @@ public class MiaoScriptSponge {
@Listener
@SneakyThrows
public void onStart(GameStartedServerEvent event) {
engine = new ScriptEngine(pluginConfigDir.getCanonicalPath(), logger, this);
public void onPreInitialization(GamePreInitializationEvent event) {
engine = MiaoScriptAPI.createEngine(pluginConfigDir.getCanonicalPath(), logger, this);
engine.loadEngine();
}
@Listener
@SneakyThrows
public void onStarted(GameStartedServerEvent event) {
engine.enableEngine();
}
@ -40,13 +48,15 @@ public class MiaoScriptSponge {
@SneakyThrows
public void onStop(GameStoppingServerEvent event) {
engine.disableEngine();
engine = null;
}
@Listener
@SneakyThrows
public void reload(GameReloadEvent event) {
engine.disableEngine();
engine = new ScriptEngine(pluginConfigDir.getCanonicalPath(), logger, this);
System.gc();
engine.loadEngine();
engine.enableEngine();
}
}

View File

@ -1,56 +0,0 @@
package pw.yumc.MiaoScript;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.script.ScriptEngineManager;
import lombok.SneakyThrows;
/**
* Created with IntelliJ IDEA
*
* @author 喵♂呜
* Created on 2017/10/25 21:01.
*/
public class ScriptEngine {
private String root;
private Object logger;
private Base base;
private MiaoScriptEngine engine;
private ScriptEngineManager manager;
public ScriptEngine(String root, Object logger, Object instance) {
this.root = root;
this.logger = logger;
this.base = new Base(instance);
this.manager = new ScriptEngineManager();
}
@SneakyThrows
public void enableEngine() {
this.engine = new MiaoScriptEngine(manager, "nashorn");
this.engine.put("base", this.base);
this.engine.put("ScriptEngineContextHolder", this);
Path bios = Paths.get(root, "bios.js");
// 如果存在自定义bios就加载自定义的
if (Files.exists(bios)) {
this.engine.eval("load('" + bios.toFile().getCanonicalPath() + "')");
} else {
this.engine.eval(new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("bios.js")));
}
engine.invokeFunction("boot", root, logger);
}
@SneakyThrows
public void disableEngine() {
this.engine.invokeFunction("engineDisable");
this.engine = null;
}
public MiaoScriptEngine getEngine() {
return engine;
}
}

View File

@ -0,0 +1,128 @@
package pw.yumc.MiaoScript.api;
import lombok.Getter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Created with IntelliJ IDEA
*
* @author 喵♂呜
* Created on 2017/10/9 12:40.
*/
@Getter
public class Base {
private final Object instance;
Base(Object instance) {
this.instance = instance;
}
public String getVersion() {
return MiaoScriptAPI.VERSION;
}
public Class<?> getClass(String name) throws ClassNotFoundException {
try {
return Class.forName(name);
} catch (Throwable ignored) {
}
try {
return Class.forName(name, true, instance.getClass().getClassLoader());
} catch (Throwable ex) {
return Class.forName(name, true, instance.getClass().getClassLoader().getParent());
}
}
public Class<?> getProxyClass() {
return ProxyClass.class;
}
public Class<?> getJavaScriptTaskClass() {
return JavaScriptTask.class;
}
public File[] loadMavenDepend(String groupId, String artifactId, String version) {
return MiaoScriptAPI.loadMavenDepend(groupId, artifactId, version);
}
public File[] loadMavenDepend(String groupId, String artifactId, String version, ClassLoader classLoader) {
return MiaoScriptAPI.loadMavenDepend(groupId, artifactId, version, classLoader);
}
public File[] parentLoadMavenDepend(String groupId, String artifactId, String version) {
return MiaoScriptAPI.parentLoadMavenDepend(groupId, artifactId, version);
}
public String read(String path) throws IOException {
return read(Paths.get(path));
}
public String read(File file) throws IOException {
return read(file.toPath());
}
public String read(Path path) throws IOException {
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
}
public Path save(String path, String content) throws IOException {
return save(Paths.get(path), content);
}
public Path save(File file, String content) throws IOException {
return save(file.toPath(), content);
}
public Path save(Path path, String content) throws IOException {
path.getParent().toFile().mkdirs();
return Files.write(path, content.getBytes(StandardCharsets.UTF_8));
}
public boolean move(String source, String target) {
return move(new File(source), new File(target));
}
public boolean move(File source, File target) {
return source.renameTo(target);
}
public boolean delete(String path) throws IOException {
return delete(new File(path));
}
public boolean delete(Path path) throws IOException {
return delete(path.toFile());
}
public boolean delete(File file) throws IOException {
if (!file.exists()) {
return false;
}
if (file.isFile()) {
return file.delete();
}
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
if (f.isFile()) {
if (!f.delete()) {
f.deleteOnExit();
}
} else {
this.delete(f);
}
}
}
boolean result = file.delete();
if (!result) {
file.deleteOnExit();
}
return result;
}
}

View File

@ -0,0 +1,41 @@
package pw.yumc.MiaoScript.api;
import lombok.Getter;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@Getter
public class JavaScriptTask implements Delayed {
private final long id;
private final Object task;
private final long startTime;
private final long executeTime;
public JavaScriptTask(Object task, long ms) {
this(0, task, ms);
}
public JavaScriptTask(long id, Object task, long ms) {
this.id = id;
this.task = task;
this.startTime = System.currentTimeMillis();
this.executeTime = ms;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert((this.startTime + this.executeTime) - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed delayed) {
JavaScriptTask task = (JavaScriptTask) delayed;
int delay = (int) ((this.startTime + this.executeTime) - (task.getStartTime() + task.getExecuteTime()));
if (delay != 0) {
return delay;
} else {
return (int) (this.id - task.getId());
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,76 @@
package pw.yumc.MiaoScript.api;
import lombok.Getter;
import lombok.SneakyThrows;
import pw.yumc.MiaoScript.engine.MiaoScriptEngine;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Created with IntelliJ IDEA
*
* @author 喵♂呜 Created on 2017/10/25 21:01.
*/
public class ScriptEngine {
private final ClassLoader loader;
private final Object logger;
private final String root;
private final Base base;
@Getter
private MiaoScriptEngine engine;
private Object future;
ScriptEngine(String root, Object logger, Object instance) {
this.loader = Thread.currentThread().getContextClassLoader();
this.root = root;
this.logger = logger;
this.base = new Base(instance);
MiaoScriptAPI.setRoot(root);
MiaoScriptAPI.setEngine(this);
}
public void createEngine() {
synchronized (logger) {
if (this.engine == null) {
this.engine = new MiaoScriptEngine(root);
this.engine.put("base", this.base);
this.engine.put("ScriptEngineContextHolder", this);
}
}
}
@SneakyThrows
public void loadEngine() {
ClassLoader originLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.loader);
createEngine();
Path bios = Paths.get(root, "bios.js");
// 如果存在自定义bios就加载自定义的
if (Files.exists(bios)) {
this.engine.eval("load('" + bios.toFile().getCanonicalPath() + "')");
} else {
this.engine.eval("load('classpath:bios.js')");
}
future = engine.invokeFunction("boot", root, logger);
Thread.currentThread().setContextClassLoader(originLoader);
}
@SneakyThrows
public void enableEngine() {
if (this.engine != null) {
engine.invokeFunction("enable", future);
}
}
@SneakyThrows
public void disableEngine() {
synchronized (logger) {
if (this.engine != null) {
this.engine.invokeFunction("disable");
this.engine = null;
}
}
}
}

View File

@ -0,0 +1,39 @@
package pw.yumc.MiaoScript.api.bukkit;
import lombok.Getter;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import javax.script.Bindings;
@Getter
public class EngineEvent extends Event implements Cancellable {
private final String event;
private final Bindings data;
private boolean cancelled = false;
public EngineEvent(String event, Bindings data) {
this.event = event;
this.data = data;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean b) {
this.cancelled = b;
}
@Getter
private static final HandlerList handlerList = new HandlerList();
@Override
public HandlerList getHandlers() {
return handlerList;
}
}

View File

@ -0,0 +1,41 @@
package pw.yumc.MiaoScript.api.bukkit;
import lombok.Getter;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import javax.script.Bindings;
@Getter
public class ScriptEvent extends Event implements Cancellable {
private final Bindings plugin;
private final String event;
private final Bindings data;
private boolean cancelled = false;
public ScriptEvent(Bindings plugin, String event, Bindings data) {
this.plugin = plugin;
this.event = event;
this.data = data;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean b) {
this.cancelled = b;
}
@Getter
private static final HandlerList handlerList = new HandlerList();
@Override
public HandlerList getHandlers() {
return handlerList;
}
}

View File

@ -0,0 +1,67 @@
package pw.yumc.MiaoScript.api.loader;
import lombok.SneakyThrows;
import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
public class JarLoader {
private static sun.misc.Unsafe unsafe;
private static long offset;
private static Object parentUcp;
private static Object ucp;
private static MethodHandle addURLMethodHandle;
static {
initReflect();
}
@SneakyThrows
public static File load(File file) {
addURLMethodHandle.invoke(ucp, file.toURI().toURL());
return file;
}
@SneakyThrows
public static File parentLoad(File file) {
if (parentUcp == null)
throw new IllegalStateException("parentUcp is null.");
addURLMethodHandle.invoke(parentUcp, file.toURI().toURL());
return file;
}
@SneakyThrows
public static File load(File file, ClassLoader loader) {
addURLMethodHandle.invoke(unsafe.getObject(loader, offset), file.toURI().toURL());
return file;
}
private static void initReflect() {
try {
ClassLoader loader = JarLoader.class.getClassLoader();
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (sun.misc.Unsafe) theUnsafe.get(null);
Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
MethodHandles.Lookup lookup = (MethodHandles.Lookup) unsafe.getObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
Field ucpField;
try {
ucpField = loader.getClass().getDeclaredField("ucp");
} catch (NoSuchFieldException e) {
ucpField = loader.getClass().getSuperclass().getDeclaredField("ucp");
}
offset = unsafe.objectFieldOffset(ucpField);
ucp = unsafe.getObject(loader, offset);
Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class);
addURLMethodHandle = lookup.unreflect(method);
if (loader.getParent() != null)
parentUcp = unsafe.getObject(loader.getParent(), offset);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,98 @@
package pw.yumc.MiaoScript.api.loader;
import lombok.SneakyThrows;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
public class MavenDependLoader {
public static final String MavenRepo = "https://maven.aliyun.com/repository/public";
public static File[] load(String libPath, String groupId, String artifactId, String version) {
return new File[]{
downloadAndCheckSha1(libPath, groupId, artifactId, version, "pom"),
JarLoader.load(downloadAndCheckSha1(libPath, groupId, artifactId, version, "jar"))
};
}
public static File[] parentLoad(String libPath, String groupId, String artifactId, String version) {
return new File[]{
downloadAndCheckSha1(libPath, groupId, artifactId, version, "pom"),
JarLoader.parentLoad(downloadAndCheckSha1(libPath, groupId, artifactId, version, "jar"))
};
}
public static File[] load(String libPath, String groupId, String artifactId, String version, ClassLoader loader) {
return new File[]{
downloadAndCheckSha1(libPath, groupId, artifactId, version, "pom"),
JarLoader.load(downloadAndCheckSha1(libPath, groupId, artifactId, version, "jar"), loader)
};
}
@SneakyThrows
public static File downloadAndCheckSha1(String libPath, String groupId, String artifactId, String version, String ext) {
File sha1 = getMavenFile(libPath, groupId, artifactId, version, ext + ".sha1");
if (!sha1.exists()) {
downloadFile(sha1, groupId, artifactId, version, ext + ".sha1");
}
File file = getMavenFile(libPath, groupId, artifactId, version, ext);
if (!file.exists()) {
downloadFile(file, groupId, artifactId, version, ext);
}
if (!new String(Files.readAllBytes(sha1.toPath())).equals(getSha1(file))) {
sha1.delete();
file.delete();
throw new IllegalStateException("file " + file.getName() + " sha1 not match.");
}
return file;
}
public static File getMavenFile(String libPath, String groupId, String artifactId, String version, String ext) {
return Paths.get(libPath, groupId.replace(".", File.separator), artifactId, version, String.format("%s-%s.%s", artifactId, version, ext)).toFile();
}
@SneakyThrows
public static void downloadFile(File target, String groupId, String artifactId, String version, String ext) {
target.getParentFile().mkdirs();
URLConnection connection = new URL(MavenRepo +
String.format("/%1$s/%2$s/%3$s/%2$s-%3$s.%4$s",
groupId.replace(".", "/"),
artifactId,
version,
ext)
).openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(120000);
connection.setUseCaches(true);
try (InputStream inputStream = connection.getInputStream()) {
Files.copy(inputStream, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
@SneakyThrows
private static String getSha1(File file) {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
try (FileInputStream in = new FileInputStream(file)) {
MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
digest.update(byteBuffer);
return getHash(digest.digest());
}
}
private static String getHash(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
}

View File

@ -0,0 +1,22 @@
package pw.yumc.MiaoScript.api.plugin;
import javax.script.Bindings;
import java.util.Map;
public interface PluginManager {
Map<String, Bindings> getPlugins();
Bindings getPlugin(String name);
boolean has(String name);
Bindings get(String name);
boolean enable(String name);
boolean disable(String name);
boolean install(String name);
boolean uninstall(String name);
}

View File

@ -0,0 +1,268 @@
package pw.yumc.MiaoScript.engine;
import lombok.Getter;
import lombok.SneakyThrows;
import pw.yumc.MiaoScript.api.loader.JarLoader;
import pw.yumc.MiaoScript.api.loader.MavenDependLoader;
import javax.script.*;
import java.io.File;
import java.io.Reader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 喵式脚本引擎
*
* @author 喵♂呜
* @since 2016年8月29日 上午7:51:43
*/
public class MiaoScriptEngine implements ScriptEngine, Invocable {
private final String libsRoot;
@Getter
private ScriptEngine engine;
@SneakyThrows
public MiaoScriptEngine(String engineRoot) {
File libRootFile = new File(engineRoot, "libs");
libRootFile.mkdirs();
this.libsRoot = libRootFile.getCanonicalPath();
if (new File(engineRoot, "debug").exists()) {
System.setProperty("nashorn.debug", "true");
}
MavenDependLoader.load(this.libsRoot, "org.kamranzafar", "jtar", "2.3");
this.loadScriptEngine(engineRoot);
if (this.engine == null)
throw new UnsupportedOperationException("当前环境不支持 Nashorn 或 GraalJS 脚本引擎.");
}
private void loadScriptEngine(String engineRoot) {
if (new File(engineRoot, "graal").exists()) {
this.engine = this.loadNetworkGraalJS();
return;
}
if (getJavaVersion() > 15) {
this.loadGraalJS();
} else {
this.loadNashorn();
}
}
private void loadGraalJS() {
try {
this.engine = this.parentLoadNetworkNashorn();
} catch (Throwable ex) {
this.engine = this.loadNetworkNashorn();
}
if (this.engine == null) {
this.engine = this.loadNetworkGraalJS();
}
}
private void loadNashorn() {
try {
this.createEngineByName();
} catch (final Throwable ex) {
ex.printStackTrace();
}
try {
String extDirs = System.getProperty("java.ext.dirs");
if (this.engine == null && extDirs != null) {
this.engine = this.loadLocalNashorn(extDirs);
}
} catch (final Throwable ex) {
ex.printStackTrace();
}
try {
if (this.engine == null) {
this.engine = this.loadNetworkNashorn();
}
} catch (final Throwable ex) {
ex.printStackTrace();
}
if (this.engine == null)
throw new UnsupportedOperationException("当前环境不支持 Nashorn 脚本引擎.");
}
private int getJavaVersion() {
String version = System.getProperty("java.version");
if (version.startsWith("1.")) {
version = version.substring(2, 3);
} else {
int dot = version.indexOf(".");
if (dot != -1) {
version = version.substring(0, dot);
}
}
return Integer.parseInt(version);
}
private ScriptEngine loadLocalNashorn(String extDirs) {
String[] dirs = extDirs.split(File.pathSeparator);
for (String dir : dirs) {
File nashorn = new File(dir, "nashorn.jar");
if (nashorn.exists()) {
JarLoader.load(nashorn);
return this.createEngineByName();
}
}
return null;
}
private String nashornVersion = "15.4";
private String asmVersion = "9.6";
private ScriptEngine loadNetworkNashorn() {
MavenDependLoader.load(this.libsRoot, "org.openjdk.nashorn", "nashorn-core", this.nashornVersion);
MavenDependLoader.load(this.libsRoot, "org.ow2.asm", "asm", this.asmVersion);
MavenDependLoader.load(this.libsRoot, "org.ow2.asm", "asm-commons", this.asmVersion);
MavenDependLoader.load(this.libsRoot, "org.ow2.asm", "asm-tree", this.asmVersion);
MavenDependLoader.load(this.libsRoot, "org.ow2.asm", "asm-util", this.asmVersion);
return createEngineByFactoryClassName("org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory", false);
}
private ScriptEngine parentLoadNetworkNashorn() {
MavenDependLoader.parentLoad(this.libsRoot, "org.openjdk.nashorn", "nashorn-core", this.nashornVersion);
MavenDependLoader.parentLoad(this.libsRoot, "org.ow2.asm", "asm", this.asmVersion);
MavenDependLoader.parentLoad(this.libsRoot, "org.ow2.asm", "asm-commons", this.asmVersion);
MavenDependLoader.parentLoad(this.libsRoot, "org.ow2.asm", "asm-tree", this.asmVersion);
MavenDependLoader.parentLoad(this.libsRoot, "org.ow2.asm", "asm-util", this.asmVersion);
return createEngineByFactoryClassName("org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory", false);
}
private String graalVersion = "23.0.2";
private String icu4jVersion = "72.1";
@SneakyThrows
private ScriptEngine loadNetworkGraalJS() {
MavenDependLoader.load(this.libsRoot, "org.graalvm.js", "js", this.graalVersion);
MavenDependLoader.load(this.libsRoot, "com.ibm.icu", "icu4j", this.icu4jVersion);
MavenDependLoader.load(this.libsRoot, "org.graalvm.js", "js-scriptengine", this.graalVersion);
MavenDependLoader.load(this.libsRoot, "org.graalvm.regex", "regex", this.graalVersion);
MavenDependLoader.load(this.libsRoot, "org.graalvm.sdk", "graal-sdk", this.graalVersion);
MavenDependLoader.load(this.libsRoot, "org.graalvm.truffle", "truffle-api", this.graalVersion);
System.setProperty("polyglot.engine.AllowExperimentalOptions", "true");
System.setProperty("polyglot.engine.WarnInterpreterOnly", "false");
System.setProperty("polyglot.js.nashorn-compat", "true");
System.setProperty("polyglot.js.scripting", "true");
System.setProperty("polyglot.js.ecmascript-version", "5");
Class<?> NashornScriptEngineFactory = Class.forName("com.oracle.truffle.js.scriptengine.GraalJSEngineFactory");
Method getScriptEngine = NashornScriptEngineFactory.getMethod("getScriptEngine");
Object factory = NashornScriptEngineFactory.newInstance();
ScriptEngine engine = (ScriptEngine) getScriptEngine.invoke(factory);
Bindings bind = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bind.put("polyglot.js.allowAllAccess", true);
return engine;
}
@SneakyThrows
private ScriptEngine createEngineByName() {
return createEngineByFactoryClassName("jdk.nashorn.api.scripting.NashornScriptEngineFactory", true);
}
@SneakyThrows
private ScriptEngine createEngineByFactoryClassName(String factoryClassName, boolean jdk) {
Class<?> NashornScriptEngineFactory = Class.forName(factoryClassName);
Method getScriptEngine = NashornScriptEngineFactory.getMethod("getScriptEngine", String[].class);
Object factory = NashornScriptEngineFactory.newInstance();
List<String> engineArgs = new ArrayList<>();
engineArgs.add("--language=es5");
engineArgs.add("--optimistic-types=false");
if (getJavaVersion() >= 11 && jdk) {
engineArgs.add("--no-deprecation-warning");
}
return (ScriptEngine) getScriptEngine.invoke(factory, (Object) engineArgs.toArray(new String[] {}));
}
@Override
public Bindings createBindings() {
return engine.createBindings();
}
@Override
public Object eval(final Reader reader) throws ScriptException {
return engine.eval(reader);
}
@Override
public Object eval(final Reader reader, final Bindings n) throws ScriptException {
return engine.eval(reader, n);
}
@Override
public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
return engine.eval(reader, context);
}
@Override
public Object eval(final String script) throws ScriptException {
return engine.eval(script);
}
@Override
public Object eval(final String script, final Bindings n) throws ScriptException {
return engine.eval(script, n);
}
@Override
public Object eval(final String script, final ScriptContext context) throws ScriptException {
return engine.eval(script, context);
}
@Override
public Object get(final String key) {
return engine.get(key);
}
@Override
public Bindings getBindings(final int scope) {
return engine.getBindings(scope);
}
@Override
public ScriptContext getContext() {
return engine.getContext();
}
@Override
public ScriptEngineFactory getFactory() {
return engine.getFactory();
}
@Override
public <T> T getInterface(final Class<T> cls) {
return ((Invocable) engine).getInterface(cls);
}
@Override
public <T> T getInterface(final Object thiz, final Class<T> cls) {
return ((Invocable) engine).getInterface(thiz, cls);
}
@Override
public Object invokeFunction(final String name, final Object... args) throws ScriptException, NoSuchMethodException {
return ((Invocable) engine).invokeFunction(name, args);
}
@Override
public Object invokeMethod(final Object thiz, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
return ((Invocable) engine).invokeMethod(thiz, name, args);
}
@Override
public void put(final String key, final Object value) {
engine.put(key, value);
}
@Override
public void setBindings(final Bindings bindings, final int scope) {
engine.setBindings(bindings, scope);
}
@Override
public void setContext(final ScriptContext context) {
engine.setContext(context);
}
}

View File

@ -1,78 +1,97 @@
'use strict';
var log;
var boot;
var engineDisable;
'use strict'
var global = this;
/**
* Init MiaoScriptEngine Runtime
*/
/*global base ScriptEngineContextHolder*/
(function () {
var loader;
boot = function(root, logger) {
log = logger;
var Files = Java.type('java.nio.file.Files')
var Paths = Java.type('java.nio.file.Paths')
var System = Java.type('java.lang.System')
var Thread = Java.type('java.lang.Thread')
var FutureTask = Java.type('java.util.concurrent.FutureTask')
global.boot = function (root, logger) {
global.scope = System.getenv("MS_NODE_CORE_SCOPE") || "@ccms"
global.logger = logger
// Development Env Detect
root = root || "src/main/resources";
if (__FILE__ !== "<eval>") {
logger.info('Loading custom BIOS file ' + __FILE__);
global.debug = true;
}
if (java.nio.file.Files.exists(java.nio.file.Paths.get(root, "debug"))) {
logger.info('Running in debug mode...');
global.debug = true;
}
if (java.nio.file.Files.exists(java.nio.file.Paths.get(root, "level"))) {
global.level = base.read(java.nio.file.Paths.get(root, "level"))
logger.info('Set system level to [' + global.level + ']...');
}
// Check Class Loader, Sometimes Server will can't find plugin.yml file
loader = checkClassLoader();
// Force decompression core|node_modules to folder when not debug mode
release(root, '(core)+/.*', !global.debug);
// Async Loading MiaoScript Engine
new java.lang.Thread(function() {
load(root + '/core/ployfill.js')(root, logger);
engineDisable = require('@ms/core').default;
}, "MiaoScript thread").start()
};
var pluginYml;
function checkClassLoader() {
var classLoader = java.lang.Thread.currentThread().contextClassLoader;
pluginYml = classLoader.getResource("plugin.yml");
if (pluginYml === null) {
throw Error("Error class loader: " + classLoader.class.name + " Please contact the author MiaoWoo!");
} else {
log.info("Class loader compatible: " + classLoader.class.name);
if (classLoader.parent) {
log.info("Parent class loader: " + classLoader.parent.class.name);
}
}
return classLoader;
global.root = root || "src/main/resources"
readEnvironment()
if (!global.debug) { checkUpgrade() }
return bootEngineThread(checkClassLoader())
}
function release(root, regex, replace) {
var filePath = pluginYml.getFile().substring(pluginYml.getFile().indexOf("/") + 1);
var jarPath = java.net.URLDecoder.decode(filePath.substring(0, filePath.indexOf('!')));
if (!java.nio.file.Files.exists(java.nio.file.Paths.get(jarPath))) {
jarPath = "/" + jarPath;
}
var jar = new java.util.jar.JarFile(jarPath);
var r = new RegExp(regex);
jar.stream().forEach(function(entry) {
try {
if (!entry.isDirectory()) {
if (r.test(entry.name)) {
var path = java.nio.file.Paths.get(root, entry.name);
var parentFile = path.toFile().parentFile;
if (!parentFile.exists()) { parentFile.mkdirs(); }
if (!java.nio.file.Files.exists(path) || replace) {
java.nio.file.Files.copy(loader.getResourceAsStream(entry.name), path, java.nio.file.StandardCopyOption['REPLACE_EXISTING']);
}
}
}
} catch (ex) {
ex.printStackTrace();
}
function bootEngineThread(loader) {
logger.info("ScriptEngine: " + ScriptEngineContextHolder.getEngine().getEngine().class.name)
var future = new FutureTask(function () {
Thread.currentThread().contextClassLoader = loader
load(System.getenv("MS_NODE_CORE_POLYFILL") || 'classpath:core/polyfill.js')(root, logger)
var core = require(System.getenv("MS_NODE_CORE_MODULE") || (global.scope + '/core'))
return core.default || core
})
// Async Loading MiaoScript Engine
new Thread(future, "MiaoScript thread").start()
return future
}
})();
global.enable = function (future) {
// await polyfill loading
if (!future.isDone()) { logger.info("MiaoScript booting...") }
// faster load core
var core = future.get()
logger.info("MiaoScript starting...")
global.engineDisableImpl = core.enable()
}
global.disable = function () {
(global.engineDisableImpl || function () {
logger.info('Error: abnormal Initialization MiaoScript Engine. Skip disable step...')
})()
}
function readEnvironment() {
if (__FILE__.indexOf('!') === -1) {
logger.info('loading custom BIOS file ' + __FILE__)
global.debug = true
}
if (Files.exists(Paths.get(root, "debug"))) {
logger.info('running in debug mode...')
global.debug = true
}
if (Files.exists(Paths.get(root, "level"))) {
global.ScriptEngineLoggerLevel = base.read(Paths.get(root, "level"))
logger.info('found level set ScriptEngineLoggerLevel to ' + global.ScriptEngineLoggerLevel + '.')
}
if (Files.exists(Paths.get(root, "channel"))) {
global.ScriptEngineChannel = base.read(Paths.get(root, "channel"))
logger.info('found channel set ScriptEngineChannel to ' + global.ScriptEngineChannel + '.')
}
}
function checkUpgrade() {
if (Files.exists(Paths.get(root, "upgrade"))) {
logger.info('found upgrade file starting upgrade...')
base.move(Paths.get(root, "node_modules"), Paths.get(root, "old_node_modules"))
base.delete(Paths.get(root, "upgrade"))
}
new Thread(function () {
try {
base.delete(Paths.get(root, "old_node_modules"))
} catch (ex) {
}
}, "MiaoScript node_modules clean thread").start()
}
function checkClassLoader() {
// Check Class Loader, Sometimes Server will can't found plugin.yml file
var classLoader = Thread.currentThread().contextClassLoader
if (classLoader.getResource("bios.js") === null) {
throw Error("Error class loader: " + classLoader.class.name + " Please contact the author MiaoWoo!")
}
logger.info("Class loader compatible: " + classLoader.class.name)
if (classLoader.parent) {
logger.info("Parent class loader: " + classLoader.parent.class.name)
}
return classLoader
}
})()

View File

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

25
src/main/resources/core/index.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
declare const global: any
declare const root: string
declare const base: Core
declare function engineLoad(str: string | { script: string, name: string }): any
interface Core {
version: string
getClass(name: String): any
getProxyClass(): any
getJavaScriptTaskClass(): any
getInstance(): any
read(path: string): string
save(path: string, content: string): void
delete(path: string): void
}
namespace Java {
function type<T = any>(clazz: string): T
function from<T = any>(javaObj: T[]): T[]
function to<T = any>(array: T[], type?: T): T[]
function extend(...parentTypes: any[]): any
function synchronized(func: () => void, lock: any): Function
function isJavaObject(obj: any): boolean
function asJSONCompatible<T = any>(obj: T): T
//@ts-ignore
// function super(type: any);
}

View File

@ -1,21 +0,0 @@
(
/**
* @param {string} root
* @param {any} logger
*/
function(root, logger) {
// Init Global Value
global.root = root;
global.logger = logger;
global.NashornEngineStartTime = new Date().getTime()
global.engineLoad = load;
global.noop = global.engineDisable = engineDisable = function() { };
global.load = load = function __PreventGlobalLoadFunction__() { throw new Error('Internal engine system not allow use `load` function!'); }
global.setGlobal = function(key, value) { global[key] = value; };
// Init console and require
global.console = engineLoad(global.root + '/core/console.js')(logger);
console.log("Loading Engine at Thread", java.lang.Thread.currentThread().name)
global.require = engineLoad(global.root + '/core/require.js')(root);
require('@ms/ployfill')
}
);

View File

@ -0,0 +1,32 @@
/// <reference path="./index.d.ts" />
(
/**
* @param {string} root
* @param {any} logger
*/
function (root, logger) {
var System = Java.type('java.lang.System')
var Thread = Java.type('java.lang.Thread')
// Init Global Value
global.root = root
global.logger = logger
global.ScriptEngineStartTime = new Date().getTime()
global.engineLoad = load
global.noop = function () { }
global.load = load = function __PreventGlobalLoadFunction__() { throw new Error('Internal engine system not allow use `load` function!') }
global.setGlobal = function (key, value, config) {
if (config) {
config.value = value
Object.defineProperty(global, key, config)
} else {
global[key] = value
}
}
// Init console and require
global.console = engineLoad(System.getenv("MS_NODE_CORE_CONSOLE") || 'classpath:core/console.js')(logger)
console.log("Loading Engine at Thread", Thread.currentThread().name)
global.require = engineLoad(System.getenv("MS_NODE_CORE_REQUIRE") || 'classpath:core/require.js')(root)
return require(global.scope + '/polyfill')
}
)

View File

@ -24,50 +24,77 @@
* 3. 如果 xx/index.json 存在 则使用 `xx/index.json` 解析为对象加载 并停止执行
* 暂不支持 4. 如果 xx/index.msm 是一个文件 则使用MScript解析器解析 并停止执行
*/
/// <reference path="./index.d.ts" />
// @ts-check
/// <reference types="@ms/nashorn" />
(
/**
* @param {any} parent
* @param {string} root
*/
function(parent) {
'use strict';
var File = Java.type('java.io.File');
var Paths = Java.type('java.nio.file.Paths');
var Files = Java.type('java.nio.file.Files');
var StandardCopyOption = Java.type('java.nio.file.StandardCopyOption');
var FileNotFoundException = Java.type('java.io.FileNotFoundException');
function (root) {
'use strict'
var System = Java.type('java.lang.System')
var TarInputStream = Java.type('org.kamranzafar.jtar.TarInputStream');
var GZIPInputStream = Java.type('java.util.zip.GZIPInputStream');
var BufferedInputStream = Java.type('java.io.BufferedInputStream');
var File = Java.type('java.io.File')
var Paths = Java.type('java.nio.file.Paths')
var Files = Java.type('java.nio.file.Files')
var StandardCopyOption = Java.type('java.nio.file.StandardCopyOption')
var TarInputStream = Java.type('org.kamranzafar.jtar.TarInputStream')
var GZIPInputStream = Java.type('java.util.zip.GZIPInputStream')
var BufferedInputStream = Java.type('java.io.BufferedInputStream')
var URL = Java.type('java.net.URL')
var JavaString = Java.type('java.lang.String')
var separatorChar = File.separatorChar;
var ByteArrayOutputStream = Java.type("java.io.ByteArrayOutputStream")
var ByteArray = Java.type("byte[]")
var Thread = Java.type('java.lang.Thread')
var Callable = Java.type('java.util.concurrent.Callable')
var Executors = Java.type('java.util.concurrent.Executors')
var TimeUnit = Java.type('java.util.concurrent.TimeUnit')
var separatorChar = File.separatorChar
var CoreModules = ['assert', 'async_hooks', 'child_process', 'cluster', 'crypto', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'inspector', 'net', 'os', 'path', 'vm', 'url', 'util', 'zlib', 'worker_threads']
var MS_NODE_PATH = System.getenv("MS_NODE_PATH") || root + separatorChar + 'node_modules'
var MS_NODE_REGISTRY = System.getenv("MS_NODE_REGISTRY") || 'https://registry.npmmirror.com'
var MS_FALLBACK_NODE_REGISTRY = System.getenv("MS_FALLBACK_NODE_REGISTRY") || 'https://repo.yumc.pw/repository/npm'
var MS_SCRIPT_PACKAGE_CENTER = System.getenv("MS_SCRIPT_PACKAGE_CENTER") || 'https://mscript.yumc.pw/api/plugin/download'
var MS_NETWORK_CONNECT_TIMEOUT = System.getenv("MS_NETWORK_CONNECT_TIMEOUT") || 5000
var MS_NETWORK_READ_TIMEOUT = System.getenv("MS_NETWORK_TIMEOUT") || 45000
var MS_NETWORK_DOWNLOAD_TIMEOUT = System.getenv("MS_NETWORK_DOWNLOAD_TIMEOUT") || 60000
var MS_NETWORK_USE_CACHES = System.getenv("MS_NETWORK_USE_CACHES") || true
var CoreModules = [
"assert", "async_hooks", "Buffer", "child_process", "cluster", "crypto",
"dgram", "dns", "domain", "events", "fs", "http", "http2", "https",
"inspector", "net", "os", "path", "perf_hooks", "process", "punycode",
"querystring", "readline", "repl", "stream", "string_decoder",
"timer", "tls", "trace_events", "tty", "url", "util",
"v8", "vm", "wasi", "worker_threads", "zlib"
]
var VersionLockModules = {}
/**
* @param {...object} t
*/
function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
if (s === undefined) { continue; };
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
s = arguments[i]
if (s === undefined) {
continue
}
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p]
}
return t
}
return t;
};
// noinspection JSValidateJSDoc
/**
* 判断是否为一个文件
* @param {any} file
* @returns {*}
*/
function _isFile(file) {
return file.isFile && file.isFile();
return file && file.isFile && file.isFile()
}
/**
@ -76,16 +103,14 @@
* @returns {*}
*/
function _canonical(file) {
return file.canonicalPath;
return file.canonicalPath
}
/**
* 获得文件绝对路径
* @param {any} file
* @returns {*}
*/
function _absolute(file) {
return file.absolutePath;
function __error(message, name) {
var error = new Error(message)
if (name) { error.name = name }
console.error(message)
return error
}
/**
@ -93,48 +118,51 @@
* 按照下列顺序查找
* 当前目录 ./
* 父目录 ../
* 模块目录 /node_modules
* 递归模块目录 ../node_modules 到root
* 寻找 ${NODE_PATH}
* @param {string} name 模块名称
* @param {string} parent 父目录
* @param {any} optional 附加参数
*/
function resolve(name, parent) {
name = _canonical(name) || name;
function resolve(name, parent, optional) {
name = _canonical(name) || name
// 解析本地目录
if (name.startsWith('./') || name.startsWith('../')) {
return resolveAsFile(name, parent) || resolveAsDirectory(name, parent) || undefined;
if (optional.local) {
return resolveAsFile(name, parent) || resolveAsDirectory(name, parent) || undefined
} else {
// 解析 root 模块目录
var rootModule = resolveAsFile(name, MS_NODE_PATH) || resolveAsDirectory(name, MS_NODE_PATH)
if (rootModule) { return rootModule }
// 解析Node目录
var dir = [parent, 'node_modules'].join(separatorChar);
if (cacheModuleIds[name]) return cacheModuleIds[name]
cacheModuleIds[name] = resolveAsFile(name, dir) || resolveAsDirectory(name, dir) ||
// @ts-ignore
(parent && parent.toString().startsWith(root) ? resolve(name, new File(parent).getParent()) : undefined);
return cacheModuleIds[name];
var dir = [parent, 'node_modules'].join(separatorChar)
return resolveAsFile(name, dir) || resolveAsDirectory(name, dir) ||
(parent && parent.toString().startsWith(root) ?
resolve(name, new File(parent).getParent(), optional) : undefined)
}
}
/**
* 解析文件
* @param {string} file 文件
* @param {any} file 文件
* @param {string | undefined} dir 目录
* @returns {*}
*/
function resolveAsFile(file, dir) {
file = dir != undefined ? new File(dir, file) : new File(file);
file = dir !== undefined ? new File(dir, file) : new File(file)
// 直接文件
// @ts-ignore
if (file.isFile()) {
return file;
// 只解析带后缀的文件 其他文件视为非法文件
if (file.isFile() && file.name.lastIndexOf('.') != -1) {
return file
}
// JS文件
var js = new File(normalizeName(_absolute(file), '.js'));
var js = new File(normalizeName(_canonical(file), '.js'))
if (js.isFile()) {
return js;
return js
}
// JSON文件
var json = new File(normalizeName(_absolute(file), '.json'));
var json = new File(normalizeName(_canonical(file), '.json'))
if (json.isFile()) {
return json;
return json
}
}
@ -145,17 +173,20 @@
* @returns {*}
*/
function resolveAsDirectory(file, dir) {
dir = dir != undefined ? new File(dir, file) : new File(file);
var _package = new File(dir, 'package.json');
dir = dir !== undefined ? new File(dir, file) : new File(file)
var _package = new File(dir, 'package.json')
if (_package.exists()) {
// @ts-ignore
var json = JSON.parse(base.read(_package));
try {
var json = JSON.parse(base.read(_package))
if (json.main) {
return resolveAsFile(json.main, dir);
return resolveAsFile(json.main, dir) || resolveAsFile('index.js', new File(dir, json.main))
}
} catch (error) {
throw __error('resolveAsDirectory ' + dir + ' package.json error ' + error)
}
}
// if no package or package.main exists, look for index.js
return resolveAsFile('index.js', dir);
return resolveAsFile('index.js', dir)
}
/**
@ -165,57 +196,69 @@
* @returns {*}
*/
function normalizeName(fileName, ext) {
var extension = ext || '.js';
var extension = ext || '.js'
if (fileName.endsWith(extension)) {
return fileName;
return fileName
}
return fileName + extension;
return fileName + extension
}
/**
* 检查模块缓存
* @param {string} id 模块ID
* @param {string} name 模块名称
* @param {any} file 模块文件
* @param {any} optional 附加选项
* @returns {Object}
*/
function getCacheModule(id, name, file, optional) {
var module = cacheModules[id];
function getCacheModule(id, file, optional) {
var module = cacheModules[id]
if (optional.cache && module) {
return module;
return module
}
return createModule(id, name, file, optional)
return createModule(id, file, optional)
}
/**
* 编译模块
* @param {string} id 模块ID
* @param {string} name 模块名称
* @param {any} file 模块文件
* @param {any} optional 附加选项
* @returns {Object}
*/
function createModule(id, name, file, optional) {
console.trace('Loading module', name + '(' + id + ')', 'Optional', JSON.stringify(optional));
function createModule(id, file, optional) {
var filename = file.name
var lastDotIndexOf = filename.lastIndexOf('.')
if (lastDotIndexOf == -1) {
throw __error("can't require file " + file + '. error: module must include file ext.')
}
var name = filename.substring(0, lastDotIndexOf)
var ext = filename.substring(lastDotIndexOf + 1)
var loader = requireLoaders[ext]
if (!loader) {
throw __error('Unsupported module ' + filename + '. require loader not found.')
}
/**
* @type any
*/
var module = {
id: id,
name: name,
ext: ext,
parent: optional.parent,
exports: {},
loaded: false,
require: getRequire(file.parentFile, id)
};
cacheModules[id] = module;
var cfile = _canonical(file);
if (cfile.endsWith('.js')) {
compileJs(module, file, optional);
} else if (cfile.endsWith('.json')) {
compileJson(module, file);
} else if (cfile.endsWith('.msm')) {
throw Error('Unsupported MiaoScript module!');
} else {
throw Error('Unknown file type ' + cfile);
loader: loader,
path: _canonical(file.parentFile),
filename: _canonical(file),
children: []
}
return module;
module.require = getRequire(module)
if (module.parent && module.parent.children && module.parent.children.indexOf(module) == -1) {
module.parent.children.push(module)
}
console.trace('Loading module', name + '(' + id + ')', 'Optional', JSON.stringify(__assign(optional, { parent: undefined })))
cacheModules[id] = module
return loader(module, file, __assign(optional, { id: id }))
}
/**
@ -223,79 +266,216 @@
* @param {any} module JS模块
* @param {any} file JS文件
* @param {any} optional 附加选项
* @returns {void}
* @returns {any}
*/
function compileJs(module, file, optional) {
// @ts-ignore
var origin = base.read(file);
if (optional.hook) {
origin = optional.hook(origin);
function compileJsFile(module, file, optional) {
return compileJs(module, base.read(file), optional)
}
/**
* 预编译JS
* @param {any} module JS模块
* @param {any} script JS脚本
* @param {any} optional 附加选项
* @returns {any}
*/
function compileJs(module, script, optional) {
if (optional.beforeCompile) {
script = optional.beforeCompile(script)
}
// 2019-09-19 使用 扩展函数直接 load 无需保存/删除文件
// 2020-02-16 结尾新增换行 防止有注释导致加载失败
// @ts-ignore
var compiledWrapper = engineLoad({ script: '(function $(module, exports, require, __dirname, __filename) {' + origin + '\n});', name: file });
var wrapperScript = '(function (module, exports, require, __dirname, __filename) {' + script + '\n});'
var compiledWrapper = engineLoad({
script: wrapperScript,
name: optional.id
})
compiledWrapper.apply(module.exports, [
module, module.exports, module.require, file.parentFile, file
]);
module.loaded = true;
module, module.exports, module.require, module.path, module.filename
])
module.loaded = true
if (optional.afterCompile) {
module = optional.afterCompile(module) || module
}
return module
}
/**
* 预编译Json
* @param {{ id?: string | null; exports?: {}; loaded: any; require?: any; }} module Json模块
* @param {any} file Json 文件
* @returns {void}
* @returns {any}
*/
function compileJson(module, file) {
// @ts-ignore
module.exports = JSON.parse(base.read(file));
module.loaded = true;
module.exports = JSON.parse(base.read(file))
module.loaded = true
return module
}
/**
* 获得网络链接
* @param {string} url 网址
*/
function getConnection(url) {
var connection = new URL(url).openConnection()
connection.setConnectTimeout(MS_NETWORK_CONNECT_TIMEOUT)
connection.setReadTimeout(MS_NETWORK_READ_TIMEOUT)
connection.setUseCaches(MS_NETWORK_USE_CACHES)
return connection
}
/**
* 获得网络流
* @param {string} url 网址
*/
function getConnectionStream(url) {
var connection = getConnection(url)
return connection.getInputStream()
}
function splitVersionFromName(name) {
// process package name
// es6-map/implement => [es6-map/implement, undefined]
// @ccms/common/dist/reflect => [@ccms/common, undefined]
var name_arr = name.split('/')
var module_name = ''
var module_version = ''
if (name.startsWith('@')) {
var module_version_arr = name_arr[1].split('@')
module_name = name_arr[0] + '/' + module_version_arr[0]
} else {
var module_version_arr = name_arr[0].split('@')
module_name = module_version_arr[0]
}
// handle internal package version
if (name.startsWith(global.scope) && global.ScriptEngineChannel) {
module_version = global.ScriptEngineChannel
} else {
module_version = module_version_arr[1]
}
return [module_name, module_version]
}
/**
* 尝试从网络下载依赖包
* @param {string} name 包名称
* @param {string} optional 附加选项
* @param {number} retry 重试次数
*/
function download(name) {
// handle name es6-map/implement => es6-map @ms/common/dist/reflect => @ms/common
var name_arr = name.split('/');
var module_name = name.startsWith('@') ? name_arr[0] + '/' + name_arr[1] : name_arr[0];
// at windows need replace file name java.lang.IllegalArgumentException: Invalid prefix or suffix
var tempFile = Files.createTempFile(module_name.replace('/', '_'), '.json');
var info = fetchPackageInfo(module_name, tempFile);
var url = info.versions[info['dist-tags']['latest']].dist.tarball;
console.log('fetch node_module ' + module_name + ' from ' + url + ' waiting...')
var tis = new TarInputStream(new BufferedInputStream(new GZIPInputStream(new URL(url).openStream())));
// @ts-ignore
var entry; var target = root + separatorChar + 'node_modules' + separatorChar + module_name;
while ((entry = tis.getNextEntry()) != null) {
var targetPath = Paths.get(target + separatorChar + entry.getName().substring(8));
targetPath.toFile().getParentFile().mkdirs();
Files.copy(tis, targetPath, StandardCopyOption.REPLACE_EXISTING);
function download(name, optional, retry) {
var name_arr = splitVersionFromName(name)
var module_name = name_arr[0]
var module_version = name_arr[1]
try {
var target = MS_NODE_PATH + separatorChar + module_name
if (new File(target, 'package.json').exists()) { return name }
var info = fetchPackageInfo(module_name)
if (!module_version) {
// if not special version get from lock or tag
module_version = VersionLockModules[module_name]
} else if (!/\d+\.\d+\.\w+/.test(module_version)) {
// maybe module_version = latest if special version not exist then fallback latest
console.log('try get node_module ' + module_name + ' version from ' + module_version + ' tag waiting...')
module_version = info['dist-tags'][module_version]
}
if (!module_version) {
console.log('try get node_module ' + module_name + ' version from latest tag waiting...')
module_version = info['dist-tags']['latest']
}
if (!module_version) { throw __error('fetch node_module ' + module_name + " failed. can't found version from " + name + ".", 'ModuleNotFoundError') }
var _version = info.versions[module_version]
if (!_version) { throw __error('fetch node_module ' + module_name + ' version ' + module_version + " failed. can't found tarball from versions.", 'ModuleNotFoundError') }
var url = _version.dist.tarball
console.log('fetch node_module ' + module_name + ' version ' + module_version + ' waiting...')
return executor.submit(new Callable(function () {
var tis = new TarInputStream(new BufferedInputStream(new GZIPInputStream(getConnectionStream(url))))
var entry
while ((entry = tis.getNextEntry()) != null) {
var targetPath = Paths.get(target + separatorChar + entry.getName().substring(8))
var parentFile = targetPath.toFile().getParentFile()
if (!parentFile.isDirectory()) {
parentFile.delete()
parentFile.mkdirs()
}
Files.copy(tis, targetPath, StandardCopyOption.REPLACE_EXISTING)
}
return name
})).get(MS_NETWORK_DOWNLOAD_TIMEOUT, TimeUnit.MILLISECONDS)
} catch (error) {
if (error.name == 'ModuleNotFoundError') { throw error }
if (retry > 3) { throw __error('fetch node_module ' + module_name + ' version ' + module_version + ' failed. greater than 3 times stop retry.') }
console.log('fetch node_module ' + module_name + ' version ' + module_version + ' failed retrying...')
return download(name, optional, ++retry)
}
return name;
}
function fetchPackageInfo(module_name, tempFile) {
/**
* 获取包信息
* @param {string} module_name
*/
function fetchPackageInfo(module_name) {
var content = ''
try {
Files.copy(new URL('https://registry.npm.taobao.org/' + module_name).openStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
content = fetchContent(MS_NODE_REGISTRY + '/' + module_name)
} catch (ex) {
console.debug('can\'t fetch package ' + module_name + ' from taobao registry. try fetch from yumc registry...')
Files.copy(new URL('https://repo.yumc.pw/repository/npm/' + module_name).openStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
console.warn("can't fetch package " + module_name + ' from ' + MS_NODE_REGISTRY + ' registry. try fetch from ' + MS_FALLBACK_NODE_REGISTRY + ' registry...')
content = fetchContent(MS_FALLBACK_NODE_REGISTRY + '/' + module_name)
}
tempFile.toFile().deleteOnExit();
return JSON.parse(new JavaString(Files.readAllBytes(tempFile), 'UTF-8'));
return JSON.parse(content)
}
/**
* 获取网络内容
* @param {string} url 网址
* @param {number} [timeout] 超时时间
*/
function fetchContent(url, timeout) {
return executor.submit(new Callable(function fetchContent() {
var input = getConnectionStream(url)
var output = new ByteArrayOutputStream()
var buffer = new ByteArray(1024)
try {
var n
while ((n = input.read(buffer)) !== -1) {
output.write(buffer, 0, n)
}
return output.toString("UTF-8")
} finally {
input.close()
output.close()
}
})).get(timeout || MS_NETWORK_READ_TIMEOUT, TimeUnit.MILLISECONDS)
}
var lastModule = ''
/**
* 检查核心模块
* @param {string} name
* @param {string} path
*/
function checkCoreModule(name) {
if (CoreModules.indexOf(name) != -1) {
throw new Error("Can't load nodejs core module " + name + " . maybe later will auto replace to @ms/" + name + ' to compatible...')
function checkCoreModule(name, path, optional) {
if (name.startsWith('@ms') && lastModule.endsWith('.js')) {
console.warn(lastModule + ' load deprecated module ' + name + ' auto replace to ' + (name = name.replace('@ms', global.scope)) + '...')
return name
} else {
lastModule = name
}
if (CoreModules.indexOf(name) !== -1) {
var newName = global.scope + '/nodejs/dist/' + name
if (resolve(newName, path, optional) !== undefined) {
return newName
}
throw __error("can't load nodejs core module " + name + " . maybe later will auto replace to " + global.scope + "/nodejs/" + name + ' to compatible...')
}
return name
}
/**
* 检查缓存模块
*/
function checkCacheModule(optional) {
return optional.local ? cacheModuleIds[optional.parent.id] && cacheModuleIds[optional.parent.id][optional.path] : cacheModuleIds[optional.path]
}
/**
@ -306,89 +486,286 @@
* @returns {*}
*/
function _require(name, path, optional) {
checkCoreModule(name);
var file = new File(name);
file = _isFile(file) ? file : resolve(name, path);
optional = __assign({ cache: true }, optional);
// require direct file
var file = _isFile(name) ? name : new File(name)
if (_isFile(file) && file.name.lastIndexOf('.') != -1) {
return _requireFile(file, optional)
}
// require cache module
var cachePath = checkCacheModule(optional)
var cacheFile = new File(cachePath)
if (cachePath && cacheFile.exists()) {
return _requireFile(cacheFile, optional)
}
// check core module
name = checkCoreModule(name, path, optional)
var file = resolve(name, path, optional)
// search module
if (file === undefined) {
try {
// excloud local dir, prevent too many recursive call and cache not found module
if (name.startsWith('.') || name.startsWith('/') || optional.recursive || notFoundModules[name]) {
console.log(name, path, optional, notFoundModules[name])
throw new Error("Can't found module " + name + '(' + JSON.stringify(optional) + ') at local ' + path + ' or network!')
if (optional.local || optional.recursive || notFoundModules[name]) {
delete optional.parent
throw __error("can't found module " + name + '(' + JSON.stringify(optional) + ') at local ' + path + ' or network!')
}
optional.recursive = true;
return _require(download(name), path, optional);
} catch (ex) {
notFoundModules[name] = true;
throw new FileNotFoundException("Can't found module " + name + ' in directory ' + path + ' ERROR: ' + ex)
optional.recursive = true
return _require(download(name, optional, 1), path, optional)
}
setCacheModule(file, optional)
return _requireFile(file, optional)
}
/**
* 设置模块缓存
* @param {any} file
* @param {any} optional
*/
function setCacheModule(file, optional) {
if (optional.local) {
var parent = cacheModuleIds[optional.parent.id]
if (!parent) {
cacheModuleIds[optional.parent.id] = {}
}
return cacheModuleIds[optional.parent.id][optional.path] = _canonical(file)
}
return cacheModuleIds[optional.path] = _canonical(file)
}
function _requireFile(file, optional) {
// 重定向文件名称和类型
return getCacheModule(_canonical(file), file.name.split('.')[0], file, optional);
return getCacheModule(_canonical(file), file, optional)
}
/**
* 闭包方法
* @param {string} parent 父目录
* @param {string} parentId
* @param {any} parent 父模块
* @returns {Function}
*/
function exports(parent, parentId) {
var __DynamicRequire__ =
function exports(parent) {
/**
* @param {string} path
* @param {any} optional
*/
function __DynamicRequire__(path, optional) {
return _require(path, parent, __assign({ parentId: parentId }, optional)).exports;
var require = function __DynamicRequire__(path, optional) {
if (!path) {
throw __error("require path can't be undefined or empty!")
}
return __DynamicRequire__
var optional = __assign({
cache: true,
parent: parent,
path: path,
local: path.startsWith('.') || path.startsWith('/')
}, optional)
return _require(path, parent.path, optional).exports
}
require.resolve = function __DynamicResolve__(path, optional) {
return _canonical(new File(resolve(path, root, __assign({
parent: parent,
cache: true,
local: path.startsWith('.') || path.startsWith('/')
}, optional))))
}
return require
}
/**
* @param {string} parent
* @param {string} parentId
*/
function getRequire(parent, parentId) {
/**
* @type {any} require
*/
var require = exports(parent, parentId)
require.resolve =
/**
* @param {string} name
*/
function __DynamicResolve__(name) {
return _canonical(new File(resolve(name, parent)))
}
require.clear =
/**
* @param {string} name
*/
function __DynamicClear__(name) {
var moduleId = require.resolve(name)
console.trace('Clear module ' + name + '(' + moduleId + ') ...')
return delete cacheModules[moduleId]
for (var cacheModule in cacheModules) {
if (cacheModule.indexOf(name) !== -1) {
console.trace('clear module ' + cacheModule + ' ...')
delete cacheModules[cacheModule]
}
return require;
}
}
function __DynamicDisable__() {
base.save(cacheModuleIdsFile, JSON.stringify(upgradeMode ? {} : cacheModuleIds))
for (var cacheModule in cacheModules) {
delete cacheModules[cacheModule]
}
cacheModules = {}
for (var cacheModuleId in cacheModuleIds) {
delete cacheModuleIds[cacheModuleId]
}
cacheModuleIds = {}
notFoundModules = {}
}
function __setUpgradeMode__(status) {
upgradeMode = status
}
/**
* @param {any} parent
*/
function getRequire(parent) {
/**
* @type {any} require
*/
var require = exports(parent)
require.main = mainRequire
require.cache = cacheModules
require.clear = __DynamicClear__
require.disable = __DynamicDisable__
require.setUpgradeMode = __setUpgradeMode__
require.loader = {
register: registerLoader,
get: getLoader,
unregister: unregisterLoader,
}
require.loaders = requireLoaders
require.internal = {
coreModules: CoreModules,
cacheModules: cacheModules,
cacheModuleIds: cacheModuleIds,
notFoundModules: notFoundModules,
versionLockModules: VersionLockModules
}
require.loadCoreScript = loadCoreScript
return require
}
/**
* @param {string} ext
* @param {any} loader
*/
function registerLoader(ext, loader) {
if (requireLoaders[ext]) {
return console.error('require loader ' + ext + ' already register ignore. if you want override loader please unregister before register.')
}
requireExts.push(ext)
requireLoaders[ext] = loader
console.info('register require loader ' + ext + ' => ' + (loader.name || '<anonymous>') + '.')
}
/**
* @param {*} ext
*/
function getLoader(ext) {
return requireLoaders[ext]
}
/**
* @param {*} ext
*/
function unregisterLoader(ext) {
requireExts.splice(requireExts.indexOf(ext), 1);
delete requireLoaders[ext]
console.info('unregister require loader ' + ext + '.')
}
function printRequireInfo() {
console.info('Initialization require module.')
console.info('ParentDir:', root)
console.info('Require module env list:')
console.info('- JAVA_VERSION:', System.getProperty("java.version"))
console.info('- PLUGIN_VERSION:', base.version)
console.info('- MS_NODE_PATH:', MS_NODE_PATH.startsWith(root) ? MS_NODE_PATH.split(root)[1] : MS_NODE_PATH)
console.info('- MS_NODE_REGISTRY:', MS_NODE_REGISTRY)
console.info('- MS_FALLBACK_NODE_REGISTRY:', MS_FALLBACK_NODE_REGISTRY)
}
function initCacheModuleIds() {
try {
cacheModuleIds = JSON.parse(base.read(cacheModuleIdsFile))
if (cacheModuleIds['@ccms-cache-module-root'] != MS_NODE_PATH) {
throw __error('canonicalRoot Change ' + cacheModuleIds['@ccms-cache-module-root'] + ' to ' + MS_NODE_PATH + ' Clear Cache!')
}
console.log('Read cacheModuleIds from file', cacheModuleIdsFile.startsWith(root) ? cacheModuleIdsFile.split(root)[1] : cacheModuleIdsFile)
} catch (error) {
cacheModuleIds = {}
cacheModuleIds['@ccms-cache-module-root'] = MS_NODE_PATH
console.log('Initialization new cacheModuleIds: ' + error)
}
}
function initVersionLock() {
try {
var version_lock_url = MS_SCRIPT_PACKAGE_CENTER + '?name=version_lock' + (global.debug ? '-debug' : '')
VersionLockModules = JSON.parse(fetchContent(version_lock_url, 5000))
try {
VersionLockModules = __assign(VersionLockModules, JSON.parse(base.read(localVersionLockFile)))
} catch (e) {
}
} catch (error) {
console.warn("无法获取到最新的版本锁定信息 使用默认配置.")
console.warn("InitVersionLock Error:", error)
console.debug(error)
VersionLockModules = {
"@babel/standalone": "7.12.18",
"crypto-js": "3.3.0",
"core-js": "3.33.1"
}
}
console.info('Lock module version List:')
for (var key in VersionLockModules) {
console.info('- ' + key + ': ' + VersionLockModules[key])
}
}
function initRequireLoader(require) {
registerLoader('js', compileJsFile)
registerLoader('json', compileJson)
try {
loadCoreScript('require_loader')(require)
} catch (error) {
console.warn("无法获取到最新的加载器信息 使用默认配置.")
console.warn("InitRequireLoader Error:", error)
console.debug(error)
registerLoader('ms', compileJsFile)
}
require.main = mainRequire = require
return require
}
function loadCoreScript(name) {
return engineLoad({
script: fetchContent(MS_SCRIPT_PACKAGE_CENTER + '?name=' + name, 5000),
name: 'core/' + name + '.js'
})
}
if (typeof parent === 'string') {
parent = new File(parent);
parent = new File(parent)
}
var mainRequire = undefined
/**
* @type {{[key:string]:any}} cacheModules
* require 支持的后缀
* @type {string[]} requireExts
*/
var cacheModules = {};
var requireExts = []
/**
* @type {{[key:string]:string}} cacheModules
* require加载器
* @type {{[key:string]:(module:any, file:string, optional?:any)=>any}} requireLoader
*/
var cacheModuleIds = {};
var requireLoaders = {}
/**
* @type {{[key:string]:boolean}} cacheModules
* 已缓存的模块
* @type {{[key:string]:any}} [cacheModules]
*/
var notFoundModules = {};
console.info('Initialization require module. ParentDir:', _canonical(parent));
return getRequire(parent, "null");
});
var cacheModules = {}
var cacheModuleIdsFile = _canonical(new File(MS_NODE_PATH, 'cacheModuleIds.json'))
var localVersionLockFile = _canonical(new File(MS_NODE_PATH, 'moduleVersionLock.json'))
/**
* 已缓存的模块ID
* @type {{[key:string]:{[key:string]:string}|string}} [cacheModuleIds]
*/
var cacheModuleIds = {}
/**
* 未找到的模块
* @type {{[key:string]:boolean}}
*/
var notFoundModules = {}
var upgradeMode = false
var executor = Executors.newSingleThreadExecutor(function (r) {
return new Thread(r, "MiaoScript require thread")
})
printRequireInfo()
initCacheModuleIds()
initVersionLock()
return initRequireLoader(getRequire({
id: 'main',
path: root
}))
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,26 @@
name: ${project.artifactId}
description: ${project.description}
main: ${project.groupId}.${project.artifactId}.${project.artifactId}
main: ${project.groupId}.${project.artifactId}.${project.artifactId}Bukkit
version: ${project.version}
api-version: 1.13
author: MiaoWoo
website: ${ciManagement.url}
load: STARTUP
softdepend:
- OriginAttribute
- AttributeSystem
- ItemLoreOrigin
- PlaceholderAPI
- AttributePlus
- PlayerPoints
- SX-Attribute
- CrazyCrates
- ProtocolLib
- DragonCore
- MythicMobs
- WorldGuard
- Adyeshach
- WorldEdit
- SkillAPI
- TradeMe
- Chemdah
- Vault