Merge changes from FAI

This commit is contained in:
Telesphoreo 2023-03-11 15:24:45 -06:00
parent bfd919b3cb
commit 221b9ea844
22 changed files with 106 additions and 980 deletions

View File

@ -59,7 +59,6 @@ public class BukkitConfiguration extends YAMLConfiguration {
}
private void migrateLegacyFolders() {
migrate(scriptsDir, "craftscripts");
migrate(saveDir, "schematics");
migrate("drawings", "draw.js images");
}

View File

@ -66,10 +66,6 @@ navigation-wand:
item: minecraft:compass
max-distance: 100
scripting:
timeout: 3000
dir: craftscripts
saving:
dir: schematics

View File

@ -10,7 +10,6 @@ super-pickaxe-drop-items=true
disallowed-blocks=minecraft:oak_sapling,minecraft:jungle_sapling,minecraft:dark_oak_sapling,minecraft:spruce_sapling,minecraft:birch_sapling,minecraft:acacia_sapling,minecraft:black_bed,minecraft:blue_bed,minecraft:brown_bed,minecraft:cyan_bed,minecraft:gray_bed,minecraft:green_bed,minecraft:light_blue_bed,minecraft:light_gray_bed,minecraft:lime_bed,minecraft:magenta_bed,minecraft:orange_bed,minecraft:pink_bed,minecraft:purple_bed,minecraft:red_bed,minecraft:white_bed,minecraft:yellow_bed,minecraft:powered_rail,minecraft:detector_rail,minecraft:grass,minecraft:dead_bush,minecraft:moving_piston,minecraft:piston_head,minecraft:sunflower,minecraft:rose_bush,minecraft:dandelion,minecraft:poppy,minecraft:brown_mushroom,minecraft:red_mushroom,minecraft:tnt,minecraft:torch,minecraft:fire,minecraft:redstone_wire,minecraft:wheat,minecraft:potatoes,minecraft:carrots,minecraft:melon_stem,minecraft:pumpkin_stem,minecraft:beetroots,minecraft:rail,minecraft:lever,minecraft:redstone_torch,minecraft:redstone_wall_torch,minecraft:repeater,minecraft:comparator,minecraft:stone_button,minecraft:birch_button,minecraft:acacia_button,minecraft:dark_oak_button,minecraft:jungle_button,minecraft:oak_button,minecraft:spruce_button,minecraft:cactus,minecraft:sugar_cane,minecraft:bedrock
max-super-pickaxe-size=5
max-brush-radius=10
craftscript-dir=craftscripts
wand-item=minecraft:wooden_axe
shell-save-type=
scripting-timeout=3000

View File

