Script Turing Completeness #152
@ -117,6 +117,7 @@ public class BauSystem extends JavaPlugin implements Listener {
|
|||||||
getCommand("watervision").setExecutor(new CommandGills());
|
getCommand("watervision").setExecutor(new CommandGills());
|
||||||
getCommand("detonator").setExecutor(new CommandDetonator());
|
getCommand("detonator").setExecutor(new CommandDetonator());
|
||||||
getCommand("detonator").setTabCompleter(new CommandDetonatorTabCompleter());
|
getCommand("detonator").setTabCompleter(new CommandDetonatorTabCompleter());
|
||||||
|
getCommand("script").setExecutor(new CommandScript());
|
||||||
|
|
||||||
Bukkit.getPluginManager().registerEvents(this, this);
|
Bukkit.getPluginManager().registerEvents(this, this);
|
||||||
Bukkit.getPluginManager().registerEvents(new RegionListener(), this);
|
Bukkit.getPluginManager().registerEvents(new RegionListener(), this);
|
||||||
|
74
BauSystem_Main/src/de/steamwar/bausystem/commands/CommandScript.java
Normale Datei
74
BauSystem_Main/src/de/steamwar/bausystem/commands/CommandScript.java
Normale Datei
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* /
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.commands;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CommandScript implements CommandExecutor {
|
||||||
|
|
||||||
|
public static final ItemStack BOOK = new ItemStack(Material.WRITTEN_BOOK, 1);
|
||||||
|
|
||||||
|
static {
|
||||||
|
List<String> pages = new ArrayList<>();
|
||||||
|
pages.add("§6Script System§8\n\n- Commands\n- Kommentare\n- Scriptausführung\n- Sleep\n- Variablen\n- Konstanten\n- Abfragen\n- Schleifen\n- \"echo\"");
|
||||||
|
pages.add("§6Commands§8\n\nEin minecraft Befehl wird im Scriptbuch so hingeschrieben. Dabei kann man ein '/' weglassen. Um Befehle zu trennen kommen diese in neue Zeilen.\n\nStatt\n/tnt -> tnt\n//pos1 -> /pos1");
|
||||||
|
pages.add("§6Kommentare§8\n\nFür ein Kommentar fängt die Zeile mit einem '#' an. Diese Zeilen werden bei dem Ausführen dann ignoriert.\n\nBeispiel:\n§9# TNT an/aus\ntnt§8");
|
||||||
|
pages.add("§6Scriptausführung§8\n\nWenn du mit dem Buch in der Hand links klickst wird dieses ausgeführt.");
|
||||||
|
pages.add("§6Sleep§8\n\nUm Sachen langsamer zu machen kann man ein 'sleep' in sein Script schreiben. Danach kommt eine Zahl mit der Anzahl der GameTicks die zu schlafen sind.\n\nBeispiel:\n§9# 1 Sekunde schlafen\nsleep 20§8");
|
||||||
|
pages.add("§6Variablen§8\n\nMit Variablen kann man sich Zahlen speichern. Man definiert diese mit 'var <NAME> <VALUE>'.\n\nBeispiel:\n§9# Setze i zu 0\nvar i 0§8\n\nEs gibt einige spezial values. Dazu zählen");
|
||||||
|
pages.add("§8'true', 'yes', 'false' und 'no', welche für 1, 1, 0 und 0 stehen.\n\nMan kann eine Variable auch um einen erhöhen oder verkleinern. Hierfür schreibt man statt einer Zahl '++', 'inc' oder '--', 'dec'.\n\nBeispiel:\n§9var i ++§8");
|
||||||
|
pages.add("§8Variablen kann man referenzieren\ndurch '$' vor dem Variablennamen. Diese kann man in jedem Befehl verwenden.\n\nBeispiel:\n§9# Stacked um 10\nvar stacks 10\n/stack $stacks§8");
|
||||||
|
pages.add("§6Konstanten§8\n\nNeben den variablen gibt es noch 4 Konstante Werte, welche nicht mit dem 'var' Befehl verändert werden können.\n\nDiese sind:\n- trace\n- tnt\n- freeze\n- fire");
|
||||||
|
pages.add("§6Abfragen§8\n\nMit Abfragen kann man nur Gleichheit von 2 Werten überprüft werden. Hierfür verwendet man\n'if <VAL> <VAL>'.\nNach den zwei Werten kann man ein oder 2 Jump-Points schreiben\n'if [...] <JP> (JP)'.");
|
||||||
|
pages.add("§8Ein Jump-Point ist eine Zeile Script, wohin man springen kann. Dieser wird mit einem '.' am Anfang der Zeile beschrieben und direkt danach der Jump-Point Namen ohne Leerzeichen.\n\nBeispiel:\n§9# Jump-Point X\n.X§8");
|
||||||
|
pages.add("§8Um zu einem Jump-Point ohne Abfrage zu springen kann man den\n'jump <JP>' Befehl verwenden.");
|
||||||
|
pages.add("§6Schleifen§8\n\nSchleifen werden mit Jump-Points, if Abfragen und Jumps gebaut.\n\nBeispiel:\n§9var i 0\n.JUMP\nvar i ++\nif i 10 END JUMP\n.END§8");
|
||||||
|
pages.add("§6\"echo\"§8\n\nDer echo Befehl ist gut um Ausgaben zu tätigen. Hier drin kann man sowohl Variablen ausgeben, als auch Farbcodes verwenden. Es wird alles nach dem Befehl ausgegeben.\n\nBeispiel:\n§9echo &eSteam&8war &7war hier!§8");
|
||||||
|
|
||||||
|
BookMeta bookMeta = (BookMeta) BOOK.getItemMeta();
|
||||||
|
bookMeta.setGeneration(BookMeta.Generation.ORIGINAL);
|
||||||
|
bookMeta.setAuthor("§eSteam§8war");
|
||||||
|
bookMeta.setTitle("§7Script Buch");
|
||||||
|
bookMeta.setDisplayName("§7Script Buch");
|
||||||
|
bookMeta.setPages(pages);
|
||||||
|
BOOK.setItemMeta(bookMeta);
|
||||||
|
}
|
||||||
|
|||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
((Player) sender).getInventory().setItemInMainHand(BOOK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,22 +20,27 @@
|
|||||||
package de.steamwar.bausystem.world;
|
package de.steamwar.bausystem.world;
|
||||||
|
|
||||||
import de.steamwar.bausystem.BauSystem;
|
import de.steamwar.bausystem.BauSystem;
|
||||||
|
import de.steamwar.bausystem.commands.CommandFire;
|
||||||
|
import de.steamwar.bausystem.commands.CommandFreeze;
|
||||||
|
import de.steamwar.bausystem.commands.CommandScript;
|
||||||
|
import de.steamwar.bausystem.commands.CommandTNT;
|
||||||
|
import de.steamwar.bausystem.tracer.recorder.RecordManager;
|
||||||
import de.steamwar.core.Core;
|
import de.steamwar.core.Core;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryCreativeEvent;
|
||||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class ScriptListener implements Listener {
|
public class ScriptListener implements Listener {
|
||||||
@ -52,6 +57,10 @@ public class ScriptListener implements Listener {
|
|||||||
if(item == null || isNoBook(item) || item.getItemMeta() == null)
|
if(item == null || isNoBook(item) || item.getItemMeta() == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (item.getItemMeta().getDisplayName().equals(CommandScript.BOOK.getItemMeta().getDisplayName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.getAction() != Action.LEFT_CLICK_AIR && event.getAction() != Action.LEFT_CLICK_BLOCK) {
|
if (event.getAction() != Action.LEFT_CLICK_AIR && event.getAction() != Action.LEFT_CLICK_BLOCK) {
|
||||||
if (event.getAction() == Action.RIGHT_CLICK_AIR) {
|
if (event.getAction() == Action.RIGHT_CLICK_AIR) {
|
||||||
playerSet.add(event.getPlayer());
|
playerSet.add(event.getPlayer());
|
||||||
@ -80,6 +89,9 @@ public class ScriptListener implements Listener {
|
|||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final List<String> commands = new ArrayList<>();
|
private final List<String> commands = new ArrayList<>();
|
||||||
|
private final Map<String, Integer> jumpPoints = new HashMap<>();
|
||||||
|
private Map<String, Integer> variables = new HashMap<>();
|
||||||
|
|
||||||
private int index = 0;
|
private int index = 0;
|
||||||
|
|
||||||
public ScriptExecutor(BookMeta bookMeta, Player player) {
|
public ScriptExecutor(BookMeta bookMeta, Player player) {
|
||||||
@ -88,6 +100,10 @@ public class ScriptListener implements Listener {
|
|||||||
for(String page : bookMeta.getPages()) {
|
for(String page : bookMeta.getPages()) {
|
||||||
for (String command : page.split("\n")) {
|
for (String command : page.split("\n")) {
|
||||||
if (command.startsWith("#") || command.trim().isEmpty()) continue;
|
if (command.startsWith("#") || command.trim().isEmpty()) continue;
|
||||||
|
if (command.startsWith(".")) {
|
||||||
|
jumpPoints.put(command.substring(1), commands.size());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
commands.add(command);
|
commands.add(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,12 +116,45 @@ public class ScriptListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int executionPoints = 0;
|
||||||
|
|
||||||
while (index < commands.size()) {
|
while (index < commands.size()) {
|
||||||
String command = commands.get(index++);
|
String command = commands.get(index++);
|
||||||
|
if (executionPoints++ > 200) {
|
||||||
|
player.sendMessage(BauSystem.PREFIX + "§cFüge ein sleep in dein Script ein");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (command.toLowerCase().startsWith("sleep")) {
|
String firstArg = command;
|
||||||
|
if (command.contains(" ")) {
|
||||||
|
firstArg = command.substring(0, command.indexOf(' '));
|
||||||
|
}
|
||||||
|
switch (firstArg.toLowerCase()) {
|
||||||
|
case "sleep":
|
||||||
ScriptListener.sleepCommand(this, generateArgumentArray("sleep", command));
|
ScriptListener.sleepCommand(this, generateArgumentArray("sleep", command));
|
||||||
return;
|
return;
|
||||||
|
case "exit":
|
||||||
|
return;
|
||||||
|
case "jump":
|
||||||
|
int jumpIndex = ScriptListener.jumpCommand(this, generateArgumentArray("jump", command));
|
||||||
|
if (jumpIndex != -1) {
|
||||||
|
index = jumpIndex;
|
||||||
|
} else {
|
||||||
|
player.sendMessage(BauSystem.PREFIX + "§cUnbekannter Jump Punkt: " + command);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case "echo":
|
||||||
|
ScriptListener.echoCommand(this, generateArgumentArray("echo", command));
|
||||||
|
continue;
|
||||||
|
case "var":
|
||||||
|
ScriptListener.variableCommand(this, generateArgumentArray("var", command));
|
||||||
|
continue;
|
||||||
|
case "if":
|
||||||
|
int ifJumpIndex = ScriptListener.ifCommand(this, generateArgumentArray("if", command));
|
||||||
|
if (ifJumpIndex != -1) {
|
||||||
|
index = ifJumpIndex;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerCommandPreprocessEvent preprocessEvent = new PlayerCommandPreprocessEvent(player, "/" + command);
|
PlayerCommandPreprocessEvent preprocessEvent = new PlayerCommandPreprocessEvent(player, "/" + command);
|
||||||
@ -114,6 +163,12 @@ public class ScriptListener implements Listener {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variable Replaces in commands.
|
||||||
|
String[] commandArgs = command.split(" ");
|
||||||
|
String[] args = Arrays.copyOfRange(commandArgs, 1, commandArgs.length);
|
||||||
|
replaceVariables(this, args);
|
||||||
|
command = commandArgs[0] + " " + String.join(" ", args);
|
||||||
|
|
||||||
Bukkit.getLogger().log(Level.INFO, player.getName() + " dispatched command: " + command);
|
Bukkit.getLogger().log(Level.INFO, player.getName() + " dispatched command: " + command);
|
||||||
Bukkit.getServer().dispatchCommand(player, command);
|
Bukkit.getServer().dispatchCommand(player, command);
|
||||||
}
|
}
|
||||||
@ -141,4 +196,128 @@ public class ScriptListener implements Listener {
|
|||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getPlugin(), scriptExecutor::resume, sleepTime);
|
Bukkit.getScheduler().runTaskLater(BauSystem.getPlugin(), scriptExecutor::resume, sleepTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int jumpCommand(ScriptExecutor scriptExecutor, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
scriptExecutor.player.sendMessage(BauSystem.PREFIX + "§cEin Jump Punkt muss angegeben sein.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return scriptExecutor.jumpPoints.getOrDefault(args[0], -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void replaceVariables(ScriptExecutor scriptExecutor, String[] args) {
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (args[i].startsWith("$") && isVariable(scriptExecutor, args[i].substring(1))) {
|
||||||
|
args[i] = getValue(scriptExecutor, args[i].substring(1)) + "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void echoCommand(ScriptExecutor scriptExecutor, String[] args) {
|
||||||
|
replaceVariables(scriptExecutor, args);
|
||||||
|
scriptExecutor.player.sendMessage("§eInfo§8» §7" + ChatColor.translateAlternateColorCodes('&', String.join(" ", args)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void variableCommand(ScriptExecutor scriptExecutor, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
scriptExecutor.player.sendMessage(BauSystem.PREFIX + "§cVariablen Namen und Zahlen/Boolsche Wert fehlt.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (args.length < 2) {
|
||||||
|
scriptExecutor.player.sendMessage(BauSystem.PREFIX + "§cZahlen/Boolsche Wert fehlt.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (args[1]) {
|
||||||
|
case "inc":
|
||||||
|
case "increment":
|
||||||
|
case "++":
|
||||||
|
add(scriptExecutor, args[0], 1);
|
||||||
|
return;
|
||||||
|
case "dec":
|
||||||
|
case "decrement":
|
||||||
|
case "--":
|
||||||
|
add(scriptExecutor, args[0], -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(scriptExecutor, args[0], args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ifCommand(ScriptExecutor scriptExecutor, String[] args) {
|
||||||
|
if (args.length < 2) {
|
||||||
|
scriptExecutor.player.sendMessage(BauSystem.PREFIX + "§cDie ersten beiden Argumente sind Zahlen/Boolsche Wertde oder Variablen.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int jumpTruePoint = scriptExecutor.jumpPoints.getOrDefault(args[2], -1);
|
||||||
|
int jumpFalsePoint = args.length > 3 ? scriptExecutor.jumpPoints.getOrDefault(args[3], -1) : -1;
|
||||||
|
|
||||||
|
int firstValue;
|
||||||
|
int secondValue;
|
||||||
|
if (isVariable(scriptExecutor, args[0])) {
|
||||||
|
firstValue = getValue(scriptExecutor, args[0]);
|
||||||
|
} else {
|
||||||
|
firstValue = parseValue(args[0]);
|
||||||
|
}
|
||||||
|
if (isVariable(scriptExecutor, args[1])) {
|
||||||
|
secondValue = getValue(scriptExecutor, args[1]);
|
||||||
|
} else {
|
||||||
|
secondValue = parseValue(args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstValue == secondValue) {
|
||||||
|
return jumpTruePoint;
|
||||||
|
} else {
|
||||||
|
return jumpFalsePoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setValue(ScriptExecutor scriptExecutor, String key, String value) {
|
||||||
|
scriptExecutor.variables.put(key, parseValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void add(ScriptExecutor scriptExecutor, String key, int value) {
|
||||||
|
if (!isVariable(scriptExecutor, key)) {
|
||||||
|
scriptExecutor.variables.put(key, 0);
|
||||||
|
}
|
||||||
|
scriptExecutor.variables.put(key, scriptExecutor.variables.get(key) + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getValue(ScriptExecutor scriptExecutor, String key) {
|
||||||
|
switch (key) {
|
||||||
|
case "trace":
|
||||||
|
return RecordManager.getStatus().isTracing() ? 1 : 0;
|
||||||
|
case "tnt":
|
||||||
|
return CommandTNT.getInstance().isOn() ? 1 : 0;
|
||||||
|
case "freeze":
|
||||||
|
return CommandFreeze.getInstance().isOn() ? 1 : 0;
|
||||||
|
case "fire":
|
||||||
|
return CommandFire.getInstance().isOn() ? 1 : 0;
|
||||||
|
}
|
||||||
|
return scriptExecutor.variables.getOrDefault(key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isVariable(ScriptExecutor scriptExecutor, String key) {
|
||||||
|
switch (key) {
|
||||||
|
case "trace":
|
||||||
|
case "tnt":
|
||||||
|
case "freeze":
|
||||||
|
case "fire":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return scriptExecutor.variables.containsKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseValue(String value) {
|
||||||
|
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes")) {
|
||||||
|
return 1;
|
||||||
|
} else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,3 +33,4 @@ commands:
|
|||||||
lockschem:
|
lockschem:
|
||||||
detonator:
|
detonator:
|
||||||
aliases: dt
|
aliases: dt
|
||||||
|
script:
|
In neuem Issue referenzieren
Einen Benutzer sperren
Kann man den Itemstack nicht in einer private static final Variable haben?