Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
cfbed99ece | |||
8c6af8e95e | |||
3dab447178 | |||
fe2e658c50 | |||
41146a74be | |||
f2ed45b524 | |||
db812c13b5 | |||
8d7667f473 | |||
8ea030205e | |||
9900f0a014 | |||
eae09072ad | |||
c628a269f4 |
25
.gitea/workflows/main.yaml
Normal file
25
.gitea/workflows/main.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Publish Project
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
- name: Build Project
|
||||
run: chmod +x gradlew && ./gradlew build --no-daemon
|
||||
- name: Publish to Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: build/*.jar
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 MiniDay
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
12
README.md
12
README.md
@@ -15,15 +15,15 @@
|
||||
|
||||
> JavaScript 代码执行时,执行命令的对象(玩家或控制台)会作为 sender 变量传入
|
||||
|
||||
| 指令 | 权限 | 描述 |
|
||||
|:-------------------|:---------------------|:------------------------|
|
||||
| script eval {脚本内容} | hamster.script.admin | 直接执行 JavaScript 脚本 |
|
||||
| script run {脚本文件} | hamster.script.admin | 执行文件中的 JavaScript 代码 |
|
||||
| script reload | hamster.script.admin | 重载插件并重设 JavaScript 引擎环境 |
|
||||
| 指令 | 权限 | 描述 |
|
||||
|:--------------------|:---------------------|:------------------------|
|
||||
| /script eval {脚本内容} | hamster.script.admin | 直接执行 JavaScript 脚本 |
|
||||
| /script run {脚本文件} | hamster.script.admin | 执行文件中的 JavaScript 代码 |
|
||||
| /script reload | hamster.script.admin | 重载插件并重设 JavaScript 引擎环境 |
|
||||
|
||||
# 注意事项
|
||||
|
||||
默认情况下禁止使用 `/script eval {脚本内容}` 指令。
|
||||
默认情况下禁止使用 `/script eval {脚本内容}` 指令。
|
||||
如有需要,可以在 `config.yml` 中将 `enable-eval-command` 设置为 `true` 并重载插件以启用该子指令。
|
||||
|
||||
# nashorn 学习资料
|
||||
|
@@ -2,11 +2,13 @@
|
||||
|
||||
plugins {
|
||||
id("java")
|
||||
id("com.github.johnrengelman.shadow") version "8+"
|
||||
}
|
||||
base {
|
||||
archivesName = "HamsterScript"
|
||||
}
|
||||
|
||||
group = "cn.hamster3.mc.plugin"
|
||||
version = "1.0.6"
|
||||
version = "1.1.0"
|
||||
description = "为Minecraft服务器导入 Nashorn 引擎来执行 JavaScript 脚本"
|
||||
|
||||
repositories {
|
||||
@@ -23,48 +25,27 @@ dependencies {
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||
|
||||
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
|
||||
}
|
||||
|
||||
// https://mvnrepository.com/artifact/org.openjdk.nashorn/nashorn-core
|
||||
implementation("org.openjdk.nashorn:nashorn-core:15.4")
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
withSourcesJar()
|
||||
}
|
||||
processResources {
|
||||
filesMatching("plugin.yml") {
|
||||
expand(project.properties)
|
||||
}
|
||||
val map = mutableMapOf<String, String>()
|
||||
map["BUILD_ID"] = System.getenv().getOrDefault("BUILD_ID", "DEV")
|
||||
map["BUILD_NUMBER"] = System.getenv().getOrDefault("BUILD_NUMBER", "DEV")
|
||||
map["BUILD_DISPLAY_NAME"] = System.getenv().getOrDefault("BUILD_DISPLAY_NAME", "DEV")
|
||||
map["JOB_URL"] = System.getenv().getOrDefault("JOB_URL", "DEV")
|
||||
map["BUILD_URL"] = System.getenv().getOrDefault("BUILD_URL", "DEV")
|
||||
map["GIT_COMMIT"] = System.getenv().getOrDefault("GIT_COMMIT", "DEV")
|
||||
filesMatching("jenkins.yml") {
|
||||
expand(map)
|
||||
}
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
jar {
|
||||
destinationDirectory = rootProject.layout.buildDirectory
|
||||
}
|
||||
shadowJar {
|
||||
destinationDirectory = rootProject.layout.buildDirectory
|
||||
}
|
||||
withType<JavaCompile>().configureEach {
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
withType<Jar>().configureEach {
|
||||
archiveBaseName = "HamsterScript"
|
||||
withType<Jar> {
|
||||
from(rootProject.file("LICENSE"))
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
destinationDirectory = rootProject.layout.buildDirectory
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +1 @@
|
||||
rootProject.name = "hamster-script"
|
||||
|
||||
|
@@ -1,205 +1,55 @@
|
||||
package cn.hamster3.mc.plugin.script;
|
||||
|
||||
import cn.hamster3.mc.plugin.script.command.ScriptCommand;
|
||||
import cn.hamster3.mc.plugin.script.core.ScriptManager;
|
||||
import cn.hamster3.mc.plugin.script.listener.MainListener;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.RegisteredListener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public class HamsterScriptPlugin extends JavaPlugin {
|
||||
private static File codeFolder;
|
||||
@Getter
|
||||
private static ScriptEngine engine;
|
||||
@Getter
|
||||
private static Invocable invocable;
|
||||
private static HamsterScriptPlugin instance;
|
||||
|
||||
private boolean enableEvalCommand;
|
||||
private Map<String, String> importClass;
|
||||
@Override
|
||||
public void onLoad() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
reload();
|
||||
codeFolder = new File(getDataFolder(), "code");
|
||||
if (codeFolder.mkdirs()) {
|
||||
getLogger().info("创建代码文件夹: " + codeFolder.getAbsolutePath());
|
||||
try {
|
||||
Files.copy(
|
||||
Objects.requireNonNull(getResource("code/example.js")),
|
||||
new File(codeFolder, "example.js").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
ScriptManager.init();
|
||||
for (HandlerList list : HandlerList.getHandlerLists()) {
|
||||
list.register(new RegisteredListener(
|
||||
MainListener.INSTANCE,
|
||||
(listener, event) -> MainListener.INSTANCE.onEvent(event),
|
||||
EventPriority.NORMAL,
|
||||
this,
|
||||
false
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
saveDefaultConfig();
|
||||
reloadConfig();
|
||||
FileConfiguration config = getConfig();
|
||||
enableEvalCommand = config.getBoolean("enable-eval-command", false);
|
||||
engine = new ScriptEngineManager(getClassLoader()).getEngineByName("JavaScript");
|
||||
invocable = (Invocable) engine;
|
||||
|
||||
importClass = new HashMap<>();
|
||||
ConfigurationSection importConfig = config.getConfigurationSection("import");
|
||||
if (importConfig != null) {
|
||||
for (String simpleName : importConfig.getKeys(false)) {
|
||||
String className = importConfig.getString(simpleName);
|
||||
importClass.put(simpleName, className);
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
engine.put(simpleName, clazz);
|
||||
engine.eval(String.format("%s = %s.static;", simpleName, simpleName));
|
||||
getLogger().info("已导入 " + className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
getLogger().warning("导入 " + className + " 失败:未找到这个类");
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("导入 " + className + " 失败");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
public ClassLoader getPluginClassLoader() {
|
||||
return getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("hamster.script.admin")) {
|
||||
sender.sendMessage("§c你没有这个权限");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§c请输入命令");
|
||||
return true;
|
||||
}
|
||||
switch (args[0]) {
|
||||
case "eval": {
|
||||
if (!enableEvalCommand) {
|
||||
sender.sendMessage("§c当前不允许直接执行 JavaScript");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("§c请输入 JavaScript 代码内容");
|
||||
return true;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
builder.append(args[i]).append(" ");
|
||||
}
|
||||
String code = builder.toString();
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Bindings bindings = engine.createBindings();
|
||||
for (String simpleName : importClass.keySet()) {
|
||||
bindings.put(simpleName, engine.get(simpleName));
|
||||
}
|
||||
bindings.put("sender", sender);
|
||||
Object eval = engine.eval(code, bindings);
|
||||
long time = System.currentTimeMillis() - start;
|
||||
sender.sendMessage("§aJavaScript 代码执行完成, 耗时: " + time + " 毫秒");
|
||||
sender.sendMessage("§a返回值: §f" + eval);
|
||||
} catch (Exception e) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
sender.sendMessage("§cJavaScript 代码执行出错, 耗时: " + time + " 毫秒");
|
||||
sender.sendMessage("§c异常原因: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "run": {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("§c请输入 JavaScript 文件名称");
|
||||
return true;
|
||||
}
|
||||
File file = new File(codeFolder, args[1]);
|
||||
if (!file.exists() && !args[1].endsWith(".js")) {
|
||||
file = new File(codeFolder, args[1] + ".js");
|
||||
}
|
||||
if (!file.exists()) {
|
||||
sender.sendMessage("§c未找到 JavaScript 文件: " + file.getAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
String code;
|
||||
try {
|
||||
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
code = String.join("\n", lines);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage("§c读取 JavaScript 文件内容 " + file.getAbsolutePath() + " 时出错!");
|
||||
e.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Bindings bindings = engine.createBindings();
|
||||
for (String simpleName : importClass.keySet()) {
|
||||
bindings.put(simpleName, engine.get(simpleName));
|
||||
}
|
||||
String[] scriptArgs = Arrays.copyOfRange(args, 2, args.length);
|
||||
bindings.put("sender", sender);
|
||||
bindings.put("args", scriptArgs);
|
||||
Object eval = engine.eval(code, bindings);
|
||||
long time = System.currentTimeMillis() - start;
|
||||
sender.sendMessage("§aJavaScript 代码执行完成, 耗时: " + time + " 毫秒");
|
||||
sender.sendMessage("§a返回值: §f" + eval);
|
||||
} catch (Exception e) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
sender.sendMessage("§cJavaScript 代码执行出错, 耗时: " + time + " 毫秒");
|
||||
sender.sendMessage("§c异常原因: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "reload": {
|
||||
reload();
|
||||
sender.sendMessage("§a插件重载完成");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sender.sendMessage("§c未找到该命令");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return ScriptCommand.INSTANCE.onCommand(sender, command, label, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("eval");
|
||||
list.add("run");
|
||||
list.add("reload");
|
||||
String startWith = args[0].toLowerCase();
|
||||
list.removeIf(o -> !o.startsWith(startWith));
|
||||
return list;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("run")) {
|
||||
File[] files = codeFolder.listFiles();
|
||||
if (files != null) {
|
||||
String startWith = args[1].toLowerCase();
|
||||
return Arrays.stream(files)
|
||||
.map(File::getName)
|
||||
.filter(o -> o.toLowerCase().startsWith(startWith))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return ScriptCommand.INSTANCE.onTabComplete(sender, command, alias, args);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,119 @@
|
||||
package cn.hamster3.mc.plugin.script.command;
|
||||
|
||||
import cn.hamster3.mc.plugin.script.HamsterScriptPlugin;
|
||||
import cn.hamster3.mc.plugin.script.core.ScriptManager;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ScriptCommand implements TabExecutor {
|
||||
public static final ScriptCommand INSTANCE = new ScriptCommand();
|
||||
|
||||
private ScriptCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("hamster.script.admin")) {
|
||||
sender.sendMessage("§c你没有这个权限");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§c请输入命令");
|
||||
return true;
|
||||
}
|
||||
switch (args[0]) {
|
||||
case "run": {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("§c请输入 JavaScript 文件名称");
|
||||
return true;
|
||||
}
|
||||
File file = new File(ScriptManager.getCodeFolder(), args[1]);
|
||||
if (!file.exists() && !args[1].endsWith(".js")) {
|
||||
file = new File(ScriptManager.getCodeFolder(), args[1] + ".js");
|
||||
}
|
||||
if (!file.exists()) {
|
||||
sender.sendMessage("§c未找到 JavaScript 文件: " + file.getAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
String code;
|
||||
try {
|
||||
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
code = String.join("\n", lines);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage("§c读取 JavaScript 文件内容 " + file.getAbsolutePath() + " 时出错!");
|
||||
HamsterScriptPlugin.getInstance().getLogger().log(Level.SEVERE, "", e);
|
||||
return true;
|
||||
}
|
||||
String[] scriptArgs = Arrays.copyOfRange(args, 2, args.length);
|
||||
ScriptManager.eval(sender, code, scriptArgs);
|
||||
break;
|
||||
}
|
||||
case "eval": {
|
||||
if (!ScriptManager.isEnableEvalCommand()) {
|
||||
sender.sendMessage("§c当前不允许直接执行 JavaScript");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("§c请输入 JavaScript 代码内容");
|
||||
return true;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
builder.append(args[i]).append(" ");
|
||||
}
|
||||
String code = builder.toString();
|
||||
ScriptManager.eval(sender, code);
|
||||
break;
|
||||
}
|
||||
case "reload": {
|
||||
ScriptManager.init();
|
||||
sender.sendMessage("§a插件重载完成");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sender.sendMessage("§c未找到该命令");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("run");
|
||||
list.add("eval");
|
||||
list.add("reload");
|
||||
String startWith = args[0].toLowerCase();
|
||||
list.removeIf(o -> !o.toLowerCase().startsWith(startWith));
|
||||
return list;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("run")) {
|
||||
File[] files = ScriptManager.getCodeFolder().listFiles();
|
||||
if (files != null) {
|
||||
String startWith = args[1].toLowerCase();
|
||||
return Arrays.stream(files)
|
||||
.map(File::getName)
|
||||
.filter(o -> o.toLowerCase().startsWith(startWith))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
package cn.hamster3.mc.plugin.script.core;
|
||||
|
||||
import cn.hamster3.mc.plugin.script.HamsterScriptPlugin;
|
||||
import cn.hamster3.mc.plugin.script.data.EventCode;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.script.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ScriptManager {
|
||||
@Getter
|
||||
private static File codeFolder;
|
||||
@Getter
|
||||
private static ScriptEngine scriptEngine;
|
||||
@Getter
|
||||
private static Invocable invocable;
|
||||
|
||||
@Getter
|
||||
private static boolean enableEvalCommand;
|
||||
@Getter
|
||||
private static boolean enableEventCode;
|
||||
@Getter
|
||||
private static Map<String, String> importClass;
|
||||
|
||||
@Getter
|
||||
private static List<EventCode> eventCodes;
|
||||
|
||||
public static void init() {
|
||||
HamsterScriptPlugin plugin = HamsterScriptPlugin.getInstance();
|
||||
plugin.saveDefaultConfig();
|
||||
plugin.reloadConfig();
|
||||
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
|
||||
enableEvalCommand = config.getBoolean("enable-eval-command", false);
|
||||
enableEventCode = config.getBoolean("enable-event-code", false);
|
||||
scriptEngine = new ScriptEngineManager(plugin.getPluginClassLoader()).getEngineByName("JavaScript");
|
||||
invocable = (Invocable) scriptEngine;
|
||||
|
||||
Logger logger = plugin.getLogger();
|
||||
importClass = new HashMap<>();
|
||||
ConfigurationSection importConfig = config.getConfigurationSection("import");
|
||||
if (importConfig != null) {
|
||||
for (String simpleName : importConfig.getKeys(false)) {
|
||||
String className = importConfig.getString(simpleName);
|
||||
importClass.put(simpleName, className);
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
scriptEngine.put(simpleName, clazz);
|
||||
scriptEngine.eval(String.format("%s = %s.static;", simpleName, simpleName));
|
||||
logger.info("已导入 " + className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.log(Level.WARNING, "导入 " + className + " 失败:未找到这个类", e);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "导入 " + className + " 失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
codeFolder = new File(plugin.getDataFolder(), "code");
|
||||
if (codeFolder.mkdirs()) {
|
||||
plugin.getLogger().info("创建代码文件夹: " + codeFolder.getAbsolutePath());
|
||||
try {
|
||||
Files.copy(
|
||||
Objects.requireNonNull(plugin.getResource("code/example.js")),
|
||||
new File(codeFolder, "example.js").toPath()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
HamsterScriptPlugin.getInstance().getLogger().log(Level.SEVERE, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
File eventFolder = new File(plugin.getDataFolder(), "event");
|
||||
if (eventFolder.mkdirs()) {
|
||||
plugin.getLogger().info("创建事件文件夹: " + codeFolder.getAbsolutePath());
|
||||
try {
|
||||
Files.copy(
|
||||
Objects.requireNonNull(plugin.getResource("event/example.js")),
|
||||
new File(eventFolder, "example.js").toPath()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
HamsterScriptPlugin.getInstance().getLogger().log(Level.SEVERE, "", e);
|
||||
}
|
||||
}
|
||||
eventCodes = loadEventCodes(eventFolder);
|
||||
}
|
||||
|
||||
private static List<EventCode> loadEventCodes(@NotNull File file) {
|
||||
ArrayList<EventCode> list = new ArrayList<>();
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File subFile : files) {
|
||||
List<EventCode> loaded = loadEventCodes(subFile);
|
||||
list.addAll(loaded);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
try {
|
||||
EventCode code = new EventCode(file);
|
||||
list.add(code);
|
||||
} catch (Exception e) {
|
||||
HamsterScriptPlugin.getInstance().getLogger().log(Level.WARNING, "加载事件代码 " + file.getAbsolutePath() + " 失败", e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Bindings createBindings() {
|
||||
Bindings bindings = ScriptManager.getScriptEngine().createBindings();
|
||||
for (String simpleName : importClass.keySet()) {
|
||||
bindings.put(simpleName, ScriptManager.getScriptEngine().get(simpleName));
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
public static Object eval(@NotNull String code) throws ScriptException {
|
||||
Bindings bindings = createBindings();
|
||||
return ScriptManager.getScriptEngine().eval(code, bindings);
|
||||
}
|
||||
|
||||
public static void eval(@NotNull CommandSender sender, @NotNull String code, @NotNull String... args) {
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Bindings bindings = createBindings();
|
||||
bindings.put("sender", sender);
|
||||
bindings.put("args", args);
|
||||
Object eval = ScriptManager.getScriptEngine().eval(code, bindings);
|
||||
long time = System.currentTimeMillis() - start;
|
||||
sender.sendMessage("§aJavaScript 代码执行完成, 耗时: " + time + " 毫秒");
|
||||
sender.sendMessage("§a返回值: §f" + eval);
|
||||
} catch (Exception e) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
sender.sendMessage("§cJavaScript 代码执行出错, 耗时: " + time + " 毫秒");
|
||||
sender.sendMessage("§c异常原因: " + e.getMessage());
|
||||
HamsterScriptPlugin.getInstance().getLogger().log(Level.SEVERE, "", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package cn.hamster3.mc.plugin.script.data;
|
||||
|
||||
import cn.hamster3.mc.plugin.script.core.ScriptManager;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.event.Event;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
||||
@Getter
|
||||
public class EventCode {
|
||||
@NotNull
|
||||
private final String id;
|
||||
@NotNull
|
||||
private final Class<?> clazz;
|
||||
@NotNull
|
||||
private final Object object;
|
||||
|
||||
public EventCode(@NotNull File file) throws IOException, ScriptException, NoSuchMethodException, ClassNotFoundException {
|
||||
String code = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
|
||||
object = ScriptManager.eval(code);
|
||||
id = ScriptManager.getInvocable().invokeMethod(object, "getID").toString();
|
||||
clazz = Class.forName(ScriptManager.getInvocable().invokeMethod(object, "getEventType").toString());
|
||||
}
|
||||
|
||||
public void invoke(@NotNull Event event) throws ScriptException, NoSuchMethodException {
|
||||
ScriptManager.getInvocable().invokeMethod(object, "invoke", event);
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package cn.hamster3.mc.plugin.script.listener;
|
||||
|
||||
import cn.hamster3.mc.plugin.script.HamsterScriptPlugin;
|
||||
import cn.hamster3.mc.plugin.script.core.ScriptManager;
|
||||
import cn.hamster3.mc.plugin.script.data.EventCode;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MainListener implements Listener {
|
||||
public static final MainListener INSTANCE = new MainListener();
|
||||
|
||||
private MainListener() {
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onEvent(Event event) {
|
||||
if (!ScriptManager.isEnableEventCode()) {
|
||||
return;
|
||||
}
|
||||
for (EventCode code : ScriptManager.getEventCodes()) {
|
||||
if (code.getClazz().isAssignableFrom(event.getClass())) {
|
||||
try {
|
||||
code.invoke(event);
|
||||
} catch (Exception e) {
|
||||
HamsterScriptPlugin.getInstance().getLogger().log(Level.WARNING, "处理事件代码 " + code.getId() + " 失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +1,19 @@
|
||||
# 是否允许通过指令执行 JavaScript 代码
|
||||
enable-eval-command: false
|
||||
|
||||
# 是否启用事件监听执行代码
|
||||
enable-event-code: false
|
||||
|
||||
# 导入的 Java 代码
|
||||
import:
|
||||
Math: java.lang.Math
|
||||
String: java.lang.String
|
||||
|
||||
UUID: java.util.UUID
|
||||
Base64: java.util.Base64
|
||||
ArrayList: java.util.ArrayList
|
||||
HashMap: java.util.HashMap
|
||||
|
||||
Bukkit: org.bukkit.Bukkit
|
||||
Material: org.bukkit.Material
|
||||
ItemStack: org.bukkit.inventory.ItemStack
|
||||
|
13
src/main/resources/event/example.js
Normal file
13
src/main/resources/event/example.js
Normal file
@@ -0,0 +1,13 @@
|
||||
(function () {
|
||||
return {
|
||||
getID: function () {
|
||||
return "test";
|
||||
},
|
||||
getEventType: function () {
|
||||
return "org.bukkit.event.player.AsyncPlayerChatEvent";
|
||||
},
|
||||
invoke: function (event) {
|
||||
print(event.getPlayer().getName() + " says: " + event.getMessage());
|
||||
},
|
||||
};
|
||||
})();
|
@@ -2,19 +2,25 @@ name: HamsterScript
|
||||
main: cn.hamster3.mc.plugin.script.HamsterScriptPlugin
|
||||
version: ${version}
|
||||
api-version: 1.13
|
||||
description: ${description}
|
||||
author: MiniDay
|
||||
|
||||
CHECK_TYPE: GITEA_RELEASES
|
||||
GIT_BASE_URL: https://git.airgame.net
|
||||
GIT_REPO: MiniDay/hamster-script
|
||||
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
|
||||
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-script/
|
||||
author: MiniDay
|
||||
description: ${description}
|
||||
website: https://git.airgame.net/MiniDay/hamster-script/
|
||||
|
||||
UPDATE_CHECKER:
|
||||
VERSION: ${version}
|
||||
CHECK_TYPE: GITEA_RELEASES
|
||||
GIT_BASE_URL: https://git.airgame.net
|
||||
GIT_REPO: MiniDay/hamster-script
|
||||
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-script/
|
||||
|
||||
Plugin:
|
||||
# HamsterScript 需要访问其他插件的类路径
|
||||
join-classpath: true
|
||||
|
||||
libraries:
|
||||
- org.openjdk.nashorn:nashorn-core:15.4
|
||||
|
||||
commands:
|
||||
hamster-script:
|
||||
aliases: [ hscript, scripts, script ]
|
||||
|
Reference in New Issue
Block a user