feat: 初版提交

This commit is contained in:
2024-03-04 20:28:38 +08:00
commit 488f4a240d
12 changed files with 669 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
package cn.hamster3.mc.plugin.script;
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.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.script.*;
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;
@SuppressWarnings("CallToPrintStackTrace")
public class HamsterScriptPlugin extends JavaPlugin {
@Getter
private static ScriptEngine engine;
@Getter
private static Invocable invocable;
private static File codeFolder;
private boolean enableEvalCommand;
@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) {
}
}
}
private void reload() {
saveDefaultConfig();
reloadConfig();
FileConfiguration config = getConfig();
enableEvalCommand = config.getBoolean("enable-eval-command", false);
engine = new ScriptEngineManager(getClassLoader()).getEngineByName("JavaScript");
invocable = (Invocable) engine;
ConfigurationSection importConfig = config.getConfigurationSection("import");
if (importConfig != null) {
for (String key : importConfig.getKeys(false)) {
String string = importConfig.getString(key);
try {
Class<?> clazz = Class.forName(string);
engine.put(key, clazz);
engine.eval(String.format("%s = %s.static;", key, key));
getLogger().info("" + string + " 导入为 " + key);
} catch (ClassNotFoundException e) {
getLogger().warning("" + string + " 导入为 " + key + " 失败:未找到这个类");
} catch (ScriptException e) {
getLogger().warning("" + string + " 导入为 " + key + " 失败");
e.printStackTrace();
}
}
}
}
@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();
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 (ScriptException 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();
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 (ScriptException e) {
long time = System.currentTimeMillis() - start;
sender.sendMessage("§cJavaScript 代码执行出错, 耗时: " + time + " 毫秒");
sender.sendMessage("§c异常原因: " + e.getMessage());
e.printStackTrace();
}
break;
}
case "reset": {
engine = new ScriptEngineManager().getEngineByName("JavaScript");
invocable = (Invocable) engine;
sender.sendMessage("§a已重设 JavaScript 引擎环境");
break;
}
case "reload": {
reload();
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 alias, @NotNull String[] args) {
if (args.length == 1) {
List<String> list = new ArrayList<>();
list.add("eval");
list.add("run");
list.add("reset");
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();
}
}

View File

@@ -0,0 +1,6 @@
function sayHi(param) {
param.sendMessage("Hi!");
}
// 执行命令的对象会自动设置为变量 sender
sayHi(sender);

View File

@@ -0,0 +1,6 @@
# 是否允许通过指令执行 JavaScript 代码
enable-eval-command: false
# 导入的 Java 代码
import:
Bukkit: org.bukkit.Bukkit

View File

@@ -0,0 +1,13 @@
name: HamsterScript
main: cn.hamster3.mc.plugin.script.HamsterScriptPlugin
version: ${version}
api-version: 1.13
commands:
scripts:
aliases: script
permissions:
hamster.script.admin:
description: Admin permission
default: op