@ -84,12 +84,10 @@ public abstract class LocalConfiguration {
public boolean navigationUseGlass = true;
public String navigationWand = "minecraft:compass";
public int navigationWandMaxDistance = 50;
public int scriptTimeout = 3000;
public int calculationTimeout = 100;
public int maxCalculationTimeout = 300;
public Set<String> allowedDataCycleBlocks = new HashSet<>();
public String saveDir = "schematics";
public String scriptsDir = "craftscripts";
public boolean showHelpInfo = true; // unused
public int butcherDefaultRadius = -1;
public int butcherMaxRadius = -1;

View File

@ -52,9 +52,6 @@ import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.invoke.ReturnException;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.scripting.CraftScriptContext;
import com.sk89q.worldedit.scripting.CraftScriptEngine;
import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine;
import com.sk89q.worldedit.session.SessionManager;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Location;
@ -745,97 +742,6 @@ public final class WorldEdit {
return event.isCancelled();
}
/**
* Executes a WorldEdit script.
*
* @param player the player
* @param f the script file to execute
* @param args arguments for the script
* @throws WorldEditException if something goes wrong
*/
public void runScript(Player player, File f, String[] args) throws WorldEditException {
String filename = f.getPath();
int index = filename.lastIndexOf('.');
String ext = filename.substring(index + 1);
if (!ext.equalsIgnoreCase("js")) {
player.print(Caption.of("worldedit.script.unsupported"));
return;
}
String script;
try {
InputStream file;
if (!f.exists()) {
file = WorldEdit.class.getResourceAsStream("craftscripts/" + filename);
if (file == null) {
player.print(Caption.of("worldedit.script.file-not-found", TextComponent.of(filename)));
return;
}
} else {
file = new FileInputStream(f);
}
DataInputStream in = new DataInputStream(file);
byte[] data = new byte[in.available()];
in.readFully(data);
in.close();
script = new String(data, 0, data.length, StandardCharsets.UTF_8);
} catch (IOException e) {
player.print(Caption.of("worldedit.script.read-error", TextComponent.of(e.getMessage())));
return;
}
LocalSession session = getSessionManager().get(player);
CraftScriptContext scriptContext = new CraftScriptContext(
this,
getPlatformManager().queryCapability(Capability.USER_COMMANDS),
getConfiguration(),
session,
player,
args
);
CraftScriptEngine engine;
try {
engine = new RhinoCraftScriptEngine();
} catch (NoClassDefFoundError ignored) {
player.print(Caption.of("worldedit.script.no-script-engine"));
return;
}
engine.setTimeLimit(getConfiguration().scriptTimeout);
Map<String, Object> vars = new HashMap<>();
vars.put("argv", args);
vars.put("context", scriptContext);
vars.put("player", player);
try {
engine.evaluate(script, filename, vars);
} catch (ScriptException e) {
// non-exceptional return check
if (!(Throwables.getRootCause(e) instanceof ReturnException)) {
player.print(Caption.of("worldedit.script.failed", TextComponent.of(e.getMessage())));
logger.warn("Failed to execute script", e);
}
} catch (NumberFormatException | WorldEditException e) {
throw e;
} catch (Throwable e) {
player.print(Caption.of("worldedit.script.failed-console", TextComponent.of(e.getClass().getCanonicalName())));
logger.warn("Failed to execute script", e);
} finally {
for (EditSession editSession : scriptContext.getEditSessions()) {
editSession.close();
session.remember(editSession);
}
}
}
/**
* Get Worldedit's configuration.
*

View File

@ -333,108 +333,6 @@ public class ClipboardCommands {
//FAWE end
}
//FAWE start
@Command(
name = "download",
aliases = {"/download"},
desc = "Downloads your clipboard through the configured web interface"
)
@Deprecated
@CommandPermissions({"worldedit.clipboard.download"})
public void download(
final Actor actor,
final LocalSession session,
@Arg(name = "format", desc = "String", def = "fast") final String formatName
) throws WorldEditException {
final ClipboardFormat format = ClipboardFormats.findByAlias(formatName);
if (format == null) {
actor.print(Caption.of("fawe.worldedit.clipboard.clipboard.invalid.format", formatName));
return;
}
actor.print(Caption.of("fawe.web.generating.link", formatName));
ClipboardHolder holder = session.getClipboard();
URL url;
if (holder instanceof MultiClipboardHolder) {
MultiClipboardHolder multi = (MultiClipboardHolder) holder;
Set<File> files = new HashSet<>();
Set<URI> invalid = new HashSet<>();
for (ClipboardHolder cur : multi.getHolders()) {
if (cur instanceof URIClipboardHolder) {
URIClipboardHolder uriHolder = (URIClipboardHolder) cur;
URI uri = uriHolder.getUri();
File file = new File(uri.getPath());
if (file.exists() && file.isFile()) {
files.add(file.getAbsoluteFile());
} else if (!uri.getPath().isEmpty()) {
invalid.add(uri);
}
}
}
final LocalConfiguration config = WorldEdit.getInstance().getConfiguration();
final File working = WorldEdit.getInstance().getWorkingDirectoryFile(config.saveDir).getAbsoluteFile();
url = MainUtil.upload(null, null, "zip", new RunnableVal<>() {
@Override
public void run(OutputStream out) {
try (ZipOutputStream zos = new ZipOutputStream(out)) {
for (File file : files) {
String fileName = file.getName();
if (MainUtil.isInSubDirectory(working, file)) {
fileName = working.toURI().relativize(file.toURI()).getPath();
}
ZipEntry ze = new ZipEntry(fileName);
zos.putNextEntry(ze);
Files.copy(file.toPath(), zos);
zos.closeEntry();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
} else {
Clipboard clipboard = holder.getClipboard();
final Transform transform = holder.getTransform();
final Clipboard target;
// If we have a transform, bake it into the copy
if (!transform.isIdentity()) {
final FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform);
target = new BlockArrayClipboard(result.getTransformedRegion(), actor.getUniqueId());
target.setOrigin(clipboard.getOrigin());
Operations.completeLegacy(result.copyTo(target));
} else {
target = clipboard;
}
if (format == BuiltInClipboardFormat.PNG) {
try {
FastByteArrayOutputStream baos = new FastByteArrayOutputStream(Short.MAX_VALUE);
ClipboardWriter writer = format.getWriter(baos);
writer.write(target);
baos.flush();
url = ImgurUtility.uploadImage(baos.toByteArray());
} catch (IOException e) {
e.printStackTrace();
url = null;
}
} else {
if (Settings.settings().WEB.URL.isEmpty()) {
actor.print(Caption.of("fawe.error.setting.disable", "web.url"));
return;
}
url = FaweAPI.upload(target, format);
}
}
if (url == null) {
actor.print(Caption.of("fawe.web.generating.link.failed"));
} else {
String urlText = url.toString();
actor.print(Caption.of("fawe.web.download.link", urlText).clickEvent(ClickEvent.openUrl(urlText)));
}
}
@Command(
name = "/place",
desc = "Place the clipboard's contents without applying transformations (e.g. rotate)"

View File

@ -77,6 +77,12 @@ public class NavigationCommands {
@Arg(desc = "# of levels to ascend", def = "1")
int levels
) throws WorldEditException {
// From Allink
if (levels < 0) {
player.print(Caption.of("worldedit.ascend.obstructed"));
return;
}
int ascentLevels = 0;
while (player.ascendLevel()) {
++ascentLevels;
@ -102,6 +108,12 @@ public class NavigationCommands {
@Arg(desc = "# of levels to descend", def = "1")
int levels
) throws WorldEditException {
// From Allink
if (levels < 0) {
player.print(Caption.of("worldedit.descend.obstructed"));
return;
}
int descentLevels = 0;
while (player.descendLevel()) {
++descentLevels;

View File

@ -150,11 +150,11 @@ public class SchematicCommands {
public void loadall(
Actor actor, LocalSession session,
@Arg(desc = "Format name.", def = "fast")
String formatName,
String formatName,
@Arg(desc = "File name.")
String filename,
String filename,
@Switch(name = 'o', desc = "Overwrite/replace existing clipboard(s)")
boolean overwrite
boolean overwrite
// @Switch(name = 'r', desc = "Apply random rotation") <- not implemented below.
// boolean randomRotate
) throws FilenameException {
@ -196,7 +196,7 @@ public class SchematicCommands {
public void unload(
Actor actor, LocalSession session,
@Arg(desc = "File name, requires extension.")
String fileName
String fileName
) throws WorldEditException {
URI uri;
if (fileName.startsWith("file:/") || fileName.startsWith("http://") || fileName.startsWith("https://")) {
@ -204,7 +204,9 @@ public class SchematicCommands {
} else {
final LocalConfiguration config = this.worldEdit.getConfiguration();
File working = this.worldEdit.getWorkingDirectoryPath(config.saveDir).toFile();
File root = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS ? new File(working, actor.getUniqueId().toString()) : working;
File root = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS
? new File(working, actor.getUniqueId().toString())
: working;
uri = new File(root, fileName).toURI();
}
@ -304,6 +306,7 @@ public class SchematicCommands {
}
//FAWE end
// From Allink
@Command(
name = "load",
desc = "Load a schematic into your clipboard"
@ -312,9 +315,9 @@ public class SchematicCommands {
public void load(
Actor actor, LocalSession session,
@Arg(desc = "File name.")
String filename,
String filename,
@Arg(desc = "Format name.", def = "fast")
String formatName
String formatName
) throws FilenameException {
LocalConfiguration config = worldEdit.getConfiguration();
@ -323,83 +326,69 @@ public class SchematicCommands {
InputStream in = null;
try {
URI uri;
if (formatName.startsWith("url:")) {
String t = filename;
filename = formatName;
formatName = t;
}
if (filename.startsWith("url:")) {
if (!actor.hasPermission("worldedit.schematic.load.web")) {
actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.web"));
File saveDir = worldEdit.getWorkingDirectoryPath(config.saveDir).toFile();
File dir = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS
? new File(saveDir, actor.getUniqueId().toString())
: saveDir;
File file;
if (filename.startsWith("#")) {
format = ClipboardFormats.findByAlias(formatName);
String[] extensions;
if (format != null) {
extensions = format.getFileExtensions().toArray(new String[0]);
} else {
extensions = ClipboardFormats.getFileExtensionArray();
}
file = actor.openFileOpenDialog(extensions);
if (file == null || !file.exists()) {
actor.print(Caption.of("worldedit.schematic.load.does-not-exist", TextComponent.of(filename)));
return;
}
UUID uuid = UUID.fromString(filename.substring(4));
URL webUrl = new URL(Settings.settings().WEB.URL);
format = ClipboardFormats.findByAlias(formatName);
URL url = new URL(webUrl, "uploads/" + uuid + "." + format.getPrimaryFileExtension());
ReadableByteChannel byteChannel = Channels.newChannel(url.openStream());
in = Channels.newInputStream(byteChannel);
uri = url.toURI();
} else {
File saveDir = worldEdit.getWorkingDirectoryPath(config.saveDir).toFile();
File dir = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS ? new File(saveDir, actor.getUniqueId().toString()) : saveDir;
File file;
if (filename.startsWith("#")) {
format = ClipboardFormats.findByAlias(formatName);
String[] extensions;
if (format != null) {
extensions = format.getFileExtensions().toArray(new String[0]);
} else {
extensions = ClipboardFormats.getFileExtensionArray();
}
file = actor.openFileOpenDialog(extensions);
if (file == null || !file.exists()) {
actor.print(Caption.of("worldedit.schematic.load.does-not-exist", TextComponent.of(filename)));
return;
}
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && !actor.hasPermission("worldedit.schematic.load.other") && Pattern
.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
.matcher(filename)
.find()) {
actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other"));
return;
}
if (filename.matches(".*\\.[\\w].*")) {
format = ClipboardFormats
.findByExtension(filename.substring(filename.lastIndexOf('.') + 1));
} else {
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && !actor.hasPermission("worldedit.schematic.load.other") && Pattern
.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
.matcher(filename)
.find()) {
actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other"));
return;
}
if (filename.matches(".*\\.[\\w].*")) {
format = ClipboardFormats
.findByExtension(filename.substring(filename.lastIndexOf('.') + 1));
} else {
format = ClipboardFormats.findByAlias(formatName);
}
format = ClipboardFormats.findByAlias(formatName);
}
file = MainUtil.resolve(dir, filename, format, false);
}
if (file == null || !file.exists()) {
if (!filename.contains("../")) {
dir = this.worldEdit.getWorkingDirectoryPath(config.saveDir).toFile();
file = MainUtil.resolve(dir, filename, format, false);
}
if (file == null || !file.exists()) {
if (!filename.contains("../")) {
dir = this.worldEdit.getWorkingDirectoryPath(config.saveDir).toFile();
file = MainUtil.resolve(dir, filename, format, false);
}
}
if (file == null || !file.exists() || !MainUtil.isInSubDirectory(saveDir, file)) {
actor.printError(TextComponent.of("Schematic " + filename + " does not exist! (" + (file != null && file.exists()) +
"|" + file + "|" + (file != null && !MainUtil
.isInSubDirectory(saveDir, file)) + ")"));
}
if (file == null || !file.exists() || !MainUtil.isInSubDirectory(saveDir, file)) {
actor.printError(TextComponent.of("Schematic " + filename + " does not exist! (" + (file != null && file.exists()) +
"|" + file + "|" + (file != null && !MainUtil
.isInSubDirectory(saveDir, file)) + ")"));
return;
}
if (format == null) {
format = ClipboardFormats.findByFile(file);
if (format == null) {
actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName)));
return;
}
if (format == null) {
format = ClipboardFormats.findByFile(file);
if (format == null) {
actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName)));
return;
}
}
in = new FileInputStream(file);
uri = file.toURI();
}
in = new FileInputStream(file);
uri = file.toURI();
format.hold(actor, uri, in);
actor.print(Caption.of("fawe.worldedit.schematic.schematic.loaded", filename));
} catch (IllegalArgumentException e) {
actor.print(Caption.of("worldedit.schematic.unknown-filename", TextComponent.of(filename)));
} catch (URISyntaxException | IOException e) {
} catch (IOException e) {
actor.print(Caption.of("worldedit.schematic.file-not-exist", TextComponent.of(Objects.toString(e.getMessage()))));
LOGGER.warn("Failed to load a saved clipboard", e);
} finally {

View File

@ -1,120 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.command;
import com.fastasyncworldedit.core.configuration.Caption;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.entity.Player;
import org.enginehub.piston.annotation.Command;
import org.enginehub.piston.annotation.CommandContainer;
import org.enginehub.piston.annotation.param.Arg;
import java.io.File;
import java.util.List;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL;
/**
* Commands related to scripting.
*/
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
public class ScriptingCommands {
private final WorldEdit worldEdit;
/**
* Create a new instance.
*
* @param worldEdit reference to WorldEdit
*/
public ScriptingCommands(WorldEdit worldEdit) {
checkNotNull(worldEdit);
this.worldEdit = worldEdit;
}
@Command(
name = "cs",
aliases = {"/cs"},
desc = "Execute a CraftScript"
)
@CommandPermissions("worldedit.scripting.execute")
@Logging(ALL)
public void execute(
Player player, LocalSession session,
@Arg(desc = "Filename of the CraftScript to load")
String filename,
@Arg(desc = "Arguments to the CraftScript", def = "", variable = true)
List<String> args
) throws WorldEditException {
if (!player.hasPermission("worldedit.scripting.execute." + filename)) {
player.print(Caption.of("worldedit.execute.script-permissions"));
return;
}
session.setLastScript(filename);
File dir = worldEdit.getWorkingDirectoryPath(worldEdit.getConfiguration().scriptsDir).toFile();
File f = worldEdit.getSafeOpenFile(player, dir, filename, "js", "js");
worldEdit.runScript(player, f, Stream.concat(Stream.of(filename), args.stream())
.toArray(String[]::new));
}
@Command(
name = ".s",
aliases = {"/.s"},
desc = "Execute last CraftScript"
)
@CommandPermissions("worldedit.scripting.execute")
@Logging(ALL)
public void executeLast(
Player player, LocalSession session,
@Arg(desc = "Arguments to the CraftScript", def = "", variable = true)
List<String> args
) throws WorldEditException {
String lastScript = session.getLastScript();
if (!player.hasPermission("worldedit.scripting.execute." + lastScript)) {
player.print(Caption.of("worldedit.execute.script-permissions"));
return;
}
if (lastScript == null) {
player.print(Caption.of("worldedit.executelast.no-script"));
return;
}
File dir = worldEdit.getWorkingDirectoryPath(worldEdit.getConfiguration().scriptsDir).toFile();
File f = worldEdit.getSafeOpenFile(player, dir, lastScript, "js", "js");
worldEdit.runScript(player, f, Stream.concat(Stream.of(lastScript), args.stream())
.toArray(String[]::new));
}
}

View File

@ -65,8 +65,6 @@ import com.sk89q.worldedit.command.RegionCommands;
import com.sk89q.worldedit.command.RegionCommandsRegistration;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.SchematicCommandsRegistration;
import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.ScriptingCommandsRegistration;
import com.sk89q.worldedit.command.SelectionCommands;
import com.sk89q.worldedit.command.SelectionCommandsRegistration;
import com.sk89q.worldedit.command.SnapshotCommands;
@ -559,11 +557,6 @@ public final class PlatformCommandManager {
RegionCommandsRegistration.builder(),
new RegionCommands()
);
this.registration.register(
commandManager,
ScriptingCommandsRegistration.builder(),
new ScriptingCommands(worldEdit)
);
this.registration.register(
commandManager,
SelectionCommandsRegistration.builder(),

View File

@ -24,6 +24,7 @@ import com.fastasyncworldedit.core.math.Vector3Impl;
import com.fastasyncworldedit.core.util.MathMan;
import com.google.common.collect.ComparisonChain;
import com.sk89q.worldedit.math.transform.AffineTransform;
import dev.plex.SanityChecks;
import java.util.Comparator;
@ -61,7 +62,10 @@ public abstract class Vector3 {
}
Fawe end
*/
return new Vector3Impl(x, y, z);
// From Allink
final double[] saneCoords = SanityChecks.getSane(x, y, z);
return new Vector3Impl(saneCoords[0], saneCoords[1], saneCoords[2]);
}
// thread-safe initialization idiom

View File

@ -1,278 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting;
import com.fastasyncworldedit.core.configuration.Caption;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.InsufficientArgumentsException;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.DisallowedUsageException;
import com.sk89q.worldedit.extension.input.NoMatchException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.expression.invoke.ReturnException;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.io.file.FilenameException;
import com.sk89q.worldedit.world.block.BaseBlock;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* The context given to scripts.
*/
public class CraftScriptContext extends CraftScriptEnvironment {
private final List<EditSession> editSessions = new ArrayList<>();
private final String[] args;
public CraftScriptContext(
WorldEdit controller,
Platform server, LocalConfiguration config,
LocalSession session, Player player, String[] args
) {
super(controller, server, config, session, player);
this.args = args;
}
/**
* Get an edit session. Every subsequent call returns a new edit session.
* Usually you only need to use one edit session.
*
* @return an edit session
*/
public EditSession remember() {
EditSession editSession = controller.newEditSessionBuilder()
.locatableActor(player)
.maxBlocks(session.getBlockChangeLimit())
.blockBag(session.getBlockBag(player))
.build();
Request.request().setEditSession(editSession);
editSession.enableStandardMode();
editSessions.add(editSession);
return editSession;
}
/**
* Get the player.
*
* @return the calling player
*/
public Player getPlayer() {
return player;
}
/**
* Get the player's session.
*
* @return a session
*/
public LocalSession getSession() {
return session;
}
/**
* Get the configuration for WorldEdit.
*
* @return the configuration
*/
public LocalConfiguration getConfiguration() {
return config;
}
/**
* Get a list of edit sessions that have been created.
*
* @return a list of created {@code EditSession}s
*/
public List<EditSession> getEditSessions() {
return Collections.unmodifiableList(editSessions);
}
/**
* Print a regular message to the user.
*
* @param message a message
*/
public void print(String message) {
player.printInfo(TextComponent.of(message));
}
/**
* Print an error message to the user.
*
* @param message a message
*/
public void error(String message) {
player.printError(TextComponent.of(message));
}
/**
* Print a raw message to the user.
*
* @param message a message
*/
public void printRaw(String message) {
player.print(TextComponent.of(message));
}
/**
* Checks to make sure that there are enough but not too many arguments.
*
* @param min a number of arguments
* @param max -1 for no maximum
* @param usage usage string
* @throws InsufficientArgumentsException if the arguments are not "sufficiently" good
*/
public void checkArgs(int min, int max, String usage)
throws InsufficientArgumentsException {
if (args.length <= min || (max != -1 && args.length - 1 > max)) {
throw new InsufficientArgumentsException(Caption.of("worldedit.error.incorrect-usage", TextComponent.of(usage)));
}
}
/**
* Immediately terminate execution of the script, but without a failure message.
* <p>
* This exits by throwing an exception, which if caught will prevent
* the script from exiting
*/
public void exit() {
throw new ReturnException(null);
}
/**
* Get an item from an item name or an item ID number.
*
* @param input input to parse
* @param allAllowed true to ignore blacklists
* @return a block
* @throws NoMatchException if no block was found
* @throws DisallowedUsageException if the block is disallowed
*/
public BaseBlock getBlock(String input, boolean allAllowed) throws WorldEditException {
ParserContext context = new ParserContext();
context.setActor(player);
context.setWorld(player.getWorld());
context.setSession(session);
context.setRestricted(!allAllowed);
context.setPreferringWildcard(false);
return controller.getBlockFactory().parseFromListInput(input, context).stream().findFirst().orElse(null);
}
/**
* Get a block.
*
* @param id the type Id
* @return a block
* @throws NoMatchException if no block was found
* @throws DisallowedUsageException if the block is disallowed
*/
public BaseBlock getBlock(String id) throws WorldEditException {
return getBlock(id, false);
}
/**
* Get a list of blocks as a set. This returns a Pattern.
*
* @param list the input
* @return pattern
* @throws NoMatchException if the pattern was invalid
* @throws DisallowedUsageException if the block is disallowed
*/
public Pattern getBlockPattern(String list) throws WorldEditException {
ParserContext context = new ParserContext();
context.setActor(player);
context.setWorld(player.getWorld());
context.setSession(session);
return controller.getPatternFactory().parseFromInput(list, context);
}
/**
* Get a list of blocks as a set.
*
* @param list a list
* @param allBlocksAllowed true if all blocks are allowed
* @return set
* @throws NoMatchException if the blocks couldn't be found
* @throws DisallowedUsageException if the block is disallowed
*/
public Set<BaseBlock> getBlocks(String list, boolean allBlocksAllowed) throws WorldEditException {
ParserContext context = new ParserContext();
context.setActor(player);
context.setWorld(player.getWorld());
context.setSession(session);
context.setRestricted(!allBlocksAllowed);
return controller.getBlockFactory().parseFromListInput(list, context);
}
/**
* Gets the path to a file for opening. This method will check to see if the
* filename has valid characters and has an extension. It also prevents
* directory traversal exploits by checking the root directory and the file
* directory. On success, a {@code java.io.File} object will be
* returned.
*
* <p>Use this method if you need to read a file from a directory.</p>
*
* @param folder sub-directory to look in
* @param filename filename (user-submitted)
* @param defaultExt default extension to append if there is none
* @param exts list of extensions for file open dialog, null for no filter
* @return a file
* @throws FilenameException if there is a problem with the name of the file
*/
public File getSafeOpenFile(String folder, String filename, String defaultExt, String... exts) throws FilenameException {
File dir = controller.getWorkingDirectoryPath(folder).toFile();
return controller.getSafeOpenFile(player, dir, filename, defaultExt, exts);
}
/**
* Gets the path to a file for saving. This method will check to see if the
* filename has valid characters and has an extension. It also prevents
* directory traversal exploits by checking the root directory and the file
* directory. On success, a {@code java.io.File} object will be
* returned.
*
* <p>Use this method if you need to read a file from a directory.</p>
*
* @param folder sub-directory to look in
* @param filename filename (user-submitted)
* @param defaultExt default extension to append if there is none
* @param exts list of extensions for file save dialog, null for no filter
* @return a file
* @throws FilenameException if there is a problem with the name of the file
*/
public File getSafeSaveFile(String folder, String filename, String defaultExt, String... exts) throws FilenameException {
File dir = controller.getWorkingDirectoryPath(folder).toFile();
return controller.getSafeSaveFile(player, dir, filename, defaultExt, exts);
}
}

View File

@ -1,33 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting;
import java.util.Map;
public interface CraftScriptEngine {
void setTimeLimit(int milliseconds);
int getTimeLimit();
Object evaluate(String script, String filename, Map<String, Object> args)
throws Throwable;
}

View File

@ -1,50 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Platform;
public abstract class CraftScriptEnvironment {
protected WorldEdit controller;
protected Player player;
protected LocalConfiguration config;
protected LocalSession session;
protected Platform server;
public CraftScriptEnvironment(
WorldEdit controller,
Platform server,
LocalConfiguration config,
LocalSession session,
Player player
) {
this.controller = controller;
this.player = player;
this.config = config;
this.server = server;
this.session = session;
}
}

View File

@ -1,34 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting;
import org.mozilla.javascript.ClassShutter;
/**
* Hides Minecraft's obfuscated names from scripts.
*/
class MinecraftHidingClassShutter implements ClassShutter {
@Override
public boolean visibleToScripts(String fullClassName) {
return fullClassName.contains(".") || fullClassName.length() >= 4;
}
}

View File

@ -1,81 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting;
import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
public class RhinoContextFactory extends ContextFactory {
protected int timeLimit;
public RhinoContextFactory(int timeLimit) {
this.timeLimit = timeLimit;
}
@Override
protected Context makeContext() {
RhinoContext cx = new RhinoContext(this);
try {
// Try to set ES6 compat flag (since 1.7.7)
Context.class.getDeclaredField("VERSION_ES6");
cx.setLanguageVersion(RhinoContext.VERSION_ES6);
} catch (NoSuchFieldException e) {
// best we can do, compatible with 1.7R2 that many people probably use
cx.setLanguageVersion(Context.VERSION_1_7);
}
cx.setInstructionObserverThreshold(10000);
return cx;
}
@Override
protected void observeInstructionCount(Context cx, int instructionCount) {
RhinoContext mcx = (RhinoContext) cx;
long currentTime = System.currentTimeMillis();
if (currentTime - mcx.startTime > timeLimit) {
throw new Error("Script timed out (" + timeLimit + "ms)");
}
}
@Override
protected Object doTopCall(
Callable callable, Context cx, Scriptable scope,
Scriptable thisObj, Object[] args
) {
RhinoContext mcx = (RhinoContext) cx;
mcx.startTime = System.currentTimeMillis();
return super.doTopCall(callable, cx, scope, thisObj, args);
}
private static class RhinoContext extends Context {
long startTime;
private RhinoContext(ContextFactory factory) {
super(factory);
}
}
}

View File

@ -1,93 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting;
import com.sk89q.worldedit.WorldEditException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrappedException;
import javax.script.ScriptException;
import java.util.Map;
public class RhinoCraftScriptEngine implements CraftScriptEngine {
private int timeLimit;
@Override
public int getTimeLimit() {
return timeLimit;
}
@Override
public void setTimeLimit(int milliseconds) {
timeLimit = milliseconds;
}
@Override
public Object evaluate(String script, String filename, Map<String, Object> args)
throws Throwable {
RhinoContextFactory factory = new RhinoContextFactory(timeLimit);
Context cx = factory.enterContext();
cx.setClassShutter(new MinecraftHidingClassShutter());
ScriptableObject scriptable = new ImporterTopLevel(cx);
Scriptable scope = cx.initStandardObjects(scriptable);
for (Map.Entry<String, Object> entry : args.entrySet()) {
ScriptableObject.putProperty(scope, entry.getKey(),
Context.javaToJS(entry.getValue(), scope)
);
}
try {
return cx.evaluateString(scope, script, filename, 1, null);
} catch (Error e) {
throw new ScriptException(e.getMessage());
} catch (RhinoException e) {
if (e instanceof WrappedException) {
Throwable cause = e.getCause();
if (cause instanceof WorldEditException) {
throw cause;
}
}
String msg;
int line = (line = e.lineNumber()) == 0 ? -1 : line;
if (e instanceof JavaScriptException) {
msg = String.valueOf(((JavaScriptException) e).getValue());
} else {
msg = e.getMessage();
}
ScriptException scriptException =
new ScriptException(msg, e.sourceName(), line);
scriptException.initCause(e);
throw scriptException;
} finally {
Context.exit();
}
}
}

View File

@ -129,11 +129,9 @@ public class PropertiesConfiguration extends LocalConfiguration {
}
navigationWandMaxDistance = getInt("nav-wand-distance", navigationWandMaxDistance);
navigationUseGlass = getBool("nav-use-glass", navigationUseGlass);
scriptTimeout = getInt("scripting-timeout", scriptTimeout);
calculationTimeout = getInt("calculation-timeout", calculationTimeout);
maxCalculationTimeout = getInt("max-calculation-timeout", maxCalculationTimeout);
saveDir = getString("schematic-save-dir", saveDir);
scriptsDir = getString("craftscript-dir", scriptsDir);
butcherDefaultRadius = getInt("butcher-default-radius", butcherDefaultRadius);
butcherMaxRadius = getInt("butcher-max-radius", butcherMaxRadius);
allowSymlinks = getBool("allow-symbolic-links", allowSymlinks);

View File

@ -127,9 +127,6 @@ public class YAMLConfiguration extends LocalConfiguration {
navigationWandMaxDistance = config.getInt("navigation-wand.max-distance", navigationWandMaxDistance);
navigationUseGlass = config.getBoolean("navigation.use-glass", navigationUseGlass);
scriptTimeout = config.getInt("scripting.timeout", scriptTimeout);
scriptsDir = config.getString("scripting.dir", scriptsDir);
calculationTimeout = config.getInt("calculation.timeout", calculationTimeout);
maxCalculationTimeout = config.getInt("calculation.max-timeout", maxCalculationTimeout);

View File

@ -0,0 +1,8 @@
package dev.plex;
// From Allink
public class Maths {
public static double clamp(double number, double max, double min) {
return Math.max(min, Math.min(max, number));
}
}

View File

@ -0,0 +1,21 @@
package dev.plex;
// From Allink
public class SanityChecks {
private static final double UPPER_BOUND = 30_000_000;
private static final double LOWER_BOUND = -UPPER_BOUND;
public static double[] getSane(double x, double y, double z) {
final double[] arr = new double[3];
arr[0] = clamp(x);
arr[1] = clamp(y);
arr[2] = clamp(z);
return arr;
}
private static double clamp(double number) {
return Maths.clamp(number, UPPER_BOUND, LOWER_BOUND);
}
}

View File

@ -110,9 +110,6 @@ public class ConfigurateConfiguration extends LocalConfiguration {
navigationWandMaxDistance = node.getNode("navigation-wand", "max-distance").getInt(navigationWandMaxDistance);
navigationUseGlass = node.getNode("navigation", "use-glass").getBoolean(navigationUseGlass);
scriptTimeout = node.getNode("scripting", "timeout").getInt(scriptTimeout);
scriptsDir = node.getNode("scripting", "dir").getString(scriptsDir);
saveDir = node.getNode("saving", "dir").getString(saveDir);
allowSymlinks = node.getNode("files", "allow-symbolic-links").getBoolean(false);