SteamWar/BauSystem
Archiviert
13
0

Script Turing Completeness #152

Manuell gemergt
YoyoNow hat 14 Commits von ScriptBranches nach master 2021-01-09 20:40:17 +01:00 zusammengeführt
4 geänderte Dateien mit 262 neuen und 7 gelöschten Zeilen

Datei anzeigen

@ -117,6 +117,7 @@ public class BauSystem extends JavaPlugin implements Listener {
getCommand("watervision").setExecutor(new CommandGills());
getCommand("detonator").setExecutor(new CommandDetonator());
getCommand("detonator").setTabCompleter(new CommandDetonatorTabCompleter());
getCommand("script").setExecutor(new CommandScript());
Bukkit.getPluginManager().registerEvents(this, this);
Bukkit.getPluginManager().registerEvents(new RegionListener(), this);

Datei anzeigen

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

Kann man den Itemstack nicht in einer private static final Variable haben?

Kann man den Itemstack nicht in einer private static final Variable haben?
@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;
}
}

Datei anzeigen

@ -20,22 +20,27 @@
package de.steamwar.bausystem.world;
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 org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
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.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.logging.Level;
public class ScriptListener implements Listener {
@ -52,6 +57,10 @@ public class ScriptListener implements Listener {
if(item == null || isNoBook(item) || item.getItemMeta() == null)
Veraltet
Review

Kann man dann CommandScript.BOOK.getItemstack().getName() machen?

Kann man dann `CommandScript.BOOK.getItemstack().getName()` machen?
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.RIGHT_CLICK_AIR) {
playerSet.add(event.getPlayer());
@ -80,6 +89,9 @@ public class ScriptListener implements Listener {
private final Player player;
Veraltet
Review

Wenn jemand mehr als 200 Befehle in ein Skriptbuch packt, dann werden eben mehr als 200 Befehle ausgeführt. Du kannst damit nicht Serverabstürze verhindern.

Wenn jemand mehr als 200 Befehle in ein Skriptbuch packt, dann werden eben mehr als 200 Befehle ausgeführt. Du kannst damit nicht Serverabstürze verhindern.
Veraltet
Review

Ich will damit auch verhindern, dass du unendlich schleifen machst. Aber ich habe es nun rausgenommen.

Ich will damit auch verhindern, dass du unendlich schleifen machst. Aber ich habe es nun rausgenommen.
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;
public ScriptExecutor(BookMeta bookMeta, Player player) {
@ -88,6 +100,10 @@ public class ScriptListener implements Listener {
for(String page : bookMeta.getPages()) {
for (String command : page.split("\n")) {
if (command.startsWith("#") || command.trim().isEmpty()) continue;
if (command.startsWith(".")) {
jumpPoints.put(command.substring(1), commands.size());
continue;
}
commands.add(command);
}
}
@ -100,12 +116,45 @@ public class ScriptListener implements Listener {
return;
}
int executionPoints = 0;
while (index < commands.size()) {
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));
return;
Veraltet
Review

Wenn du hier mit so vielen Extracommands anfängst: Mach ein switch case draus.

Wenn du hier mit so vielen Extracommands anfängst: Mach ein switch case draus.
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);
@ -114,6 +163,12 @@ public class ScriptListener implements Listener {
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.getServer().dispatchCommand(player, command);
}
@ -141,4 +196,128 @@ public class ScriptListener implements Listener {
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++) {
Veraltet
Review

Den Info-Befehl finde ich etwas unnötig, sicherlich wird er interessant zum Debuggen sein, aber wir wollen die Leute nicht dazu ermuntern, horrend aufwändige Skripte zu schreiben, und wenn, dann soll es eine Herausforderung sein. Der Info-Command bläst das System nur ohne nennenswerten Mehrwert auf.

Den Info-Befehl finde ich etwas unnötig, sicherlich wird er interessant zum Debuggen sein, aber wir wollen die Leute nicht dazu ermuntern, horrend aufwändige Skripte zu schreiben, und wenn, dann soll es eine Herausforderung sein. Der Info-Command bläst das System nur ohne nennenswerten Mehrwert auf.
Veraltet
Review

Mit diesem Command soll man, besonders, wenn man einem anderen sein eigenes Skript gibt, So dass er weiß wie man diesen verwendet? Bzw auch um user Rückgaben zu machen.

Mit diesem Command soll man, besonders, wenn man einem anderen sein eigenes Skript gibt, So dass er weiß wie man diesen verwendet? Bzw auch um user Rückgaben zu machen.
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)));
Veraltet
Review

Bislang sind keine freien Variablen außer den 4 Konstanten fire, tnt, trace & freeze nötig. Das Programm hat so wenige Input- und Outputmöglichkeiten (Man kann Variablen schließlich nur in ifs verwenden), dass man es daher immer auf die Konstanten zurückgreifen kann und einfach keine freien Variablen benötigt. Entweder das System wird mächtiger, oder Variablen sind sinnlos.

Bislang sind keine freien Variablen außer den 4 Konstanten fire, tnt, trace & freeze nötig. Das Programm hat so wenige Input- und Outputmöglichkeiten (Man kann Variablen schließlich nur in ifs verwenden), dass man es daher immer auf die Konstanten zurückgreifen kann und einfach keine freien Variablen benötigt. Entweder das System wird mächtiger, oder Variablen sind sinnlos.
Veraltet
Review

Du kannst mit variablen schleifenabbrüche und sonstige Sachen machen. Ich glaube das das doch sinnvoll wäre.

Du kannst mit variablen schleifenabbrüche und sonstige Sachen machen. Ich glaube das das doch sinnvoll wäre.
Veraltet
Review

Du hast aber nix, wofür Schleifen sinnvoll wären. Variablen kannst du letztendlich nur für ifs verwenden. Und eine "Schleife" kannst du ohne variablen Input auch einfach entrollen, da die Länge schon während des Schreibens des Skriptes bekannt ist, d.h besteht dafür keine Notwendigkeit.

Du hast aber nix, wofür Schleifen sinnvoll wären. Variablen kannst du letztendlich nur für ifs verwenden. Und eine "Schleife" kannst du ohne variablen Input auch einfach entrollen, da die Länge schon während des Schreibens des Skriptes bekannt ist, d.h besteht dafür keine Notwendigkeit.
Veraltet
Review

Jedoch ist es einfacher, wenn man diese als Schleife darstellen kann. Außerdem welche Output/Input Systeme kann man noch einbauen?

Jedoch ist es einfacher, wenn man diese als Schleife darstellen kann. Außerdem welche Output/Input Systeme kann man noch einbauen?
Veraltet
Review

Nein, das ist nicht mal einfacher. Die Variablen fügen dem Skriptsystem derzeit nix hinzu.

Anders würde es aussehen, wenn man die Befehle auch für andere Befehle als für info verwenden könnte...., dann würde ich allerdings info umbenennen in echo und in einen normalen Befehl umwandeln.

Nein, das ist nicht mal einfacher. Die Variablen fügen dem Skriptsystem derzeit nix hinzu. Anders würde es aussehen, wenn man die Befehle auch für andere Befehle als für info verwenden könnte...., dann würde ich allerdings info umbenennen in echo und in einen normalen Befehl umwandeln.
Veraltet
Review

Welche Befehle soll man für andere Befehle verwenden? Meinst du variablen soll man für andere Befehle verwenden können sollen. Und das umwandeln könnte ich. Ich weiß halt nicht wie man das mit den Variablen dann sinnvoll machen sollte. Weil es dann ja scheinbar globale und nicht globale variablen geben sollte?

Welche Befehle soll man für andere Befehle verwenden? Meinst du variablen soll man für andere Befehle verwenden können sollen. Und das umwandeln könnte ich. Ich weiß halt nicht wie man das mit den Variablen dann sinnvoll machen sollte. Weil es dann ja scheinbar globale und nicht globale variablen geben sollte?
Veraltet
Review

Nein, ich habe nix von globalen und nicht globalen Variablen geschrieben. Ich habe etwas von der Einsetzbarkeit von Variablen geschrieben. Ich möchte die Variablen innerhalb eines Skripts für nahezu jeden Befehl verwenden können, z.B. für ein //pos1 X,Y,Z

Nein, ich habe nix von globalen und nicht globalen Variablen geschrieben. Ich habe etwas von der Einsetzbarkeit von Variablen geschrieben. Ich möchte die Variablen innerhalb eines Skripts für nahezu jeden Befehl verwenden können, z.B. für ein //pos1 X,Y,Z
Veraltet
Review

Achso. Ich glaube das lässt sich einrichten.

Achso. Ich glaube das lässt sich einrichten.
}
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;
}
Veraltet
Review

Das System ist viel zu aufwändig für das wenige, dass es kann. Einfach eine Map<String, Integer> statt einem "VariableHolder". Es gibt nur Integer, keine Boolean. Alles außer 0 wird true bewertet. Die Konstanten "trace", "tnt", "freeze" und "fire" kannst du als solche extra behandeln wie hier in getValue().

Das System ist viel zu aufwändig für das wenige, dass es kann. Einfach eine Map<String, Integer> statt einem "VariableHolder". Es gibt nur Integer, keine Boolean. Alles außer 0 wird true bewertet. Die Konstanten "trace", "tnt", "freeze" und "fire" kannst du als solche extra behandeln wie hier in getValue().
}
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;
}
}
}

Datei anzeigen

@ -33,3 +33,4 @@ commands:
lockschem:
detonator:
aliases: dt
script: