14 Commits

Author SHA1 Message Date
9f35b1e64d ci: 优化工作流配置 2025-08-23 04:15:15 +08:00
c4c09fcbb5 docs: 修复文档信息
[skip ci]
2025-06-16 02:31:30 +08:00
cfbed99ece ci: 添加自动化构建脚本
All checks were successful
Publish Project / build (push) Successful in 1m39s
2025-06-15 09:29:34 +08:00
8c6af8e95e docs: 修改文档 2024-08-09 01:24:21 +08:00
3dab447178 build: 优化构建脚本 2024-08-09 01:23:54 +08:00
fe2e658c50 docs: 使用 MIT 开源协议 2024-08-09 01:23:34 +08:00
41146a74be feat: 允许使用JavaScript监听事件 2024-08-08 07:21:50 +08:00
f2ed45b524 style: 优化代码命名 2024-07-16 01:00:59 +08:00
db812c13b5 style: 优化代码格式 2024-04-15 09:21:36 +08:00
8d7667f473 build: 优化构建脚本 2024-03-19 16:11:20 +08:00
8ea030205e docs: 更改描述 2024-03-19 15:38:59 +08:00
9900f0a014 docs: 更改描述 2024-03-19 15:32:27 +08:00
eae09072ad perf: 移除无效 token 2024-03-19 15:28:24 +08:00
c628a269f4 feat: 兼容新版仓鼠核心检测更新 2024-03-19 15:27:45 +08:00
13 changed files with 461 additions and 228 deletions

View 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 --console plain --no-daemon
- name: Publish to Release
uses: softprops/action-gh-release@v2
with:
files: build/*.jar

21
LICENSE Normal file
View 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.

View File

@@ -1,6 +1,7 @@
# hamster-script
# HamsterScript
Minecraft服务器导入 [Nashorn](https://github.com/openjdk/nashorn) 引擎来执行 JavaScript 脚本
HamsterScript 通过为 Minecraft 服务器导入 Nashorn 引擎,来允许用户执行 JavaScript 脚本
这个插件主要用于提供调试功能。
# 手动构建
@@ -9,25 +10,22 @@
3. 命令行窗口中执行`./gradlew clean build`
4. 构建成品在 `build` 文件夹
也可访问我的 [Jenkins网站](https://jenkins.airgame.net/job/opensource/job/hamster-script/) 获取最新版
# 指令
> 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 {脚本内容}` 指令。
如有需要,可以在 `config.yml` 中将 `enable-eval-command` 设置为 `true` 并重载插件以启用该子指令。
# nashorn 学习资料
# Nashorn 学习资料
- [介绍 Nashorn —— Java 8 JavaScript 引擎](https://mouse0w0.github.io/2018/12/02/Introduction-to-Nashorn/)
- [Oracle Nashorn面向 JVM 的下一代 JavaScript 引擎
](https://www.oracle.com/technical-resources/articles/java/jf14-nashorn.html)
- [Oracle Nashorn面向 JVM 的下一代 JavaScript 引擎](https://www.oracle.com/technical-resources/articles/java/jf14-nashorn.html)

View File

@@ -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")
}
tasks {
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
withSourcesJar()
}
tasks {
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
}
}

View File

@@ -1,2 +1 @@
rootProject.name = "hamster-script"

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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

View 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());
},
};
})();

View File

@@ -2,19 +2,25 @@ name: HamsterScript
main: cn.hamster3.mc.plugin.script.HamsterScriptPlugin
version: ${version}
api-version: 1.13
description: ${description}
author: MiniDay
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
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-script/
DOWNLOAD_URL: https://git.airgame.net/MiniDay/hamster-script/releases
Plugin:
# HamsterScript 需要访问其他插件的类路径
join-classpath: true
libraries:
- org.openjdk.nashorn:nashorn-core:15.4
commands:
hamster-script:
aliases: [ hscript, scripts, script ]