mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-06-11 20:13:55 +00:00
Merge remote-tracking branch 'upstream/master' into merge
This commit is contained in:
@ -21,14 +21,14 @@ configurations.all {
|
||||
dependencies {
|
||||
"compile"(project(":worldedit-libs:core"))
|
||||
"compile"("de.schlichtherle:truezip:6.8.3")
|
||||
"compile"("rhino:js:1.7R2")
|
||||
"compile"("rhino:js:1.7.11")
|
||||
"compile"("org.yaml:snakeyaml:1.23")
|
||||
"compile"("com.google.guava:guava:21.0")
|
||||
"compile"("com.google.code.findbugs:jsr305:3.0.2")
|
||||
"compile"("com.google.code.gson:gson:2.8.0")
|
||||
"compile"("org.slf4j:slf4j-api:1.7.26")
|
||||
"compile"("it.unimi.dsi:fastutil:8.2.1")
|
||||
"compile"("com.googlecode.json-simple:json-simple:1.1.1") { isTransitive = false }
|
||||
"compile"("com.googlecode.json-simple:json-simple:1.3.9") { isTransitive = false }
|
||||
"compileOnly"(project(":worldedit-libs:core:ap"))
|
||||
"annotationProcessor"(project(":worldedit-libs:core:ap"))
|
||||
// ensure this is on the classpath for the AP
|
||||
@ -48,6 +48,26 @@ tasks.withType<JavaCompile>().configureEach {
|
||||
options.compilerArgs.add("-Aarg.name.key.prefix=")
|
||||
}
|
||||
|
||||
tasks.named<AntlrTask>("generateGrammarSource").configure {
|
||||
val pkg = "com.sk89q.worldedit.antlr"
|
||||
outputDirectory = file("build/generated-src/antlr/main/${pkg.replace('.', '/')}")
|
||||
arguments = listOf(
|
||||
"-visitor", "-package", pkg,
|
||||
"-Xexact-output-dir"
|
||||
)
|
||||
}
|
||||
|
||||
// Give intellij info about where ANTLR code comes from
|
||||
plugins.withId("idea") {
|
||||
configure<IdeaModel> {
|
||||
afterEvaluate {
|
||||
module.sourceDirs.add(file("src/main/antlr"))
|
||||
module.sourceDirs.add(file("build/generated-src/antlr/main"))
|
||||
module.generatedSourceDirs.add(file("build/generated-src/antlr/main"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
|
24
worldedit-core/doctools/build.gradle.kts
Normal file
24
worldedit-core/doctools/build.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.3.41"
|
||||
application
|
||||
}
|
||||
|
||||
applyCommonConfiguration()
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
application.mainClassName = "com.sk89q.worldedit.internal.util.DocumentationPrinter"
|
||||
tasks.named<JavaExec>("run") {
|
||||
workingDir = rootProject.projectDir
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"implementation"(project(":worldedit-libs:core:ap"))
|
||||
"implementation"(project(":worldedit-core"))
|
||||
"implementation"(kotlin("stdlib-jdk8"))
|
||||
"implementation"(kotlin("reflect"))
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.util
|
||||
|
||||
import com.google.common.base.Strings
|
||||
import com.sk89q.worldedit.WorldEdit
|
||||
import com.sk89q.worldedit.command.BiomeCommands
|
||||
import com.sk89q.worldedit.command.ChunkCommands
|
||||
import com.sk89q.worldedit.command.ClipboardCommands
|
||||
import com.sk89q.worldedit.command.GeneralCommands
|
||||
import com.sk89q.worldedit.command.GenerationCommands
|
||||
import com.sk89q.worldedit.command.HistoryCommands
|
||||
import com.sk89q.worldedit.command.NavigationCommands
|
||||
import com.sk89q.worldedit.command.RegionCommands
|
||||
import com.sk89q.worldedit.command.ScriptingCommands
|
||||
import com.sk89q.worldedit.command.SelectionCommands
|
||||
import com.sk89q.worldedit.command.SnapshotUtilCommands
|
||||
import com.sk89q.worldedit.command.ToolCommands
|
||||
import com.sk89q.worldedit.command.ToolUtilCommands
|
||||
import com.sk89q.worldedit.command.UtilityCommands
|
||||
import com.sk89q.worldedit.command.util.PermissionCondition
|
||||
import com.sk89q.worldedit.internal.command.CommandUtil
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent
|
||||
import org.enginehub.piston.Command
|
||||
import org.enginehub.piston.config.TextConfig
|
||||
import org.enginehub.piston.part.SubCommandPart
|
||||
import org.enginehub.piston.util.HelpGenerator
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.stream.Stream
|
||||
import kotlin.streams.toList
|
||||
|
||||
class DocumentationPrinter private constructor() {
|
||||
|
||||
private val nameRegex = Regex("name = \"(.+?)\"")
|
||||
private val commands = WorldEdit.getInstance().platformManager.platformCommandManager.commandManager.allCommands
|
||||
.map { it.name to it }.toList().toMap()
|
||||
private val cmdOutput = StringBuilder()
|
||||
private val permsOutput = StringBuilder()
|
||||
private val matchedCommands = mutableSetOf<String>()
|
||||
|
||||
private suspend inline fun <reified T> SequenceScope<String>.yieldAllCommandsIn() {
|
||||
val sourceFile = Paths.get("worldedit-core/src/main/java/" + T::class.qualifiedName!!.replace('.', '/') + ".java")
|
||||
require(Files.exists(sourceFile)) {
|
||||
"Source not found for ${T::class.qualifiedName}, looked at ${sourceFile.toAbsolutePath()}"
|
||||
}
|
||||
Files.newBufferedReader(sourceFile).useLines { lines ->
|
||||
var inCommand = false
|
||||
for (line in lines) {
|
||||
if (inCommand) {
|
||||
when (val match = nameRegex.find(line)) {
|
||||
null -> if (line.trim() == ")") inCommand = false
|
||||
else -> yield(match.groupValues[1])
|
||||
}
|
||||
} else if (line.contains("@Command(")) {
|
||||
inCommand = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeAllCommands() {
|
||||
writeHeader()
|
||||
|
||||
dumpSection("General Commands") {
|
||||
yield("worldedit")
|
||||
yieldAllCommandsIn<HistoryCommands>()
|
||||
yieldAllCommandsIn<GeneralCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Navigation Commands") {
|
||||
yieldAllCommandsIn<NavigationCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Selection Commands") {
|
||||
yieldAllCommandsIn<SelectionCommands>()
|
||||
yield("/expand")
|
||||
}
|
||||
|
||||
dumpSection("Region Commands") {
|
||||
yieldAllCommandsIn<RegionCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Generation Commands") {
|
||||
yieldAllCommandsIn<GenerationCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Schematic and Clipboard Commands") {
|
||||
yield("schematic")
|
||||
yieldAllCommandsIn<ClipboardCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Tool Commands") {
|
||||
yield("tool")
|
||||
yieldAllCommandsIn<ToolCommands>()
|
||||
yieldAllCommandsIn<ToolUtilCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Super Pickaxe Commands") {
|
||||
yield("superpickaxe")
|
||||
}
|
||||
|
||||
dumpSection("Brush Commands") {
|
||||
yield("brush")
|
||||
}
|
||||
|
||||
dumpSection("Biome Commands") {
|
||||
yieldAllCommandsIn<BiomeCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Chunk Commands") {
|
||||
yieldAllCommandsIn<ChunkCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Snapshot Commands") {
|
||||
yieldAllCommandsIn<SnapshotUtilCommands>()
|
||||
yield("snapshot")
|
||||
}
|
||||
|
||||
dumpSection("Scripting Commands") {
|
||||
yieldAllCommandsIn<ScriptingCommands>()
|
||||
}
|
||||
|
||||
dumpSection("Utility Commands") {
|
||||
yieldAllCommandsIn<UtilityCommands>()
|
||||
}
|
||||
|
||||
writeFooter()
|
||||
|
||||
val missingCommands = commands.keys.filterNot { it in matchedCommands }
|
||||
require(missingCommands.isEmpty()) { "Missing commands: $missingCommands" }
|
||||
}
|
||||
|
||||
private fun writeHeader() {
|
||||
cmdOutput.appendln("""
|
||||
========
|
||||
Commands
|
||||
========
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
.. note::
|
||||
|
||||
Arguments enclosed in ``[ ]`` are optional, those enclosed in ``< >`` are required.
|
||||
|
||||
.. tip::
|
||||
|
||||
You can access a command listing in-game via the ``//help`` command.
|
||||
""".trim())
|
||||
|
||||
permsOutput.appendln("""
|
||||
===========
|
||||
Permissions
|
||||
===========
|
||||
|
||||
By default, no one can use WorldEdit. In order for yourself, moderators, and players to use WorldEdit, you must provide the proper permissions. One way is to provide op to moderators and administrators (unless disabled in the :doc:`configuration <config>`), but providing the permission nodes on this page (through a permissions plugin) is the more flexible.
|
||||
|
||||
You can give the ``worldedit.*`` permission to give yourself and other administrators full access to WorldEdit.
|
||||
|
||||
Commands
|
||||
=========
|
||||
|
||||
See the :doc:`commands` page for an explanation of some of these commands.
|
||||
|
||||
.. csv-table::
|
||||
:header: Command, Permission
|
||||
:widths: 15, 25
|
||||
""".trim())
|
||||
permsOutput.appendln()
|
||||
}
|
||||
|
||||
private fun writeFooter() {
|
||||
permsOutput.appendln()
|
||||
permsOutput.append("""
|
||||
Other Permissions
|
||||
==================
|
||||
|
||||
.. csv-table::
|
||||
:header: Permission, Explanation
|
||||
:widths: 15, 25
|
||||
|
||||
``worldedit.navigation.jumpto.tool``,"Allows usage of the navigation wand's ``/jumpto`` shortcut (left click)."
|
||||
``worldedit.navigation.thru.tool``,"Allows usage of the navigation wand's ``/thru`` shortcut (right click)."
|
||||
``worldedit.anyblock``,"Allows usage of blocks in the :doc:`disallowed-blocks <config>` config option."
|
||||
``worldedit.limit.unrestricted``,"Allows setting the limit via the ``//limit`` :doc:`command <commands>` higher than the maximum in the :doc:`configuration <config>`, as well as other limit bypasses."
|
||||
``worldedit.timeout.unrestricted``,"Allows setting the calculation timeout via the ``//timeout`` :doc:`command <commands>` higher than the maximum in the :doc:`configuration <config>`."
|
||||
``worldedit.inventory.unrestricted``,"Override the ``use-inventory`` option if enabled in the :doc:`configuration <config>`."
|
||||
``worldedit.override.bedrock``,"Allows breaking of bedrock with the super-pickaxe tool."
|
||||
``worldedit.override.data-cycler``,"Allows cycling non-whitelisted blocks with the data cycler tool."
|
||||
``worldedit.setnbt``,"Allows setting `extra data <https://minecraft.gamepedia.com/Block_entity>`_ on blocks (such as signs, chests, etc)."
|
||||
``worldedit.report.pastebin``,"Allows uploading report files to pastebin automatically for the ``/worldedit report`` :doc:`command <commands>`."
|
||||
``worldedit.scripting.execute.<filename>``,"Allows using the CraftScript with the given filename."
|
||||
""".trim())
|
||||
}
|
||||
|
||||
private fun dumpSection(title: String, addCommandNames: suspend SequenceScope<String>.() -> Unit) {
|
||||
cmdOutput.append("\n").append(title).append("\n").append(Strings.repeat("~", title.length)).append("\n")
|
||||
|
||||
val prefix = reduceToRst(TextConfig.commandPrefixValue())
|
||||
val commands = sequence(addCommandNames).map { this.commands.getValue(it) }.toList()
|
||||
matchedCommands.addAll(commands.map { it.name })
|
||||
|
||||
cmdsToPerms(commands, prefix)
|
||||
|
||||
for (command in commands) {
|
||||
writeCommandBlock(command, prefix, Stream.empty())
|
||||
command.parts.stream().filter { p -> p is SubCommandPart }
|
||||
.flatMap { p -> (p as SubCommandPart).commands.stream() }
|
||||
.forEach { sc ->
|
||||
writeCommandBlock(sc, prefix + command.name + " ", Stream.of(command))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cmdsToPerms(cmds: List<Command>, prefix: String) {
|
||||
cmds.forEach { c ->
|
||||
permsOutput.append(" ").append(cmdToPerm(prefix, c)).append("\n")
|
||||
c.parts.filterIsInstance<SubCommandPart>()
|
||||
.forEach { scp ->
|
||||
cmdsToPerms(scp.commands.sortedBy { it.name }, prefix + c.name + " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cmdToPerm(prefix: String, c: Command): String {
|
||||
val cond = c.condition
|
||||
val permissions = when {
|
||||
cond is PermissionCondition && cond.permissions.isNotEmpty() ->
|
||||
cond.permissions.joinToString(", ") { "``$it``" }
|
||||
else -> ""
|
||||
}
|
||||
return "``$prefix${c.name}``,\"$permissions\""
|
||||
}
|
||||
|
||||
private fun writeCommandBlock(command: Command, prefix: String, parents: Stream<Command>) {
|
||||
val name = prefix + command.name
|
||||
val entries = commandTableEntries(command, parents)
|
||||
|
||||
cmdOutput.appendln(".. raw:: html")
|
||||
cmdOutput.appendln()
|
||||
cmdOutput.appendln(""" <span id="command-${linkSafe(name)}"></span>""")
|
||||
cmdOutput.appendln()
|
||||
cmdOutput.append(".. topic:: ``$name``")
|
||||
if (!command.aliases.isEmpty()) {
|
||||
command.aliases.joinTo(cmdOutput, ", ",
|
||||
prefix = " (or ",
|
||||
postfix = ")",
|
||||
transform = { "``$prefix$it``" })
|
||||
}
|
||||
cmdOutput.appendln()
|
||||
cmdOutput.appendln(" :class: command-topic").appendln()
|
||||
CommandUtil.deprecationWarning(command).ifPresent { warning ->
|
||||
cmdOutput.appendln("""
|
||||
| .. WARNING::
|
||||
| ${reduceToRst(warning).makeRstSafe("\n\n")}
|
||||
""".trimMargin())
|
||||
}
|
||||
cmdOutput.appendln("""
|
||||
| .. csv-table::
|
||||
| :widths: 8, 15
|
||||
""".trimMargin())
|
||||
cmdOutput.appendln()
|
||||
for ((k, v) in entries) {
|
||||
val rstSafe = v.makeRstSafe("\n")
|
||||
cmdOutput.append(" ".repeat(2))
|
||||
.append(k)
|
||||
.append(",")
|
||||
.append('"')
|
||||
.append(rstSafe)
|
||||
.append('"').appendln()
|
||||
}
|
||||
cmdOutput.appendln()
|
||||
}
|
||||
|
||||
private fun String.makeRstSafe(lineJoiner: String) = trim()
|
||||
.replace("\"", "\\\"").replace("\n", "\n" + " ".repeat(2))
|
||||
.lineSequence()
|
||||
.map { line -> line.ifBlank { "" } }
|
||||
.joinToString(separator = lineJoiner)
|
||||
|
||||
private fun linkSafe(text: String) = text.replace(" ", "-")
|
||||
|
||||
private fun commandTableEntries(command: Command, parents: Stream<Command>): Map<String, String> {
|
||||
return sequence {
|
||||
val desc = command.description.run {
|
||||
val footer = CommandUtil.footerWithoutDeprecation(command)
|
||||
when {
|
||||
footer.isPresent -> append(
|
||||
TextComponent.builder("\n\n").append(footer.get())
|
||||
)
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
yield("**Description**" to reduceToRst(desc))
|
||||
val cond = command.condition
|
||||
if (cond is PermissionCondition && cond.permissions.isNotEmpty()) {
|
||||
val perms = cond.permissions.joinToString(", ") { "``$it``" }
|
||||
yield("**Permissions**" to perms)
|
||||
}
|
||||
val usage = reduceToRst(HelpGenerator.create(Stream.concat(parents, Stream.of(command)).toList()).usage)
|
||||
yield("**Usage**" to "``$usage``")
|
||||
|
||||
// Part descriptions
|
||||
command.parts.filterNot { it is SubCommandPart }
|
||||
.forEach {
|
||||
val title = "\u2001\u2001``" + reduceToRst(it.textRepresentation) + "``"
|
||||
yield(title to reduceToRst(it.description))
|
||||
}
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Generates documentation.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
try {
|
||||
val printer = DocumentationPrinter()
|
||||
|
||||
printer.writeAllCommands()
|
||||
writeOutput("commands.rst", printer.cmdOutput.toString())
|
||||
writeOutput("permissions.rst", printer.permsOutput.toString())
|
||||
} finally {
|
||||
WorldEdit.getInstance().sessionManager.unload()
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeOutput(file: String, output: String) {
|
||||
Files.newBufferedWriter(Paths.get(file)).use {
|
||||
it.write(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -121,7 +121,6 @@ public class MobSpawnerBlock extends BaseBlock {
|
||||
@Override
|
||||
public CompoundTag getNbtData() {
|
||||
Map<String, Tag> values = new HashMap<>();
|
||||
values.put("EntityId", new StringTag(mobType));
|
||||
values.put("Delay", new ShortTag(delay));
|
||||
values.put("SpawnCount", new ShortTag(spawnCount));
|
||||
values.put("SpawnRange", new ShortTag(spawnRange));
|
||||
@ -180,7 +179,6 @@ public class MobSpawnerBlock extends BaseBlock {
|
||||
this.delay = -1;
|
||||
}
|
||||
|
||||
|
||||
ShortTag spawnCountTag = null;
|
||||
ShortTag spawnRangeTag = null;
|
||||
ShortTag minSpawnDelayTag = null;
|
||||
|
@ -0,0 +1,236 @@
|
||||
grammar Expression;
|
||||
|
||||
// Lexer tokens:
|
||||
|
||||
PLUS : '+' ;
|
||||
MINUS : '-' ;
|
||||
TIMES : '*' ;
|
||||
DIVIDE : '/' ;
|
||||
MODULO : '%' ;
|
||||
POWER : '^' | '**' ;
|
||||
LEFT_SHIFT : '<<' ;
|
||||
RIGHT_SHIFT : '>>' ;
|
||||
ASSIGN : '=' ;
|
||||
COMPLEMENT : '~' ;
|
||||
|
||||
PLUS_ASSIGN : '+=' ;
|
||||
MINUS_ASSIGN : '-=' ;
|
||||
TIMES_ASSIGN : '*=' ;
|
||||
DIVIDE_ASSIGN : '/=' ;
|
||||
MODULO_ASSIGN : '%=' ;
|
||||
POWER_ASSIGN : '^=' ;
|
||||
|
||||
EQUAL : '==' ;
|
||||
NOT_EQUAL : '!=' ;
|
||||
NEAR : '~=' ;
|
||||
LESS_THAN : '<' ;
|
||||
LESS_THAN_OR_EQUAL : '<=' ;
|
||||
GREATER_THAN : '>' ;
|
||||
GREATER_THAN_OR_EQUAL : '>=' ;
|
||||
// SC = "Short Circuit"
|
||||
// Non-SC variants not currently implemented.
|
||||
AND_SC : '&&' ;
|
||||
OR_SC : '||' ;
|
||||
|
||||
INCREMENT : '++' ;
|
||||
DECREMENT : '--' ;
|
||||
|
||||
COMMA : ',' ;
|
||||
OPEN_PAREN : '(' ;
|
||||
CLOSE_PAREN : ')' ;
|
||||
OPEN_BRACKET : '{' ;
|
||||
CLOSE_BRACKET : '}' ;
|
||||
SEMI_COLON : ';' ;
|
||||
QUESTION_MARK : '?' ;
|
||||
COLON : ':' ;
|
||||
EXCLAMATION_MARK : '!' ;
|
||||
|
||||
IF : 'if' ;
|
||||
ELSE : 'else' ;
|
||||
WHILE : 'while' ;
|
||||
DO : 'do' ;
|
||||
FOR : 'for' ;
|
||||
BREAK : 'break' ;
|
||||
CONTINUE : 'continue' ;
|
||||
RETURN : 'return' ;
|
||||
SWITCH : 'switch' ;
|
||||
CASE : 'case' ;
|
||||
DEFAULT : 'default' ;
|
||||
|
||||
fragment DIGIT : [0-9] ;
|
||||
fragment SIGN : [+-] ;
|
||||
fragment EXP_CHAR : [eE] ;
|
||||
fragment DECIMAL : '.' DIGIT+ ( EXP_CHAR SIGN? DIGIT+ )? ;
|
||||
|
||||
// All numbers are treated the same. No int/dec divide.
|
||||
NUMBER : ( DIGIT+ DECIMAL? | DECIMAL ) ;
|
||||
|
||||
ID : [A-Za-z] [0-9A-Za-z_]* ;
|
||||
|
||||
WS : [ \t\r\n\u000C]+ -> skip ;
|
||||
|
||||
// Parser rules:
|
||||
|
||||
/**
|
||||
* All statements parseable from the input. Forces consumption of EOF token.
|
||||
*/
|
||||
allStatements : statements EOF ;
|
||||
|
||||
statements : statement+ ;
|
||||
|
||||
statement
|
||||
: ( block
|
||||
| ifStatement
|
||||
| whileStatement
|
||||
| doStatement
|
||||
| forStatement
|
||||
| simpleForStatement
|
||||
| breakStatement
|
||||
| continueStatement
|
||||
| returnStatement
|
||||
| switchStatement
|
||||
| expressionStatement
|
||||
| emptyStatement
|
||||
) SEMI_COLON?
|
||||
;
|
||||
|
||||
block : '{' statements '}' ;
|
||||
|
||||
ifStatement : IF '(' condition=expression ')' trueBranch=statement ( ELSE falseBranch=statement )? ;
|
||||
|
||||
whileStatement : WHILE '(' condition=expression ')' body=statement ;
|
||||
|
||||
doStatement : DO body=statement WHILE '(' condition=expression ')' ;
|
||||
|
||||
// C-Style for loop
|
||||
forStatement
|
||||
: FOR '(' init=expression ';' condition=expression ';' update=expression ')' body=statement ;
|
||||
|
||||
// Range for loop
|
||||
simpleForStatement
|
||||
: FOR '(' counter=ID ASSIGN first=expression ',' last=expression ')' body=statement ;
|
||||
|
||||
breakStatement : BREAK ;
|
||||
|
||||
continueStatement : CONTINUE ;
|
||||
|
||||
returnStatement : RETURN value=expression? ;
|
||||
|
||||
switchStatement : SWITCH '(' target=expression ')' '{' (labels+=switchLabel ':' bodies+=statements )+ '}' ;
|
||||
|
||||
switchLabel
|
||||
: CASE constant=constantExpression # Case
|
||||
| DEFAULT # Default
|
||||
;
|
||||
|
||||
expressionStatement : expression ;
|
||||
|
||||
emptyStatement: SEMI_COLON ;
|
||||
|
||||
expression : assignmentExpression ;
|
||||
|
||||
assignmentExpression
|
||||
: conditionalExpression
|
||||
| assignment
|
||||
;
|
||||
|
||||
assignment
|
||||
: target=ID assignmentOperator source=expression
|
||||
;
|
||||
|
||||
assignmentOperator
|
||||
: ASSIGN
|
||||
| POWER_ASSIGN
|
||||
| TIMES_ASSIGN
|
||||
| DIVIDE_ASSIGN
|
||||
| MODULO_ASSIGN
|
||||
| PLUS_ASSIGN
|
||||
| MINUS_ASSIGN
|
||||
;
|
||||
|
||||
conditionalExpression
|
||||
: conditionalOrExpression # CEFallthrough
|
||||
| condition=conditionalOrExpression QUESTION_MARK
|
||||
trueBranch=expression COLON falseBranch=conditionalExpression # TernaryExpr
|
||||
;
|
||||
|
||||
conditionalOrExpression
|
||||
: conditionalAndExpression # COFallthrough
|
||||
| left=conditionalOrExpression OR_SC right=conditionalAndExpression # ConditionalOrExpr
|
||||
;
|
||||
|
||||
conditionalAndExpression
|
||||
: equalityExpression # CAFallthrough
|
||||
| left=conditionalAndExpression AND_SC right=equalityExpression # ConditionalAndExpr
|
||||
;
|
||||
|
||||
equalityExpression
|
||||
: relationalExpression # EqFallthrough
|
||||
| left=equalityExpression
|
||||
op=
|
||||
( EQUAL
|
||||
| NOT_EQUAL
|
||||
| NEAR
|
||||
) right=relationalExpression # EqualityExpr
|
||||
;
|
||||
|
||||
relationalExpression
|
||||
: shiftExpression # ReFallthrough
|
||||
| left=relationalExpression
|
||||
op=
|
||||
( LESS_THAN
|
||||
| GREATER_THAN
|
||||
| LESS_THAN_OR_EQUAL
|
||||
| GREATER_THAN_OR_EQUAL
|
||||
) right=shiftExpression # RelationalExpr
|
||||
;
|
||||
|
||||
shiftExpression
|
||||
: additiveExpression # ShFallthrough
|
||||
| left=shiftExpression op=( LEFT_SHIFT | RIGHT_SHIFT ) right=additiveExpression # ShiftExpr
|
||||
;
|
||||
|
||||
additiveExpression
|
||||
: multiplicativeExpression # AdFallthrough
|
||||
| left=additiveExpression op=( PLUS | MINUS ) right=multiplicativeExpression # AddExpr
|
||||
;
|
||||
|
||||
multiplicativeExpression
|
||||
: powerExpression # MuFallthrough
|
||||
| left=multiplicativeExpression
|
||||
op=
|
||||
( TIMES
|
||||
| DIVIDE
|
||||
| MODULO
|
||||
) right=powerExpression # MultiplicativeExpr
|
||||
;
|
||||
|
||||
powerExpression
|
||||
: unaryExpression # PwFallthrough
|
||||
| left=powerExpression POWER right=unaryExpression # PowerExpr
|
||||
;
|
||||
|
||||
unaryExpression
|
||||
: op=( INCREMENT | DECREMENT ) target=ID # PreCrementExpr
|
||||
| op=( PLUS | MINUS ) expr=unaryExpression # PlusMinusExpr
|
||||
| postfixExpression # UaFallthrough
|
||||
| COMPLEMENT expr=unaryExpression # ComplementExpr
|
||||
| EXCLAMATION_MARK expr=unaryExpression # NotExpr
|
||||
;
|
||||
|
||||
postfixExpression
|
||||
: unprioritizedExpression # PoFallthrough
|
||||
| target=ID op=( INCREMENT | DECREMENT) # PostCrementExpr
|
||||
| expr=postfixExpression op=EXCLAMATION_MARK # PostfixExpr
|
||||
;
|
||||
|
||||
unprioritizedExpression
|
||||
: functionCall # FunctionCallExpr
|
||||
| constantExpression # ConstantExpr
|
||||
| source=ID # IdExpr
|
||||
| '(' expression ')' # WrappedExpr
|
||||
;
|
||||
|
||||
constantExpression : NUMBER ;
|
||||
|
||||
functionCall : name=ID '(' (args+=expression ( ',' args+=expression )*)? ')' ;
|
@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
<format>tar.bz2</format>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<files>
|
||||
<file>
|
||||
<source>${project.build.directory}/${artifactId}-${project.version}.jar</source>
|
||||
<destName>WorldEdit.jar</destName>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<filtered>false</filtered>
|
||||
</file>
|
||||
<file>
|
||||
<source>README.html</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
</files>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<includes>
|
||||
<include>LICENSE.txt</include>
|
||||
<include>CHANGELOG.txt</include>
|
||||
<include>contrib/craftscripts/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
@ -404,4 +404,4 @@ public class SimplexNoise {
|
||||
this.w = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The {@code TAG_Int_Array} tag.
|
||||
*/
|
||||
|
@ -23,6 +23,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The {@code TAG_Long_Array} tag.
|
||||
*/
|
||||
|
@ -21,4 +21,4 @@
|
||||
* This package contains the old command system. It is no longer in use. Please switch
|
||||
* to Piston, Intake, ACF, or similar systems.
|
||||
*/
|
||||
package com.sk89q.minecraft.util.commands;
|
||||
package com.sk89q.minecraft.util.commands;
|
@ -43,6 +43,8 @@ import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.extent.EditSessionEvent;
|
||||
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.extension.platform.Watchdog;
|
||||
import com.sk89q.worldedit.extent.ChangeSetExtent;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.MaskingExtent;
|
||||
@ -60,6 +62,7 @@ import com.sk89q.worldedit.extent.world.FastModeExtent;
|
||||
import com.sk89q.worldedit.extent.world.SurvivalModeExtent;
|
||||
import com.sk89q.worldedit.extent.world.WatchdogTickingExtent;
|
||||
import com.sk89q.worldedit.function.GroundFunction;
|
||||
import com.sk89q.worldedit.function.biome.BiomeReplace;
|
||||
import com.sk89q.worldedit.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.block.BlockReplace;
|
||||
import com.sk89q.worldedit.function.block.Naturalizer;
|
||||
@ -94,11 +97,11 @@ import com.sk89q.worldedit.history.changeset.ChangeSet;
|
||||
import com.sk89q.worldedit.internal.expression.Expression;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.ExpressionTimeoutException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.RValue;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.math.MathUtils;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionTimeoutException;
|
||||
import com.sk89q.worldedit.internal.expression.LocalSlot.Variable;
|
||||
import com.sk89q.worldedit.math.MutableBlockVector2;
|
||||
import com.sk89q.worldedit.math.MutableBlockVector3;
|
||||
import com.sk89q.worldedit.math.Vector2;
|
||||
@ -319,6 +322,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
public Extent getBypassHistory() {
|
||||
return bypassHistory;
|
||||
}
|
||||
private final List<WatchdogTickingExtent> watchdogExtents = new ArrayList<>(2);
|
||||
|
||||
public void setExtent(AbstractDelegateExtent extent) {
|
||||
new ExtentTraverser<>(getExtent()).setNext(extent);
|
||||
@ -1164,6 +1168,18 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
return this.changes = visitor.getAffected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of blocks of a list of types in a region.
|
||||
*
|
||||
* @param region the region
|
||||
* @param searchBlocks the list of blocks to search
|
||||
* @return the number of blocks that matched the block
|
||||
*/
|
||||
public int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
|
||||
BlockMask mask = new BlockMask(this, searchBlocks);
|
||||
return countBlocks(region, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a cuboid above the given position with a given apothem and a given height.
|
||||
*
|
||||
@ -1182,8 +1198,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
getWorld(), // Causes clamping of Y range
|
||||
position.add(-apothem + 1, 0, -apothem + 1),
|
||||
position.add(apothem - 1, height - 1, apothem - 1));
|
||||
Pattern pattern = BlockTypes.AIR.getDefaultState();
|
||||
return setBlocks(region, pattern);
|
||||
return setBlocks(region, BlockTypes.AIR.getDefaultState());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1231,16 +1246,15 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
* Remove blocks of a certain type nearby a given position.
|
||||
*
|
||||
* @param position center position of cuboid
|
||||
* @param blockType the block type to match
|
||||
* @param mask the mask to match
|
||||
* @param apothem an apothem of the cuboid, where the minimum is 1
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
*/
|
||||
public int removeNear(BlockVector3 position, BlockType blockType, int apothem) throws MaxChangedBlocksException {
|
||||
public int removeNear(BlockVector3 position, Mask mask, int apothem) throws MaxChangedBlocksException {
|
||||
checkNotNull(position);
|
||||
checkArgument(apothem >= 1, "apothem >= 1");
|
||||
|
||||
Mask mask = new SingleBlockTypeMask(this, blockType);
|
||||
BlockVector3 adjustment = BlockVector3.ONE.multiply(apothem - 1);
|
||||
Region region = new CuboidRegion(
|
||||
getWorld(), // Causes clamping of Y range
|
||||
@ -1450,7 +1464,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
* @param region the region to stack
|
||||
* @param dir the direction to stack
|
||||
* @param count the number of times to stack
|
||||
* @param copyAir true to also copy air blocks
|
||||
* @param copyEntities true to copy entities
|
||||
* @param copyBiomes true to copy biomes
|
||||
* @param mask source mask for the operation (only matching blocks are copied)
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
*/
|
||||
@ -1485,10 +1501,13 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
* @param region the region to move
|
||||
* @param dir the direction
|
||||
* @param distance the distance to move
|
||||
* @param copyAir true to copy air blocks
|
||||
* @param moveEntities true to move entities
|
||||
* @param copyBiomes true to copy biomes (source biome is unchanged)
|
||||
* @param mask source mask for the operation (only matching blocks are moved)
|
||||
* @param replacement the replacement pattern to fill in after moving, or null to use air
|
||||
* @return number of blocks moved
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
* @throws IllegalArgumentException thrown if the region is not a flat region, but copyBiomes is true
|
||||
*/
|
||||
public int moveRegion(Region region, BlockVector3 dir, int distance, boolean copyAir,
|
||||
boolean moveEntities, boolean copyBiomes, Pattern replacement) throws MaxChangedBlocksException {
|
||||
@ -1563,7 +1582,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
/**
|
||||
* Drain nearby pools of water or lava, optionally removed waterlogged states from blocks.
|
||||
*
|
||||
* @param origin the origin to drain from, which will search a 3×3 area
|
||||
* @param origin the origin to drain from, which will search a 3x3 area
|
||||
* @param radius the radius of the removal, where a value should be 0 or greater
|
||||
* @param waterlogged true to make waterlogged blocks non-waterlogged as well
|
||||
* @return number of blocks affected
|
||||
@ -1592,14 +1611,14 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
}
|
||||
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1));
|
||||
|
||||
// Around the origin in a 3×3 block
|
||||
// Around the origin in a 3x3 block
|
||||
for (BlockVector3 position : CuboidRegion.fromCenter(origin, 1)) {
|
||||
if (mask.test(position)) {
|
||||
visitor.visit(position);
|
||||
}
|
||||
}
|
||||
|
||||
Operations.completeBlindly(visitor);
|
||||
Operations.completeLegacy(visitor);
|
||||
|
||||
return this.changes = visitor.getAffected();
|
||||
}
|
||||
@ -1680,6 +1699,21 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
return makeCylinder(pos, block, radiusX, radiusZ, height, thickness, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack a cuboid region. For compatibility, entities are copied by biomes are not.
|
||||
* Use {@link #stackCuboidRegion(Region, BlockVector3, int, boolean, boolean, Mask)} to fine tune.
|
||||
*
|
||||
* @param region the region to stack
|
||||
* @param dir the direction to stack
|
||||
* @param count the number of times to stack
|
||||
* @param copyAir true to also copy air blocks
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
*/
|
||||
public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean copyAir) throws MaxChangedBlocksException {
|
||||
return stackCuboidRegion(region, dir, count, true, false, copyAir ? null : new ExistingBlockMask(this));
|
||||
}
|
||||
|
||||
private int makeCylinder(BlockVector3 pos, Pattern block, double radiusX, double radiusZ, int height, double thickness, boolean filled) throws MaxChangedBlocksException {
|
||||
int affected = 0;
|
||||
|
||||
@ -1790,6 +1824,21 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
return this.changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the blocks in a region a certain direction.
|
||||
*
|
||||
* @param region the region to move
|
||||
* @param dir the direction
|
||||
* @param distance the distance to move
|
||||
* @param copyAir true to copy air blocks
|
||||
* @param replacement the replacement pattern to fill in after moving, or null to use air
|
||||
* @return number of blocks moved
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
*/
|
||||
public int moveRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException {
|
||||
return moveRegion(region, dir, distance, true, false, copyAir ? new ExistingBlockMask(this) : null, replacement);
|
||||
}
|
||||
|
||||
public int makeCircle(BlockVector3 pos, final Pattern block, double radiusX, double radiusY, double radiusZ, boolean filled, Vector3 normal) throws MaxChangedBlocksException {
|
||||
radiusX += 0.5;
|
||||
radiusY += 0.5;
|
||||
@ -2322,8 +2371,10 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
final Expression expression = Expression.compile(expressionString, "x", "y", "z", "type", "data");
|
||||
expression.optimize();
|
||||
|
||||
final RValue typeVariable = expression.getVariable("type", false);
|
||||
final RValue dataVariable = expression.getVariable("data", false);
|
||||
final Variable typeVariable = expression.getSlots().getVariable("type")
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
final Variable dataVariable = expression.getSlots().getVariable("data")
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
|
||||
final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero);
|
||||
expression.setEnvironment(environment);
|
||||
@ -2371,9 +2422,12 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
final Expression expression = Expression.compile(expressionString, "x", "y", "z");
|
||||
expression.optimize();
|
||||
|
||||
final RValue x = expression.getVariable("x", false).optimize();
|
||||
final RValue y = expression.getVariable("y", false).optimize();
|
||||
final RValue z = expression.getVariable("z", false).optimize();
|
||||
final Variable x = expression.getSlots().getVariable("x")
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
final Variable y = expression.getSlots().getVariable("y")
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
final Variable z = expression.getSlots().getVariable("z")
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
|
||||
final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero);
|
||||
expression.setEnvironment(environment);
|
||||
|
@ -20,8 +20,6 @@
|
||||
package com.sk89q.worldedit;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.extent.EditSessionEvent;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
||||
|
@ -78,7 +78,6 @@ import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.item.ItemTypes;
|
||||
import com.sk89q.worldedit.world.snapshot.Snapshot;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.io.File;
|
||||
@ -148,6 +147,7 @@ public class LocalSession implements TextureHolder {
|
||||
private transient ResettableExtent transform = null;
|
||||
private transient ZoneId timezone = ZoneId.systemDefault();
|
||||
private transient World currentWorld;
|
||||
private transient boolean tickingWatchdog = false;
|
||||
private transient UUID uuid;
|
||||
private transient volatile long historySize = 0;
|
||||
|
||||
@ -157,8 +157,6 @@ public class LocalSession implements TextureHolder {
|
||||
private transient World worldOverride;
|
||||
private transient boolean tickingWatchdog = false;
|
||||
|
||||
private transient boolean loadDefaults = true;
|
||||
|
||||
// Saved properties
|
||||
private String lastScript;
|
||||
private RegionSelectorType defaultSelector;
|
||||
@ -1104,6 +1102,14 @@ public class LocalSession implements TextureHolder {
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlaceAtPos1(boolean placeAtPos1) {
|
||||
this.placeAtPos1 = placeAtPos1;
|
||||
}
|
||||
|
||||
public boolean isPlaceAtPos1() {
|
||||
return placeAtPos1;
|
||||
}
|
||||
|
||||
public void setTool(BaseItem item, @Nullable Tool tool, Player player) throws InvalidToolBindException {
|
||||
ItemType type = item.getType();
|
||||
if (type.hasBlockType() && type.getBlockType().getMaterial().isAir()) {
|
||||
@ -1603,4 +1609,13 @@ public class LocalSession implements TextureHolder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareEditingExtents(EditSession editSession, Actor actor) {
|
||||
editSession.setFastMode(fastMode);
|
||||
editSession.setReorderMode(reorderMode);
|
||||
if (editSession.getSurvivalExtent() != null) {
|
||||
editSession.getSurvivalExtent().setStripNbt(!actor.hasPermission("worldedit.setnbt"));
|
||||
}
|
||||
editSession.setTickingWatchdog(tickingWatchdog);
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.blocks.metadata;
|
||||
|
||||
/**
|
||||
* Represents the possible types of mobs.
|
||||
*/
|
||||
public enum MobType {
|
||||
BAT("Bat"),
|
||||
BLAZE("Blaze"),
|
||||
CAVE_SPIDER("CaveSpider"),
|
||||
CHICKEN("Chicken"),
|
||||
COW("Cow"),
|
||||
CREEPER("Creeper"),
|
||||
ENDERDRAGON("EnderDragon"),
|
||||
ENDERMAN("Enderman"),
|
||||
GHAST("Ghast"),
|
||||
GIANT("Giant"),
|
||||
VILLAGER_GOLEM("VillagerGolem"),
|
||||
HORSE("EntityHorse"),
|
||||
MAGMA_CUBE("LavaSlime"),
|
||||
MOOSHROOM("MushroomCow"),
|
||||
OCELOT("Ozelot"),
|
||||
PIG("Pig"),
|
||||
PIG_ZOMBIE("PigZombie"),
|
||||
SHEEP("Sheep"),
|
||||
SILVERFISH("Silverfish"),
|
||||
SKELETON("Skeleton"),
|
||||
SLIME("Slime"),
|
||||
SNOWMAN("SnowMan"),
|
||||
SPIDER("Spider"),
|
||||
SQUID("Squid"),
|
||||
VILLAGER("Villager"),
|
||||
WITCH("Witch"),
|
||||
WITHER("WitherBoss"),
|
||||
WOLF("Wolf"),
|
||||
ZOMBIE("Zombie");
|
||||
|
||||
private final String name;
|
||||
|
||||
MobType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
@ -27,6 +27,7 @@ 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.WorldEditAsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import com.sk89q.worldedit.command.util.Logging;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
@ -81,6 +82,7 @@ public class BiomeCommands {
|
||||
public void biomeList(Actor actor,
|
||||
@ArgFlag(name = 'p', desc = "Page number.", def = "1")
|
||||
int page) {
|
||||
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> {
|
||||
BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager()
|
||||
.queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry();
|
||||
|
||||
@ -97,7 +99,8 @@ public class BiomeCommands {
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
actor.print(paginationBox.create(page));
|
||||
return paginationBox.create(page);
|
||||
}, null);
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -180,7 +183,8 @@ public class BiomeCommands {
|
||||
Mask2D mask2d = mask != null ? mask.toMask2D() : null;
|
||||
|
||||
if (atPosition) {
|
||||
region = new CuboidRegion(player.getLocation().toVector().toBlockPoint(), player.getLocation().toVector().toBlockPoint());
|
||||
final BlockVector3 pos = player.getLocation().toVector().toBlockPoint();
|
||||
region = new CuboidRegion(pos, pos);
|
||||
} else {
|
||||
region = session.getSelection(world);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
@ -67,6 +65,7 @@ import com.sk89q.worldedit.EmptyClipboardException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.command.factory.ReplaceFactory;
|
||||
import com.sk89q.worldedit.blocks.BaseItem;
|
||||
import com.sk89q.worldedit.command.argument.Arguments;
|
||||
import com.sk89q.worldedit.command.factory.TreeGeneratorFactory;
|
||||
@ -127,6 +126,7 @@ import java.nio.file.FileSystems;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import com.sk89q.worldedit.function.factory.Apply;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.ArgFlag;
|
||||
@ -134,6 +134,8 @@ import org.enginehub.piston.annotation.param.Switch;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
import org.enginehub.piston.inject.Key;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Commands to set brush shape.
|
||||
*/
|
||||
@ -152,6 +154,15 @@ public class BrushCommands {
|
||||
this.worldEdit = worldEdit;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "none",
|
||||
aliases = "unbind",
|
||||
desc = "Unbind a bound brush from your current item"
|
||||
)
|
||||
void none(Player player, LocalSession session) throws WorldEditException {
|
||||
ToolCommands.setToolNone(player, session, "Brush");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "blendball",
|
||||
aliases = {"bb", "blend"},
|
||||
@ -644,18 +655,18 @@ public class BrushCommands {
|
||||
)
|
||||
@Deprecated
|
||||
@CommandPermissions("worldedit.brush.clipboard")
|
||||
public void clipboardBrush(LocalSession session, InjectedValueAccess context,
|
||||
@Switch(name = 'a', desc = "Don't paste air from the clipboard")
|
||||
boolean ignoreAir,
|
||||
@Switch(name = 'o', desc = "Paste starting at the target location, instead of centering on it")
|
||||
boolean usingOrigin,
|
||||
@Switch(name = 'e', desc = "Skip paste entities if available")
|
||||
boolean skipEntities,
|
||||
@Switch(name = 'b', desc = "Paste biomes if available")
|
||||
boolean pasteBiomes,
|
||||
@ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard", def = "")
|
||||
@ClipboardMask
|
||||
Mask sourceMask) throws WorldEditException {
|
||||
public void clipboardBrush(Player player, LocalSession session,
|
||||
@Switch(name = 'a', desc = "Don't paste air from the clipboard")
|
||||
boolean ignoreAir,
|
||||
@Switch(name = 'o', desc = "Paste starting at the target location, instead of centering on it")
|
||||
boolean usingOrigin,
|
||||
@Switch(name = 'e', desc = "Paste entities if available")
|
||||
boolean pasteEntities,
|
||||
@Switch(name = 'b', desc = "Paste biomes if available")
|
||||
boolean pasteBiomes,
|
||||
@ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard", def = "")
|
||||
@ClipboardMask
|
||||
Mask sourceMask) throws WorldEditException {
|
||||
ClipboardHolder holder = session.getClipboard();
|
||||
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
@ -678,13 +689,13 @@ public class BrushCommands {
|
||||
descFooter = "Example: '/brush smooth 2 4 grass_block,dirt,stone'"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.smooth")
|
||||
public void smoothBrush(Player player, InjectedValueAccess context, EditSession editSession,
|
||||
@Arg(desc = "The radius to sample for softening", def = "2")
|
||||
Expression radius,
|
||||
@Arg(desc = "The number of iterations to perform", def = "4")
|
||||
int iterations,
|
||||
@Arg(desc = "The mask of blocks to use for the heightmap", def = "")
|
||||
Mask maskOpt) throws WorldEditException {
|
||||
public void smoothBrush(Player player, LocalSession session,
|
||||
@Arg(desc = "The radius to sample for softening", def = "2")
|
||||
Expression radius,
|
||||
@Arg(desc = "The number of iterations to perform", def = "4")
|
||||
int iterations,
|
||||
@Arg(desc = "The mask of blocks to use for the heightmap", def = "")
|
||||
Mask maskOpt) throws WorldEditException {
|
||||
worldEdit.checkMaxBrushRadius(radius);
|
||||
|
||||
FaweLimit limit = Settings.IMP.getLimit(player);
|
||||
@ -1089,4 +1100,106 @@ public class BrushCommands {
|
||||
|
||||
player.print("Set brush to " + factory);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "deform",
|
||||
desc = "Deform brush, applies an expression to an area"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.deform")
|
||||
public void deform(Player player, LocalSession localSession,
|
||||
@Arg(desc = "The shape of the region")
|
||||
RegionFactory shape,
|
||||
@Arg(desc = "The size of the brush", def = "5")
|
||||
double radius,
|
||||
@Arg(desc = "Expression to apply", def = "y-=0.2")
|
||||
String expression,
|
||||
@Switch(name = 'r', desc = "Use the game's coordinate origin")
|
||||
boolean useRawCoords,
|
||||
@Switch(name = 'o', desc = "Use the placement position as the origin")
|
||||
boolean usePlacement) throws WorldEditException {
|
||||
Deform deform = new Deform(expression);
|
||||
if (useRawCoords) {
|
||||
deform.setMode(Deform.Mode.RAW_COORD);
|
||||
} else if (usePlacement) {
|
||||
deform.setMode(Deform.Mode.OFFSET);
|
||||
deform.setOffset(localSession.getPlacementPosition(player).toVector3());
|
||||
}
|
||||
setOperationBasedBrush(player, localSession, radius,
|
||||
deform, shape, "worldedit.brush.deform");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "set",
|
||||
desc = "Set brush, sets all blocks in the area"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.set")
|
||||
public void set(Player player, LocalSession localSession,
|
||||
@Arg(desc = "The shape of the region")
|
||||
RegionFactory shape,
|
||||
@Arg(desc = "The size of the brush", def = "5")
|
||||
double radius,
|
||||
@Arg(desc = "The pattern of blocks to set")
|
||||
Pattern pattern) throws WorldEditException {
|
||||
setOperationBasedBrush(player, localSession, radius,
|
||||
new Apply(new ReplaceFactory(pattern)), shape, "worldedit.brush.set");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "forest",
|
||||
desc = "Forest brush, creates a forest in the area"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.forest")
|
||||
public void forest(Player player, LocalSession localSession,
|
||||
@Arg(desc = "The shape of the region")
|
||||
RegionFactory shape,
|
||||
@Arg(desc = "The size of the brush", def = "5")
|
||||
double radius,
|
||||
@Arg(desc = "The density of the brush", def = "20")
|
||||
double density,
|
||||
@Arg(desc = "The type of tree to use")
|
||||
TreeGenerator.TreeType type) throws WorldEditException {
|
||||
setOperationBasedBrush(player, localSession, radius,
|
||||
new Paint(new TreeGeneratorFactory(type), density / 100), shape, "worldedit.brush.forest");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "raise",
|
||||
desc = "Raise brush, raise all blocks by one"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.raise")
|
||||
public void raise(Player player, LocalSession localSession,
|
||||
@Arg(desc = "The shape of the region")
|
||||
RegionFactory shape,
|
||||
@Arg(desc = "The size of the brush", def = "5")
|
||||
double radius) throws WorldEditException {
|
||||
setOperationBasedBrush(player, localSession, radius,
|
||||
new Deform("y-=1"), shape, "worldedit.brush.raise");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "lower",
|
||||
desc = "Lower brush, lower all blocks by one"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.lower")
|
||||
public void lower(Player player, LocalSession localSession,
|
||||
@Arg(desc = "The shape of the region")
|
||||
RegionFactory shape,
|
||||
@Arg(desc = "The size of the brush", def = "5")
|
||||
double radius) throws WorldEditException {
|
||||
setOperationBasedBrush(player, localSession, radius,
|
||||
new Deform("y+=1"), shape, "worldedit.brush.lower");
|
||||
}
|
||||
|
||||
static void setOperationBasedBrush(Player player, LocalSession session, double radius,
|
||||
Contextual<? extends Operation> factory,
|
||||
RegionFactory shape,
|
||||
String permission) throws WorldEditException {
|
||||
WorldEdit.getInstance().checkMaxBrushRadius(radius);
|
||||
BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType());
|
||||
tool.setSize(radius);
|
||||
tool.setFill(null);
|
||||
tool.setBrush(new OperationFactoryBrush(factory, shape, session), permission);
|
||||
|
||||
player.print("Set brush to " + factory);
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME;
|
||||
|
||||
@ -39,6 +37,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.formatting.component.PaginationBox;
|
||||
import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
@ -46,9 +45,10 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.storage.LegacyChunkStore;
|
||||
import com.sk89q.worldedit.world.storage.McRegionChunkStore;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
@ -59,6 +59,8 @@ import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.ArgFlag;
|
||||
import org.enginehub.piston.exception.StopExecutionException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Commands for working with chunks.
|
||||
*/
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.boydti.fawe.FaweAPI;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
@ -37,12 +39,10 @@ import com.boydti.fawe.util.MaskTraverser;
|
||||
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.util.CommandPermissions;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import com.sk89q.worldedit.command.util.Logging;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.extent.PasteEvent;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
@ -53,6 +53,8 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
|
||||
import com.sk89q.worldedit.function.block.BlockReplace;
|
||||
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Masks;
|
||||
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
|
||||
@ -91,11 +93,10 @@ import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Clipboard commands.
|
||||
@ -103,18 +104,6 @@ import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
|
||||
public class ClipboardCommands {
|
||||
|
||||
private WorldEdit worldEdit;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param worldEdit reference to WorldEdit
|
||||
*/
|
||||
public ClipboardCommands(WorldEdit worldEdit) {
|
||||
checkNotNull(worldEdit);
|
||||
this.worldEdit = worldEdit;
|
||||
}
|
||||
|
||||
|
||||
@Command(
|
||||
name = "/copy",
|
||||
@ -442,6 +431,8 @@ public class ClipboardCommands {
|
||||
boolean atOrigin,
|
||||
@Switch(name = 's', desc = "Select the region after pasting")
|
||||
boolean selectPasted,
|
||||
@Switch(name = 'n', desc = "No paste, select only. (Implies -s)")
|
||||
boolean onlySelect,
|
||||
@Switch(name = 'e', desc = "Paste entities if available")
|
||||
boolean pasteEntities,
|
||||
@Switch(name = 'b', desc = "Paste biomes if available")
|
||||
@ -457,6 +448,7 @@ public class ClipboardCommands {
|
||||
}
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
Region region = clipboard.getRegion();
|
||||
List<String> messages = Lists.newArrayList();
|
||||
|
||||
BlockVector3 to = atOrigin ? clipboard.getOrigin() : session.getPlacementPosition(actor);
|
||||
checkPaste(actor, editSession, to, holder, clipboard);
|
||||
@ -471,7 +463,7 @@ public class ClipboardCommands {
|
||||
.build();
|
||||
Operations.completeLegacy(operation);
|
||||
|
||||
if (selectPasted) {
|
||||
if (selectPasted || onlySelect) {
|
||||
BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
|
||||
Vector3 realTo = to.toVector3().add(holder.getTransform().apply(clipboardOffset.toVector3()));
|
||||
Vector3 max = realTo.add(holder.getTransform().apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3()));
|
||||
@ -578,7 +570,7 @@ public class ClipboardCommands {
|
||||
AffineTransform transform = new AffineTransform();
|
||||
transform = transform.scale(direction.abs().multiply(-2).add(1, 1, 1).toVector3());
|
||||
holder.setTransform(holder.getTransform().combine(transform));
|
||||
actor.print(BBC.COMMAND_FLIPPED.s());
|
||||
actor.print("The clipboard copy has been flipped.");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -588,6 +580,6 @@ public class ClipboardCommands {
|
||||
@CommandPermissions("worldedit.clipboard.clear")
|
||||
public void clearClipboard(Actor actor, LocalSession session) throws WorldEditException {
|
||||
session.setClipboard(null);
|
||||
actor.print(BBC.CLIPBOARD_CLEARED.s());
|
||||
actor.print("Clipboard cleared.");
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
import static com.sk89q.worldedit.internal.command.CommandUtil.requireIV;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.sk89q.worldedit.IncompleteRegionException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
@ -39,7 +36,6 @@ import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import java.util.List;
|
||||
import org.enginehub.piston.Command;
|
||||
import org.enginehub.piston.CommandManager;
|
||||
import org.enginehub.piston.CommandManagerService;
|
||||
@ -48,6 +44,11 @@ import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.inject.Key;
|
||||
import org.enginehub.piston.part.SubCommandPart;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
import static com.sk89q.worldedit.internal.command.CommandUtil.requireIV;
|
||||
|
||||
/**
|
||||
* Extracted from {@link SelectionCommands} to allow importing of {@link Command}.
|
||||
*/
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.extent.ResettableExtent;
|
||||
@ -44,6 +42,8 @@ import com.sk89q.worldedit.extension.input.DisallowedUsageException;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
@ -52,7 +52,6 @@ import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -64,6 +63,8 @@ import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.ArgFlag;
|
||||
import org.enginehub.piston.annotation.param.Switch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* General WorldEdit commands.
|
||||
*/
|
||||
@ -132,8 +133,8 @@ public class GeneralCommands {
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/fast",
|
||||
desc = "Toggle fast mode"
|
||||
name = "/fast",
|
||||
desc = "Toggle fast mode"
|
||||
)
|
||||
@CommandPermissions("worldedit.fast")
|
||||
public void fast(Actor actor, LocalSession session,
|
||||
@ -144,12 +145,13 @@ public class GeneralCommands {
|
||||
actor.printError("Fast mode already " + (fastMode ? "enabled" : "disabled") + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasFastMode) {
|
||||
session.setFastMode(false);
|
||||
actor.print(BBC.FAST_DISABLED.s());
|
||||
actor.print("Fast mode disabled.");
|
||||
} else {
|
||||
session.setFastMode(true);
|
||||
actor.print(BBC.FAST_ENABLED.s());
|
||||
actor.print("Fast mode enabled. Lighting in the affected chunks may be wrong and/or you may need to rejoin to see changes.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,20 +198,20 @@ public class GeneralCommands {
|
||||
}
|
||||
}
|
||||
|
||||
// @Command(
|
||||
// name = "/world",
|
||||
// desc = "Sets the world override"
|
||||
// )
|
||||
// @CommandPermissions("worldedit.world")
|
||||
// public void worldOverride(Actor actor, LocalSession session,
|
||||
// @Arg(desc = "The world override", def = "") World world) {
|
||||
// session.setWorldOverride(world);
|
||||
// if (world == null) {
|
||||
// actor.print("Removed world override.");
|
||||
// } else {
|
||||
// actor.print("Set the world override to " + world.getId() + ". (Use //world to go back to default)");
|
||||
// }
|
||||
// }
|
||||
@Command(
|
||||
name = "/world",
|
||||
desc = "Sets the world override"
|
||||
)
|
||||
@CommandPermissions("worldedit.world")
|
||||
public void world(Actor actor, LocalSession session,
|
||||
@Arg(desc = "The world override", def = "") World world) {
|
||||
session.setWorldOverride(world);
|
||||
if (world == null) {
|
||||
actor.print("Removed world override.");
|
||||
} else {
|
||||
actor.print("Set the world override to " + world.getId() + ". (Use //world to go back to default)");
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/watchdog",
|
||||
@ -237,16 +239,18 @@ public class GeneralCommands {
|
||||
@Command(
|
||||
name = "gmask",
|
||||
aliases = {"/gmask"},
|
||||
descFooter = "The global destination mask applies to all edits you do and masks based on the destination blocks (i.e., the blocks in the world).",
|
||||
desc = "Set the global mask"
|
||||
)
|
||||
@CommandPermissions({"worldedit.global-mask", "worldedit.mask.global"})
|
||||
public void gmask(Actor actor, LocalSession session, @Arg(desc = "The mask to set", def = "") Mask mask) {
|
||||
session.setMask(mask);
|
||||
@CommandPermissions("worldedit.global-mask")
|
||||
public void gmask(Actor actor, LocalSession session,
|
||||
@Arg(desc = "The mask to set", def = "")
|
||||
Mask mask) {
|
||||
if (mask == null) {
|
||||
actor.print(BBC.MASK_DISABLED.s());
|
||||
session.setMask(null);
|
||||
actor.print("Global mask disabled.");
|
||||
} else {
|
||||
actor.print(BBC.MASK.s());
|
||||
session.setMask(mask);
|
||||
actor.print("Global mask set.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,7 +295,7 @@ public class GeneralCommands {
|
||||
actor.print(new ItemSearcher(search, blocksOnly, itemsOnly, page).call());
|
||||
}
|
||||
|
||||
public static class ItemSearcher implements Callable<Component> {
|
||||
private static class ItemSearcher implements Callable<Component> {
|
||||
private final boolean blocksOnly;
|
||||
private final boolean itemsOnly;
|
||||
private final String search;
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.command.MethodCommands.getArguments;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT;
|
||||
@ -42,10 +40,13 @@ import com.sk89q.worldedit.command.util.Logging;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.function.generator.CavesGen;
|
||||
|
||||
import java.util.List;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||
import com.sk89q.worldedit.internal.annotation.Radii;
|
||||
import com.sk89q.worldedit.internal.annotation.Range;
|
||||
import com.sk89q.worldedit.internal.annotation.Selection;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
@ -61,11 +62,12 @@ import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.Switch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
|
||||
/**
|
||||
@ -184,20 +186,15 @@ public class GenerationCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.generation.cylinder")
|
||||
@Logging(PLACEMENT)
|
||||
public void hcyl(Actor actor, LocalSession session, EditSession editSession,
|
||||
public int hcyl(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Arg(desc = "The pattern of blocks to generate")
|
||||
Pattern pattern,
|
||||
@Arg(desc = "The radii of the cylinder. Order is N/S, E/W") BlockVector2 radius,
|
||||
Pattern pattern,
|
||||
@Arg(desc = "The radii of the cylinder. 1st is N/S, 2nd is E/W")
|
||||
@Radii(2)
|
||||
List<Double> radii,
|
||||
@Arg(desc = "The height of the cylinder", def = "1")
|
||||
int height,
|
||||
@Range(min = 1) @Arg(desc = "double", def = "1") double thickness, InjectedValueAccess context) throws WorldEditException {
|
||||
double max = MathMan.max(radius.getBlockX(), radius.getBlockZ());
|
||||
worldEdit.checkMaxRadius(max);
|
||||
BlockVector3 pos = session.getPlacementPosition(actor);
|
||||
actor.checkConfirmationRadius(() -> {
|
||||
int affected = editSession.makeHollowCylinder(pos, pattern, radius.getX(), radius.getZ(), Math.min(256, height), thickness - 1);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
}, "/hcyl", (int) max, context);
|
||||
int height) throws WorldEditException {
|
||||
return cyl(actor, session, editSession, pattern, radii, height, true);
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -278,9 +275,10 @@ public class GenerationCommands {
|
||||
int size,
|
||||
@Arg(desc = "The type of forest", def = "tree")
|
||||
TreeType type,
|
||||
@Range(min = 0, max = 100) @Arg(desc = "The density of the forest, between 0 and 100", def = "5")
|
||||
@Arg(desc = "The density of the forest, between 0 and 100", def = "5")
|
||||
double density) throws WorldEditException {
|
||||
checkCommandArgument(0 <= density && density <= 100, "Density must be between 0 and 100");
|
||||
worldEdit.checkMaxRadius(size);
|
||||
density /= 100;
|
||||
int affected = editSession.makeForest(session.getPlacementPosition(actor), size, density, type);
|
||||
actor.print(affected + " trees created.");
|
||||
@ -295,14 +293,10 @@ public class GenerationCommands {
|
||||
@Logging(POSITION)
|
||||
public int pumpkins(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Arg(desc = "The size of the patch", def = "10")
|
||||
int size,
|
||||
@Arg(desc = "//TODO", def = "10")
|
||||
int apothem,
|
||||
@Arg(desc = "//TODO ", def = "0.02")
|
||||
double density) throws WorldEditException {
|
||||
checkCommandArgument(0 <= density && density <= 100, "Density must be between 0 and 100");
|
||||
int affected = editSession.makePumpkinPatches(session.getPlacementPosition(actor), apothem, density);
|
||||
BBC.COMMAND_PUMPKIN.send(actor, affected);
|
||||
int size) throws WorldEditException {
|
||||
worldEdit.checkMaxRadius(size);
|
||||
int affected = editSession.makePumpkinPatches(session.getPlacementPosition(actor), size);
|
||||
actor.print(affected + " pumpkin patches created.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweAPI;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
@ -57,6 +55,8 @@ import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.Switch;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Commands to undo, redo, and clear history.
|
||||
*/
|
||||
@ -223,6 +223,16 @@ public class HistoryCommands {
|
||||
aliases = { "/un", "/ud", "undo" },
|
||||
desc = "Undoes the last action (from history)"
|
||||
)
|
||||
} else {
|
||||
undoSession = session;
|
||||
}
|
||||
int finalTimes = times;
|
||||
player.checkConfirmation(() -> {
|
||||
EditSession undone = null;
|
||||
int i = 0;
|
||||
for (; i < finalTimes; ++i) {
|
||||
undone = undoSession.undo(undoSession.getBlockBag(player), player);
|
||||
if (undone == null) break;
|
||||
@CommandPermissions({"worldedit.history.undo", "worldedit.history.undo.self"})
|
||||
public void undo(Player player, LocalSession session,
|
||||
@Range(min = 1) @Arg(desc = "Number of undoes to perform", def = "1")
|
||||
@ -243,16 +253,6 @@ public class HistoryCommands {
|
||||
BBC.COMMAND_HISTORY_OTHER_ERROR.send(player, playerName);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
undoSession = session;
|
||||
}
|
||||
int finalTimes = times;
|
||||
player.checkConfirmation(() -> {
|
||||
EditSession undone = null;
|
||||
int i = 0;
|
||||
for (; i < finalTimes; ++i) {
|
||||
undone = undoSession.undo(undoSession.getBlockBag(player), player);
|
||||
if (undone == null) break;
|
||||
worldEdit.flushBlockBag(player, undone);
|
||||
}
|
||||
if (undone == null) i--;
|
||||
@ -311,7 +311,7 @@ public class HistoryCommands {
|
||||
@CommandPermissions("worldedit.history.clear")
|
||||
public void clearHistory(Actor actor, LocalSession session) {
|
||||
session.clearHistory();
|
||||
actor.print(BBC.COMMAND_HISTORY_CLEAR.s());
|
||||
actor.print("History cleared.");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.POSITION;
|
||||
|
||||
import com.boydti.fawe.config.BBC;
|
||||
@ -36,6 +34,8 @@ import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.Switch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Commands for moving the player around.
|
||||
*/
|
||||
@ -62,7 +62,7 @@ public class NavigationCommands {
|
||||
@CommandPermissions("worldedit.navigation.unstuck")
|
||||
public void unstuck(Player player) throws WorldEditException {
|
||||
player.findFreePosition();
|
||||
player.print(BBC.UNSTUCK.s());
|
||||
player.print("There you go!");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -84,11 +84,7 @@ public class NavigationCommands {
|
||||
if (ascentLevels == 0) {
|
||||
player.printError(BBC.ASCEND_FAIL.s());
|
||||
} else {
|
||||
if (ascentLevels == 1) {
|
||||
player.print(BBC.ASCENDED_SINGULAR.s());
|
||||
} else {
|
||||
BBC.ASCENDED_PLURAL.send(player, ascentLevels);
|
||||
}
|
||||
player.print((ascentLevels != 1) ? "Ascended " + ascentLevels + " levels." : "Ascended a level.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +109,7 @@ public class NavigationCommands {
|
||||
} else if (descentLevels == 1) {
|
||||
player.print(BBC.DESCEND_SINGULAR.s());
|
||||
} else {
|
||||
BBC.DESCEND_PLURAL.send(player, descentLevels);
|
||||
player.print((descentLevels != 1) ? "Descended " + descentLevels + " levels." : "Descended a level.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,19 +19,23 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import com.boydti.fawe.FaweAPI;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.implementation.processors.ChunkSendProcessor;
|
||||
import com.boydti.fawe.beta.implementation.processors.NullProcessor;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.FaweLimit;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
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.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.block.BlockReplace;
|
||||
import com.sk89q.worldedit.command.util.Logging;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
@ -39,9 +43,11 @@ import com.sk89q.worldedit.function.FlatRegionFunction;
|
||||
import com.sk89q.worldedit.function.GroundFunction;
|
||||
import com.sk89q.worldedit.function.biome.BiomeReplace;
|
||||
import com.sk89q.worldedit.function.generator.FloraGenerator;
|
||||
import com.sk89q.worldedit.function.mask.MaskIntersection;
|
||||
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.NoiseFilter2D;
|
||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
@ -63,6 +69,8 @@ import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.regions.Regions;
|
||||
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
@ -81,10 +89,7 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.command.MethodCommands.getArguments;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument;
|
||||
@ -98,16 +103,35 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
|
||||
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
|
||||
public class RegionCommands {
|
||||
|
||||
private final WorldEdit worldEdit;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param worldEdit reference to WorldEdit
|
||||
*/
|
||||
public RegionCommands(WorldEdit worldEdit) {
|
||||
checkNotNull(worldEdit);
|
||||
this.worldEdit = worldEdit;
|
||||
public RegionCommands() {
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/set",
|
||||
desc = "Sets all the blocks in the region"
|
||||
)
|
||||
@CommandPermissions("worldedit.region.set")
|
||||
@Logging(REGION)
|
||||
public int set(Actor actor, EditSession editSession,
|
||||
@Selection Region region,
|
||||
@Arg(desc = "The pattern of blocks to set")
|
||||
Pattern pattern) {
|
||||
RegionFunction set = new BlockReplace(editSession, pattern);
|
||||
RegionVisitor visitor = new RegionVisitor(region, set);
|
||||
|
||||
Operations.completeBlindly(visitor);
|
||||
List<String> messages = Lists.newArrayList();
|
||||
visitor.addStatusMessages(messages);
|
||||
if (messages.isEmpty()) {
|
||||
actor.print("Operation completed.");
|
||||
} else {
|
||||
actor.print("Operation completed (" + Joiner.on(", ").join(messages) + ").");
|
||||
}
|
||||
|
||||
return visitor.getAffected();
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -254,7 +278,7 @@ public class RegionCommands {
|
||||
@Selection Region region,
|
||||
@Arg(desc = "The pattern of blocks to place")
|
||||
Pattern pattern,
|
||||
@Range(min = 1) @Arg(desc = "The thickness of the line", def = "0")
|
||||
@Arg(desc = "The thickness of the line", def = "0")
|
||||
int thickness,
|
||||
@Switch(name = 'h', desc = "Generate only a shell")
|
||||
boolean shell) throws WorldEditException {
|
||||
@ -269,7 +293,7 @@ public class RegionCommands {
|
||||
BlockVector3 pos2 = cuboidregion.getPos2();
|
||||
int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell);
|
||||
|
||||
BBC.VISITOR_BLOCK.send(actor, blocksChanged);
|
||||
actor.print(blocksChanged + " block(s) have been changed.");
|
||||
return blocksChanged;
|
||||
}
|
||||
|
||||
@ -290,7 +314,7 @@ public class RegionCommands {
|
||||
boolean shell, InjectedValueAccess context) throws WorldEditException {
|
||||
if (!(region instanceof ConvexPolyhedralRegion)) {
|
||||
actor.printError("//curve only works with convex polyhedral selections");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
checkCommandArgument(thickness >= 0, "Thickness must be >= 0");
|
||||
|
||||
@ -300,7 +324,7 @@ public class RegionCommands {
|
||||
|
||||
int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, thickness, !shell);
|
||||
|
||||
BBC.VISITOR_BLOCK.send(actor, blocksChanged);
|
||||
actor.print(blocksChanged + " block(s) have been changed.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -321,14 +345,8 @@ public class RegionCommands {
|
||||
}
|
||||
Mask finalFrom = from;
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.replaceBlocks(region, finalFrom, to);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
if (!actor.hasPermission("fawe.tips")) {
|
||||
BBC.TIP_REPLACE_ID
|
||||
.or(BBC.TIP_REPLACE_LIGHT, BBC.TIP_REPLACE_MARKER, BBC.TIP_TAB_COMPLETE,
|
||||
BBC.TIP_REPLACE_REGEX, BBC.TIP_REPLACE_REGEX_2, BBC.TIP_REPLACE_REGEX_3,
|
||||
BBC.TIP_REPLACE_REGEX_4, BBC.TIP_REPLACE_REGEX_5).send(actor);
|
||||
}
|
||||
int affected = editSession.replaceBlocks(region, finalFrom, to);
|
||||
actor.print(affected + " block(s) have been replaced.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -342,8 +360,8 @@ public class RegionCommands {
|
||||
@Arg(desc = "The pattern of blocks to overlay")
|
||||
Pattern pattern, InjectedValueAccess context) throws WorldEditException {
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.overlayCuboidBlocks(region, pattern);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
int affected = editSession.overlayCuboidBlocks(region, pattern);
|
||||
actor.print(affected + " block(s) have been overlaid.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -380,11 +398,12 @@ public class RegionCommands {
|
||||
)
|
||||
@Logging(REGION)
|
||||
@CommandPermissions("worldedit.region.center")
|
||||
public void center(Actor actor, EditSession editSession, @Selection Region region,
|
||||
public int center(Actor actor, EditSession editSession, @Selection Region region,
|
||||
@Arg(desc = "The pattern of blocks to set")
|
||||
Pattern pattern) throws WorldEditException {
|
||||
int affected = editSession.center(region, pattern);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print("Center set (" + affected + " block(s) changed)");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -396,7 +415,7 @@ public class RegionCommands {
|
||||
public void naturalize(Actor actor, EditSession editSession, @Selection Region region, InjectedValueAccess context) throws WorldEditException {
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.naturalizeCuboidBlocks(region);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been made to look more natural.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -411,7 +430,7 @@ public class RegionCommands {
|
||||
Pattern pattern, InjectedValueAccess context) throws WorldEditException {
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.makeWalls(region, pattern);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been changed.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -427,7 +446,7 @@ public class RegionCommands {
|
||||
Pattern pattern, InjectedValueAccess context) throws WorldEditException {
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.makeCuboidFaces(region, pattern);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been changed.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -456,7 +475,7 @@ public class RegionCommands {
|
||||
HeightMap heightMap = new HeightMap(editSession, region, mask, snow);
|
||||
HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0));
|
||||
int affected = heightMap.applyFilter(filter, iterations);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print("Terrain's height map smoothed. " + affected + " block(s) changed.");
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -522,8 +541,20 @@ public class RegionCommands {
|
||||
boolean copyBiomes,
|
||||
InjectedValueAccess context) throws WorldEditException {
|
||||
checkCommandArgument(count >= 1, "Count must be >= 1");
|
||||
|
||||
Mask combinedMask;
|
||||
if (ignoreAirBlocks) {
|
||||
if (mask == null) {
|
||||
combinedMask = new ExistingBlockMask(editSession);
|
||||
} else {
|
||||
combinedMask = new MaskIntersection(mask, new ExistingBlockMask(editSession));
|
||||
}
|
||||
} else {
|
||||
combinedMask = mask;
|
||||
}
|
||||
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, !skipEntities, copyBiomes, replace);
|
||||
int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, !skipEntities, copyBiomes, combinedMask, replace);
|
||||
|
||||
if (moveSelection) {
|
||||
try {
|
||||
@ -583,17 +614,29 @@ public class RegionCommands {
|
||||
@ArgFlag(name = 'm', desc = "Source mask", def="")
|
||||
Mask sourceMask,
|
||||
InjectedValueAccess context) throws WorldEditException {
|
||||
|
||||
Mask combinedMask;
|
||||
if (ignoreAirBlocks) {
|
||||
if (mask == null) {
|
||||
combinedMask = new ExistingBlockMask(editSession);
|
||||
} else {
|
||||
combinedMask = new MaskIntersection(mask, new ExistingBlockMask(editSession));
|
||||
}
|
||||
} else {
|
||||
combinedMask = mask;
|
||||
}
|
||||
|
||||
actor.checkConfirmationStack(() -> {
|
||||
if (sourceMask != null) {
|
||||
editSession.addSourceMask(sourceMask);
|
||||
}
|
||||
int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks, !skipEntities, copyBiomes);
|
||||
int affected = editSession.stackCuboidRegion(region, direction, count, !skipEntities, copyBiomes, combinedMask);
|
||||
|
||||
if (moveSelection) {
|
||||
try {
|
||||
final BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint());
|
||||
final BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
|
||||
|
||||
final BlockVector3 shiftVector = direction.toVector3().multiply(count * (Math.abs(direction.dot(size)) + 1)).toBlockPoint();
|
||||
final BlockVector3 shiftVector = direction.multiply(size).multiply(count);
|
||||
region.shift(shiftVector);
|
||||
|
||||
session.getRegionSelector(world).learnChanges();
|
||||
@ -652,7 +695,7 @@ public class RegionCommands {
|
||||
if (actor instanceof Player) {
|
||||
((Player) actor).findFreePosition();
|
||||
}
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been deformed.");
|
||||
} catch (ExpressionException e) {
|
||||
actor.printError(e.getMessage());
|
||||
}
|
||||
@ -718,7 +761,7 @@ public class RegionCommands {
|
||||
Mask finalMask = mask == null ? new SolidBlockMask(editSession) : mask;
|
||||
actor.checkConfirmationRegion(() -> {
|
||||
int affected = editSession.hollowOutRegion(region, thickness, pattern, finalMask);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been changed.");
|
||||
}, getArguments(context), region, context);
|
||||
}
|
||||
|
||||
@ -735,7 +778,7 @@ public class RegionCommands {
|
||||
double density) throws WorldEditException {
|
||||
checkCommandArgument(0 <= density && density <= 100, "Density must be in [0, 100]");
|
||||
int affected = editSession.makeForest(region, density / 100, type);
|
||||
BBC.COMMAND_TREE.send(actor, affected);
|
||||
actor.print(affected + " trees created.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@ -756,7 +799,7 @@ public class RegionCommands {
|
||||
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density / 100));
|
||||
Operations.completeLegacy(visitor);
|
||||
|
||||
BBC.COMMAND_FLORA.send(actor, ground.getAffected());
|
||||
actor.print(affected + " flora created.");
|
||||
}, "/flora", region, context);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.boydti.fawe.util.ReflectionUtils.as;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.boydti.fawe.config.BBC;
|
||||
@ -41,6 +40,7 @@ import com.sk89q.worldedit.command.argument.Arguments;
|
||||
import com.sk89q.worldedit.command.util.AsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissions;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.extent.ActorSaveClipboardEvent;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
@ -60,6 +60,7 @@ import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.util.formatting.component.CodeFormat;
|
||||
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
import com.sk89q.worldedit.util.io.Closer;
|
||||
import com.sk89q.worldedit.util.io.file.FilenameException;
|
||||
@ -70,6 +71,8 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
@ -843,6 +846,24 @@ public class SchematicCommands {
|
||||
return fileList;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "delete",
|
||||
aliases = {"d"},
|
||||
desc = "Delete a saved schematic"
|
||||
)
|
||||
@CommandPermissions("worldedit.schematic.delete")
|
||||
public void delete(Actor actor,
|
||||
@Arg(desc = "File name.")
|
||||
String filename) throws WorldEditException {
|
||||
LocalConfiguration config = worldEdit.getConfiguration();
|
||||
File dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
|
||||
|
||||
File f = worldEdit.getSafeOpenFile(actor instanceof Player ? ((Player) actor) : null,
|
||||
dir, filename, "schematic", ClipboardFormats.getFileExtensionArray());
|
||||
|
||||
if (!f.exists()) {
|
||||
actor.printError("Schematic " + filename + " does not exist!");
|
||||
return;
|
||||
private boolean delete(File file) {
|
||||
if (file.delete()) {
|
||||
new File(file.getParentFile(), "." + file.getName() + ".cached").delete();
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL;
|
||||
|
||||
import com.boydti.fawe.config.BBC;
|
||||
@ -39,6 +37,8 @@ import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
/**
|
||||
* Commands related to scripting.
|
||||
@ -68,8 +68,8 @@ public class ScriptingCommands {
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "cs",
|
||||
desc = "Execute a CraftScript"
|
||||
name = "cs",
|
||||
desc = "Execute a CraftScript"
|
||||
)
|
||||
@CommandPermissions("worldedit.scripting.execute")
|
||||
@Logging(ALL)
|
||||
@ -77,9 +77,9 @@ public class ScriptingCommands {
|
||||
@Arg(desc = "Filename of the CraftScript to load")
|
||||
String filename,
|
||||
@Arg(desc = "Arguments to the CraftScript", def = "", variable = true)
|
||||
List<String> commandStr) throws WorldEditException {
|
||||
List<String> args) throws WorldEditException {
|
||||
if (!player.hasPermission("worldedit.scripting.execute." + filename)) {
|
||||
player.printError(BBC.SCRIPTING_NO_PERM.s());
|
||||
player.printError("You don't have permission to use that script.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -88,19 +88,19 @@ public class ScriptingCommands {
|
||||
File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().scriptsDir);
|
||||
File f = worldEdit.getSafeOpenFile(player, dir, filename, "js", "js");
|
||||
|
||||
worldEdit.runScript(player, f, Stream.concat(Stream.of(filename), commandStr.stream())
|
||||
.toArray(String[]::new));
|
||||
worldEdit.runScript(player, f, Stream.concat(Stream.of(filename), args.stream())
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = ".s",
|
||||
desc = "Execute last CraftScript"
|
||||
name = ".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> commandStr) throws WorldEditException {
|
||||
List<String> args) throws WorldEditException {
|
||||
|
||||
String lastScript = session.getLastScript();
|
||||
|
||||
@ -117,7 +117,7 @@ public class ScriptingCommands {
|
||||
File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().scriptsDir);
|
||||
File f = worldEdit.getSafeOpenFile(player, dir, lastScript, "js", "js");
|
||||
|
||||
worldEdit.runScript(player, f, Stream.concat(Stream.of(lastScript), commandStr.stream())
|
||||
.toArray(String[]::new));
|
||||
worldEdit.runScript(player, f, Stream.concat(Stream.of(lastScript), args.stream())
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.POSITION;
|
||||
import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION;
|
||||
|
||||
@ -44,6 +46,7 @@ import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Locatable;
|
||||
import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
|
||||
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
||||
import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.internal.annotation.Direction;
|
||||
@ -77,12 +80,19 @@ import com.sk89q.worldedit.world.storage.ChunkStore;
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import com.sk89q.worldedit.util.formatting.component.PaginationBox;
|
||||
import com.sk89q.worldedit.util.formatting.component.InvalidComponentException;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import java.util.Optional;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import java.util.stream.Stream;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.Switch;
|
||||
import org.enginehub.piston.annotation.param.ArgFlag;
|
||||
import org.enginehub.piston.exception.StopExecutionException;
|
||||
|
||||
/**
|
||||
* Selection commands.
|
||||
@ -98,7 +108,6 @@ public class SelectionCommands {
|
||||
|
||||
@Command(
|
||||
name = "/pos1",
|
||||
aliases = "/1",
|
||||
desc = "Set position 1"
|
||||
)
|
||||
@Logging(POSITION)
|
||||
@ -116,18 +125,17 @@ public class SelectionCommands {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.getRegionSelector(world).selectPrimary(pos.toBlockPoint(), ActorSelectorLimits.forActor(actor))) {
|
||||
actor.printError(BBC.SELECTOR_ALREADY_SET.s());
|
||||
if (!session.getRegionSelector(world).selectPrimary(pos.toVector().toBlockPoint(), ActorSelectorLimits.forActor(actor))) {
|
||||
actor.printError("Position already set.");
|
||||
return;
|
||||
}
|
||||
|
||||
session.getRegionSelector(world)
|
||||
.explainPrimarySelection(actor, session, pos.toBlockPoint());
|
||||
.explainPrimarySelection(actor, session, pos.toVector().toBlockPoint());
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/pos2",
|
||||
aliases = "/2",
|
||||
desc = "Set position 2"
|
||||
)
|
||||
@Logging(POSITION)
|
||||
@ -145,13 +153,13 @@ public class SelectionCommands {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.getRegionSelector(world).selectSecondary(pos.toBlockPoint(), ActorSelectorLimits.forActor(actor))) {
|
||||
actor.printError(BBC.SELECTOR_ALREADY_SET.s());
|
||||
if (!session.getRegionSelector(world).selectSecondary(pos.toVector().toBlockPoint(), ActorSelectorLimits.forActor(actor))) {
|
||||
actor.printError("Position already set.");
|
||||
return;
|
||||
}
|
||||
|
||||
session.getRegionSelector(world)
|
||||
.explainSecondarySelection(actor, session, pos.toBlockPoint());
|
||||
.explainSecondarySelection(actor, session, pos.toVector().toBlockPoint());
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -204,7 +212,7 @@ public class SelectionCommands {
|
||||
)
|
||||
@Logging(POSITION)
|
||||
@CommandPermissions("worldedit.selection.chunk")
|
||||
public void chunk(Player player, LocalSession session,
|
||||
public void chunk(Actor actor, World world, LocalSession session,
|
||||
@Arg(desc = "The chunk to select", def = "")
|
||||
BlockVector2 coordinates,
|
||||
@Switch(name = 's', desc = "Expand your selection to encompass all chunks that are part of it")
|
||||
@ -213,7 +221,6 @@ public class SelectionCommands {
|
||||
boolean useChunkCoordinates) throws WorldEditException {
|
||||
final BlockVector3 min;
|
||||
final BlockVector3 max;
|
||||
final World world = player.getWorld();
|
||||
if (expandSelection) {
|
||||
Region region = session.getSelection(world);
|
||||
|
||||
@ -223,7 +230,9 @@ public class SelectionCommands {
|
||||
min = BlockVector3.at(min2D.getBlockX() * 16, 0, min2D.getBlockZ() * 16);
|
||||
max = BlockVector3.at(max2D.getBlockX() * 16 + 15, world.getMaxY(), max2D.getBlockZ() * 16 + 15);
|
||||
|
||||
BBC.SELECTION_CHUNKS.send(player, min2D.getBlockX() + ", " + min2D.getBlockZ(), max2D.getBlockX() + ", " + max2D.getBlockZ());
|
||||
actor.print("Chunks selected: ("
|
||||
+ min2D.getBlockX() + ", " + min2D.getBlockZ() + ") - ("
|
||||
+ max2D.getBlockX() + ", " + max2D.getBlockZ() + ")");
|
||||
} else {
|
||||
final BlockVector2 min2D;
|
||||
if (coordinates != null) {
|
||||
@ -233,13 +242,18 @@ public class SelectionCommands {
|
||||
: ChunkStore.toChunk(coordinates.toBlockVector3());
|
||||
} else {
|
||||
// use player loc
|
||||
min2D = ChunkStore.toChunk(player.getBlockLocation().toBlockPoint());
|
||||
if (actor instanceof Locatable) {
|
||||
min2D = ChunkStore.toChunk(((Locatable) actor).getBlockLocation().toVector().toBlockPoint());
|
||||
} else {
|
||||
throw new StopExecutionException(TextComponent.of("A player or coordinates are required."));
|
||||
}
|
||||
}
|
||||
|
||||
min = BlockVector3.at(min2D.getBlockX() * 16, 0, min2D.getBlockZ() * 16);
|
||||
max = min.add(15, world.getMaxY(), 15);
|
||||
|
||||
BBC.SELECTION_CHUNK.send(player, min2D.getBlockX() + ", " + min2D.getBlockZ());
|
||||
actor.print("Chunk selected: "
|
||||
+ min2D.getBlockX() + ", " + min2D.getBlockZ());
|
||||
}
|
||||
|
||||
final CuboidRegionSelector selector;
|
||||
@ -248,11 +262,11 @@ public class SelectionCommands {
|
||||
} else {
|
||||
selector = new CuboidRegionSelector(world);
|
||||
}
|
||||
selector.selectPrimary(min, ActorSelectorLimits.forActor(player));
|
||||
selector.selectSecondary(max, ActorSelectorLimits.forActor(player));
|
||||
selector.selectPrimary(min, ActorSelectorLimits.forActor(actor));
|
||||
selector.selectSecondary(max, ActorSelectorLimits.forActor(actor));
|
||||
session.setRegionSelector(world, selector);
|
||||
|
||||
session.dispatchCUISelection(player);
|
||||
session.dispatchCUISelection(actor);
|
||||
|
||||
}
|
||||
|
||||
@ -333,8 +347,7 @@ public class SelectionCommands {
|
||||
|
||||
session.getRegionSelector(world).explainRegionAdjust(actor, session);
|
||||
|
||||
|
||||
BBC.SELECTION_CONTRACT.send(actor, (oldSize - newSize));
|
||||
actor.print("Region contracted " + (oldSize - newSize) + " blocks.");
|
||||
} catch (RegionOperationException e) {
|
||||
actor.printError(e.getMessage());
|
||||
}
|
||||
@ -363,7 +376,7 @@ public class SelectionCommands {
|
||||
|
||||
session.getRegionSelector(world).explainRegionAdjust(actor, session);
|
||||
|
||||
actor.print(BBC.SELECTION_SHIFT.s());
|
||||
actor.print("Region shifted.");
|
||||
} catch (RegionOperationException e) {
|
||||
actor.printError(e.getMessage());
|
||||
}
|
||||
@ -386,7 +399,7 @@ public class SelectionCommands {
|
||||
region.expand(getChangesForEachDir(amount, onlyHorizontal, onlyVertical));
|
||||
session.getRegionSelector(world).learnChanges();
|
||||
session.getRegionSelector(world).explainRegionAdjust(actor, session);
|
||||
actor.print(BBC.SELECTION_OUTSET.s());
|
||||
actor.print("Region outset.");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -496,11 +509,11 @@ public class SelectionCommands {
|
||||
desc = "Counts the number of blocks matching a mask"
|
||||
)
|
||||
@CommandPermissions("worldedit.analysis.count")
|
||||
public void count(Player player, LocalSession session, EditSession editSession,
|
||||
public void count(Actor actor, World world, LocalSession session, EditSession editSession,
|
||||
@Arg(desc = "The mask of blocks to match")
|
||||
Mask mask) throws WorldEditException {
|
||||
int count = editSession.countBlocks(session.getSelection(player.getWorld()), mask);
|
||||
BBC.SELECTION_COUNT.send(player, count);
|
||||
int count = editSession.countBlocks(session.getSelection(world), mask);
|
||||
actor.print("Counted: " + count);
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -531,7 +544,7 @@ public class SelectionCommands {
|
||||
|
||||
|
||||
if (distribution.isEmpty()) { // *Should* always be false
|
||||
player.printError("No blocks counted.");
|
||||
actor.printError("No blocks counted.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -549,6 +562,65 @@ public class SelectionCommands {
|
||||
}
|
||||
}
|
||||
|
||||
private static class BlockDistributionResult extends PaginationBox {
|
||||
|
||||
private final List<Countable<BlockState>> distribution;
|
||||
private final int totalBlocks;
|
||||
private final boolean separateStates;
|
||||
|
||||
BlockDistributionResult(List<Countable<BlockState>> distribution, boolean separateStates) {
|
||||
super("Block Distribution", "//distr -p %page%" + (separateStates ? " -d" : ""));
|
||||
this.distribution = distribution;
|
||||
// note: doing things like region.getArea is inaccurate for non-cuboids.
|
||||
this.totalBlocks = distribution.stream().mapToInt(Countable::getAmount).sum();
|
||||
this.separateStates = separateStates;
|
||||
setComponentsPerPage(7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent(int number) {
|
||||
Countable<BlockState> c = distribution.get(number);
|
||||
TextComponent.Builder line = TextComponent.builder();
|
||||
|
||||
final int count = c.getAmount();
|
||||
|
||||
final double perc = count / (double) totalBlocks * 100;
|
||||
final int maxDigits = (int) (Math.log10(totalBlocks) + 1);
|
||||
final int curDigits = (int) (Math.log10(count) + 1);
|
||||
line.append(String.format("%s%.3f%% ", perc < 10 ? " " : "", perc), TextColor.GOLD);
|
||||
final int space = maxDigits - curDigits;
|
||||
String pad = Strings.repeat(" ", space == 0 ? 2 : 2 * space + 1);
|
||||
line.append(String.format("%s%s", count, pad), TextColor.YELLOW);
|
||||
|
||||
final BlockState state = c.getID();
|
||||
final BlockType blockType = state.getBlockType();
|
||||
TextComponent blockName = TextComponent.of(blockType.getName(), TextColor.LIGHT_PURPLE);
|
||||
TextComponent toolTip;
|
||||
if (separateStates && state != blockType.getDefaultState()) {
|
||||
toolTip = TextComponent.of(state.getAsString(), TextColor.GRAY);
|
||||
blockName = blockName.append(TextComponent.of("*", TextColor.LIGHT_PURPLE));
|
||||
} else {
|
||||
toolTip = TextComponent.of(blockType.getId(), TextColor.GRAY);
|
||||
}
|
||||
blockName = blockName.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, toolTip));
|
||||
line.append(blockName);
|
||||
|
||||
return line.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getComponentsSize() {
|
||||
return distribution.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component create(int page) throws InvalidComponentException {
|
||||
super.getContents().append(TextComponent.of("Total Block Count: " + totalBlocks, TextColor.GRAY))
|
||||
.append(TextComponent.newline());
|
||||
return super.create(page);
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/sel",
|
||||
aliases = { ";", "/desel", "/deselect" },
|
||||
|
@ -76,7 +76,7 @@ public class SnapshotCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (config.snapshotRepo == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_CONFIGURED.s());
|
||||
actor.printError("Snapshot/backup restore is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ public class SnapshotCommands {
|
||||
if (!snapshots.isEmpty()) {
|
||||
actor.print(new SnapshotListBox(world.getName(), snapshots).create(page));
|
||||
} else {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_AVAILABLE.s());
|
||||
actor.printError("No snapshots are available. See console for details.");
|
||||
|
||||
// Okay, let's toss some debugging information!
|
||||
File dir = config.snapshotRepo.getDirectory();
|
||||
@ -101,7 +101,7 @@ public class SnapshotCommands {
|
||||
}
|
||||
}
|
||||
} catch (MissingWorldException ex) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND_WORLD.s());
|
||||
actor.printError("No snapshots were found for this world.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ public class SnapshotCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (config.snapshotRepo == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_CONFIGURED.s());
|
||||
actor.printError("Snapshot/backup restore is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -128,19 +128,19 @@ public class SnapshotCommands {
|
||||
|
||||
if (snapshot != null) {
|
||||
session.setSnapshot(null);
|
||||
actor.print(BBC.SNAPSHOT_NEWEST.s());
|
||||
actor.print("Now using newest snapshot.");
|
||||
} else {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND.s());
|
||||
actor.printError("No snapshots were found.");
|
||||
}
|
||||
} catch (MissingWorldException ex) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND_WORLD.s());
|
||||
actor.printError("No snapshots were found for this world.");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
session.setSnapshot(config.snapshotRepo.getSnapshot(name));
|
||||
BBC.SNAPSHOT_SET.send(actor, name);
|
||||
actor.print("Snapshot set to: " + name);
|
||||
} catch (InvalidSnapshotException e) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_AVAILABLE.s());
|
||||
actor.printError("That snapshot does not exist or is not available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,12 +156,12 @@ public class SnapshotCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (config.snapshotRepo == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_CONFIGURED.s());
|
||||
actor.printError("Snapshot/backup restore is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 1) {
|
||||
actor.printError(BBC.SNAPSHOT_INVALID_INDEX.s());
|
||||
actor.printError("Invalid index, must be equal or higher then 1.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -173,13 +173,13 @@ public class SnapshotCommands {
|
||||
}
|
||||
Snapshot snapshot = snapshots.get(index - 1);
|
||||
if (snapshot == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_AVAILABLE.s());
|
||||
actor.printError("That snapshot does not exist or is not available.");
|
||||
return;
|
||||
}
|
||||
session.setSnapshot(snapshot);
|
||||
BBC.SNAPSHOT_SET.send(actor, snapshot.getName());
|
||||
actor.print("Snapshot set to: " + snapshot.getName());
|
||||
} catch (MissingWorldException e) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND_WORLD.s());
|
||||
actor.printError("No snapshots were found for this world.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ public class SnapshotCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (config.snapshotRepo == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_CONFIGURED.s());
|
||||
actor.printError("Snapshot/backup restore is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -207,10 +207,10 @@ public class SnapshotCommands {
|
||||
+ dateFormat.withZone(session.getTimeZone()).format(date) + ".");
|
||||
} else {
|
||||
session.setSnapshot(snapshot);
|
||||
BBC.SNAPSHOT_SET.send(actor, snapshot.getName());
|
||||
actor.print("Snapshot set to: " + snapshot.getName());
|
||||
}
|
||||
} catch (MissingWorldException ex) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND_WORLD.s());
|
||||
actor.printError("No snapshots were found for this world.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +226,7 @@ public class SnapshotCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (config.snapshotRepo == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_CONFIGURED.s());
|
||||
actor.printError("Snapshot/backup restore is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ public class SnapshotCommands {
|
||||
actor.print("Snapshot set to: " + snapshot.getName());
|
||||
}
|
||||
} catch (MissingWorldException ex) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND_WORLD.s());
|
||||
actor.printError("No snapshots were found for this world.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ public class SnapshotUtilCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (config.snapshotRepo == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_CONFIGURED.s());
|
||||
actor.printError("Snapshot/backup restore is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ public class SnapshotUtilCommands {
|
||||
try {
|
||||
snapshot = config.snapshotRepo.getSnapshot(snapshotName);
|
||||
} catch (InvalidSnapshotException e) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_AVAILABLE.s());
|
||||
actor.printError("That snapshot does not exist or is not available.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -92,7 +92,7 @@ public class SnapshotUtilCommands {
|
||||
snapshot = config.snapshotRepo.getDefaultSnapshot(world.getName());
|
||||
|
||||
if (snapshot == null) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_AVAILABLE.s());
|
||||
actor.printError("No snapshots were found. See console for details.");
|
||||
|
||||
// Okay, let's toss some debugging information!
|
||||
File dir = config.snapshotRepo.getDirectory();
|
||||
@ -109,15 +109,21 @@ public class SnapshotUtilCommands {
|
||||
return;
|
||||
}
|
||||
} catch (MissingWorldException ex) {
|
||||
actor.printError(BBC.SNAPSHOT_NOT_FOUND_WORLD.s());
|
||||
actor.printError("No snapshots were found for this world.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ChunkStore chunkStore;
|
||||
|
||||
// Load chunk store
|
||||
try (ChunkStore chunkStore = snapshot.getChunkStore()) {
|
||||
BBC.SNAPSHOT_LOADED.send(actor, snapshot.getName());
|
||||
try {
|
||||
chunkStore = snapshot.getChunkStore();
|
||||
actor.print("Snapshot '" + snapshot.getName() + "' loaded; now restoring...");
|
||||
} catch (DataException | IOException e) {
|
||||
actor.printError("Failed to load snapshot: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore snapshot
|
||||
SnapshotRestore restore = new SnapshotRestore(chunkStore, editSession, region);
|
||||
@ -128,12 +134,12 @@ public class SnapshotUtilCommands {
|
||||
if (restore.hadTotalFailure()) {
|
||||
String error = restore.getLastErrorMessage();
|
||||
if (!restore.getMissingChunks().isEmpty()) {
|
||||
actor.printError(BBC.SNAPSHOT_ERROR_RESTORE.s());
|
||||
actor.printError("Chunks were not present in snapshot.");
|
||||
} else if (error != null) {
|
||||
actor.printError("Errors prevented any blocks from being restored.");
|
||||
actor.printError("Last error: " + error);
|
||||
} else {
|
||||
actor.printError(BBC.SNAPSHOT_ERROR_RESTORE_CHUNKS.s());
|
||||
actor.printError("No chunks could be loaded. (Bad archive?)");
|
||||
}
|
||||
} else {
|
||||
actor.print(String.format("Restored; %d "
|
||||
@ -143,6 +149,6 @@ public class SnapshotUtilCommands {
|
||||
}
|
||||
} catch (DataException | IOException e) {
|
||||
actor.printError("Failed to load snapshot: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.command;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.brush.InspectBrush;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
@ -28,6 +30,7 @@ import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.command.tool.BlockDataCyler;
|
||||
import com.sk89q.worldedit.command.tool.BlockReplacer;
|
||||
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
|
||||
import com.sk89q.worldedit.command.tool.DistanceWand;
|
||||
import com.sk89q.worldedit.command.tool.FloatingTreeRemover;
|
||||
import com.sk89q.worldedit.command.tool.FloodFillTool;
|
||||
@ -44,27 +47,95 @@ import com.sk89q.worldedit.util.HandSide;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import com.sk89q.worldedit.internal.command.CommandRegistrationHandler;
|
||||
import com.sk89q.worldedit.internal.command.CommandUtil;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import org.enginehub.piston.CommandManager;
|
||||
import org.enginehub.piston.CommandManagerService;
|
||||
import org.enginehub.piston.CommandMetadata;
|
||||
import org.enginehub.piston.CommandParameters;
|
||||
import org.enginehub.piston.part.SubCommandPart;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
|
||||
public class ToolCommands {
|
||||
|
||||
public static void register(CommandRegistrationHandler registration,
|
||||
CommandManager commandManager,
|
||||
CommandManagerService commandManagerService,
|
||||
WorldEdit worldEdit) {
|
||||
// Collect the tool commands
|
||||
CommandManager collect = commandManagerService.newCommandManager();
|
||||
|
||||
registration.register(
|
||||
collect,
|
||||
ToolCommandsRegistration.builder(),
|
||||
new ToolCommands(worldEdit)
|
||||
);
|
||||
|
||||
// Register deprecated global commands
|
||||
Set<org.enginehub.piston.Command> commands = collect.getAllCommands()
|
||||
.collect(Collectors.toSet());
|
||||
for (org.enginehub.piston.Command command : commands) {
|
||||
if (command.getAliases().contains("unbind")) {
|
||||
// Don't register new /tool unbind alias
|
||||
command = command.toBuilder().aliases(
|
||||
Collections2.filter(command.getAliases(), alias -> !"unbind".equals(alias))
|
||||
).build();
|
||||
}
|
||||
commandManager.register(CommandUtil.deprecate(
|
||||
command, "Global tool names cause conflicts " +
|
||||
"and will be removed in WorldEdit 8", ToolCommands::asNonGlobal
|
||||
));
|
||||
}
|
||||
|
||||
// Remove aliases with / in them, since it doesn't make sense for sub-commands.
|
||||
Set<org.enginehub.piston.Command> nonGlobalCommands = commands.stream()
|
||||
.map(command ->
|
||||
command.toBuilder().aliases(
|
||||
Collections2.filter(command.getAliases(), alias -> !alias.startsWith("/"))
|
||||
).build()
|
||||
)
|
||||
.collect(Collectors.toSet());
|
||||
commandManager.register("tool", command -> {
|
||||
command.addPart(SubCommandPart.builder(
|
||||
TranslatableComponent.of("tool"),
|
||||
TextComponent.of("The tool to bind")
|
||||
)
|
||||
.withCommands(nonGlobalCommands)
|
||||
.required()
|
||||
.build());
|
||||
command.description(TextComponent.of("Binds a tool to the item in your hand"));
|
||||
});
|
||||
}
|
||||
|
||||
private static String asNonGlobal(org.enginehub.piston.Command oldCommand,
|
||||
CommandParameters oldParameters) {
|
||||
String name = Optional.ofNullable(oldParameters.getMetadata())
|
||||
.map(CommandMetadata::getCalledName)
|
||||
.filter(n -> !n.startsWith("/"))
|
||||
.orElseGet(oldCommand::getName);
|
||||
return "/tool " + name;
|
||||
}
|
||||
|
||||
static void setToolNone(Player player, LocalSession session, String type)
|
||||
throws InvalidToolBindException {
|
||||
session.setTool(player.getItemInHand(HandSide.MAIN_HAND).getType(), null);
|
||||
player.print(type + " unbound from your current item.");
|
||||
}
|
||||
private final WorldEdit we;
|
||||
|
||||
public ToolCommands(WorldEdit we) {
|
||||
this.we = we;
|
||||
}
|
||||
|
||||
// @Command(
|
||||
// name = "none",
|
||||
// desc = "Unbind a bound tool from your current item"
|
||||
// )
|
||||
// public void none(Player player, LocalSession session) throws WorldEditException {
|
||||
//
|
||||
// session.setTool(player.getItemInHand(HandSide.MAIN_HAND).getType(), null);
|
||||
// player.print("Tool unbound from your current item.");
|
||||
// }
|
||||
|
||||
@Command(
|
||||
name = "selwand",
|
||||
aliases = "/selwand",
|
||||
@ -74,7 +145,7 @@ public class ToolCommands {
|
||||
public void selwand(Player player, LocalSession session) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, SelectionWand.INSTANCE);
|
||||
session.setTool(itemType, new SelectionWand());
|
||||
player.print("Selection wand bound to " + itemType.getName() + ".");
|
||||
}
|
||||
|
||||
@ -215,8 +286,17 @@ public class ToolCommands {
|
||||
Pattern secondary) throws WorldEditException {
|
||||
BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND);
|
||||
|
||||
session.setTool(player, new LongRangeBuildTool(primary, secondary));
|
||||
BBC.TOOL_LRBUILD_BOUND.send(player, itemStack.getType().getName());
|
||||
BBC.TOOL_LRBUILD_INFO.send(player, secondary, primary);
|
||||
session.setTool(itemStack.getType(), new LongRangeBuildTool(primary, secondary));
|
||||
player.print("Long-range building tool bound to " + itemStack.getType().getName() + ".");
|
||||
String primaryName = "pattern";
|
||||
String secondaryName = "pattern";
|
||||
if (primary instanceof BlockStateHolder) {
|
||||
primaryName = ((BlockStateHolder<?>) primary).getBlockType().getName();
|
||||
}
|
||||
if (secondary instanceof BlockStateHolder) {
|
||||
secondaryName = ((BlockStateHolder<?>) secondary).getBlockType().getName();
|
||||
}
|
||||
player.print("Left-click set to " + primaryName + "; right-click set to "
|
||||
+ secondaryName + ".");
|
||||
}
|
||||
}
|
||||
|
@ -82,16 +82,16 @@ public class ToolUtilCommands {
|
||||
return;
|
||||
}
|
||||
if (maskOpt == null) {
|
||||
player.print(BBC.BRUSH_MASK_DISABLED.s());
|
||||
player.print("Brush mask disabled.");
|
||||
tool.setMask(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
BrushSettings settings = offHand ? tool.getOffHand() : tool.getContext();
|
||||
String lastArg = Iterables.getLast(CommandArgParser.spaceSplit(arguments.get())).getSubstring();
|
||||
settings.addSetting(BrushSettings.SettingType.MASK, lastArg);
|
||||
settings.setMask(maskOpt);
|
||||
tool.update();
|
||||
player.print(BBC.BRUSH_MASK.s());
|
||||
player.print("Brush mask set.");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -110,7 +110,7 @@ public class ToolUtilCommands {
|
||||
if (tool == null) {
|
||||
player.print(BBC.BRUSH_NONE.s());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pattern == null) {
|
||||
player.print(BBC.BRUSH_MATERIAL.s());
|
||||
tool.setFill(null);
|
||||
@ -125,61 +125,29 @@ public class ToolUtilCommands {
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "range",
|
||||
desc = "Set the brush range"
|
||||
)
|
||||
name = "range",
|
||||
desc = "Set the brush range"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.options.range")
|
||||
public void range(Player player, LocalSession session,
|
||||
@Arg(desc = "The range of the brush")
|
||||
int range) throws WorldEditException {
|
||||
range = Math.max(0, Math.min(256, range));
|
||||
BrushTool tool = session.getBrushTool(player, false);
|
||||
if (tool == null) {
|
||||
player.print(BBC.BRUSH_NONE.s());
|
||||
return;
|
||||
}
|
||||
tool.setRange(range);
|
||||
player.print(BBC.BRUSH_RANGE.s());
|
||||
int range) throws WorldEditException {
|
||||
session.getBrushTool(player, false).setRange(range);
|
||||
player.print("Brush range set.");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "size",
|
||||
desc = "Set the brush size"
|
||||
name = "size",
|
||||
desc = "Set the brush size"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.options.size")
|
||||
public void size(Player player, LocalSession session,
|
||||
@Arg(desc = "The size of the brush", def = "5")
|
||||
int size,
|
||||
@Switch(name = 'h', desc = "TODO")
|
||||
boolean offHand) throws WorldEditException {
|
||||
@Arg(desc = "The size of the brush")
|
||||
int size) throws WorldEditException {
|
||||
we.checkMaxBrushRadius(size);
|
||||
BrushTool tool = session.getBrushTool(player, false);
|
||||
if (tool == null) {
|
||||
player.print(BBC.BRUSH_NONE.s());
|
||||
return;
|
||||
}
|
||||
BrushSettings settings = offHand ? tool.getOffHand() : tool.getContext();
|
||||
settings.setSize(size);
|
||||
tool.update();
|
||||
player.print(BBC.BRUSH_SIZE.s());
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "tracemask",
|
||||
aliases = {"tarmask", "tm", "targetmask"},
|
||||
desc = "Set the mask used to stop tool traces"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.options.tracemask")
|
||||
public void traceMask(Player player, LocalSession session,
|
||||
@Arg(desc = "The trace mask to set", def = "")
|
||||
Mask maskOpt) throws WorldEditException {
|
||||
BrushTool tool = session.getBrushTool(player, false);
|
||||
if (tool == null) {
|
||||
player.print(BBC.BRUSH_NONE.s());
|
||||
return;
|
||||
}
|
||||
tool.setTraceMask(maskOpt);
|
||||
BBC.BRUSH_TARGET_MASK_SET.send(player, maskOpt.toString());
|
||||
session.getBrushTool(player, false).setSize(size);
|
||||
player.print("Brush size set.");
|
||||
}
|
||||
|
||||
//todo none should be moved to the same class where it is in upstream
|
||||
@ -194,28 +162,42 @@ public class ToolUtilCommands {
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/superpickaxe",
|
||||
aliases = {",", "/sp", "/pickaxe"},
|
||||
desc = "Toggle the super pickaxe function"
|
||||
name = "tracemask",
|
||||
aliases = {"tarmask", "tm", "targetmask"},
|
||||
desc = "Set the mask used to stop tool traces"
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.options.tracemask")
|
||||
public void traceMask(Player player, LocalSession session,
|
||||
@Arg(desc = "The trace mask to set", def = "")
|
||||
Mask maskOpt) throws WorldEditException {
|
||||
session.getBrushTool(player, false).setTraceMask(maskOpt);
|
||||
if (maskOpt == null) {
|
||||
player.print("Trace mask disabled.");
|
||||
} else {
|
||||
player.print("Trace mask set.");
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/",
|
||||
aliases = { "," },
|
||||
desc = "Toggle the super pickaxe function"
|
||||
)
|
||||
@CommandPermissions("worldedit.superpickaxe")
|
||||
public void togglePickaxe(Player player, LocalSession session,
|
||||
@Arg(desc = "state", def = "on") String state) throws WorldEditException {
|
||||
if (session.hasSuperPickAxe()) {
|
||||
if ("on".equals(state)) {
|
||||
player.print(BBC.SUPERPICKAXE_ENABLED.s());
|
||||
@Arg(desc = "The new super pickaxe state", def = "")
|
||||
Boolean superPickaxe) {
|
||||
boolean hasSuperPickAxe = session.hasSuperPickAxe();
|
||||
if (superPickaxe != null && superPickaxe == hasSuperPickAxe) {
|
||||
player.printError("Super pickaxe already " + (superPickaxe ? "enabled" : "disabled") + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasSuperPickAxe) {
|
||||
session.disableSuperPickAxe();
|
||||
player.print(BBC.SUPERPICKAXE_DISABLED.s());
|
||||
player.print("Super pickaxe disabled.");
|
||||
} else {
|
||||
if ("off".equals(state)) {
|
||||
player.print(BBC.SUPERPICKAXE_DISABLED.s());
|
||||
return;
|
||||
}
|
||||
session.enableSuperPickAxe();
|
||||
player.print(BBC.SUPERPICKAXE_ENABLED.s());
|
||||
player.print("Super pickaxe enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@ import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.image.ImageUtil;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.IncompleteRegionException;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
@ -46,21 +45,23 @@ import com.sk89q.worldedit.command.util.Logging;
|
||||
import com.sk89q.worldedit.command.util.PrintCommandHelp;
|
||||
import com.sk89q.worldedit.command.util.SkipQueue;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||
import com.sk89q.worldedit.function.EntityFunction;
|
||||
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.function.mask.BlockTypeMask;
|
||||
import com.sk89q.worldedit.function.visitor.EntityVisitor;
|
||||
import com.sk89q.worldedit.internal.annotation.Direction;
|
||||
import com.sk89q.worldedit.internal.annotation.Range;
|
||||
import com.sk89q.worldedit.internal.expression.Expression;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.CylinderRegion;
|
||||
@ -77,7 +78,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
@ -99,7 +99,8 @@ import org.enginehub.piston.annotation.param.Switch;
|
||||
/**
|
||||
* Utility commands.
|
||||
*/
|
||||
@CommandContainer(superTypes = {
|
||||
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
|
||||
public class UtilityCommands {
|
||||
// CommandQueuedConditionGenerator.Registration.class,
|
||||
CommandPermissionsConditionGenerator.Registration.class // TODO NOT IMPLEMENTED - Piston doesn't seem to work with multiple conditions???
|
||||
})
|
||||
@ -193,6 +194,7 @@ public class UtilityCommands {
|
||||
@Command(
|
||||
name = "/fill",
|
||||
desc = "Fill a hole"
|
||||
|
||||
)
|
||||
@CommandPermissions("worldedit.fill")
|
||||
@Logging(PLACEMENT)
|
||||
@ -218,6 +220,8 @@ public class UtilityCommands {
|
||||
|
||||
/*
|
||||
@Command(
|
||||
name = "/fillr",
|
||||
desc = "Fill a hole recursively"
|
||||
name = "patterns",
|
||||
desc = "View help about patterns",
|
||||
descFooter = "Patterns determine what blocks are placed\n" +
|
||||
@ -296,10 +300,10 @@ public class UtilityCommands {
|
||||
public int fillr(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Arg(desc = "The blocks to fill with")
|
||||
Pattern pattern,
|
||||
@Range(min=1) @Arg(desc = "The radius to fill in")
|
||||
@Arg(desc = "The radius to fill in")
|
||||
Expression radiusExp,
|
||||
@Arg(desc = "The depth to fill", def = "")
|
||||
Integer depth) throws WorldEditException, EvaluationException {
|
||||
Integer depth) throws WorldEditException {
|
||||
double radius = radiusExp.evaluate();
|
||||
radius = Math.max(1, radius);
|
||||
we.checkMaxRadius(radius);
|
||||
@ -319,10 +323,10 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.drain")
|
||||
@Logging(PLACEMENT)
|
||||
public int drain(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=0) @Arg(desc = "The radius to drain")
|
||||
@Arg(desc = "The radius to drain")
|
||||
Expression radiusExp,
|
||||
@Switch(name = 'w', desc = "Also un-waterlog blocks")
|
||||
boolean waterlogged) throws WorldEditException, EvaluationException {
|
||||
boolean waterlogged) throws WorldEditException {
|
||||
double radius = radiusExp.evaluate();
|
||||
radius = Math.max(0, radius);
|
||||
we.checkMaxRadius(radius);
|
||||
@ -340,9 +344,8 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.fixlava")
|
||||
@Logging(PLACEMENT)
|
||||
public int fixLava(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=0) @Arg(desc = "The radius to fix in")
|
||||
Expression radiusExp) throws WorldEditException, EvaluationException {
|
||||
double radius = radiusExp.evaluate();
|
||||
@Arg(desc = "The radius to fix in")
|
||||
double radius) throws WorldEditException {
|
||||
radius = Math.max(0, radius);
|
||||
we.checkMaxRadius(radius);
|
||||
int affected = editSession.fixLiquid(session.getPlacementPosition(actor), radius, BlockTypes.LAVA);
|
||||
@ -358,13 +361,12 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.fixwater")
|
||||
@Logging(PLACEMENT)
|
||||
public int fixWater(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=0) @Arg(desc = "The radius to fix in")
|
||||
Expression radiusExp) throws WorldEditException, EvaluationException {
|
||||
double radius = radiusExp.evaluate();
|
||||
@Arg(desc = "The radius to fix in")
|
||||
double radius) throws WorldEditException {
|
||||
radius = Math.max(0, radius);
|
||||
we.checkMaxRadius(radius);
|
||||
int affected = editSession.fixLiquid(session.getPlacementPosition(actor), radius, BlockTypes.WATER);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been changed.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@ -376,15 +378,16 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.removeabove")
|
||||
@Logging(PLACEMENT)
|
||||
public int removeAbove(Actor actor, World world, LocalSession session, EditSession editSession,
|
||||
@Range(min=1) @Arg(name = "size", desc = "The apothem of the square to remove from", def = "1")
|
||||
@Arg(desc = "The apothem of the square to remove from", def = "1")
|
||||
int size,
|
||||
@Arg(desc = "The maximum height above you to remove from", def = "")
|
||||
Integer height) throws WorldEditException {
|
||||
size = Math.max(1, size);
|
||||
we.checkMaxRadius(size);
|
||||
height = height != null ? Math.min((world.getMaxY() + 1), height + 1) : (world.getMaxY() + 1);
|
||||
|
||||
int affected = editSession.removeAbove(session.getPlacementPosition(actor), size, height);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been removed.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@ -396,7 +399,7 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.removebelow")
|
||||
@Logging(PLACEMENT)
|
||||
public int removeBelow(Actor actor, World world, LocalSession session, EditSession editSession,
|
||||
@Arg(name = "size", desc = "The apothem of the square to remove from", def = "1")
|
||||
@Arg(desc = "The apothem of the square to remove from", def = "1")
|
||||
int size,
|
||||
@Arg(desc = "The maximum height below you to remove from", def = "")
|
||||
Integer height) throws WorldEditException {
|
||||
@ -405,7 +408,7 @@ public class UtilityCommands {
|
||||
height = height != null ? Math.min((world.getMaxY() + 1), height + 1) : (world.getMaxY() + 1);
|
||||
|
||||
int affected = editSession.removeBelow(session.getPlacementPosition(actor), size, height);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been removed.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@ -419,25 +422,25 @@ public class UtilityCommands {
|
||||
public int removeNear(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Arg(desc = "The mask of blocks to remove")
|
||||
Mask mask,
|
||||
@Range(min=1) @Arg(desc = "The radius of the square to remove from", def = "50")
|
||||
@Arg(desc = "The radius of the square to remove from", def = "50")
|
||||
int radius) throws WorldEditException {
|
||||
radius = Math.max(1, radius);
|
||||
we.checkMaxRadius(radius);
|
||||
|
||||
int affected = editSession.removeNear(session.getPlacementPosition(actor), mask, radius);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been removed.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "replacenear",
|
||||
aliases = { "/replacenear", "/rn" },
|
||||
aliases = { "/replacenear" },
|
||||
desc = "Replace nearby blocks"
|
||||
)
|
||||
@CommandPermissions("worldedit.replacenear")
|
||||
@Logging(PLACEMENT)
|
||||
public int replaceNear(Actor actor, World world, LocalSession session, EditSession editSession,
|
||||
@Range(min=1) @Arg(desc = "The radius of the square to remove in")
|
||||
@Arg(desc = "The radius of the square to remove in")
|
||||
int radius,
|
||||
@Arg(desc = "The mask matching blocks to remove", def = "")
|
||||
Mask from,
|
||||
@ -456,7 +459,7 @@ public class UtilityCommands {
|
||||
}
|
||||
|
||||
int affected = editSession.replaceBlocks(region, from, to);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been replaced.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
@ -468,7 +471,7 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.snow")
|
||||
@Logging(PLACEMENT)
|
||||
public int snow(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=1) @Arg(desc = "The radius of the circle to snow in", def = "10")
|
||||
@Arg(desc = "The radius of the circle to snow in", def = "10")
|
||||
double size) throws WorldEditException {
|
||||
size = Math.max(1, size);
|
||||
we.checkMaxRadius(size);
|
||||
@ -486,7 +489,7 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.thaw")
|
||||
@Logging(PLACEMENT)
|
||||
public int thaw(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=1) @Arg(desc = "The radius of the circle to thaw in", def = "10")
|
||||
@Arg(desc = "The radius of the circle to thaw in", def = "10")
|
||||
double size) throws WorldEditException {
|
||||
size = Math.max(1, size);
|
||||
we.checkMaxRadius(size);
|
||||
@ -504,7 +507,7 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.green")
|
||||
@Logging(PLACEMENT)
|
||||
public int green(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=1) @Arg(desc = "The radius of the circle to convert in", def = "10")
|
||||
@Arg(desc = "The radius of the circle to convert in", def = "10")
|
||||
double size,
|
||||
@Switch(name = 'f', desc = "Also convert coarse dirt")
|
||||
boolean convertCoarse) throws WorldEditException {
|
||||
@ -513,10 +516,37 @@ public class UtilityCommands {
|
||||
final boolean onlyNormalDirt = !convertCoarse;
|
||||
|
||||
final int affected = editSession.green(session.getPlacementPosition(actor), size, onlyNormalDirt);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " surface(s) greened.");
|
||||
return affected;
|
||||
}
|
||||
|
||||
private int killMatchingEntities(Integer radius, Actor actor, Supplier<EntityFunction> func) throws IncompleteRegionException,
|
||||
MaxChangedBlocksException {
|
||||
List<EntityVisitor> visitors = new ArrayList<>();
|
||||
|
||||
LocalSession session = we.getSessionManager().get(actor);
|
||||
BlockVector3 center = session.getPlacementPosition(actor);
|
||||
EditSession editSession = session.createEditSession(actor);
|
||||
List<? extends Entity> entities;
|
||||
if (radius >= 0) {
|
||||
CylinderRegion region = CylinderRegion.createRadius(editSession, center, radius);
|
||||
entities = editSession.getEntities(region);
|
||||
} else {
|
||||
entities = editSession.getEntities();
|
||||
}
|
||||
visitors.add(new EntityVisitor(entities.iterator(), func.get()));
|
||||
|
||||
int killed = 0;
|
||||
for (EntityVisitor visitor : visitors) {
|
||||
Operations.completeLegacy(visitor);
|
||||
killed += visitor.getAffected();
|
||||
}
|
||||
|
||||
session.remember(editSession);
|
||||
editSession.flushSession();
|
||||
return killed;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "extinguish",
|
||||
aliases = { "/ex", "/ext", "/extinguish", "ex", "ext" },
|
||||
@ -525,7 +555,7 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.extinguish")
|
||||
@Logging(PLACEMENT)
|
||||
public void extinguish(Actor actor, LocalSession session, EditSession editSession,
|
||||
@Range(min=1) @Arg(desc = "The radius of the square to remove in", def = "")
|
||||
@Arg(desc = "The radius of the square to remove in", def = "")
|
||||
Integer radius) throws WorldEditException {
|
||||
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
@ -534,9 +564,9 @@ public class UtilityCommands {
|
||||
int size = radius != null ? Math.max(1, radius) : defaultRadius;
|
||||
we.checkMaxRadius(size);
|
||||
|
||||
Mask mask = BlockTypes.FIRE.toMask();
|
||||
Mask mask = new BlockTypeMask(editSession, BlockTypes.FIRE);
|
||||
int affected = editSession.removeNear(session.getPlacementPosition(actor), mask, size);
|
||||
BBC.VISITOR_BLOCK.send(actor, affected);
|
||||
actor.print(affected + " block(s) have been removed.");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -597,6 +627,22 @@ public class UtilityCommands {
|
||||
return killed;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/help",
|
||||
desc = "Displays help for WorldEdit commands"
|
||||
)
|
||||
@CommandPermissions("worldedit.help")
|
||||
public void help(Actor actor,
|
||||
@Switch(name = 's', desc = "List sub-commands of the given command, if applicable")
|
||||
boolean listSubCommands,
|
||||
@ArgFlag(name = 'p', desc = "The page to retrieve", def = "1")
|
||||
int page,
|
||||
@Arg(desc = "The command to retrieve help for", def = "", variable = true)
|
||||
List<String> command) throws WorldEditException {
|
||||
PrintCommandHelp.help(command, page, listSubCommands,
|
||||
we.getPlatformManager().getPlatformCommandManager().getCommandManager(), actor, "//help");
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "remove",
|
||||
aliases = { "rem", "rement" },
|
||||
@ -607,9 +653,8 @@ public class UtilityCommands {
|
||||
public int remove(Actor actor,
|
||||
@Arg(desc = "The type of entity to remove")
|
||||
EntityRemover remover,
|
||||
@Range(min=-1) @Arg(desc = "The radius of the cuboid to remove from")
|
||||
@Arg(desc = "The radius of the cuboid to remove from")
|
||||
int radius) throws WorldEditException {
|
||||
|
||||
if (radius < -1) {
|
||||
actor.printError("Use -1 to remove all entities in loaded chunks");
|
||||
return 0;
|
||||
@ -621,34 +666,6 @@ public class UtilityCommands {
|
||||
return removed;
|
||||
}
|
||||
|
||||
private int killMatchingEntities(Integer radius, Actor actor, Supplier<EntityFunction> func) throws IncompleteRegionException, MaxChangedBlocksException {
|
||||
List<EntityVisitor> visitors = new ArrayList<>();
|
||||
|
||||
LocalSession session = we.getSessionManager().get(actor);
|
||||
BlockVector3 center = session.getPlacementPosition(actor);
|
||||
EditSession editSession = session.createEditSession(actor);
|
||||
List<? extends Entity> entities;
|
||||
if (radius >= 0) {
|
||||
CylinderRegion region = CylinderRegion.createRadius(editSession, center, radius);
|
||||
entities = editSession.getEntities(region);
|
||||
} else {
|
||||
entities = editSession.getEntities();
|
||||
}
|
||||
visitors.add(new EntityVisitor(entities.iterator(), func.get()));
|
||||
|
||||
int killed = 0;
|
||||
for (EntityVisitor visitor : visitors) {
|
||||
Operations.completeLegacy(visitor);
|
||||
killed += visitor.getAffected();
|
||||
}
|
||||
|
||||
BBC.KILL_SUCCESS.send(actor, killed, radius);
|
||||
|
||||
session.remember(editSession);
|
||||
editSession.flushSession();
|
||||
return killed;
|
||||
}
|
||||
|
||||
// get the formatter with the system locale. in the future, if we can get a local from a player, we can use that
|
||||
private static final DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.getDefault());
|
||||
static {
|
||||
@ -663,7 +680,7 @@ public class UtilityCommands {
|
||||
@CommandPermissions("worldedit.calc")
|
||||
public void calc(Actor actor,
|
||||
@Arg(desc = "Expression to evaluate", variable = true)
|
||||
List<String> input) throws EvaluationException {
|
||||
List<String> input) {
|
||||
Expression expression;
|
||||
try {
|
||||
expression = Expression.compile(String.join(" ", input));
|
||||
@ -672,11 +689,12 @@ public class UtilityCommands {
|
||||
"'%s' could not be parsed as a valid expression", input));
|
||||
return;
|
||||
}
|
||||
double result = expression.evaluate(
|
||||
new double[]{}, WorldEdit.getInstance().getSessionManager().get(actor).getTimeout());
|
||||
String formatted = Double.isNaN(result) ? "NaN" : formatter.format(result);
|
||||
TextComponent msg = SubtleFormat.wrap(input + " = ").append(TextComponent.of(formatted, TextColor.LIGHT_PURPLE));
|
||||
actor.print(msg);
|
||||
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> {
|
||||
double result = expression.evaluate(
|
||||
new double[]{}, WorldEdit.getInstance().getSessionManager().get(actor).getTimeout());
|
||||
String formatted = Double.isNaN(result) ? "NaN" : formatter.format(result);
|
||||
return SubtleFormat.wrap(input + " = ").append(TextComponent.of(formatted, TextColor.LIGHT_PURPLE));
|
||||
}, null);
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -690,22 +708,6 @@ public class UtilityCommands {
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/help",
|
||||
desc = "Displays help for WorldEdit commands"
|
||||
)
|
||||
@CommandPermissions("worldedit.help")
|
||||
public void help(Actor actor,
|
||||
@Switch(name = 's', desc = "List sub-commands of the given command, if applicable")
|
||||
boolean listSubCommands,
|
||||
@ArgFlag(name = 'p', desc = "The page to retrieve", def = "1")
|
||||
int page,
|
||||
@Arg(desc = "The command to retrieve help for", def = "", variable = true)
|
||||
List<String> commandStr) throws WorldEditException {
|
||||
PrintCommandHelp.help(commandStr, page, listSubCommands,
|
||||
we.getPlatformManager().getPlatformCommandManager().getCommandManager(), actor, "//help");
|
||||
}
|
||||
|
||||
public static List<Map.Entry<URI, String>> filesToEntry(final File root, final List<File> files, final UUID uuid) {
|
||||
return Lists.transform(files, input -> { // Keep this functional, as transform is evaluated lazily
|
||||
URI uri = input.toURI();
|
||||
|
@ -56,11 +56,9 @@ import org.enginehub.piston.annotation.param.Arg;
|
||||
import org.enginehub.piston.annotation.param.ArgFlag;
|
||||
import org.enginehub.piston.annotation.param.Switch;
|
||||
|
||||
@CommandContainer(superTypes = {CommandPermissionsConditionGenerator.Registration.class, CommandQueuedConditionGenerator.Registration.class})
|
||||
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
|
||||
public class WorldEditCommands {
|
||||
|
||||
private static final DateTimeFormatter dateFormat = DateTimeFormatter
|
||||
.ofPattern("yyyy-MM-dd HH:mm:ss z");
|
||||
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
|
||||
|
||||
private final WorldEdit we;
|
||||
|
||||
@ -174,11 +172,10 @@ public class WorldEditCommands {
|
||||
try {
|
||||
ZoneId tz = ZoneId.of(timezone);
|
||||
session.setTimezone(tz);
|
||||
BBC.TIMEZONE_SET.send(actor, tz.getDisplayName(
|
||||
TextStyle.FULL, Locale.ENGLISH
|
||||
actor.print("Timezone set for this session to: " + tz.getDisplayName(
|
||||
TextStyle.FULL, Locale.ENGLISH
|
||||
));
|
||||
BBC.TIMEZONE_DISPLAY
|
||||
.send(actor, dateFormat.format(ZonedDateTime.now(tz)));
|
||||
actor.print("The current time in that timezone is: " + dateFormat.format(ZonedDateTime.now(tz)));
|
||||
} catch (ZoneRulesException e) {
|
||||
actor.printError("Invalid timezone");
|
||||
}
|
||||
@ -186,7 +183,7 @@ public class WorldEditCommands {
|
||||
|
||||
@Command(
|
||||
name = "help",
|
||||
desc = "Displays help for FAWE commands"
|
||||
desc = "Displays help for WorldEdit commands"
|
||||
)
|
||||
@SkipQueue
|
||||
@CommandPermissions("worldedit.help")
|
||||
@ -196,8 +193,8 @@ public class WorldEditCommands {
|
||||
@ArgFlag(name = 'p', desc = "The page to retrieve", def = "1")
|
||||
int page,
|
||||
@Arg(desc = "The command to retrieve help for", def = "", variable = true)
|
||||
List<String> commandStr) throws WorldEditException {
|
||||
PrintCommandHelp.help(commandStr, page, listSubCommands,
|
||||
we.getPlatformManager().getPlatformCommandManager().getCommandManager(), actor, "/worldedit help");
|
||||
List<String> command) throws WorldEditException {
|
||||
PrintCommandHelp.help(command, page, listSubCommands,
|
||||
we.getPlatformManager().getPlatformCommandManager().getCommandManager(), actor, "/worldedit help");
|
||||
}
|
||||
}
|
||||
|
@ -19,20 +19,21 @@
|
||||
|
||||
package com.sk89q.worldedit.command.argument;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.sk89q.worldedit.util.formatting.text.TextComponent.space;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import java.util.List;
|
||||
import org.enginehub.piston.converter.ArgumentConverter;
|
||||
import org.enginehub.piston.converter.ConversionResult;
|
||||
import org.enginehub.piston.converter.SuccessfulConversion;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.sk89q.worldedit.util.formatting.text.TextComponent.space;
|
||||
|
||||
public class CommaSeparatedValuesConverter<T> implements ArgumentConverter<T> {
|
||||
|
||||
public static <T> CommaSeparatedValuesConverter<T> wrap(ArgumentConverter<T> delegate) {
|
||||
|
@ -28,12 +28,13 @@ import org.enginehub.piston.converter.ArgumentConverter;
|
||||
import org.enginehub.piston.converter.ConversionResult;
|
||||
import org.enginehub.piston.converter.FailedConversion;
|
||||
import org.enginehub.piston.converter.SuccessfulConversion;
|
||||
import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
import org.enginehub.piston.inject.Key;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix;
|
||||
|
||||
public class EntityRemoverConverter implements ArgumentConverter<EntityRemover> {
|
||||
|
||||
public static void register(CommandManager commandManager) {
|
||||
|
@ -1,44 +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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.command.factory;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.function.Contextual;
|
||||
import com.sk89q.worldedit.function.EditContext;
|
||||
import com.sk89q.worldedit.function.generator.ForestGenerator;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
|
||||
public final class TreeGeneratorFactory implements Contextual<ForestGenerator> {
|
||||
private final TreeGenerator.TreeType type;
|
||||
|
||||
public TreeGeneratorFactory(TreeGenerator.TreeType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForestGenerator createFromContext(EditContext input) {
|
||||
return new ForestGenerator((EditSession) input.getDestination(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "tree of type " + type;
|
||||
}
|
||||
}
|
@ -66,8 +66,8 @@ public class AreaPickaxe implements BlockTool {
|
||||
|
||||
try {
|
||||
for (int x = ox - range; x <= ox + range; ++x) {
|
||||
for (int z = oz - range; z <= oz + range; ++z) {
|
||||
for (int y = oy + range; y >= oy - range; --y) {
|
||||
for (int y = oy - range; y <= oy + range; ++y) {
|
||||
for (int z = oz - range; z <= oz + range; ++z) {
|
||||
if (initialType.equals(editSession.getBlock(x, y, z))) {
|
||||
continue;
|
||||
}
|
||||
|
@ -70,7 +70,6 @@ import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.internal.expression.Expression;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import com.sk89q.worldedit.session.request.Request;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
@ -437,7 +436,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
|
||||
@Override
|
||||
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
||||
return act(BrushAction.PRIMARY, player, session);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockVector3 getPosition(EditSession editSession, Player player) {
|
||||
Location loc = player.getLocation();
|
||||
|
@ -23,7 +23,6 @@ import com.boydti.fawe.config.BBC;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
@ -40,11 +39,6 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool {
|
||||
super("worldedit.selection.pos");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Actor player) {
|
||||
return player.hasPermission("worldedit.wand");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
||||
Location target = getTarget(player);
|
||||
@ -74,8 +68,7 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool {
|
||||
private Location getTarget(Player player) {
|
||||
Location target;
|
||||
Mask mask = getTraceMask();
|
||||
int range = getRange();
|
||||
if (range < MAX_RANGE) {
|
||||
if (this.range > -1) {
|
||||
target = player.getBlockTrace(getRange(), true, mask);
|
||||
} else {
|
||||
target = player.getBlockTrace(MAX_RANGE, false, mask);
|
||||
|
@ -118,7 +118,7 @@ public class FloatingTreeRemover implements BlockTool {
|
||||
* @param origin any point contained in the floating tree
|
||||
* @return a set containing all blocks in the tree/shroom or null if this is not a floating tree/shroom.
|
||||
*/
|
||||
private Set<BlockVector3> bfs(World world, BlockVector3 origin) throws MaxChangedBlocksException {
|
||||
private Set<BlockVector3> bfs(World world, BlockVector3 origin) {
|
||||
final LocalBlockVectorSet visited = new LocalBlockVectorSet();
|
||||
final LocalBlockVectorSet queue = new LocalBlockVectorSet();
|
||||
|
||||
|
@ -27,6 +27,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
@ -56,39 +57,56 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo
|
||||
public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
||||
Location pos = getTargetFace(player);
|
||||
if (pos == null) return false;
|
||||
try (EditSession eS = session.createEditSession(player)) {
|
||||
BlockBag bag = session.getBlockBag(player);
|
||||
|
||||
try (EditSession editSession = session.createEditSession(player)) {
|
||||
try {
|
||||
editSession.disableBuffering();
|
||||
BlockVector3 blockPoint = pos.toVector().toBlockPoint();
|
||||
BaseBlock applied = secondary.apply(blockPoint);
|
||||
if (applied.getBlockType().getMaterial().isAir()) {
|
||||
eS.setBlock(blockPoint, secondary);
|
||||
editSession.setBlock(blockPoint, secondary);
|
||||
} else {
|
||||
eS.setBlock(pos.toVector().subtract(pos.getDirection()).toBlockPoint(), secondary);
|
||||
editSession.setBlock(pos.toVector().subtract(pos.getDirection()).toBlockPoint(), secondary);
|
||||
}
|
||||
} catch (MaxChangedBlocksException ignored) {
|
||||
} finally {
|
||||
session.remember(editSession);
|
||||
}
|
||||
return true;
|
||||
} catch (MaxChangedBlocksException ignored) {
|
||||
// one block? eat it
|
||||
} finally {
|
||||
if (bag != null) {
|
||||
bag.flushChanges();
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
||||
Location pos = getTargetFace(player);
|
||||
if (pos == null) return false;
|
||||
try (EditSession eS = session.createEditSession(player)) {
|
||||
BlockBag bag = session.getBlockBag(player);
|
||||
|
||||
try (EditSession editSession = session.createEditSession(player)) {
|
||||
try {
|
||||
editSession.disableBuffering();
|
||||
BlockVector3 blockPoint = pos.toVector().toBlockPoint();
|
||||
BaseBlock applied = primary.apply(blockPoint);
|
||||
if (applied.getBlockType().getMaterial().isAir()) {
|
||||
eS.setBlock(blockPoint, primary);
|
||||
editSession.setBlock(blockPoint, primary);
|
||||
} else {
|
||||
eS.setBlock(pos.toVector().subtract(pos.getDirection()).toBlockPoint(), primary);
|
||||
editSession.setBlock(pos.toVector().subtract(pos.getDirection()).toBlockPoint(), primary);
|
||||
}
|
||||
} catch (MaxChangedBlocksException ignored) {
|
||||
} finally {
|
||||
session.remember(editSession);
|
||||
}
|
||||
return true;
|
||||
} catch (MaxChangedBlocksException ignored) {
|
||||
// one block? eat it
|
||||
} finally {
|
||||
if (bag != null) {
|
||||
bag.flushChanges();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Location getTargetFace(Player player) {
|
||||
@ -99,6 +117,7 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo
|
||||
} else {
|
||||
target = player.getBlockTrace(MAX_RANGE, false, mask);
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
player.printError(BBC.NO_BLOCK.s());
|
||||
return null;
|
||||
|
@ -24,6 +24,7 @@ import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
@ -33,6 +34,8 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
|
||||
/**
|
||||
* Looks up information about a block.
|
||||
*/
|
||||
|
@ -84,4 +84,36 @@ public class RecursivePickaxe implements BlockTool {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void recurse(Platform server, EditSession editSession, World world, BlockVector3 pos,
|
||||
BlockVector3 origin, double size, BlockType initialType, Set<BlockVector3> visited) throws MaxChangedBlocksException {
|
||||
|
||||
final double distanceSq = origin.distanceSq(pos);
|
||||
if (distanceSq > size*size || visited.contains(pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
visited.add(pos);
|
||||
|
||||
if (editSession.getBlock(pos).getBlockType() != initialType) {
|
||||
return;
|
||||
}
|
||||
|
||||
editSession.setBlock(pos, BlockTypes.AIR.getDefaultState());
|
||||
|
||||
world.queueBlockBreakEffect(server, pos, initialType, distanceSq);
|
||||
|
||||
recurse(server, editSession, world, pos.add(1, 0, 0),
|
||||
origin, size, initialType, visited);
|
||||
recurse(server, editSession, world, pos.add(-1, 0, 0),
|
||||
origin, size, initialType, visited);
|
||||
recurse(server, editSession, world, pos.add(0, 0, 1),
|
||||
origin, size, initialType, visited);
|
||||
recurse(server, editSession, world, pos.add(0, 0, -1),
|
||||
origin, size, initialType, visited);
|
||||
recurse(server, editSession, world, pos.add(0, 1, 0),
|
||||
origin, size, initialType, visited);
|
||||
recurse(server, editSession, world, pos.add(0, -1, 0),
|
||||
origin, size, initialType, visited);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class ClipboardBrush implements Brush {
|
||||
this.ignoreAirBlocks = ignoreAirBlocks;
|
||||
this.usingOrigin = usingOrigin;
|
||||
this.pasteBiomes = false;
|
||||
this.pasteEntities = true;
|
||||
this.pasteEntities = false;
|
||||
this.sourceMask = null;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,12 @@ import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.util.LocatedBlock;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.util.collection.LocatedBlockList;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
|
||||
public class GravityBrush implements Brush {
|
||||
@ -48,12 +53,15 @@ public class GravityBrush implements Brush {
|
||||
if (y != freeSpot) {
|
||||
editSession.setBlock((int)x, (int)y, (int)z, BlockTypes.AIR.getDefaultState());
|
||||
editSession.setBlock((int)x, (int)freeSpot, (int)z, block);
|
||||
}
|
||||
}
|
||||
freeSpot = y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
column.clear();
|
||||
removedBlocks.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.command.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
@ -42,6 +40,9 @@ import javax.annotation.Nullable;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public final class AsyncCommandBuilder<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AsyncCommandBuilder.class);
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.command.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.enginehub.piston.Command;
|
||||
import org.enginehub.piston.gen.CommandConditionGenerator;
|
||||
@ -28,6 +27,8 @@ import org.enginehub.piston.util.NonnullByDefault;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@NonnullByDefault
|
||||
public final class CommandPermissionsConditionGenerator implements CommandConditionGenerator {
|
||||
|
||||
|
@ -20,12 +20,12 @@
|
||||
package com.sk89q.worldedit.command.util;
|
||||
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.sk89q.worldedit.entity.metadata.EntityProperties;
|
||||
import com.sk89q.worldedit.function.EntityFunction;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
|
@ -24,8 +24,6 @@ import org.enginehub.piston.Command;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
import org.enginehub.piston.inject.Key;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class PermissionCondition implements Command.Condition {
|
||||
|
@ -303,14 +303,6 @@ public interface Player extends Entity, Actor {
|
||||
*/
|
||||
void setPosition(Vector3 pos, float pitch, float yaw);
|
||||
|
||||
/**
|
||||
* Move the player.
|
||||
*
|
||||
* @param pos where to move them
|
||||
*/
|
||||
@Override
|
||||
void setPosition(Vector3 pos);
|
||||
|
||||
/**
|
||||
* Sends a fake block to the client.
|
||||
*
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.event.platform;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.sk89q.worldedit.event.Event;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.internal.util.Substring;
|
||||
@ -27,6 +25,8 @@ import com.sk89q.worldedit.internal.util.Substring;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Posted when suggestions for auto-completion are requested for command input.
|
||||
*/
|
||||
|
@ -32,6 +32,7 @@ import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A registry of known {@link Mask}s. Provides methods to instantiate
|
||||
@ -48,19 +49,36 @@ public final class MaskFactory extends AbstractFactory<Mask> {
|
||||
* @param worldEdit the WorldEdit instance
|
||||
*/
|
||||
public MaskFactory(WorldEdit worldEdit) {
|
||||
super(worldEdit, new DefaultMaskParser(worldEdit));
|
||||
super(worldEdit, new DefaultMaskParser(worldEdit));
|
||||
/*
|
||||
super(worldEdit, new BlocksMaskParser(worldEdit));
|
||||
|
||||
// register(new ExistingMaskParser(worldEdit));
|
||||
// register(new SolidMaskParser(worldEdit));
|
||||
// register(new LazyRegionMaskParser(worldEdit));
|
||||
// register(new RegionMaskParser(worldEdit));
|
||||
// register(new OffsetMaskParser(worldEdit));
|
||||
// register(new NoiseMaskParser(worldEdit));
|
||||
// register(new BlockStateMaskParser(worldEdit));
|
||||
// register(new NegateMaskParser(worldEdit));
|
||||
// register(new ExpressionMaskParser(worldEdit));
|
||||
register(new BlockCategoryMaskParser(worldEdit)); // TODO implement in DefaultMaskParser
|
||||
// register(new BiomeMaskParser(worldEdit));
|
||||
register(new ExistingMaskParser(worldEdit));
|
||||
register(new SolidMaskParser(worldEdit));
|
||||
register(new LazyRegionMaskParser(worldEdit));
|
||||
register(new RegionMaskParser(worldEdit));
|
||||
register(new OffsetMaskParser(worldEdit));
|
||||
register(new NoiseMaskParser(worldEdit));
|
||||
register(new BlockStateMaskParser(worldEdit));
|
||||
register(new NegateMaskParser(worldEdit));
|
||||
register(new ExpressionMaskParser(worldEdit));
|
||||
*/
|
||||
register(new BlockCategoryMaskParser(worldEdit));
|
||||
/*
|
||||
register(new BiomeMaskParser(worldEdit));
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSuggestions(String input) {
|
||||
final String[] split = input.split(" ");
|
||||
if (split.length > 1) {
|
||||
String prev = input.substring(0, input.lastIndexOf(" ")) + " ";
|
||||
return super.getSuggestions(split[split.length -1]).stream().map(s -> prev + s).collect(Collectors.toList());
|
||||
}
|
||||
return super.getSuggestions(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,16 +40,19 @@ public final class PatternFactory extends AbstractFactory<Pattern> {
|
||||
* @param worldEdit the WorldEdit instance
|
||||
*/
|
||||
public PatternFactory(WorldEdit worldEdit) {
|
||||
super(worldEdit, new DefaultPatternParser(worldEdit));
|
||||
super(worldEdit, new DefaultPatternParser(worldEdit));
|
||||
/*
|
||||
super(worldEdit, new SingleBlockPatternParser(worldEdit));
|
||||
|
||||
// split and parse each sub-pattern
|
||||
// register(new RandomPatternParser(worldEdit));
|
||||
register(new RandomPatternParser(worldEdit));
|
||||
|
||||
// individual patterns
|
||||
// register(new ClipboardPatternParser(worldEdit));
|
||||
// register(new TypeOrStateApplyingPatternParser(worldEdit));
|
||||
// register(new RandomStatePatternParser(worldEdit));
|
||||
register(new BlockCategoryPatternParser(worldEdit)); // TODO implement in pattern parser
|
||||
register(new ClipboardPatternParser(worldEdit));
|
||||
register(new TypeOrStateApplyingPatternParser(worldEdit));
|
||||
register(new RandomStatePatternParser(worldEdit));
|
||||
*/
|
||||
register(new BlockCategoryPatternParser(worldEdit));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.extension.factory.parser;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.boydti.fawe.command.SuggestInputParseException;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.jnbt.JSON2NBT;
|
||||
@ -35,7 +37,7 @@ import com.sk89q.worldedit.blocks.BaseItem;
|
||||
import com.sk89q.worldedit.blocks.MobSpawnerBlock;
|
||||
import com.sk89q.worldedit.blocks.SignBlock;
|
||||
import com.sk89q.worldedit.blocks.SkullBlock;
|
||||
import com.sk89q.worldedit.blocks.metadata.MobType;
|
||||
import com.sk89q.worldedit.command.util.SuggestionHelper;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.input.DisallowedUsageException;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
@ -61,6 +63,8 @@ import com.sk89q.worldedit.world.registry.LegacyMapper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.entity.EntityTypes;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -111,6 +115,8 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] EMPTY_STRING_ARRAY = {};
|
||||
|
||||
/**
|
||||
* Backwards compatibility for wool colours in block syntax.
|
||||
*
|
||||
@ -162,6 +168,71 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<Property<?>, Object> parseProperties(BlockType type, String[] stateProperties, ParserContext context) throws NoMatchException {
|
||||
Map<Property<?>, Object> blockStates = new HashMap<>();
|
||||
|
||||
if (stateProperties.length > 0) { // Block data not yet detected
|
||||
// Parse the block data (optional)
|
||||
for (String parseableData : stateProperties) {
|
||||
try {
|
||||
String[] parts = parseableData.split("=");
|
||||
if (parts.length != 2) {
|
||||
throw new NoMatchException("Bad state format in " + parseableData);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Property<Object> propertyKey = (Property<Object>) type.getPropertyMap().get(parts[0]);
|
||||
if (propertyKey == null) {
|
||||
if (context.getActor() != null) {
|
||||
throw new NoMatchException("Unknown property " + parts[0] + " for block " + type.getId());
|
||||
} else {
|
||||
WorldEdit.logger.warn("Unknown property " + parts[0] + " for block " + type.getId());
|
||||
}
|
||||
return Maps.newHashMap();
|
||||
}
|
||||
if (blockStates.containsKey(propertyKey)) {
|
||||
throw new NoMatchException("Duplicate property " + parts[0]);
|
||||
}
|
||||
Object value;
|
||||
try {
|
||||
value = propertyKey.getValueFor(parts[1]);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new NoMatchException("Unknown value " + parts[1] + " for state " + parts[0]);
|
||||
}
|
||||
|
||||
blockStates.put(propertyKey, value);
|
||||
} catch (NoMatchException e) {
|
||||
throw e; // Pass-through
|
||||
} catch (Exception e) {
|
||||
WorldEdit.logger.warn("Unknown state '" + parseableData + "'", e);
|
||||
throw new NoMatchException("Unknown state '" + parseableData + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blockStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String input) {
|
||||
final int idx = input.lastIndexOf('[');
|
||||
if (idx < 0) {
|
||||
return SuggestionHelper.getNamespacedRegistrySuggestions(BlockType.REGISTRY, input);
|
||||
}
|
||||
String blockType = input.substring(0, idx);
|
||||
BlockType type = BlockTypes.get(blockType.toLowerCase(Locale.ROOT));
|
||||
if (type == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
String props = input.substring(idx + 1);
|
||||
if (props.isEmpty()) {
|
||||
return type.getProperties().stream().map(p -> input + p.getName() + "=");
|
||||
}
|
||||
|
||||
return SuggestionHelper.getBlockPropertySuggestions(blockType, props);
|
||||
}
|
||||
|
||||
private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException {
|
||||
String[] blockAndExtraData = input.trim().split("\\|", 2);
|
||||
blockAndExtraData[0] = woolMapper(blockAndExtraData[0]);
|
||||
@ -193,7 +264,7 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
||||
state = LegacyMapper.getInstance().getBlockFromLegacy(type.getLegacyCombinedId() >> 4, data);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,6 +277,13 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
||||
typeString = blockAndExtraData[0];
|
||||
} else {
|
||||
typeString = blockAndExtraData[0].substring(0, stateStart);
|
||||
if (stateStart + 1 >= blockAndExtraData[0].length()) {
|
||||
throw new InputParseException("Invalid format. Hanging bracket @ " + stateStart + ".");
|
||||
}
|
||||
int stateEnd = blockAndExtraData[0].lastIndexOf(']');
|
||||
if (stateEnd < 0) {
|
||||
throw new InputParseException("Invalid format. Unclosed property.");
|
||||
}
|
||||
stateString = blockAndExtraData[0].substring(stateStart + 1, blockAndExtraData[0].length() - 1);
|
||||
}
|
||||
if (typeString.isEmpty()) {
|
||||
@ -288,6 +366,10 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// this should be impossible but IntelliJ isn't that smart
|
||||
if (blockType == null) {
|
||||
throw new NoMatchException("Does not match a valid block type: '" + input + "'");
|
||||
}
|
||||
|
||||
if (blockAndExtraData.length > 1 && blockAndExtraData[1].startsWith("{")) {
|
||||
String joined = StringMan.join(Arrays.copyOfRange(blockAndExtraData, 1, blockAndExtraData.length), "|");
|
||||
@ -322,6 +404,7 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mobName = ent.getId();
|
||||
if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) {
|
||||
String finalMobName = mobName.toLowerCase(Locale.ROOT);
|
||||
throw new SuggestInputParseException("Unknown mob type '" + mobName + "'", mobName, () -> Stream.of(MobType.values())
|
||||
|
@ -28,7 +28,6 @@ import com.sk89q.worldedit.function.mask.BiomeMask2D;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Masks;
|
||||
import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
import com.sk89q.worldedit.session.request.RequestExtent;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -26,7 +26,8 @@ import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import com.sk89q.worldedit.function.mask.BlockStateMask;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
import com.sk89q.worldedit.session.request.RequestExtent;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -25,7 +25,6 @@ import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.internal.registry.SimpleInputParser;
|
||||
import com.sk89q.worldedit.session.request.RequestExtent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -29,7 +29,8 @@ import com.sk89q.worldedit.function.mask.Masks;
|
||||
import com.sk89q.worldedit.function.mask.OffsetMask;
|
||||
import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.session.request.RequestExtent;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -25,7 +25,6 @@ import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||
import com.sk89q.worldedit.internal.registry.SimpleInputParser;
|
||||
import com.sk89q.worldedit.session.request.RequestExtent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -30,6 +30,7 @@ import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.function.pattern.StateApplyingPattern;
|
||||
import com.sk89q.worldedit.function.pattern.TypeApplyingPattern;
|
||||
import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -19,45 +19,12 @@
|
||||
|
||||
package com.sk89q.worldedit.extension.platform;
|
||||
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.boydti.fawe.object.task.SimpleAsyncNotifyQueue;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.internal.cui.CUIEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public abstract class AbstractNonPlayerActor implements Actor {
|
||||
|
||||
private final ConcurrentHashMap<String, Object> meta = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getRawMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
// Queue for async tasks
|
||||
private AtomicInteger runningCount = new AtomicInteger();
|
||||
private SimpleAsyncNotifyQueue asyncNotifyQueue = new SimpleAsyncNotifyQueue(
|
||||
(thread, throwable) -> {
|
||||
while (throwable.getCause() != null) {
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
if (throwable instanceof WorldEditException) {
|
||||
printError(throwable.getLocalizedMessage());
|
||||
} else {
|
||||
FaweException fe = FaweException.get(throwable);
|
||||
if (fe != null) {
|
||||
printError(fe.getMessage());
|
||||
} else {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public boolean canDestroyBedrock() {
|
||||
return true;
|
||||
@ -81,35 +48,4 @@ public abstract class AbstractNonPlayerActor implements Actor {
|
||||
@Override
|
||||
public void dispatchCUIEvent(CUIEvent event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a task either async, or on the current thread
|
||||
*
|
||||
* @param ifFree
|
||||
* @param checkFree Whether to first check if a task is running
|
||||
* @param async
|
||||
* @return false if the task was ran or queued
|
||||
*/
|
||||
@Override
|
||||
public boolean runAction(Runnable ifFree, boolean checkFree, boolean async) {
|
||||
if (checkFree) {
|
||||
if (runningCount.get() != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Runnable wrapped = () -> {
|
||||
try {
|
||||
runningCount.addAndGet(1);
|
||||
ifFree.run();
|
||||
} finally {
|
||||
runningCount.decrementAndGet();
|
||||
}
|
||||
};
|
||||
if (async) {
|
||||
asyncNotifyQueue.queue(wrapped);
|
||||
} else {
|
||||
TaskManager.IMP.taskNow(wrapped, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,14 @@
|
||||
|
||||
package com.sk89q.worldedit.extension.platform;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.boydti.fawe.object.task.SimpleAsyncNotifyQueue;
|
||||
import com.boydti.fawe.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.boydti.fawe.util.WEManager;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
@ -45,6 +46,8 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.regions.RegionSelector;
|
||||
import com.sk89q.worldedit.regions.selector.ConvexPolyhedralRegionSelector;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
||||
import com.sk89q.worldedit.regions.selector.CylinderRegionSelector;
|
||||
import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector;
|
||||
@ -70,7 +73,6 @@ import java.text.NumberFormat;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.annotation.Nullable;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -249,7 +251,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
||||
if (!lastState) {
|
||||
lastState = BlockTypeUtil.centralBottomLimit(state) != 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (freeStart == -1) {
|
||||
freeStart = level + BlockTypeUtil.centralTopLimit(state);
|
||||
} else {
|
||||
@ -257,13 +259,13 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
||||
double space = level + bottomLimit - freeStart;
|
||||
if (space >= height) {
|
||||
setPosition(Vector3.at(x + 0.5, freeStart, z + 0.5));
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Not enough room, reset the free position
|
||||
if (bottomLimit != 1) {
|
||||
freeStart = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
freeStart = -1;
|
||||
lastState = true;
|
||||
@ -414,7 +416,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
||||
session.setBlock(spot, BlockTypes.GLASS.getDefaultState());
|
||||
} catch (MaxChangedBlocksException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setFlying(true);
|
||||
}
|
||||
@ -477,6 +479,23 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
||||
return getBlockTrace(range, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the block target block until the current block is a free
|
||||
* @return true if a free spot is found
|
||||
*/
|
||||
private boolean advanceToFree(TargetBlock hitBlox) {
|
||||
Location curBlock;
|
||||
while ((curBlock = hitBlox.getCurrentBlock()) != null) {
|
||||
if (canPassThroughBlock(curBlock)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hitBlox.getNextBlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getSolidBlockTrace(int range) {
|
||||
TargetBlock tb = new TargetBlock(this, range, 0.2);
|
||||
@ -541,33 +560,17 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Advances the block target block until the current block is a free
|
||||
* @return true if a free spot is found
|
||||
*/
|
||||
private boolean advanceToFree(TargetBlock hitBlox) {
|
||||
Location curBlock;
|
||||
while ((curBlock = hitBlox.getCurrentBlock()) != null) {
|
||||
if (canPassThroughBlock(curBlock)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hitBlox.getNextBlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passThroughForwardWall(int range) {
|
||||
TargetBlock hitBlox = new TargetBlock(this, range, 0.2);
|
||||
|
||||
if (!advanceToWall(hitBlox)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!advanceToFree(hitBlox)) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Location foundBlock = hitBlox.getCurrentBlock();
|
||||
@ -576,7 +579,6 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,11 @@
|
||||
|
||||
package com.sk89q.worldedit.extension.platform;
|
||||
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.registry.BlockRegistry;
|
||||
|
||||
/**
|
||||
* A collection of capabilities that a {@link Platform} may support.
|
||||
*/
|
||||
@ -73,7 +78,22 @@ public enum Capability {
|
||||
/**
|
||||
* The capability of a platform to perform modifications to a world.
|
||||
*/
|
||||
WORLD_EDITING;
|
||||
WORLD_EDITING {
|
||||
@Override
|
||||
void initialize(PlatformManager platformManager, Platform platform) {
|
||||
BlockRegistry blockRegistry = platform.getRegistries().getBlockRegistry();
|
||||
for (BlockType type : BlockType.REGISTRY) {
|
||||
for (BlockState state : type.getAllStates()) {
|
||||
BlockStateIdAccess.register(state, blockRegistry.getInternalBlockStateId(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void unload(PlatformManager platformManager, Platform platform) {
|
||||
BlockStateIdAccess.clear();
|
||||
}
|
||||
};
|
||||
|
||||
void initialize(PlatformManager platformManager, Platform platform) {
|
||||
|
||||
|
@ -321,45 +321,45 @@ public class PlatformManager {
|
||||
if (!(actor instanceof Player)) {
|
||||
return;
|
||||
}
|
||||
Player player = (Player) actor;
|
||||
LocalSession session = worldEdit.getSessionManager().get(actor);
|
||||
Player player = (Player) actor;
|
||||
LocalSession session = worldEdit.getSessionManager().get(actor);
|
||||
|
||||
Request.reset();
|
||||
Request.request().setSession(session);
|
||||
Request.request().setWorld(player.getWorld());
|
||||
Request.reset();
|
||||
Request.request().setSession(session);
|
||||
Request.request().setWorld(player.getWorld());
|
||||
|
||||
try {
|
||||
try {
|
||||
Vector3 vector = location.toVector();
|
||||
|
||||
VirtualWorld virtual = session.getVirtualWorld();
|
||||
if (virtual != null) {
|
||||
virtual.handleBlockInteract(player, vector.toBlockPoint(), event);
|
||||
if (event.isCancelled()) return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getType() == Interaction.HIT) {
|
||||
// superpickaxe is special because its primary interaction is a left click, not a right click
|
||||
// in addition, it is implicitly bound to all pickaxe items, not just a single tool item
|
||||
if (session.hasSuperPickAxe() && player.isHoldingPickAxe()) {
|
||||
final BlockTool superPickaxe = session.getSuperPickaxe();
|
||||
if (superPickaxe != null && superPickaxe.canUse(player)) {
|
||||
final BlockTool superPickaxe = session.getSuperPickaxe();
|
||||
if (superPickaxe != null && superPickaxe.canUse(player)) {
|
||||
player.runAction(() -> reset(superPickaxe)
|
||||
.actPrimary(queryCapability(Capability.WORLD_EDITING),
|
||||
getConfiguration(), player, session, location), false, true);
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tool tool = session.getTool(player);
|
||||
if (tool instanceof DoubleActionBlockTool && tool.canUse(player)) {
|
||||
player.runAction(() -> reset(((DoubleActionBlockTool) tool))
|
||||
.actSecondary(queryCapability(Capability.WORLD_EDITING),
|
||||
getConfiguration(), player, session, location), false, true);
|
||||
event.setCancelled(true);
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
} else if (event.getType() == Interaction.OPEN) {
|
||||
} else if (event.getType() == Interaction.OPEN) {
|
||||
Tool tool = session.getTool(player);
|
||||
if (tool instanceof BlockTool && tool.canUse(player)) {
|
||||
if (player.checkAction()) {
|
||||
@ -367,20 +367,20 @@ public class PlatformManager {
|
||||
BlockTool blockTool = (BlockTool) tool;
|
||||
if (!(tool instanceof BrushTool)) {
|
||||
blockTool = reset(blockTool);
|
||||
}
|
||||
}
|
||||
blockTool.actPrimary(queryCapability(Capability.WORLD_EDITING),
|
||||
getConfiguration(), player, session, location);
|
||||
}, false, true);
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
handleThrowable(e, actor);
|
||||
} finally {
|
||||
Request.reset();
|
||||
} finally {
|
||||
Request.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleThrowable(Throwable e, Actor actor) {
|
||||
FaweException faweException = FaweException.get(e);
|
||||
@ -409,53 +409,29 @@ public class PlatformManager {
|
||||
try {
|
||||
switch (event.getInputType()) {
|
||||
case PRIMARY: {
|
||||
if (getConfiguration().navigationWandMaxDistance > 0 && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().navigationWand)) {
|
||||
if (!player.hasPermission("worldedit.navigation.jumpto.tool")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location pos = player.getSolidBlockTrace(getConfiguration().navigationWandMaxDistance);
|
||||
if (pos != null) {
|
||||
player.findFreePosition(pos);
|
||||
} else {
|
||||
player.printError(BBC.NO_BLOCK.s());
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
Tool tool = session.getTool(player);
|
||||
if (tool instanceof DoubleActionTraceTool && tool.canUse(player)) {
|
||||
player.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING),
|
||||
getConfiguration(), player, session));
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SECONDARY: {
|
||||
if (getConfiguration().navigationWandMaxDistance > 0 && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().navigationWand)) {
|
||||
if (!player.hasPermission("worldedit.navigation.thru.tool")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.passThroughForwardWall(40)) {
|
||||
player.printError(BBC.NAVIGATION_WAND_ERROR.s());
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
Tool tool = session.getTool(player);
|
||||
if (tool instanceof TraceTool && tool.canUse(player)) {
|
||||
//todo this needs to be fixed so the event is canceled after actPrimary is used and returns true
|
||||
player.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING),
|
||||
getConfiguration(), player, session), false, true);
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -221,4 +221,9 @@ public class PlayerProxy extends AbstractPlayerActor {
|
||||
public Player getBasePlayer() {
|
||||
return basePlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void floatAt(int x, int y, int z, boolean alwaysGlass) {
|
||||
basePlayer.floatAt(x, y, z, alwaysGlass);
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ public interface Extent extends InputExtent, OutputExtent {
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
*/
|
||||
default <B extends BlockStateHolder<B>> int replaceBlocks(Region region, Set<BaseBlock> filter, B replacement) throws MaxChangedBlocksException {
|
||||
return replaceBlocks(region, filter, new BlockPattern(replacement));
|
||||
return replaceBlocks(region, filter, (Pattern) replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,6 @@ import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Masks;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -74,26 +73,6 @@ public class ExtentBuffer extends AbstractBufferingExtent {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(BlockVector3 position) {
|
||||
if (mask.test(position)) {
|
||||
return getOrDefault(position).toImmutableState();
|
||||
}
|
||||
return super.getBlock(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(BlockVector3 position) {
|
||||
if (mask.test(position)) {
|
||||
return getOrDefault(position);
|
||||
}
|
||||
return super.getFullBlock(position);
|
||||
}
|
||||
|
||||
private BaseBlock getOrDefault(BlockVector3 position) {
|
||||
return buffer.computeIfAbsent(position, (pos -> getExtent().getFullBlock(pos)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 location, T block) throws WorldEditException {
|
||||
if (mask.test(location)) {
|
||||
|
@ -31,12 +31,12 @@ import com.sk89q.worldedit.function.pattern.BiomePattern;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.AbstractRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.regions.AbstractFlatRegion;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import java.util.Iterator;
|
||||
@ -84,7 +84,6 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
|
||||
*/
|
||||
public ForgetfulExtentBuffer(Extent delegate, Mask mask) {
|
||||
super(delegate);
|
||||
checkNotNull(delegate);
|
||||
checkNotNull(mask);
|
||||
this.mask = mask;
|
||||
Mask2D bmask = mask.toMask2D();
|
||||
@ -189,7 +188,7 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
|
||||
* @return a region
|
||||
*/
|
||||
public Region asRegion() {
|
||||
return new AbstractRegion(null) {
|
||||
return new AbstractFlatRegion(null) {
|
||||
@Override
|
||||
public BlockVector3 getMinimumPoint() {
|
||||
return min != null ? min : BlockVector3.ZERO;
|
||||
@ -220,6 +219,7 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
|
||||
return buffer.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<BlockVector2> asFlatRegion() {
|
||||
return biomeBuffer.keySet();
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -44,6 +45,15 @@ public interface ClipboardReader extends Closeable {
|
||||
return read(UUID.randomUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DataVersion from a file (if possible).
|
||||
*
|
||||
* @return The data version, or empty
|
||||
*/
|
||||
default OptionalInt getDataVersion() {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
default Clipboard read(UUID uuid) throws IOException {
|
||||
return read(uuid, DiskOptimizedClipboard::new);
|
||||
}
|
||||
|
@ -19,8 +19,9 @@
|
||||
|
||||
package com.sk89q.worldedit.extent.clipboard.io;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.sk89q.jnbt.ByteArrayTag;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.IntTag;
|
||||
@ -59,6 +60,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Reads schematic files that are compatible with MCEdit and other editors.
|
||||
@ -176,7 +179,8 @@ public class MCEditSchematicReader extends NBTSchematicReader {
|
||||
}
|
||||
|
||||
// Need to pull out tile entities
|
||||
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
|
||||
final ListTag tileEntityTag = getTag(schematic, "TileEntities", ListTag.class);
|
||||
List<Tag> tileEntities = tileEntityTag == null ? new ArrayList<>() : tileEntityTag.getValue();
|
||||
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
|
||||
Map<BlockVector3, BlockState> blockStates = new HashMap<>();
|
||||
|
||||
@ -206,6 +210,11 @@ public class MCEditSchematicReader extends NBTSchematicReader {
|
||||
if (values.isEmpty()) {
|
||||
t = null;
|
||||
}
|
||||
if (values.isEmpty()) {
|
||||
t = null;
|
||||
} else {
|
||||
t = new CompoundTag(values);
|
||||
}
|
||||
|
||||
if (fixer != null && t != null) {
|
||||
t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1);
|
||||
@ -221,9 +230,8 @@ public class MCEditSchematicReader extends NBTSchematicReader {
|
||||
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
|
||||
clipboard.setOrigin(origin);
|
||||
|
||||
// Don't log a torrent of errors
|
||||
int failedBlockSets = 0;
|
||||
|
||||
Set<Integer> unknownBlocks = new HashSet<>();
|
||||
for (int x = 0; x < width; ++x) {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int z = 0; z < length; ++z) {
|
||||
@ -242,18 +250,7 @@ public class MCEditSchematicReader extends NBTSchematicReader {
|
||||
log.warn("Unknown block when pasting schematic: "
|
||||
+ blocks[index] + ":" + blockData[index] + ". Please report this issue.");
|
||||
}
|
||||
} catch (WorldEditException e) {
|
||||
switch (failedBlockSets) {
|
||||
case 0:
|
||||
log.warn("Failed to set block on a Clipboard", e);
|
||||
break;
|
||||
case 1:
|
||||
log.warn("Failed to set block on a Clipboard (again) -- no more messages will be logged", e);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
failedBlockSets++;
|
||||
} catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.entity.EntityTypes;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
import org.slf4j.Logger;
|
||||
@ -63,7 +65,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -72,6 +73,8 @@ import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.sk89q.worldedit.world.storage.NBTConversions;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
/**
|
||||
* Reads schematic files using the Sponge Schematic Specification.
|
||||
@ -96,6 +99,7 @@ public class SpongeSchematicReader extends NBTSchematicReader {
|
||||
private int offsetX, offsetY, offsetZ;
|
||||
private char[] palette, biomePalette;
|
||||
private BlockVector3 min = BlockVector3.ZERO;
|
||||
private int schematicVersion = -1;
|
||||
|
||||
|
||||
/**
|
||||
@ -210,7 +214,58 @@ public class SpongeSchematicReader extends NBTSchematicReader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard read(UUID uuid, Function<BlockVector3, Clipboard> createOutput) throws IOException {
|
||||
public Clipboard read() throws IOException {
|
||||
CompoundTag schematicTag = getBaseTag();
|
||||
Map<String, Tag> schematic = schematicTag.getValue();
|
||||
|
||||
final Platform platform = WorldEdit.getInstance().getPlatformManager()
|
||||
.queryCapability(Capability.WORLD_EDITING);
|
||||
int liveDataVersion = platform.getDataVersion();
|
||||
|
||||
if (schematicVersion == 1) {
|
||||
dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1-
|
||||
fixer = platform.getDataFixer();
|
||||
return readVersion1(schematicTag);
|
||||
} else if (schematicVersion == 2) {
|
||||
dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue();
|
||||
if (dataVersion > liveDataVersion) {
|
||||
log.warn("Schematic was made in a newer Minecraft version ({} > {}). Data may be incompatible.",
|
||||
dataVersion, liveDataVersion);
|
||||
} else if (dataVersion < liveDataVersion) {
|
||||
fixer = platform.getDataFixer();
|
||||
if (fixer != null) {
|
||||
log.debug("Schematic was made in an older Minecraft version ({} < {}), will attempt DFU.",
|
||||
dataVersion, liveDataVersion);
|
||||
} else {
|
||||
log.info("Schematic was made in an older Minecraft version ({} < {}), but DFU is not available. Data may be incompatible.",
|
||||
dataVersion, liveDataVersion);
|
||||
}
|
||||
}
|
||||
|
||||
BlockArrayClipboard clip = readVersion1(schematicTag);
|
||||
return readVersion2(clip, schematicTag);
|
||||
}
|
||||
throw new IOException("This schematic version is currently not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt getDataVersion() {
|
||||
try {
|
||||
CompoundTag schematicTag = getBaseTag();
|
||||
Map<String, Tag> schematic = schematicTag.getValue();
|
||||
if (schematicVersion == 1) {
|
||||
return OptionalInt.of(1631);
|
||||
} else if (schematicVersion == 2) {
|
||||
return OptionalInt.of(requireTag(schematic, "DataVersion", IntTag.class).getValue());
|
||||
}
|
||||
return OptionalInt.empty();
|
||||
} catch (IOException e) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getBaseTag(UUID uuid, Function<BlockVector3, Clipboard> createOutput) throws IOException {
|
||||
StreamDelegate root = createDelegate();
|
||||
inputStream.readNamedTagLazy(root);
|
||||
if (blocks != null) blocks.close();
|
||||
@ -349,6 +404,106 @@ public class SpongeSchematicReader extends NBTSchematicReader {
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException {
|
||||
Map<String, Tag> schematic = schematicTag.getValue();
|
||||
if (schematic.containsKey("BiomeData")) {
|
||||
readBiomes(version1, schematic);
|
||||
}
|
||||
if (schematic.containsKey("Entities")) {
|
||||
readEntities(version1, schematic);
|
||||
}
|
||||
return version1;
|
||||
}
|
||||
|
||||
private void readBiomes(BlockArrayClipboard clipboard, Map<String, Tag> schematic) throws IOException {
|
||||
ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class);
|
||||
IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class);
|
||||
CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class);
|
||||
|
||||
Map<Integer, BiomeType> palette = new HashMap<>();
|
||||
if (maxTag.getValue() != paletteTag.getValue().size()) {
|
||||
throw new IOException("Biome palette size does not match expected size.");
|
||||
}
|
||||
|
||||
for (Entry<String, Tag> palettePart : paletteTag.getValue().entrySet()) {
|
||||
String key = palettePart.getKey();
|
||||
if (fixer != null) {
|
||||
key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion);
|
||||
}
|
||||
BiomeType biome = BiomeTypes.get(key);
|
||||
if (biome == null) {
|
||||
log.warn("Unknown biome type :" + key +
|
||||
" in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?");
|
||||
}
|
||||
Tag idTag = palettePart.getValue();
|
||||
if (!(idTag instanceof IntTag)) {
|
||||
throw new IOException("Biome mapped to non-Int tag.");
|
||||
}
|
||||
palette.put(((IntTag) idTag).getValue(), biome);
|
||||
}
|
||||
|
||||
int width = clipboard.getDimensions().getX();
|
||||
|
||||
byte[] biomes = dataTag.getValue();
|
||||
int biomeIndex = 0;
|
||||
int biomeJ = 0;
|
||||
int bVal;
|
||||
int varIntLength;
|
||||
BlockVector2 min = clipboard.getMinimumPoint().toBlockVector2();
|
||||
while (biomeJ < biomes.length) {
|
||||
bVal = 0;
|
||||
varIntLength = 0;
|
||||
|
||||
while (true) {
|
||||
bVal |= (biomes[biomeJ] & 127) << (varIntLength++ * 7);
|
||||
if (varIntLength > 5) {
|
||||
throw new IOException("VarInt too big (probably corrupted data)");
|
||||
}
|
||||
if (((biomes[biomeJ] & 128) != 128)) {
|
||||
biomeJ++;
|
||||
break;
|
||||
}
|
||||
biomeJ++;
|
||||
}
|
||||
int z = biomeIndex / width;
|
||||
int x = biomeIndex % width;
|
||||
BiomeType type = palette.get(bVal);
|
||||
clipboard.setBiome(min.add(x, z), type);
|
||||
biomeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void readEntities(BlockArrayClipboard clipboard, Map<String, Tag> schematic) throws IOException {
|
||||
List<Tag> entList = requireTag(schematic, "Entities", ListTag.class).getValue();
|
||||
if (entList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Tag et : entList) {
|
||||
if (!(et instanceof CompoundTag)) {
|
||||
continue;
|
||||
}
|
||||
CompoundTag entityTag = (CompoundTag) et;
|
||||
Map<String, Tag> tags = entityTag.getValue();
|
||||
String id = requireTag(tags, "Id", StringTag.class).getValue();
|
||||
entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build();
|
||||
|
||||
if (fixer != null) {
|
||||
entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion);
|
||||
}
|
||||
|
||||
EntityType entityType = EntityTypes.get(id);
|
||||
if (entityType != null) {
|
||||
Location location = NBTConversions.toLocation(clipboard,
|
||||
requireTag(tags, "Pos", ListTag.class),
|
||||
requireTag(tags, "Rotation", ListTag.class));
|
||||
BaseEntity state = new BaseEntity(entityType, entityTag);
|
||||
clipboard.createEntity(location, state);
|
||||
} else {
|
||||
log.warn("Unknown entity when pasting schematic: " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
inputStream.close();
|
||||
|
@ -20,13 +20,16 @@
|
||||
package com.sk89q.worldedit.extent.clipboard.io;
|
||||
|
||||
import com.boydti.fawe.jnbt.streamer.IntValueReader;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.boydti.fawe.object.FaweOutputStream;
|
||||
import com.boydti.fawe.object.clipboard.LinearClipboard;
|
||||
import com.boydti.fawe.util.IOUtil;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.IntArrayTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.FloatTag;
|
||||
import com.sk89q.jnbt.DoubleTag;
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.jnbt.NBTOutputStream;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
@ -61,7 +64,9 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import java.util.Objects;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -334,6 +339,90 @@ public class SpongeSchematicWriter implements ClipboardWriter {
|
||||
schematic.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities));
|
||||
}
|
||||
|
||||
private void writeBiomes(Clipboard clipboard, Map<String, Tag> schematic) {
|
||||
BlockVector3 min = clipboard.getMinimumPoint();
|
||||
int width = clipboard.getRegion().getWidth();
|
||||
int length = clipboard.getRegion().getLength();
|
||||
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * length);
|
||||
|
||||
int paletteMax = 0;
|
||||
Map<String, Integer> palette = new HashMap<>();
|
||||
|
||||
for (int z = 0; z < length; z++) {
|
||||
int z0 = min.getBlockZ() + z;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int x0 = min.getBlockX() + x;
|
||||
BlockVector2 pt = BlockVector2.at(x0, z0);
|
||||
BiomeType biome = clipboard.getBiome(pt);
|
||||
|
||||
String biomeKey = biome.getId();
|
||||
int biomeId;
|
||||
if (palette.containsKey(biomeKey)) {
|
||||
biomeId = palette.get(biomeKey);
|
||||
} else {
|
||||
biomeId = paletteMax;
|
||||
palette.put(biomeKey, biomeId);
|
||||
paletteMax++;
|
||||
}
|
||||
|
||||
while ((biomeId & -128) != 0) {
|
||||
buffer.write(biomeId & 127 | 128);
|
||||
biomeId >>>= 7;
|
||||
}
|
||||
buffer.write(biomeId);
|
||||
}
|
||||
}
|
||||
|
||||
schematic.put("BiomePaletteMax", new IntTag(paletteMax));
|
||||
|
||||
Map<String, Tag> paletteTag = new HashMap<>();
|
||||
palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value)));
|
||||
|
||||
schematic.put("BiomePalette", new CompoundTag(paletteTag));
|
||||
schematic.put("BiomeData", new ByteArrayTag(buffer.toByteArray()));
|
||||
}
|
||||
|
||||
private void writeEntities(Clipboard clipboard, Map<String, Tag> schematic) {
|
||||
List<CompoundTag> entities = clipboard.getEntities().stream().map(e -> {
|
||||
BaseEntity state = e.getState();
|
||||
if (state == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Tag> values = Maps.newHashMap();
|
||||
CompoundTag rawData = state.getNbtData();
|
||||
if (rawData != null) {
|
||||
values.putAll(rawData.getValue());
|
||||
}
|
||||
values.remove("id");
|
||||
values.put("Id", new StringTag(state.getType().getId()));
|
||||
final Location location = e.getLocation();
|
||||
values.put("Pos", writeVector(location.toVector()));
|
||||
values.put("Rotation", writeRotation(location));
|
||||
|
||||
return new CompoundTag(values);
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
if (entities.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
schematic.put("Entities", new ListTag(CompoundTag.class, entities));
|
||||
}
|
||||
|
||||
private Tag writeVector(Vector3 vector) {
|
||||
List<DoubleTag> list = new ArrayList<>();
|
||||
list.add(new DoubleTag(vector.getX()));
|
||||
list.add(new DoubleTag(vector.getY()));
|
||||
list.add(new DoubleTag(vector.getZ()));
|
||||
return new ListTag(DoubleTag.class, list);
|
||||
}
|
||||
|
||||
private Tag writeRotation(Location location) {
|
||||
List<FloatTag> list = new ArrayList<>();
|
||||
list.add(new FloatTag(location.getYaw()));
|
||||
list.add(new FloatTag(location.getPitch()));
|
||||
return new ListTag(FloatTag.class, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
outputStream.close();
|
||||
|
@ -25,7 +25,6 @@ import com.sk89q.worldedit.extent.AbstractBufferingExtent;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.operation.Operation;
|
||||
import com.sk89q.worldedit.function.operation.RunContext;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.math.RegionOptimizedComparator;
|
||||
import com.sk89q.worldedit.util.collection.BlockMap;
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.extent.transform;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.util.Direction.ASCENDING_EAST;
|
||||
import static com.sk89q.worldedit.util.Direction.ASCENDING_NORTH;
|
||||
import static com.sk89q.worldedit.util.Direction.ASCENDING_SOUTH;
|
||||
@ -69,8 +67,11 @@ import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import java.util.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Transforms blocks themselves (but not their position) according to a
|
||||
* given transform.
|
||||
|
@ -24,8 +24,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
|
||||
/**
|
||||
@ -67,4 +69,12 @@ public class ChunkLoadingExtent extends AbstractDelegateExtent {
|
||||
}
|
||||
return super.setBlock(location, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
||||
if (enabled) {
|
||||
world.checkLoadedChunk(position.toBlockVector3());
|
||||
}
|
||||
return super.setBiome(position, biome);
|
||||
}
|
||||
}
|
||||
|
@ -59,23 +59,23 @@ public class ForestGenerator implements RegionFunction {
|
||||
case BlockID.DIRT:
|
||||
case BlockID.PODZOL:
|
||||
case BlockID.COARSE_DIRT:
|
||||
return treeType.generate(editSession, position.add(0, 1, 0));
|
||||
return treeType.generate(editSession, position.add(0, 1, 0));
|
||||
default:
|
||||
if (t.getMaterial().isReplacedDuringPlacement()) {
|
||||
// since the implementation's tree generators generally don't generate in non-air spots,
|
||||
// we trick editsession history here in the first call
|
||||
editSession.setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// and then trick the generator here by directly setting into the world
|
||||
editSession.getWorld().setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// so that now the generator can generate the tree
|
||||
boolean success = treeType.generate(editSession, position);
|
||||
if (!success) {
|
||||
editSession.setBlock(position, block); // restore on failure
|
||||
}
|
||||
return success;
|
||||
} else { // Trees won't grow on this!
|
||||
return false;
|
||||
if (t.getMaterial().isReplacedDuringPlacement()) {
|
||||
// since the implementation's tree generators generally don't generate in non-air spots,
|
||||
// we trick editsession history here in the first call
|
||||
editSession.setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// and then trick the generator here by directly setting into the world
|
||||
editSession.getWorld().setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// so that now the generator can generate the tree
|
||||
boolean success = treeType.generate(editSession, position);
|
||||
if (!success) {
|
||||
editSession.setBlock(position, block); // restore on failure
|
||||
}
|
||||
return success;
|
||||
} else { // Trees won't grow on this!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import com.sk89q.worldedit.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
|
||||
import java.util.Random;
|
||||
@ -39,6 +38,7 @@ public class GardenPatchGenerator implements RegionFunction {
|
||||
private final Random random = new Random();
|
||||
private final EditSession editSession;
|
||||
private Pattern plant = getPumpkinPattern();
|
||||
private Pattern leafPattern = BlockTypes.OAK_LEAVES.getDefaultState().with(BlockTypes.OAK_LEAVES.getProperty("persistent"), true);
|
||||
private int affected;
|
||||
|
||||
/**
|
||||
@ -96,7 +96,7 @@ public class GardenPatchGenerator implements RegionFunction {
|
||||
}
|
||||
}
|
||||
|
||||
setBlockIfAir(editSession, pos, BlockTypes.OAK_LEAVES.getDefaultState());
|
||||
setBlockIfAir(editSession, pos, leafPattern);
|
||||
affected++;
|
||||
|
||||
int t = random.nextInt(4);
|
||||
@ -166,10 +166,9 @@ public class GardenPatchGenerator implements RegionFunction {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState leavesBlock = BlockTypes.OAK_LEAVES.getDefaultState();
|
||||
|
||||
if (editSession.getBlock(position).getBlockType().getMaterial().isAir()) {
|
||||
editSession.setBlock(position, leavesBlock);
|
||||
editSession.setBlock(position, leafPattern);
|
||||
}
|
||||
|
||||
placeVine(position, position.add(0, 0, 1));
|
||||
@ -193,12 +192,12 @@ public class GardenPatchGenerator implements RegionFunction {
|
||||
* Set a block only if there's no block already there.
|
||||
*
|
||||
* @param position the position
|
||||
* @param block the block to set
|
||||
* @param pattern the pattern to set
|
||||
* @return if block was changed
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
*/
|
||||
private static <B extends BlockStateHolder<B>> boolean setBlockIfAir(EditSession session, BlockVector3 position, B block) throws MaxChangedBlocksException {
|
||||
return session.getBlock(position).getBlockType().getMaterial().isAir() && session.setBlock(position, block);
|
||||
private static boolean setBlockIfAir(EditSession session, BlockVector3 position, Pattern pattern) throws MaxChangedBlocksException {
|
||||
return session.getBlock(position).getBlockType().getMaterial().isAir() && session.setBlock(position, pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,8 +22,8 @@ package com.sk89q.worldedit.function.mask;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.Expression;
|
||||
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
|
||||
|
||||
|
@ -22,8 +22,8 @@ package com.sk89q.worldedit.function.mask;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.Expression;
|
||||
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import java.util.function.IntSupplier;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -36,16 +36,21 @@ import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.function.CombinedRegionFunction;
|
||||
import com.sk89q.worldedit.function.FlatRegionFunction;
|
||||
import com.sk89q.worldedit.function.FlatRegionMaskingFilter;
|
||||
import com.sk89q.worldedit.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.RegionMaskTestFunction;
|
||||
import com.sk89q.worldedit.function.biome.ExtentBiomeCopy;
|
||||
import com.sk89q.worldedit.function.RegionMaskingFilter;
|
||||
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Masks;
|
||||
import com.sk89q.worldedit.function.mask.Mask2D;
|
||||
import com.sk89q.worldedit.function.visitor.EntityVisitor;
|
||||
import com.sk89q.worldedit.function.visitor.IntersectRegionFunction;
|
||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.function.visitor.FlatRegionVisitor;
|
||||
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||
import com.sk89q.worldedit.math.transform.Identity;
|
||||
import com.sk89q.worldedit.math.transform.Transform;
|
||||
@ -78,7 +83,7 @@ public class ForwardExtentCopy implements Operation {
|
||||
private RegionFunction sourceFunction = null;
|
||||
private Transform transform = new Identity();
|
||||
private Transform currentTransform = null;
|
||||
private int affected;
|
||||
private int affectedBlocks;
|
||||
private RegionFunction filterFunction;
|
||||
|
||||
/**
|
||||
@ -94,6 +99,10 @@ public class ForwardExtentCopy implements Operation {
|
||||
public ForwardExtentCopy(Extent source, Region region, Extent destination, BlockVector3 to) {
|
||||
this(source, region, region.getMinimumPoint(), destination, to);
|
||||
}
|
||||
private FlatRegionVisitor lastBiomeVisitor;
|
||||
private EntityVisitor lastEntityVisitor;
|
||||
private int affectedBiomeCols;
|
||||
private int affectedEntities;
|
||||
|
||||
/**
|
||||
* Create a new copy.
|
||||
@ -267,7 +276,7 @@ public class ForwardExtentCopy implements Operation {
|
||||
* @return the number of affected
|
||||
*/
|
||||
public int getAffected() {
|
||||
return affected;
|
||||
return affectedBlocks + affectedBiomeCols + affectedEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -275,6 +284,14 @@ public class ForwardExtentCopy implements Operation {
|
||||
if (currentTransform == null) {
|
||||
currentTransform = transform;
|
||||
}
|
||||
if (lastBiomeVisitor != null) {
|
||||
affectedBiomeCols += lastBiomeVisitor.getAffected();
|
||||
lastBiomeVisitor = null;
|
||||
}
|
||||
if (lastEntityVisitor != null) {
|
||||
affectedEntities += lastEntityVisitor.getAffected();
|
||||
lastEntityVisitor = null;
|
||||
}
|
||||
|
||||
Extent finalDest = destination;
|
||||
BlockVector3 translation = to.subtract(from);
|
||||
@ -405,7 +422,21 @@ public class ForwardExtentCopy implements Operation {
|
||||
@Override
|
||||
public void addStatusMessages(List<String> messages) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append(affected).append(" objects(s)");
|
||||
msg.append(affectedBlocks).append(" block(s)");
|
||||
if (affectedBiomeCols > 0) {
|
||||
if (affectedEntities > 0) {
|
||||
msg.append(", ");
|
||||
} else {
|
||||
msg.append(" and ");
|
||||
}
|
||||
msg.append(affectedBiomeCols).append(" biome(s)");
|
||||
}
|
||||
if (affectedEntities > 0) {
|
||||
if (affectedBiomeCols > 0) {
|
||||
msg.append(",");
|
||||
}
|
||||
msg.append(" and ").append(affectedEntities).append(" entities(s)");
|
||||
}
|
||||
msg.append(" affected.");
|
||||
messages.add(msg.toString());
|
||||
}
|
||||
|
@ -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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies a range of values for numbers.
|
||||
*
|
||||
* @see PrimitiveBindings a user of this annotation as a modifier
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Range {
|
||||
|
||||
/**
|
||||
* The minimum value that the number can be at, inclusive.
|
||||
*
|
||||
* @return the minimum value
|
||||
*/
|
||||
double min() default Double.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* The maximum value that the number can be at, inclusive.
|
||||
*
|
||||
* @return the maximum value
|
||||
*/
|
||||
double max() default Double.MAX_VALUE;
|
||||
|
||||
}
|
@ -1,45 +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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Used to validate a string.
|
||||
*
|
||||
* @see PrimitiveBindings where this validation is used
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
public @interface Validate {
|
||||
|
||||
/**
|
||||
* An optional regular expression that must match the string.
|
||||
*
|
||||
* @see Pattern regular expression class
|
||||
* @return the pattern
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.command.exception;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.worldedit.DisallowedItemException;
|
||||
import com.sk89q.worldedit.EmptyClipboardException;
|
||||
@ -29,6 +27,7 @@ import com.sk89q.worldedit.InvalidItemException;
|
||||
import com.sk89q.worldedit.MaxBrushRadiusException;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.MaxRadiusException;
|
||||
import com.sk89q.worldedit.MissingWorldException;
|
||||
import com.sk89q.worldedit.UnknownDirectionException;
|
||||
import com.sk89q.worldedit.UnknownItemException;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
@ -47,6 +46,8 @@ import java.util.regex.Pattern;
|
||||
import org.enginehub.piston.exception.CommandException;
|
||||
import org.enginehub.piston.exception.UsageException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* converts WorldEdit exceptions and converts them into {@link CommandException}s.
|
||||
*/
|
||||
|
@ -17,18 +17,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.runtime;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* Thrown when a break or continue is encountered.
|
||||
* Loop constructs catch this exception.
|
||||
*/
|
||||
public class BreakException extends EvaluationException {
|
||||
public class BreakException extends RuntimeException {
|
||||
|
||||
final boolean doContinue;
|
||||
public final boolean doContinue;
|
||||
|
||||
public BreakException(boolean doContinue) {
|
||||
super(-1, doContinue ? "'continue' encountered outside a loop" : "'break' encountered outside a loop");
|
||||
super(doContinue ? "'continue' encountered outside a loop" : "'break' encountered outside a loop",
|
||||
null, true, false);
|
||||
|
||||
this.doContinue = doContinue;
|
||||
}
|
@ -0,0 +1,625 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.sk89q.worldedit.antlr.ExpressionBaseVisitor;
|
||||
import com.sk89q.worldedit.antlr.ExpressionParser;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectMaps;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.RuleNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleBinaryOperator;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.DIVIDE;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.DIVIDE_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.EXCLAMATION_MARK;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.GREATER_THAN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.GREATER_THAN_OR_EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.INCREMENT;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.LEFT_SHIFT;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.LESS_THAN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.LESS_THAN_OR_EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MINUS;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MINUS_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MODULO;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MODULO_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.NEAR;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.NOT_EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.PLUS;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.PLUS_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.POWER_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.RIGHT_SHIFT;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES_ASSIGN;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.WRAPPED_CONSTANT;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.check;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.checkIterations;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.checkTimeout;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.evalException;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.getArgumentHandleName;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.resolveFunction;
|
||||
|
||||
class EvaluatingVisitor extends ExpressionBaseVisitor<Double> {
|
||||
|
||||
private final SlotTable slots;
|
||||
private final SetMultimap<String, MethodHandle> functions;
|
||||
|
||||
EvaluatingVisitor(SlotTable slots,
|
||||
SetMultimap<String, MethodHandle> functions) {
|
||||
this.slots = slots;
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
private LocalSlot.Variable initVariable(String name, ParserRuleContext ctx) {
|
||||
return slots.initVariable(name)
|
||||
.orElseThrow(() -> evalException(ctx, "Cannot overwrite non-variable '" + name + "'"));
|
||||
}
|
||||
|
||||
private Supplier<EvaluationException> varNotInitException(String name, ParserRuleContext ctx) {
|
||||
return () -> evalException(ctx, "'" + name + "' is not initialized yet");
|
||||
}
|
||||
|
||||
private LocalSlot.Variable getVariable(String name, ParserRuleContext ctx) {
|
||||
LocalSlot slot = slots.getSlot(name)
|
||||
.orElseThrow(varNotInitException(name, ctx));
|
||||
check(slot instanceof LocalSlot.Variable, ctx, "'" + name + "' is not a variable");
|
||||
return (LocalSlot.Variable) slot;
|
||||
}
|
||||
|
||||
private double getSlotValue(String name, ParserRuleContext ctx) {
|
||||
return slots.getSlotValue(name)
|
||||
.orElseThrow(varNotInitException(name, ctx));
|
||||
}
|
||||
|
||||
private Token extractToken(ParserRuleContext ctx) {
|
||||
List<TerminalNode> children = ctx.children.stream()
|
||||
.filter(TerminalNode.class::isInstance)
|
||||
.map(TerminalNode.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
check(children.size() == 1, ctx, "Expected exactly one token, got " + children.size());
|
||||
return children.get(0).getSymbol();
|
||||
}
|
||||
|
||||
private Double evaluate(ParserRuleContext ctx) {
|
||||
return ctx.accept(this);
|
||||
}
|
||||
|
||||
private double evaluateForValue(ParserRuleContext ctx) {
|
||||
Double result = evaluate(ctx);
|
||||
check(result != null, ctx, "Invalid expression for a value");
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean evaluateBoolean(ParserRuleContext boolExpression) {
|
||||
Double bool = evaluate(boolExpression);
|
||||
check(bool != null, boolExpression, "Invalid expression for boolean");
|
||||
return doubleToBool(bool);
|
||||
}
|
||||
|
||||
private boolean doubleToBool(double bool) {
|
||||
return bool > 0;
|
||||
}
|
||||
|
||||
private double boolToDouble(boolean bool) {
|
||||
return bool ? 1 : 0;
|
||||
}
|
||||
|
||||
private Double evaluateConditional(ParserRuleContext condition,
|
||||
ParserRuleContext trueBranch,
|
||||
ParserRuleContext falseBranch) {
|
||||
ParserRuleContext ctx = evaluateBoolean(condition) ? trueBranch : falseBranch;
|
||||
return ctx == null ? null : evaluate(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitIfStatement(ExpressionParser.IfStatementContext ctx) {
|
||||
return evaluateConditional(ctx.condition, ctx.trueBranch, ctx.falseBranch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitTernaryExpr(ExpressionParser.TernaryExprContext ctx) {
|
||||
return evaluateConditional(ctx.condition, ctx.trueBranch, ctx.falseBranch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitWhileStatement(ExpressionParser.WhileStatementContext ctx) {
|
||||
Double result = defaultResult();
|
||||
int iterations = 0;
|
||||
while (evaluateBoolean(ctx.condition)) {
|
||||
checkIterations(iterations, ctx.body);
|
||||
checkTimeout();
|
||||
iterations++;
|
||||
try {
|
||||
result = evaluate(ctx.body);
|
||||
} catch (BreakException ex) {
|
||||
if (!ex.doContinue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitDoStatement(ExpressionParser.DoStatementContext ctx) {
|
||||
Double result = defaultResult();
|
||||
int iterations = 0;
|
||||
do {
|
||||
checkIterations(iterations, ctx.body);
|
||||
checkTimeout();
|
||||
iterations++;
|
||||
try {
|
||||
result = evaluate(ctx.body);
|
||||
} catch (BreakException ex) {
|
||||
if (!ex.doContinue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (evaluateBoolean(ctx.condition));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitForStatement(ExpressionParser.ForStatementContext ctx) {
|
||||
Double result = defaultResult();
|
||||
int iterations = 0;
|
||||
evaluate(ctx.init);
|
||||
while (evaluateBoolean(ctx.condition)) {
|
||||
checkIterations(iterations, ctx.body);
|
||||
checkTimeout();
|
||||
iterations++;
|
||||
try {
|
||||
result = evaluate(ctx.body);
|
||||
} catch (BreakException ex) {
|
||||
if (!ex.doContinue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
evaluate(ctx.update);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) {
|
||||
Double result = defaultResult();
|
||||
int iterations = 0;
|
||||
double first = evaluateForValue(ctx.first);
|
||||
double last = evaluateForValue(ctx.last);
|
||||
String counter = ctx.counter.getText();
|
||||
LocalSlot.Variable variable = initVariable(counter, ctx);
|
||||
for (double i = first; i <= last; i++) {
|
||||
checkIterations(iterations, ctx.body);
|
||||
checkTimeout();
|
||||
iterations++;
|
||||
variable.setValue(i);
|
||||
try {
|
||||
result = evaluate(ctx.body);
|
||||
} catch (BreakException ex) {
|
||||
if (!ex.doContinue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitBreakStatement(ExpressionParser.BreakStatementContext ctx) {
|
||||
throw new BreakException(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitContinueStatement(ExpressionParser.ContinueStatementContext ctx) {
|
||||
throw new BreakException(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitReturnStatement(ExpressionParser.ReturnStatementContext ctx) {
|
||||
if (ctx.value != null) {
|
||||
return evaluate(ctx.value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitSwitchStatement(ExpressionParser.SwitchStatementContext ctx) {
|
||||
Double2ObjectMap<ParserRuleContext> cases = new Double2ObjectLinkedOpenHashMap<>(ctx.labels.size());
|
||||
ParserRuleContext defaultCase = null;
|
||||
for (int i = 0; i < ctx.labels.size(); i++) {
|
||||
ExpressionParser.SwitchLabelContext label = ctx.labels.get(i);
|
||||
ExpressionParser.StatementsContext body = ctx.bodies.get(i);
|
||||
if (label instanceof ExpressionParser.CaseContext) {
|
||||
ExpressionParser.CaseContext caseContext = (ExpressionParser.CaseContext) label;
|
||||
double key = evaluateForValue(caseContext.constant);
|
||||
check(!cases.containsKey(key), body, "Duplicate cases detected.");
|
||||
cases.put(key, body);
|
||||
} else {
|
||||
check(defaultCase == null, body, "Duplicate default cases detected.");
|
||||
defaultCase = body;
|
||||
}
|
||||
}
|
||||
double value = evaluateForValue(ctx.target);
|
||||
boolean matched = false;
|
||||
Double evaluated = null;
|
||||
boolean falling = false;
|
||||
for (Double2ObjectMap.Entry<ParserRuleContext> entry : Double2ObjectMaps.fastIterable(cases)) {
|
||||
if (falling || entry.getDoubleKey() == value) {
|
||||
matched = true;
|
||||
try {
|
||||
evaluated = evaluate(entry.getValue());
|
||||
falling = true;
|
||||
} catch (BreakException brk) {
|
||||
check(!brk.doContinue, entry.getValue(), "Cannot continue in a switch");
|
||||
falling = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This if is like the one in the loop, default's "case" is `!matched` & present
|
||||
if ((falling || !matched) && defaultCase != null) {
|
||||
try {
|
||||
evaluated = evaluate(defaultCase);
|
||||
} catch (BreakException brk) {
|
||||
check(!brk.doContinue, defaultCase, "Cannot continue in a switch");
|
||||
}
|
||||
}
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitExpressionStatement(ExpressionParser.ExpressionStatementContext ctx) {
|
||||
return evaluate(ctx.expression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitPostCrementExpr(ExpressionParser.PostCrementExprContext ctx) {
|
||||
String target = ctx.target.getText();
|
||||
LocalSlot.Variable variable = getVariable(target, ctx);
|
||||
double value = variable.getValue();
|
||||
if (ctx.op.getType() == INCREMENT) {
|
||||
value++;
|
||||
} else {
|
||||
value--;
|
||||
}
|
||||
variable.setValue(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitPreCrementExpr(ExpressionParser.PreCrementExprContext ctx) {
|
||||
String target = ctx.target.getText();
|
||||
LocalSlot.Variable variable = getVariable(target, ctx);
|
||||
double value = variable.getValue();
|
||||
double result = value;
|
||||
if (ctx.op.getType() == INCREMENT) {
|
||||
value++;
|
||||
} else {
|
||||
value--;
|
||||
}
|
||||
variable.setValue(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitPlusMinusExpr(ExpressionParser.PlusMinusExprContext ctx) {
|
||||
double value = evaluateForValue(ctx.expr);
|
||||
switch (ctx.op.getType()) {
|
||||
case PLUS:
|
||||
return +value;
|
||||
case MINUS:
|
||||
return -value;
|
||||
}
|
||||
throw evalException(ctx, "Invalid text for plus/minus expr: " + ctx.op.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitNotExpr(ExpressionParser.NotExprContext ctx) {
|
||||
return boolToDouble(!evaluateBoolean(ctx.expr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitComplementExpr(ExpressionParser.ComplementExprContext ctx) {
|
||||
return (double) ~(long) evaluateForValue(ctx.expr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitConditionalAndExpr(ExpressionParser.ConditionalAndExprContext ctx) {
|
||||
if (!evaluateBoolean(ctx.left)) {
|
||||
return boolToDouble(false);
|
||||
}
|
||||
return evaluateForValue(ctx.right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitConditionalOrExpr(ExpressionParser.ConditionalOrExprContext ctx) {
|
||||
double left = evaluateForValue(ctx.left);
|
||||
if (doubleToBool(left)) {
|
||||
return left;
|
||||
}
|
||||
return evaluateForValue(ctx.right);
|
||||
}
|
||||
|
||||
private double evaluateBinary(ParserRuleContext left,
|
||||
ParserRuleContext right,
|
||||
DoubleBinaryOperator op) {
|
||||
return op.applyAsDouble(evaluateForValue(left), evaluateForValue(right));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitPowerExpr(ExpressionParser.PowerExprContext ctx) {
|
||||
return evaluateBinary(ctx.left, ctx.right, Math::pow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitMultiplicativeExpr(ExpressionParser.MultiplicativeExprContext ctx) {
|
||||
return evaluateBinary(ctx.left, ctx.right, (l, r) -> {
|
||||
switch (ctx.op.getType()) {
|
||||
case TIMES:
|
||||
return l * r;
|
||||
case DIVIDE:
|
||||
return l / r;
|
||||
case MODULO:
|
||||
return l % r;
|
||||
}
|
||||
throw evalException(ctx, "Invalid text for multiplicative expr: " + ctx.op.getText());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitAddExpr(ExpressionParser.AddExprContext ctx) {
|
||||
return evaluateBinary(ctx.left, ctx.right, (l, r) -> {
|
||||
switch (ctx.op.getType()) {
|
||||
case PLUS:
|
||||
return l + r;
|
||||
case MINUS:
|
||||
return l - r;
|
||||
}
|
||||
throw evalException(ctx, "Invalid text for additive expr: " + ctx.op.getText());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitShiftExpr(ExpressionParser.ShiftExprContext ctx) {
|
||||
return evaluateBinary(ctx.left, ctx.right, (l, r) -> {
|
||||
switch (ctx.op.getType()) {
|
||||
case LEFT_SHIFT:
|
||||
return (double) ((long) l << (long) r);
|
||||
case RIGHT_SHIFT:
|
||||
return (double) ((long) l >> (long) r);
|
||||
}
|
||||
throw evalException(ctx, "Invalid text for shift expr: " + ctx.op.getText());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitRelationalExpr(ExpressionParser.RelationalExprContext ctx) {
|
||||
return evaluateBinary(ctx.left, ctx.right, (l, r) -> {
|
||||
switch (ctx.op.getType()) {
|
||||
case LESS_THAN:
|
||||
return boolToDouble(l < r);
|
||||
case LESS_THAN_OR_EQUAL:
|
||||
return boolToDouble(l <= r);
|
||||
case GREATER_THAN:
|
||||
return boolToDouble(l > r);
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
return boolToDouble(l >= r);
|
||||
}
|
||||
throw evalException(ctx, "Invalid text for relational expr: " + ctx.op.getText());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitEqualityExpr(ExpressionParser.EqualityExprContext ctx) {
|
||||
return evaluateBinary(ctx.left, ctx.right, (l, r) -> {
|
||||
switch (ctx.op.getType()) {
|
||||
case EQUAL:
|
||||
return boolToDouble(l == r);
|
||||
case NOT_EQUAL:
|
||||
return boolToDouble(l != r);
|
||||
case NEAR:
|
||||
return boolToDouble(almostEqual2sComplement(l, r, 450359963L));
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
return boolToDouble(l >= r);
|
||||
}
|
||||
throw evalException(ctx, "Invalid text for equality expr: " + ctx.op.getText());
|
||||
});
|
||||
}
|
||||
|
||||
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
|
||||
private static boolean almostEqual2sComplement(double a, double b, long maxUlps) {
|
||||
// Make sure maxUlps is non-negative and small enough that the
|
||||
// default NAN won't compare as equal to anything.
|
||||
//assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); // this is for floats, not doubles
|
||||
|
||||
long aLong = Double.doubleToRawLongBits(a);
|
||||
// Make aLong lexicographically ordered as a twos-complement long
|
||||
if (aLong < 0) aLong = 0x8000000000000000L - aLong;
|
||||
|
||||
long bLong = Double.doubleToRawLongBits(b);
|
||||
// Make bLong lexicographically ordered as a twos-complement long
|
||||
if (bLong < 0) bLong = 0x8000000000000000L - bLong;
|
||||
|
||||
final long longDiff = Math.abs(aLong - bLong);
|
||||
return longDiff <= maxUlps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitPostfixExpr(ExpressionParser.PostfixExprContext ctx) {
|
||||
double value = evaluateForValue(ctx.expr);
|
||||
if (ctx.op.getType() == EXCLAMATION_MARK) {
|
||||
return factorial(value);
|
||||
}
|
||||
throw evalException(ctx,
|
||||
"Invalid text for post-unary expr: " + ctx.op.getText());
|
||||
}
|
||||
|
||||
private static final double[] factorials = new double[171];
|
||||
|
||||
static {
|
||||
factorials[0] = 1;
|
||||
for (int i = 1; i < factorials.length; ++i) {
|
||||
factorials[i] = factorials[i - 1] * i;
|
||||
}
|
||||
}
|
||||
|
||||
private static double factorial(double x) throws EvaluationException {
|
||||
final int n = (int) x;
|
||||
|
||||
if (n < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n >= factorials.length) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
return factorials[n];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitAssignment(ExpressionParser.AssignmentContext ctx) {
|
||||
int type = extractToken(ctx.assignmentOperator()).getType();
|
||||
String target = ctx.target.getText();
|
||||
double value;
|
||||
double arg = evaluateForValue(ctx.expression());
|
||||
LocalSlot.Variable variable;
|
||||
if (type == ASSIGN) {
|
||||
variable = initVariable(target, ctx);
|
||||
value = arg;
|
||||
} else {
|
||||
variable = getVariable(target, ctx);
|
||||
value = variable.getValue();
|
||||
switch (type) {
|
||||
case POWER_ASSIGN:
|
||||
value = Math.pow(value, arg);
|
||||
break;
|
||||
case TIMES_ASSIGN:
|
||||
value *= arg;
|
||||
break;
|
||||
case DIVIDE_ASSIGN:
|
||||
value /= arg;
|
||||
break;
|
||||
case MODULO_ASSIGN:
|
||||
value %= arg;
|
||||
break;
|
||||
case PLUS_ASSIGN:
|
||||
value += arg;
|
||||
break;
|
||||
case MINUS_ASSIGN:
|
||||
value -= arg;
|
||||
break;
|
||||
default:
|
||||
throw evalException(ctx, "Invalid text for assign expr: " +
|
||||
ctx.assignmentOperator().getText());
|
||||
}
|
||||
}
|
||||
variable.setValue(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitFunctionCall(ExpressionParser.FunctionCallContext ctx) {
|
||||
MethodHandle handle = resolveFunction(functions, ctx);
|
||||
String fnName = ctx.name.getText();
|
||||
Object[] arguments = new Object[ctx.args.size()];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
ExpressionParser.ExpressionContext arg = ctx.args.get(i);
|
||||
Object transformed = getArgument(fnName, handle.type(), i, arg);
|
||||
arguments[i] = transformed;
|
||||
}
|
||||
try {
|
||||
// Some methods return other Numbers
|
||||
Number number = (Number) handle.invokeWithArguments(arguments);
|
||||
return number == null ? null : number.doubleValue();
|
||||
} catch (Throwable throwable) {
|
||||
Throwables.throwIfUnchecked(throwable);
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getArgument(String fnName, MethodType type, int i, ParserRuleContext arg) {
|
||||
// Pass variable handle in for modification?
|
||||
String handleName = getArgumentHandleName(fnName, type, i, arg);
|
||||
if (handleName == null) {
|
||||
return evaluateForValue(arg);
|
||||
}
|
||||
if (handleName.equals(WRAPPED_CONSTANT)) {
|
||||
return new LocalSlot.Constant(evaluateForValue(arg));
|
||||
}
|
||||
return getVariable(handleName, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitConstantExpression(ExpressionParser.ConstantExpressionContext ctx) {
|
||||
try {
|
||||
return Double.parseDouble(ctx.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
// Rare, but might happen, e.g. if too many digits
|
||||
throw evalException(ctx, "Invalid constant: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitIdExpr(ExpressionParser.IdExprContext ctx) {
|
||||
String source = ctx.source.getText();
|
||||
return getSlotValue(source, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double visitChildren(RuleNode node) {
|
||||
Double result = defaultResult();
|
||||
int n = node.getChildCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
ParseTree c = node.getChild(i);
|
||||
if (c instanceof TerminalNode && ((TerminalNode) c).getSymbol().getType() == Token.EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
Double childResult = c.accept(this);
|
||||
if (c instanceof ExpressionParser.ReturnStatementContext) {
|
||||
return childResult;
|
||||
}
|
||||
result = aggregateResult(result, childResult);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double aggregateResult(Double aggregate, Double nextResult) {
|
||||
return Optional.ofNullable(nextResult).orElse(aggregate);
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.runtime;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
|
@ -19,26 +19,28 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.Lexer;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.Token;
|
||||
import com.sk89q.worldedit.internal.expression.parser.Parser;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.Constant;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.ExpressionTimeoutException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.Functions;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.RValue;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.ReturnException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.Variable;
|
||||
import com.sk89q.worldedit.antlr.ExpressionLexer;
|
||||
import com.sk89q.worldedit.antlr.ExpressionParser;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import com.sk89q.worldedit.session.request.Request;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
@ -68,61 +70,64 @@ import java.util.concurrent.TimeoutException;
|
||||
* If you wish to run the equation multiple times, you can then optimize it,
|
||||
* by calling {@link #optimize()}. You can then run the equation as many times
|
||||
* as you want by calling {@link #evaluate(double...)}. You do not need to
|
||||
* pass values for all variables specified while compiling.
|
||||
* To query variables after evaluation, you can use
|
||||
* {@link #getVariable(String, boolean)}. To get a value out of these, use
|
||||
* {@link Variable#getValue()}.</p>
|
||||
*
|
||||
* <p>Variables are also supported and can be set either by passing values
|
||||
* to {@link #evaluate(double...)}.</p>
|
||||
* pass values for all slots specified while compiling.
|
||||
* To query slots after evaluation, you can use the {@linkplain #getSlots() slot table}.
|
||||
*/
|
||||
public class Expression {
|
||||
|
||||
private static final ThreadLocal<ArrayDeque<Expression>> instance = ThreadLocal.withInitial(ArrayDeque::new);
|
||||
private static final ExecutorService evalThread = Executors.newCachedThreadPool(
|
||||
new ThreadFactoryBuilder()
|
||||
.setDaemon(true)
|
||||
.setNameFormat("worldedit-expression-eval-%d")
|
||||
.build());
|
||||
private static final ExecutorService evalThread = Executors.newFixedThreadPool(
|
||||
Runtime.getRuntime().availableProcessors(),
|
||||
new ThreadFactoryBuilder()
|
||||
.setDaemon(true)
|
||||
.setNameFormat("worldedit-expression-eval-%d")
|
||||
.build());
|
||||
|
||||
private final Map<String, RValue> variables = new HashMap<>();
|
||||
private final SlotTable slots = new SlotTable();
|
||||
private final List<String> providedSlots;
|
||||
private Variable[] variableArray;
|
||||
private RValue root;
|
||||
private final Functions functions = new Functions();
|
||||
private ExpressionParser.AllStatementsContext root;
|
||||
private final SetMultimap<String, MethodHandle> functions = Functions.getFunctionMap();
|
||||
private ExpressionEnvironment environment;
|
||||
|
||||
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
|
||||
return new Expression(expression, variableNames);
|
||||
}
|
||||
|
||||
private Expression(String expression, String... variableNames) throws ExpressionException {
|
||||
slots.putSlot("e", new LocalSlot.Constant(Math.E));
|
||||
slots.putSlot("pi", new LocalSlot.Constant(Math.PI));
|
||||
slots.putSlot("true", new LocalSlot.Constant(1));
|
||||
slots.putSlot("false", new LocalSlot.Constant(0));
|
||||
|
||||
for (String variableName : variableNames) {
|
||||
slots.initVariable(variableName)
|
||||
.orElseThrow(() -> new ExpressionException(-1,
|
||||
"Tried to overwrite identifier '" + variableName + "'"));
|
||||
}
|
||||
this.providedSlots = ImmutableList.copyOf(variableNames);
|
||||
|
||||
CharStream cs = CharStreams.fromString(expression, "<input>");
|
||||
ExpressionLexer lexer = new ExpressionLexer(cs);
|
||||
lexer.removeErrorListeners();
|
||||
lexer.addErrorListener(new LexerErrorListener());
|
||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||
ExpressionParser parser = new ExpressionParser(tokens);
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(new ParserErrorListener());
|
||||
try {
|
||||
root = parser.allStatements();
|
||||
Objects.requireNonNull(root, "Unable to parse root, but no exceptions?");
|
||||
} catch (ParseCancellationException e) {
|
||||
throw new ParserException(parser.getState(), e);
|
||||
}
|
||||
ParseTreeWalker.DEFAULT.walk(new ExpressionValidator(slots.keySet(), functions), root);
|
||||
}
|
||||
|
||||
public Expression(double constant) {
|
||||
root = new Constant(0, constant);
|
||||
}
|
||||
|
||||
private Expression(String expression, String... variableNames) throws ExpressionException {
|
||||
this(Lexer.tokenize(expression), variableNames);
|
||||
}
|
||||
|
||||
private Expression(List<Token> tokens, String... variableNames) throws ExpressionException {
|
||||
|
||||
variables.put("e", new Constant(-1, Math.E));
|
||||
variables.put("pi", new Constant(-1, Math.PI));
|
||||
variables.put("true", new Constant(-1, 1));
|
||||
variables.put("false", new Constant(-1, 0));
|
||||
|
||||
variableArray = new Variable[variableNames.length];
|
||||
for (int i = 0; i < variableNames.length; i++) {
|
||||
if (variables.containsKey(variableNames[i])) {
|
||||
throw new ExpressionException(-1, "Tried to overwrite identifier '" + variableNames[i] + "'");
|
||||
}
|
||||
Variable var = new Variable(0);
|
||||
variables.put(variableNames[i], var);
|
||||
variableArray[i] = var;
|
||||
}
|
||||
|
||||
root = Parser.parse(tokens, this);
|
||||
}
|
||||
|
||||
public double evaluate(double x, double y, double z) throws EvaluationException {
|
||||
return evaluateTimeout(WorldEdit.getInstance().getConfiguration().calculationTimeout, x, y, z);
|
||||
}
|
||||
@ -157,10 +162,19 @@ public class Expression {
|
||||
return root.getValue();
|
||||
}
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
Variable var = variableArray[i];
|
||||
var.value = values[i];
|
||||
String slotName = providedSlots.get(i);
|
||||
LocalSlot.Variable slot = slots.getVariable(slotName)
|
||||
.orElseThrow(() -> new EvaluationException(-1,
|
||||
"Tried to assign to non-variable " + slotName + "."));
|
||||
|
||||
slot.setValue(values[i]);
|
||||
}
|
||||
return evaluateFinal(timeout);
|
||||
|
||||
// evaluation exceptions are thrown out of this method
|
||||
if (timeout < 0) {
|
||||
return evaluateRoot();
|
||||
}
|
||||
return evaluateRootTimed(timeout);
|
||||
}
|
||||
|
||||
private double evaluateFinal(int timeout) throws EvaluationException {
|
||||
@ -175,6 +189,7 @@ public class Expression {
|
||||
}
|
||||
|
||||
private double evaluateRootTimed(int timeout) throws EvaluationException {
|
||||
CountDownLatch startLatch = new CountDownLatch(1);
|
||||
Request request = Request.request();
|
||||
Future<Double> result = evalThread.submit(() -> {
|
||||
Request local = Request.request();
|
||||
@ -182,12 +197,14 @@ public class Expression {
|
||||
local.setWorld(request.getWorld());
|
||||
local.setEditSession(request.getEditSession());
|
||||
try {
|
||||
startLatch.countDown();
|
||||
return Expression.this.evaluateRoot();
|
||||
} finally {
|
||||
Request.reset();
|
||||
}
|
||||
});
|
||||
try {
|
||||
startLatch.await();
|
||||
return result.get(timeout, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
@ -197,12 +214,8 @@ public class Expression {
|
||||
throw new ExpressionTimeoutException("Calculations exceeded time limit.");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof EvaluationException) {
|
||||
throw (EvaluationException) cause;
|
||||
}
|
||||
if (cause instanceof RuntimeException) {
|
||||
throw (RuntimeException) cause;
|
||||
}
|
||||
Throwables.throwIfInstanceOf(cause, EvaluationException.class);
|
||||
Throwables.throwIfUnchecked(cause);
|
||||
throw new RuntimeException(cause);
|
||||
}
|
||||
}
|
||||
@ -210,14 +223,18 @@ public class Expression {
|
||||
private Double evaluateRoot() throws EvaluationException {
|
||||
pushInstance();
|
||||
try {
|
||||
return root.getValue();
|
||||
return root.accept(new EvaluatingVisitor(slots, functions));
|
||||
} finally {
|
||||
popInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public void optimize() throws EvaluationException {
|
||||
root = root.optimize();
|
||||
public void optimize() {
|
||||
// TODO optimizing
|
||||
}
|
||||
|
||||
public SlotTable getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
public RValue getRoot() {
|
||||
@ -229,15 +246,6 @@ public class Expression {
|
||||
return root.toString();
|
||||
}
|
||||
|
||||
public RValue getVariable(String name, boolean create) {
|
||||
RValue variable = variables.get(name);
|
||||
if (variable == null && create) {
|
||||
variables.put(name, variable = new Variable(0));
|
||||
}
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
public static Expression getInstance() {
|
||||
return instance.get().peek();
|
||||
}
|
||||
@ -253,10 +261,6 @@ public class Expression {
|
||||
foo.pop();
|
||||
}
|
||||
|
||||
public Functions getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public ExpressionEnvironment getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.runtime;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* Represents a way to access blocks in a world. Has to accept non-rounded coordinates.
|
@ -23,7 +23,7 @@ package com.sk89q.worldedit.internal.expression;
|
||||
* Thrown when there's a problem during any stage of the expression
|
||||
* compilation or evaluation.
|
||||
*/
|
||||
public class ExpressionException extends Exception {
|
||||
public class ExpressionException extends RuntimeException {
|
||||
|
||||
private final int position;
|
||||
|
||||
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.sk89q.worldedit.antlr.ExpressionParser;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.ID;
|
||||
|
||||
class ExpressionHelper {
|
||||
|
||||
static void check(boolean condition, ParserRuleContext ctx, String message) {
|
||||
if (!condition) {
|
||||
throw evalException(ctx, message);
|
||||
}
|
||||
}
|
||||
|
||||
static EvaluationException evalException(ParserRuleContext ctx, String message) {
|
||||
return new EvaluationException(
|
||||
ctx.getStart().getCharPositionInLine(),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
static void checkIterations(int iterations, ParserRuleContext ctx) {
|
||||
check(iterations <= 256, ctx, "Loop exceeded 256 iterations");
|
||||
}
|
||||
|
||||
static void checkTimeout() {
|
||||
if (Thread.interrupted()) {
|
||||
throw new ExpressionTimeoutException("Calculations exceeded time limit.");
|
||||
}
|
||||
}
|
||||
|
||||
static MethodHandle resolveFunction(SetMultimap<String, MethodHandle> functions,
|
||||
ExpressionParser.FunctionCallContext ctx) {
|
||||
String fnName = ctx.name.getText();
|
||||
Set<MethodHandle> matchingFns = functions.get(fnName);
|
||||
check(!matchingFns.isEmpty(), ctx, "Unknown function '" + fnName + "'");
|
||||
for (MethodHandle function : matchingFns) {
|
||||
MethodType type = function.type();
|
||||
// Validate argc if not varargs
|
||||
if (!function.isVarargsCollector() && type.parameterCount() != ctx.args.size()) {
|
||||
// skip non-matching function
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < ctx.args.size(); i++) {
|
||||
ExpressionParser.ExpressionContext arg = ctx.args.get(i);
|
||||
getArgumentHandleName(fnName, type, i, arg);
|
||||
}
|
||||
// good match!
|
||||
return function;
|
||||
}
|
||||
// We matched no function, fail with appropriate message.
|
||||
String possibleCounts = matchingFns.stream()
|
||||
.map(mh -> mh.isVarargsCollector()
|
||||
? (mh.type().parameterCount() - 1) + "+"
|
||||
: String.valueOf(mh.type().parameterCount()))
|
||||
.collect(Collectors.joining("/"));
|
||||
throw evalException(ctx, "Incorrect number of arguments for function '" + fnName + "', " +
|
||||
"expected " + possibleCounts + ", " +
|
||||
"got " + ctx.args.size());
|
||||
}
|
||||
|
||||
// Special argument handle names
|
||||
/**
|
||||
* The argument should be wrapped in a {@link LocalSlot.Constant} before being passed.
|
||||
*/
|
||||
static final String WRAPPED_CONSTANT = "<wrapped constant>";
|
||||
|
||||
/**
|
||||
* If this argument needs a handle, returns the name of the handle needed. Otherwise, returns
|
||||
* {@code null}. If {@code arg} isn't a valid handle reference, throws.
|
||||
*/
|
||||
static String getArgumentHandleName(String fnName, MethodType type, int i,
|
||||
ParserRuleContext arg) {
|
||||
// Pass variable handle in for modification?
|
||||
Class<?> pType = type.parameterType(i);
|
||||
Optional<String> id = tryResolveId(arg);
|
||||
if (pType == LocalSlot.Variable.class) {
|
||||
// MUST be an id
|
||||
check(id.isPresent(), arg,
|
||||
"Function '" + fnName + "' requires a variable in parameter " + i);
|
||||
return id.get();
|
||||
} else if (pType == LocalSlot.class) {
|
||||
return id.orElse(WRAPPED_CONSTANT);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Optional<String> tryResolveId(ParserRuleContext arg) {
|
||||
Optional<ExpressionParser.WrappedExprContext> wrappedExprContext =
|
||||
tryAs(arg, ExpressionParser.WrappedExprContext.class);
|
||||
if (wrappedExprContext.isPresent()) {
|
||||
return tryResolveId(wrappedExprContext.get().expression());
|
||||
}
|
||||
Token token = arg.start;
|
||||
int tokenType = token.getType();
|
||||
boolean isId = arg.start == arg.stop && tokenType == ID;
|
||||
return isId ? Optional.of(token.getText()) : Optional.empty();
|
||||
}
|
||||
|
||||
private static <T extends ParserRuleContext> Optional<T> tryAs(
|
||||
ParserRuleContext ctx,
|
||||
Class<T> rule
|
||||
) {
|
||||
if (rule.isInstance(ctx)) {
|
||||
return Optional.of(rule.cast(ctx));
|
||||
}
|
||||
if (ctx.children.size() != 1) {
|
||||
return Optional.empty();
|
||||
}
|
||||
List<ParserRuleContext> ctxs = ctx.getRuleContexts(ParserRuleContext.class);
|
||||
if (ctxs.size() != 1) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return tryAs(ctxs.get(0), rule);
|
||||
}
|
||||
|
||||
private ExpressionHelper() {
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.runtime;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* Thrown when an evaluation exceeds the timeout time.
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.sk89q.worldedit.antlr.ExpressionBaseListener;
|
||||
import com.sk89q.worldedit.antlr.ExpressionParser;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.check;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.resolveFunction;
|
||||
|
||||
class ExpressionValidator extends ExpressionBaseListener {
|
||||
|
||||
private final Set<String> variableNames = new HashSet<>();
|
||||
private final SetMultimap<String, MethodHandle> functions;
|
||||
|
||||
ExpressionValidator(Collection<String> variableNames,
|
||||
SetMultimap<String, MethodHandle> functions) {
|
||||
this.variableNames.addAll(variableNames);
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
private void bindVariable(String name) {
|
||||
variableNames.add(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterAssignment(ExpressionParser.AssignmentContext ctx) {
|
||||
bindVariable(ctx.target.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) {
|
||||
bindVariable(ctx.counter.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterIdExpr(ExpressionParser.IdExprContext ctx) {
|
||||
String text = ctx.source.getText();
|
||||
check(variableNames.contains(text), ctx,
|
||||
"Variable '" + text + "' is not bound");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterFunctionCall(ExpressionParser.FunctionCallContext ctx) {
|
||||
resolveFunction(functions, ctx);
|
||||
}
|
||||
}
|
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.primitives.Doubles;
|
||||
import com.sk89q.worldedit.internal.expression.LocalSlot.Variable;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import com.sk89q.worldedit.math.noise.PerlinNoise;
|
||||
import com.sk89q.worldedit.math.noise.RidgedMultiFractalNoise;
|
||||
import com.sk89q.worldedit.math.noise.VoronoiNoise;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
/**
|
||||
* Contains all functions that can be used in expressions.
|
||||
*/
|
||||
final class Functions {
|
||||
|
||||
static SetMultimap<String, MethodHandle> getFunctionMap() {
|
||||
SetMultimap<String, MethodHandle> map = HashMultimap.create();
|
||||
Functions functions = new Functions();
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
try {
|
||||
addMathHandles(map, lookup);
|
||||
addStaticFunctionHandles(map, lookup);
|
||||
functions.addInstanceFunctionHandles(map, lookup);
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
return ImmutableSetMultimap.copyOf(map);
|
||||
}
|
||||
|
||||
private static void addMathHandles(
|
||||
SetMultimap<String, MethodHandle> map,
|
||||
MethodHandles.Lookup lookup
|
||||
) throws NoSuchMethodException, IllegalAccessException {
|
||||
// double <name>(double) functions
|
||||
for (String name : ImmutableList.of(
|
||||
"sin", "cos", "tan", "asin", "acos", "atan",
|
||||
"sinh", "cosh", "tanh", "sqrt", "cbrt", "abs",
|
||||
"ceil", "floor", "rint", "exp", "log", "log10"
|
||||
)) {
|
||||
map.put(name, lookup.findStatic(Math.class, name,
|
||||
methodType(double.class, double.class)));
|
||||
}
|
||||
// Alias ln -> log
|
||||
map.put("ln", lookup.findStatic(Math.class, "log",
|
||||
methodType(double.class, double.class)));
|
||||
map.put("round", lookup.findStatic(Math.class, "round",
|
||||
methodType(long.class, double.class)));
|
||||
|
||||
map.put("atan2", lookup.findStatic(Math.class, "atan2",
|
||||
methodType(double.class, double.class, double.class)));
|
||||
|
||||
// Special cases: we accept varargs for these
|
||||
map.put("min", lookup.findStatic(Doubles.class, "min",
|
||||
methodType(double.class, double[].class))
|
||||
.asVarargsCollector(double[].class));
|
||||
map.put("max", lookup.findStatic(Doubles.class, "max",
|
||||
methodType(double.class, double[].class))
|
||||
.asVarargsCollector(double[].class));
|
||||
}
|
||||
|
||||
private static void addStaticFunctionHandles(
|
||||
SetMultimap<String, MethodHandle> map,
|
||||
MethodHandles.Lookup lookup
|
||||
) throws NoSuchMethodException, IllegalAccessException {
|
||||
map.put("rotate", lookup.findStatic(Functions.class, "rotate",
|
||||
methodType(double.class, Variable.class, Variable.class, double.class)));
|
||||
map.put("swap", lookup.findStatic(Functions.class, "swap",
|
||||
methodType(double.class, Variable.class, Variable.class)));
|
||||
map.put("gmegabuf", lookup.findStatic(Functions.class, "gmegabuf",
|
||||
methodType(double.class, double.class)));
|
||||
map.put("gmegabuf", lookup.findStatic(Functions.class, "gmegabuf",
|
||||
methodType(double.class, double.class, double.class)));
|
||||
map.put("gclosest", lookup.findStatic(Functions.class, "gclosest",
|
||||
methodType(double.class, double.class, double.class, double.class, double.class,
|
||||
double.class, double.class)));
|
||||
map.put("random", lookup.findStatic(Functions.class, "random",
|
||||
methodType(double.class)));
|
||||
map.put("randint", lookup.findStatic(Functions.class, "randint",
|
||||
methodType(double.class, double.class)));
|
||||
map.put("perlin", lookup.findStatic(Functions.class, "perlin",
|
||||
methodType(double.class, double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class)));
|
||||
map.put("voronoi", lookup.findStatic(Functions.class, "voronoi",
|
||||
methodType(double.class, double.class, double.class, double.class, double.class,
|
||||
double.class)));
|
||||
map.put("ridgedmulti", lookup.findStatic(Functions.class, "ridgedmulti",
|
||||
methodType(double.class, double.class, double.class, double.class, double.class,
|
||||
double.class, double.class)));
|
||||
map.put("query", lookup.findStatic(Functions.class, "query",
|
||||
methodType(double.class, double.class, double.class, double.class, LocalSlot.class,
|
||||
LocalSlot.class)));
|
||||
map.put("queryAbs", lookup.findStatic(Functions.class, "queryAbs",
|
||||
methodType(double.class, double.class, double.class, double.class, LocalSlot.class,
|
||||
LocalSlot.class)));
|
||||
map.put("queryRel", lookup.findStatic(Functions.class, "queryRel",
|
||||
methodType(double.class, double.class, double.class, double.class, LocalSlot.class,
|
||||
LocalSlot.class)));
|
||||
}
|
||||
|
||||
private void addInstanceFunctionHandles(
|
||||
SetMultimap<String, MethodHandle> map,
|
||||
MethodHandles.Lookup lookup
|
||||
) throws NoSuchMethodException, IllegalAccessException {
|
||||
map.put("megabuf", lookup.findSpecial(Functions.class, "megabuf",
|
||||
methodType(double.class, double.class), Functions.class)
|
||||
.bindTo(this));
|
||||
map.put("megabuf", lookup.findSpecial(Functions.class, "megabuf",
|
||||
methodType(double.class, double.class, double.class), Functions.class)
|
||||
.bindTo(this));
|
||||
map.put("closest", lookup.findSpecial(Functions.class, "closest",
|
||||
methodType(double.class, double.class, double.class, double.class, double.class,
|
||||
double.class, double.class), Functions.class)
|
||||
.bindTo(this));
|
||||
}
|
||||
|
||||
private static double rotate(Variable x, Variable y, double angle) {
|
||||
final double cosF = Math.cos(angle);
|
||||
final double sinF = Math.sin(angle);
|
||||
|
||||
final double xOld = x.getValue();
|
||||
final double yOld = y.getValue();
|
||||
|
||||
x.setValue(xOld * cosF - yOld * sinF);
|
||||
y.setValue(xOld * sinF + yOld * cosF);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
private static double swap(Variable x, Variable y) {
|
||||
final double tmp = x.getValue();
|
||||
|
||||
x.setValue(y.getValue());
|
||||
y.setValue(tmp);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
private static final Int2ObjectMap<double[]> globalMegaBuffer = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<double[]> megaBuffer = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static double[] getSubBuffer(Int2ObjectMap<double[]> megabuf, int key) {
|
||||
return megabuf.computeIfAbsent(key, k -> new double[1024]);
|
||||
}
|
||||
|
||||
private static double getBufferItem(final Int2ObjectMap<double[]> megabuf, final int index) {
|
||||
return getSubBuffer(megabuf, index & ~1023)[index & 1023];
|
||||
}
|
||||
|
||||
private static double setBufferItem(final Int2ObjectMap<double[]> megabuf, final int index, double value) {
|
||||
return getSubBuffer(megabuf, index & ~1023)[index & 1023] = value;
|
||||
}
|
||||
|
||||
private static double gmegabuf(double index) {
|
||||
return getBufferItem(globalMegaBuffer, (int) index);
|
||||
}
|
||||
|
||||
private static double gmegabuf(double index, double value) {
|
||||
return setBufferItem(globalMegaBuffer, (int) index, value);
|
||||
}
|
||||
|
||||
private double megabuf(double index) {
|
||||
return getBufferItem(megaBuffer, (int) index);
|
||||
}
|
||||
|
||||
private double megabuf(double index, double value) {
|
||||
return setBufferItem(megaBuffer, (int) index, value);
|
||||
}
|
||||
|
||||
private double closest(double x, double y, double z, double index, double count, double stride) {
|
||||
return findClosest(
|
||||
megaBuffer, x, y, z, (int) index, (int) count, (int) stride
|
||||
);
|
||||
}
|
||||
|
||||
private static double gclosest(double x, double y, double z, double index, double count, double stride) {
|
||||
return findClosest(
|
||||
globalMegaBuffer, x, y, z, (int) index, (int) count, (int) stride
|
||||
);
|
||||
}
|
||||
|
||||
private static double findClosest(Int2ObjectMap<double[]> megabuf, double x, double y, double z, int index, int count, int stride) {
|
||||
int closestIndex = -1;
|
||||
double minDistanceSquared = Double.MAX_VALUE;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
double currentX = getBufferItem(megabuf, index) - x;
|
||||
double currentY = getBufferItem(megabuf, index+1) - y;
|
||||
double currentZ = getBufferItem(megabuf, index+2) - z;
|
||||
|
||||
double currentDistanceSquared = currentX*currentX + currentY*currentY + currentZ*currentZ;
|
||||
|
||||
if (currentDistanceSquared < minDistanceSquared) {
|
||||
minDistanceSquared = currentDistanceSquared;
|
||||
closestIndex = index;
|
||||
}
|
||||
|
||||
index += stride;
|
||||
}
|
||||
|
||||
return closestIndex;
|
||||
}
|
||||
|
||||
private static double random() {
|
||||
return ThreadLocalRandom.current().nextDouble();
|
||||
}
|
||||
|
||||
private static double randint(double max) {
|
||||
return ThreadLocalRandom.current().nextInt((int) Math.floor(max));
|
||||
}
|
||||
|
||||
private static final ThreadLocal<PerlinNoise> localPerlin = ThreadLocal.withInitial(PerlinNoise::new);
|
||||
|
||||
private static double perlin(double seed, double x, double y, double z,
|
||||
double frequency, double octaves, double persistence) {
|
||||
PerlinNoise perlin = localPerlin.get();
|
||||
try {
|
||||
perlin.setSeed((int) seed);
|
||||
perlin.setFrequency(frequency);
|
||||
perlin.setOctaveCount((int) octaves);
|
||||
perlin.setPersistence(persistence);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new EvaluationException(0, "Perlin noise error: " + e.getMessage());
|
||||
}
|
||||
return perlin.noise(Vector3.at(x, y, z));
|
||||
}
|
||||
|
||||
private static final ThreadLocal<VoronoiNoise> localVoronoi = ThreadLocal.withInitial(VoronoiNoise::new);
|
||||
|
||||
private static double voronoi(double seed, double x, double y, double z, double frequency) {
|
||||
VoronoiNoise voronoi = localVoronoi.get();
|
||||
try {
|
||||
voronoi.setSeed((int) seed);
|
||||
voronoi.setFrequency(frequency);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new EvaluationException(0, "Voronoi error: " + e.getMessage());
|
||||
}
|
||||
return voronoi.noise(Vector3.at(x, y, z));
|
||||
}
|
||||
|
||||
private static final ThreadLocal<RidgedMultiFractalNoise> localRidgedMulti = ThreadLocal.withInitial(RidgedMultiFractalNoise::new);
|
||||
|
||||
private static double ridgedmulti(double seed, double x, double y, double z,
|
||||
double frequency, double octaves) {
|
||||
RidgedMultiFractalNoise ridgedMulti = localRidgedMulti.get();
|
||||
try {
|
||||
ridgedMulti.setSeed((int) seed);
|
||||
ridgedMulti.setFrequency(frequency);
|
||||
ridgedMulti.setOctaveCount((int) octaves);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new EvaluationException(0, "Ridged multi error: " + e.getMessage());
|
||||
}
|
||||
return ridgedMulti.noise(Vector3.at(x, y, z));
|
||||
}
|
||||
|
||||
private static double queryInternal(LocalSlot type, LocalSlot data, double typeId, double dataValue) {
|
||||
// Compare to input values and determine return value
|
||||
// -1 is a wildcard, always true
|
||||
double ret = ((type.getValue() == -1 || typeId == type.getValue())
|
||||
&& (data.getValue() == -1 || dataValue == data.getValue())) ? 1.0 : 0.0;
|
||||
|
||||
if (type instanceof Variable) {
|
||||
((Variable) type).setValue(typeId);
|
||||
}
|
||||
if (data instanceof Variable) {
|
||||
((Variable) data).setValue(dataValue);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static double query(double x, double y, double z, LocalSlot type, LocalSlot data) {
|
||||
final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
|
||||
|
||||
// Read values from world
|
||||
final double typeId = environment.getBlockType(x, y, z);
|
||||
final double dataValue = environment.getBlockData(x, y, z);
|
||||
|
||||
return queryInternal(type, data, typeId, dataValue);
|
||||
}
|
||||
|
||||
private static double queryAbs(double x, double y, double z, LocalSlot type, LocalSlot data) {
|
||||
final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
|
||||
|
||||
// Read values from world
|
||||
final double typeId = environment.getBlockTypeAbs(x, y, z);
|
||||
final double dataValue = environment.getBlockDataAbs(x, y, z);
|
||||
|
||||
return queryInternal(type, data, typeId, dataValue);
|
||||
}
|
||||
|
||||
private static double queryRel(double x, double y, double z, LocalSlot type, LocalSlot data) {
|
||||
final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
|
||||
|
||||
// Read values from world
|
||||
final double typeId = environment.getBlockTypeRel(x, y, z);
|
||||
final double dataValue = environment.getBlockDataRel(x, y, z);
|
||||
|
||||
return queryInternal(type, data, typeId, dataValue);
|
||||
}
|
||||
|
||||
private Functions() {
|
||||
}
|
||||
|
||||
}
|
@ -17,28 +17,15 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.lexer.tokens;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* A unary or binary operator.
|
||||
*/
|
||||
public class OperatorToken extends Token {
|
||||
|
||||
public final String operator;
|
||||
|
||||
public OperatorToken(int position, String operator) {
|
||||
super(position);
|
||||
this.operator = operator;
|
||||
}
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
|
||||
class LexerErrorListener extends BaseErrorListener {
|
||||
@Override
|
||||
public char id() {
|
||||
return 'o';
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
|
||||
throw new LexerException(charPositionInLine, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OperatorToken(" + operator + ")";
|
||||
}
|
||||
|
||||
}
|
@ -17,9 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.lexer;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* Thrown when the lexer encounters a problem.
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* Represents the metadata for a named local slot.
|
||||
*/
|
||||
public interface LocalSlot {
|
||||
|
||||
final class Constant implements LocalSlot {
|
||||
private final double value;
|
||||
|
||||
public Constant(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
final class Variable implements LocalSlot {
|
||||
private double value;
|
||||
|
||||
public Variable(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
double getValue();
|
||||
|
||||
}
|
@ -17,28 +17,15 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.lexer.tokens;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* A keyword.
|
||||
*/
|
||||
public class KeywordToken extends Token {
|
||||
|
||||
public final String value;
|
||||
|
||||
public KeywordToken(int position, String value) {
|
||||
super(position);
|
||||
this.value = value;
|
||||
}
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
|
||||
class ParserErrorListener extends BaseErrorListener {
|
||||
@Override
|
||||
public char id() {
|
||||
return 'k';
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
|
||||
throw new ParserException(charPositionInLine, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KeywordToken(" + value + ")";
|
||||
}
|
||||
|
||||
}
|
@ -17,9 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.parser;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
/**
|
||||
* Thrown when the parser encounters a problem.
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalDouble;
|
||||
import java.util.Set;
|
||||
|
||||
public class SlotTable {
|
||||
|
||||
private final Map<String, LocalSlot> slots = new HashMap<>();
|
||||
|
||||
public Set<String> keySet() {
|
||||
return slots.keySet();
|
||||
}
|
||||
|
||||
public void putSlot(String name, LocalSlot slot) {
|
||||
slots.put(name, slot);
|
||||
}
|
||||
|
||||
public boolean containsSlot(String name) {
|
||||
return slots.containsKey(name);
|
||||
}
|
||||
|
||||
public Optional<LocalSlot.Variable> initVariable(String name) {
|
||||
slots.computeIfAbsent(name, n -> new LocalSlot.Variable(0));
|
||||
return getVariable(name);
|
||||
}
|
||||
|
||||
public Optional<LocalSlot> getSlot(String name) {
|
||||
return Optional.ofNullable(slots.get(name));
|
||||
}
|
||||
|
||||
public Optional<LocalSlot.Variable> getVariable(String name) {
|
||||
return getSlot(name)
|
||||
.filter(LocalSlot.Variable.class::isInstance)
|
||||
.map(LocalSlot.Variable.class::cast);
|
||||
}
|
||||
|
||||
public OptionalDouble getSlotValue(String name) {
|
||||
LocalSlot slot = slots.get(name);
|
||||
return slot == null ? OptionalDouble.empty() : OptionalDouble.of(slot.getValue());
|
||||
}
|
||||
|
||||
}
|
@ -1,240 +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 Lesser 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression.lexer;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.CharacterToken;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.IdentifierToken;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.KeywordToken;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.NumberToken;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.OperatorToken;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.tokens.Token;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Processes a string into a list of tokens.
|
||||
*
|
||||
* <p>Tokens can be numbers, identifiers, operators and assorted other
|
||||
* characters.</p>
|
||||
*/
|
||||
public class Lexer {
|
||||
|
||||
private final String expression;
|
||||
private int position = 0;
|
||||
|
||||
private Lexer(String expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public static List<Token> tokenize(String expression) throws LexerException {
|
||||
return new Lexer(expression).tokenize();
|
||||
}
|
||||
|
||||
private final DecisionTree operatorTree = new DecisionTree(null,
|
||||
'+', new DecisionTree("+",
|
||||
'=', new DecisionTree("+="),
|
||||
'+', new DecisionTree("++")
|
||||
),
|
||||
'-', new DecisionTree("-",
|
||||
'=', new DecisionTree("-="),
|
||||
'-', new DecisionTree("--")
|
||||
),
|
||||
'*', new DecisionTree("*",
|
||||
'=', new DecisionTree("*="),
|
||||
'*', new DecisionTree("**")
|
||||
),
|
||||
'/', new DecisionTree("/",
|
||||
'=', new DecisionTree("/=")
|
||||
),
|
||||
'%', new DecisionTree("%",
|
||||
'=', new DecisionTree("%=")
|
||||
),
|
||||
'^', new DecisionTree("^",
|
||||
'=', new DecisionTree("^=")
|
||||
),
|
||||
'=', new DecisionTree("=",
|
||||
'=', new DecisionTree("==")
|
||||
),
|
||||
'!', new DecisionTree("!",
|
||||
'=', new DecisionTree("!=")
|
||||
),
|
||||
'<', new DecisionTree("<",
|
||||
'<', new DecisionTree("<<"),
|
||||
'=', new DecisionTree("<=")
|
||||
),
|
||||
'>', new DecisionTree(">",
|
||||
'>', new DecisionTree(">>"),
|
||||
'=', new DecisionTree(">=")
|
||||
),
|
||||
'&', new DecisionTree(null, // not implemented
|
||||
'&', new DecisionTree("&&")
|
||||
),
|
||||
'|', new DecisionTree(null, // not implemented
|
||||
'|', new DecisionTree("||")
|
||||
),
|
||||
'~', new DecisionTree("~",
|
||||
'=', new DecisionTree("~=")
|
||||
)
|
||||
);
|
||||
|
||||
private static final Set<Character> characterTokens = new HashSet<>();
|
||||
static {
|
||||
characterTokens.add(',');
|
||||
characterTokens.add('(');
|
||||
characterTokens.add(')');
|
||||
characterTokens.add('{');
|
||||
characterTokens.add('}');
|
||||
characterTokens.add(';');
|
||||
characterTokens.add('?');
|
||||
characterTokens.add(':');
|
||||
}
|
||||
|
||||
private static final Set<String> keywords =
|
||||
new HashSet<>(Arrays.asList("if", "else", "while", "do", "for", "break", "continue", "return", "switch", "case", "default"));
|
||||
|
||||
private static final Pattern numberPattern = Pattern.compile("^([0-9]*(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)");
|
||||
private static final Pattern identifierPattern = Pattern.compile("^([A-Za-z][0-9A-Za-z_]*)");
|
||||
|
||||
private List<Token> tokenize() throws LexerException {
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
|
||||
do {
|
||||
skipWhitespace();
|
||||
if (position >= expression.length()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Token token = operatorTree.evaluate(position);
|
||||
if (token != null) {
|
||||
tokens.add(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
final char ch = peek();
|
||||
|
||||
if (characterTokens.contains(ch)) {
|
||||
tokens.add(new CharacterToken(position++, ch));
|
||||
continue;
|
||||
}
|
||||
|
||||
final Matcher numberMatcher = numberPattern.matcher(expression.substring(position));
|
||||
if (numberMatcher.lookingAt()) {
|
||||
String numberPart = numberMatcher.group(1);
|
||||
if (!numberPart.isEmpty()) {
|
||||
try {
|
||||
tokens.add(new NumberToken(position, Double.parseDouble(numberPart)));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new LexerException(position, "Number parsing failed", e);
|
||||
}
|
||||
|
||||
position += numberPart.length();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
final Matcher identifierMatcher = identifierPattern.matcher(expression.substring(position));
|
||||
if (identifierMatcher.lookingAt()) {
|
||||
String identifierPart = identifierMatcher.group(1);
|
||||
if (!identifierPart.isEmpty()) {
|
||||
if (keywords.contains(identifierPart)) {
|
||||
tokens.add(new KeywordToken(position, identifierPart));
|
||||
} else {
|
||||
tokens.add(new IdentifierToken(position, identifierPart));
|
||||
}
|
||||
|
||||
position += identifierPart.length();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new LexerException(position, "Unknown character '" + ch + "'");
|
||||
} while (position < expression.length());
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private char peek() {
|
||||
return expression.charAt(position);
|
||||
}
|
||||
|
||||
private void skipWhitespace() {
|
||||
while (position < expression.length() && Character.isWhitespace(peek())) {
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
public class DecisionTree {
|
||||
private final String tokenName;
|
||||
private final Map<Character, DecisionTree> subTrees = new HashMap<>();
|
||||
|
||||
private DecisionTree(String tokenName, Object... args) {
|
||||
this.tokenName = tokenName;
|
||||
|
||||
if (args.length % 2 != 0) {
|
||||
throw new UnsupportedOperationException("You need to pass an even number of arguments.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i += 2) {
|
||||
if (!(args[i] instanceof Character)) {
|
||||
throw new UnsupportedOperationException("Argument #" + i + " expected to be 'Character', not '" + args[i].getClass().getName() + "'.");
|
||||
}
|
||||
if (!(args[i + 1] instanceof DecisionTree)) {
|
||||
throw new UnsupportedOperationException("Argument #" + (i + 1) + " expected to be 'DecisionTree', not '" + args[i + 1].getClass().getName() + "'.");
|
||||
}
|
||||
|
||||
Character next = (Character) args[i];
|
||||
DecisionTree subTree = (DecisionTree) args[i + 1];
|
||||
|
||||
subTrees.put(next, subTree);
|
||||
}
|
||||
}
|
||||
|
||||
private Token evaluate(int startPosition) throws LexerException {
|
||||
if (position < expression.length()) {
|
||||
final char next = peek();
|
||||
|
||||
final DecisionTree subTree = subTrees.get(next);
|
||||
if (subTree != null) {
|
||||
++position;
|
||||
final Token subTreeResult = subTree.evaluate(startPosition);
|
||||
if (subTreeResult != null) {
|
||||
return subTreeResult;
|
||||
}
|
||||
--position;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new OperatorToken(startPosition, tokenName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user