mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-05 04:26:42 +00:00
Major command changes that don't work yet.
This commit is contained in:
@ -19,7 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.annotation;
|
||||
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import org.enginehub.piston.inject.InjectAnnotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -27,12 +28,13 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotates a {@link Vector3} parameter to inject a direction.
|
||||
* Annotates a {@link BlockVector3} parameter to inject a direction.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@InjectAnnotation
|
||||
public @interface Direction {
|
||||
|
||||
|
||||
String AIM = "me";
|
||||
|
||||
boolean includeDiagonals() default false;
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 org.enginehub.piston.inject.InjectAnnotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotates a {@code List<BlockVector3>} parameter to inject multiple direction.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@InjectAnnotation
|
||||
public @interface MultiDirection {
|
||||
boolean includeDiagonals() default false;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 org.enginehub.piston.inject.InjectAnnotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotates a {@code double} parameter to inject multiple radii values.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@InjectAnnotation
|
||||
public @interface Radii {
|
||||
/**
|
||||
* Number of radii values to inject at maximum. May inject less.
|
||||
*/
|
||||
int value();
|
||||
}
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.annotation;
|
||||
|
||||
import org.enginehub.piston.inject.InjectAnnotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@ -29,6 +31,6 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@InjectAnnotation
|
||||
public @interface Selection {
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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.anvil;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class ChunkDeleter {
|
||||
|
||||
public static final String DELCHUNKS_FILE_NAME = "delete_chunks.json";
|
||||
private static final Logger logger = LoggerFactory.getLogger(ChunkDeleter.class);
|
||||
|
||||
private static final Comparator<BlockVector2> chunkSorter = Comparator.comparing(
|
||||
pos -> (pos.getBlockX() & 31) + (pos.getBlockZ() & 31) * 32);
|
||||
|
||||
private static Gson chunkDeleterGson = new GsonBuilder()
|
||||
.registerTypeAdapter(BlockVector2.class, new BlockVector2Adapter())
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
public static ChunkDeletionInfo readInfo(Path chunkFile) throws IOException, JsonSyntaxException {
|
||||
String json = new String(Files.readAllBytes(chunkFile), StandardCharsets.UTF_8);
|
||||
return chunkDeleterGson.fromJson(json, ChunkDeletionInfo.class);
|
||||
}
|
||||
|
||||
public static void writeInfo(ChunkDeletionInfo info, Path chunkFile) throws IOException, JsonIOException {
|
||||
String json = chunkDeleterGson.toJson(info, new TypeToken<ChunkDeletionInfo>() {}.getType());
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(chunkFile)) {
|
||||
writer.write(json);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runFromFile(Path chunkFile, boolean deleteOnSuccess) {
|
||||
ChunkDeleter chunkDeleter;
|
||||
try {
|
||||
chunkDeleter = createFromFile(chunkFile);
|
||||
} catch (JsonSyntaxException | IOException e) {
|
||||
logger.error("Could not parse chunk deletion file. Invalid file?", e);
|
||||
return;
|
||||
}
|
||||
logger.info("Found chunk deletions. Proceeding with deletion...");
|
||||
long start = System.currentTimeMillis();
|
||||
if (chunkDeleter.runDeleter()) {
|
||||
logger.info("Successfully deleted {} matching chunks (out of {}, taking {} ms).",
|
||||
chunkDeleter.getDeletedChunkCount(), chunkDeleter.getDeletionsRequested(),
|
||||
System.currentTimeMillis() - start);
|
||||
if (deleteOnSuccess) {
|
||||
boolean deletedFile = false;
|
||||
try {
|
||||
deletedFile = Files.deleteIfExists(chunkFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
if (!deletedFile) {
|
||||
logger.warn("Chunk deletion file could not be cleaned up. This may have unintended consequences" +
|
||||
" on next startup, or if /delchunks is used again.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error("Error occurred while deleting chunks. " +
|
||||
"If world errors occur, stop the server and restore the *.bak backup files.");
|
||||
}
|
||||
}
|
||||
|
||||
private ChunkDeleter(ChunkDeletionInfo chunkDeletionInfo) {
|
||||
this.chunkDeletionInfo = chunkDeletionInfo;
|
||||
}
|
||||
|
||||
private static ChunkDeleter createFromFile(Path chunkFile) throws IOException {
|
||||
ChunkDeletionInfo info = readInfo(chunkFile);
|
||||
if (info == null) {
|
||||
throw new IOException("Read null json. Empty file?");
|
||||
}
|
||||
return new ChunkDeleter(info);
|
||||
}
|
||||
|
||||
private final ChunkDeletionInfo chunkDeletionInfo;
|
||||
private Set<Path> backedUpRegions = new HashSet<>();
|
||||
private boolean shouldPreload;
|
||||
private int debugRate = 100;
|
||||
private int totalChunksDeleted = 0;
|
||||
private int deletionsRequested = 0;
|
||||
|
||||
private boolean runDeleter() {
|
||||
return chunkDeletionInfo.batches.stream().allMatch(this::runBatch);
|
||||
}
|
||||
|
||||
private boolean runBatch(ChunkDeletionInfo.ChunkBatch chunkBatch) {
|
||||
int chunkCount = chunkBatch.getChunkCount();
|
||||
logger.debug("Processing deletion batch with {} chunks.", chunkCount);
|
||||
final Map<Path, Stream<BlockVector2>> regionToChunkList = groupChunks(chunkBatch);
|
||||
BiPredicate<RegionAccess, BlockVector2> predicate = createPredicates(chunkBatch.deletionPredicates);
|
||||
shouldPreload = chunkBatch.chunks == null;
|
||||
deletionsRequested += chunkCount;
|
||||
debugRate = chunkCount / 10;
|
||||
|
||||
return regionToChunkList.entrySet().stream().allMatch(entry -> {
|
||||
Path regionPath = entry.getKey();
|
||||
if (!Files.exists(regionPath)) return true;
|
||||
if (chunkBatch.backup && !backedUpRegions.contains(regionPath)) {
|
||||
try {
|
||||
backupRegion(regionPath);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error backing up region file: " + regionPath + ". Aborting the process.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return deleteChunks(regionPath, entry.getValue(), predicate);
|
||||
});
|
||||
}
|
||||
|
||||
private Map<Path, Stream<BlockVector2>> groupChunks(ChunkDeletionInfo.ChunkBatch chunkBatch) {
|
||||
Path worldPath = Paths.get(chunkBatch.worldPath);
|
||||
if (chunkBatch.chunks != null) {
|
||||
return chunkBatch.chunks.stream()
|
||||
.collect(Collectors.groupingBy(RegionFilePos::new))
|
||||
.entrySet().stream().collect(Collectors.toMap(
|
||||
e -> worldPath.resolve("region").resolve(e.getKey().getFileName()),
|
||||
e -> e.getValue().stream().sorted(chunkSorter)));
|
||||
} else {
|
||||
final BlockVector2 minChunk = chunkBatch.minChunk;
|
||||
final BlockVector2 maxChunk = chunkBatch.maxChunk;
|
||||
final RegionFilePos minRegion = new RegionFilePos(minChunk);
|
||||
final RegionFilePos maxRegion = new RegionFilePos(maxChunk);
|
||||
Map<Path, Stream<BlockVector2>> groupedChunks = new HashMap<>();
|
||||
for (int regX = minRegion.getX(); regX <= maxRegion.getX(); regX++) {
|
||||
for (int regZ = minRegion.getZ(); regZ <= maxRegion.getZ(); regZ++) {
|
||||
final Path regionPath = worldPath.resolve("region").resolve(new RegionFilePos(regX, regZ).getFileName());
|
||||
if (!Files.exists(regionPath)) continue;
|
||||
int startX = regX << 5;
|
||||
int endX = (regX << 5) + 31;
|
||||
int startZ = regZ << 5;
|
||||
int endZ = (regZ << 5) + 31;
|
||||
|
||||
int minX = Math.max(Math.min(startX, endX), minChunk.getBlockX());
|
||||
int minZ = Math.max(Math.min(startZ, endZ), minChunk.getBlockZ());
|
||||
int maxX = Math.min(Math.max(startX, endX), maxChunk.getBlockX());
|
||||
int maxZ = Math.min(Math.max(startZ, endZ), maxChunk.getBlockZ());
|
||||
Stream<BlockVector2> stream = Stream.iterate(BlockVector2.at(minX, minZ),
|
||||
bv2 -> {
|
||||
int nextX = bv2.getBlockX();
|
||||
int nextZ = bv2.getBlockZ();
|
||||
if (++nextX > maxX) {
|
||||
nextX = minX;
|
||||
if (++nextZ > maxZ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return BlockVector2.at(nextX, nextZ);
|
||||
});
|
||||
groupedChunks.put(regionPath, stream);
|
||||
}
|
||||
}
|
||||
return groupedChunks;
|
||||
}
|
||||
}
|
||||
|
||||
private BiPredicate<RegionAccess, BlockVector2> createPredicates(List<ChunkDeletionInfo.DeletionPredicate> deletionPredicates) {
|
||||
if (deletionPredicates == null) return (r, p) -> true;
|
||||
return deletionPredicates.stream()
|
||||
.map(this::createPredicate)
|
||||
.reduce(BiPredicate::and)
|
||||
.orElse((r, p) -> true);
|
||||
}
|
||||
|
||||
private BiPredicate<RegionAccess, BlockVector2> createPredicate(ChunkDeletionInfo.DeletionPredicate deletionPredicate) {
|
||||
if ("modification".equals(deletionPredicate.property)) {
|
||||
int time;
|
||||
try {
|
||||
time = Integer.parseInt(deletionPredicate.value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalStateException("Modification time predicate specified invalid time: " + deletionPredicate.value);
|
||||
}
|
||||
switch (deletionPredicate.comparison) {
|
||||
case "<":
|
||||
return (r, p) -> {
|
||||
try {
|
||||
return r.getModificationTime(p) < time;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
case ">":
|
||||
return (r, p) -> {
|
||||
try {
|
||||
return r.getModificationTime(p) > time;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected comparison value: " + deletionPredicate.comparison);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Unexpected property value: " + deletionPredicate.property);
|
||||
}
|
||||
|
||||
private void backupRegion(Path regionFile) throws IOException {
|
||||
Path backupFile = regionFile.resolveSibling(regionFile.getFileName() + ".bak");
|
||||
Files.copy(regionFile, backupFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
backedUpRegions.add(backupFile);
|
||||
}
|
||||
|
||||
private boolean deleteChunks(Path regionFile, Stream<BlockVector2> chunks,
|
||||
BiPredicate<RegionAccess, BlockVector2> deletionPredicate) {
|
||||
try (RegionAccess region = new RegionAccess(regionFile, shouldPreload)) {
|
||||
for (Iterator<BlockVector2> iterator = chunks.iterator(); iterator.hasNext();) {
|
||||
BlockVector2 chunk = iterator.next();
|
||||
if (chunk == null) break;
|
||||
if (deletionPredicate.test(region, chunk)) {
|
||||
region.deleteChunk(chunk);
|
||||
totalChunksDeleted++;
|
||||
if (debugRate != 0 && totalChunksDeleted % debugRate == 0) {
|
||||
logger.debug("Deleted {} chunks so far.", totalChunksDeleted);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Chunk did not match predicates: " + chunk);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error deleting chunks from region: " + regionFile + ". Aborting the process.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getDeletedChunkCount() {
|
||||
return totalChunksDeleted;
|
||||
}
|
||||
|
||||
public int getDeletionsRequested() {
|
||||
return deletionsRequested;
|
||||
}
|
||||
|
||||
private static class BlockVector2Adapter extends TypeAdapter<BlockVector2> {
|
||||
@Override
|
||||
public void write(JsonWriter out, BlockVector2 value) throws IOException {
|
||||
out.beginArray();
|
||||
out.value(value.getBlockX());
|
||||
out.value(value.getBlockZ());
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector2 read(JsonReader in) throws IOException {
|
||||
in.beginArray();
|
||||
int x = in.nextInt();
|
||||
int z = in.nextInt();
|
||||
in.endArray();
|
||||
return BlockVector2.at(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RegionFilePos {
|
||||
private final int x;
|
||||
private final int z;
|
||||
|
||||
RegionFilePos(BlockVector2 chunk) {
|
||||
this.x = chunk.getBlockX() >> 5;
|
||||
this.z = chunk.getBlockZ() >> 5;
|
||||
}
|
||||
|
||||
RegionFilePos(int regX, int regZ) {
|
||||
this.x = regX;
|
||||
this.z = regZ;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return "r." + x + "." + z + ".mca";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
RegionFilePos that = (RegionFilePos) o;
|
||||
|
||||
if (x != that.x) return false;
|
||||
return z == that.z;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = x;
|
||||
result = 31 * result + z;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + ", " + z + ")";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.anvil;
|
||||
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Internal class. Subject to changes.
|
||||
*/
|
||||
public class ChunkDeletionInfo {
|
||||
|
||||
public List<ChunkBatch> batches;
|
||||
|
||||
public static class ChunkBatch {
|
||||
public String worldPath;
|
||||
public boolean backup;
|
||||
public List<DeletionPredicate> deletionPredicates;
|
||||
// specify either list of chunks, or min-max
|
||||
public List<BlockVector2> chunks;
|
||||
public BlockVector2 minChunk;
|
||||
public BlockVector2 maxChunk;
|
||||
|
||||
public int getChunkCount() {
|
||||
if (chunks != null) return chunks.size();
|
||||
final BlockVector2 dist = maxChunk.subtract(minChunk).add(1, 1);
|
||||
return dist.getBlockX() * dist.getBlockZ();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeletionPredicate {
|
||||
public String property;
|
||||
public String comparison;
|
||||
public String value;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.anvil;
|
||||
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Internal class. Subject to changes.
|
||||
*/
|
||||
class RegionAccess implements AutoCloseable {
|
||||
|
||||
private RandomAccessFile raf;
|
||||
private int[] offsets;
|
||||
private int[] timestamps;
|
||||
|
||||
RegionAccess(Path file) throws IOException {
|
||||
this(file, false);
|
||||
}
|
||||
|
||||
RegionAccess(Path file, boolean preload) throws IOException {
|
||||
raf = new RandomAccessFile(file.toFile(), "rw");
|
||||
if (preload) {
|
||||
readHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
private void readHeaders() throws IOException {
|
||||
offsets = new int[1024];
|
||||
timestamps = new int[1024];
|
||||
for (int idx = 0; idx < 1024; ++idx) {
|
||||
offsets[idx] = raf.readInt();
|
||||
}
|
||||
for (int idx = 0; idx < 1024; ++idx) {
|
||||
timestamps[idx] = raf.readInt();
|
||||
}
|
||||
}
|
||||
|
||||
private static int indexChunk(BlockVector2 pos) {
|
||||
int x = pos.getBlockX() & 31;
|
||||
int z = pos.getBlockZ() & 31;
|
||||
return x + z * 32;
|
||||
}
|
||||
|
||||
int getModificationTime(BlockVector2 pos) throws IOException {
|
||||
int idx = indexChunk(pos);
|
||||
if (timestamps != null) {
|
||||
return timestamps[idx];
|
||||
}
|
||||
raf.seek(idx * 4L + 4096);
|
||||
return raf.readInt();
|
||||
}
|
||||
|
||||
int getChunkSize(BlockVector2 pos) throws IOException {
|
||||
int idx = indexChunk(pos);
|
||||
if (offsets != null) {
|
||||
return offsets[idx] & 0xFF;
|
||||
}
|
||||
raf.seek(idx * 4L);
|
||||
// 3 bytes for offset
|
||||
raf.read();
|
||||
raf.read();
|
||||
raf.read();
|
||||
// one byte for size - note, yes, could do raf.readInt() & 0xFF but that does extra checks
|
||||
return raf.read();
|
||||
}
|
||||
|
||||
void deleteChunk(BlockVector2 pos) throws IOException {
|
||||
int idx = indexChunk(pos);
|
||||
raf.seek(idx * 4L);
|
||||
raf.writeInt(0);
|
||||
if (offsets != null) {
|
||||
offsets[idx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
raf.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.command;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.sk89q.worldedit.internal.util.Substring;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CommandArgParser {
|
||||
|
||||
public static ImmutableList<Substring> spaceSplit(String string) {
|
||||
ImmutableList.Builder<Substring> result = ImmutableList.builder();
|
||||
int index = 0;
|
||||
for (String part : Splitter.on(' ').split(string)) {
|
||||
result.add(Substring.from(string, index, index + part.length()));
|
||||
index += part.length() + 1;
|
||||
}
|
||||
return result.build();
|
||||
}
|
||||
|
||||
private enum State {
|
||||
NORMAL,
|
||||
QUOTE
|
||||
}
|
||||
|
||||
private final Stream.Builder<Substring> args = Stream.builder();
|
||||
private final List<Substring> input;
|
||||
private final List<Substring> currentArg = new ArrayList<>();
|
||||
private int index = 0;
|
||||
private State state = State.NORMAL;
|
||||
|
||||
public CommandArgParser(List<Substring> input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public Stream<Substring> parseArgs() {
|
||||
for (; index < input.size(); index++) {
|
||||
Substring nextPart = input.get(index);
|
||||
switch (state) {
|
||||
case NORMAL:
|
||||
handleNormal(nextPart);
|
||||
break;
|
||||
case QUOTE:
|
||||
handleQuote(nextPart);
|
||||
}
|
||||
}
|
||||
return args.build();
|
||||
}
|
||||
|
||||
private void handleNormal(Substring part) {
|
||||
if (part.getSubstring().startsWith("\"")) {
|
||||
state = State.QUOTE;
|
||||
currentArg.add(Substring.wrap(
|
||||
part.getSubstring().substring(1),
|
||||
part.getStart(), part.getEnd()
|
||||
));
|
||||
} else {
|
||||
currentArg.add(part);
|
||||
finishArg();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleQuote(Substring part) {
|
||||
if (part.getSubstring().endsWith("\"")) {
|
||||
state = State.NORMAL;
|
||||
currentArg.add(Substring.wrap(
|
||||
part.getSubstring().substring(0, part.getSubstring().length() - 1),
|
||||
part.getStart(), part.getEnd()
|
||||
));
|
||||
finishArg();
|
||||
} else {
|
||||
currentArg.add(part);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishArg() {
|
||||
// Merge the arguments into a single, space-joined, string
|
||||
// Keep the original start + end points.
|
||||
int start = currentArg.get(0).getStart();
|
||||
int end = Iterables.getLast(currentArg).getEnd();
|
||||
args.add(Substring.wrap(currentArg.stream()
|
||||
.map(Substring::getSubstring)
|
||||
.collect(Collectors.joining(" ")),
|
||||
start, end
|
||||
));
|
||||
currentArg.clear();
|
||||
}
|
||||
|
||||
}
|
@ -19,31 +19,29 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.command;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.Logging;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.sk89q.worldedit.IncompleteRegionException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.command.util.Logging;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import com.sk89q.worldedit.util.command.parametric.AbstractInvokeListener;
|
||||
import com.sk89q.worldedit.util.command.parametric.InvokeHandler;
|
||||
import com.sk89q.worldedit.util.command.parametric.ParameterData;
|
||||
import com.sk89q.worldedit.util.command.parametric.ParameterException;
|
||||
import org.enginehub.piston.CommandParameters;
|
||||
import org.enginehub.piston.gen.CommandCallListener;
|
||||
import org.enginehub.piston.inject.Key;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Logs called commands to a logger.
|
||||
*/
|
||||
public class CommandLoggingHandler extends AbstractInvokeListener implements InvokeHandler, Closeable {
|
||||
public class CommandLoggingHandler implements CommandCallListener, AutoCloseable {
|
||||
|
||||
private final WorldEdit worldEdit;
|
||||
private final Logger logger;
|
||||
@ -62,11 +60,7 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preProcess(Object object, Method method, ParameterData[] parameters, CommandContext context) throws CommandException, ParameterException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preInvoke(Object object, Method method, ParameterData[] parameters, Object[] args, CommandContext context) throws CommandException {
|
||||
public void beforeCall(Method method, CommandParameters parameters) {
|
||||
Logging loggingAnnotation = method.getAnnotation(Logging.class);
|
||||
Logging.LogMode logMode;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@ -77,78 +71,66 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv
|
||||
logMode = loggingAnnotation.value();
|
||||
}
|
||||
|
||||
Actor sender = context.getLocals().get(Actor.class);
|
||||
Player player;
|
||||
Optional<Player> playerOpt = parameters.injectedValue(Key.of(Actor.class))
|
||||
.filter(Player.class::isInstance)
|
||||
.map(Player.class::cast);
|
||||
|
||||
if (sender == null) {
|
||||
if (!playerOpt.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender instanceof Player) {
|
||||
player = (Player) sender;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
Player player = playerOpt.get();
|
||||
|
||||
builder.append("WorldEdit: ").append(sender.getName());
|
||||
if (sender.isPlayer()) {
|
||||
builder.append(" (in \"").append(player.getWorld().getName()).append("\")");
|
||||
}
|
||||
builder.append("WorldEdit: ").append(player.getName());
|
||||
builder.append(" (in \"").append(player.getWorld().getName()).append("\")");
|
||||
|
||||
builder.append(": ").append(context.getCommand());
|
||||
builder.append(": ").append(parameters.getMetadata().getCalledName());
|
||||
|
||||
if (context.argsLength() > 0) {
|
||||
builder.append(" ").append(context.getJoinedStrings(0));
|
||||
}
|
||||
builder.append(": ")
|
||||
.append(Stream.concat(
|
||||
Stream.of(parameters.getMetadata().getCalledName()),
|
||||
parameters.getMetadata().getArguments().stream()
|
||||
).collect(Collectors.joining(" ")));
|
||||
|
||||
if (logMode != null && sender.isPlayer()) {
|
||||
if (logMode != null) {
|
||||
Vector3 position = player.getLocation().toVector();
|
||||
LocalSession session = worldEdit.getSessionManager().get(player);
|
||||
|
||||
switch (logMode) {
|
||||
case PLACEMENT:
|
||||
try {
|
||||
position = session.getPlacementPosition(player).toVector3();
|
||||
} catch (IncompleteRegionException e) {
|
||||
case PLACEMENT:
|
||||
try {
|
||||
position = session.getPlacementPosition(player).toVector3();
|
||||
} catch (IncompleteRegionException e) {
|
||||
break;
|
||||
}
|
||||
/* FALL-THROUGH */
|
||||
|
||||
case POSITION:
|
||||
builder.append(" - Position: ").append(position);
|
||||
break;
|
||||
}
|
||||
/* FALL-THROUGH */
|
||||
|
||||
case POSITION:
|
||||
builder.append(" - Position: ").append(position);
|
||||
break;
|
||||
case ALL:
|
||||
builder.append(" - Position: ").append(position);
|
||||
/* FALL-THROUGH */
|
||||
|
||||
case ALL:
|
||||
builder.append(" - Position: ").append(position);
|
||||
/* FALL-THROUGH */
|
||||
case ORIENTATION_REGION:
|
||||
builder.append(" - Orientation: ").append(player.getCardinalDirection().name());
|
||||
/* FALL-THROUGH */
|
||||
|
||||
case ORIENTATION_REGION:
|
||||
builder.append(" - Orientation: ").append(player.getCardinalDirection().name());
|
||||
/* FALL-THROUGH */
|
||||
|
||||
case REGION:
|
||||
try {
|
||||
builder.append(" - Region: ")
|
||||
case REGION:
|
||||
try {
|
||||
builder.append(" - Region: ")
|
||||
.append(session.getSelection(player.getWorld()));
|
||||
} catch (IncompleteRegionException e) {
|
||||
} catch (IncompleteRegionException e) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(builder.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInvoke(Object object, Method method, ParameterData[] parameters, Object[] args, CommandContext context) throws CommandException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvokeHandler createInvokeHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (Handler h : logger.getHandlers()) {
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.command;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import org.enginehub.piston.CommandManager;
|
||||
import org.enginehub.piston.gen.CommandCallListener;
|
||||
import org.enginehub.piston.gen.CommandRegistration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandRegistrationHandler {
|
||||
|
||||
private static final CommandPermissionsConditionGenerator PERM_GEN = new CommandPermissionsConditionGenerator();
|
||||
|
||||
private final List<CommandCallListener> callListeners;
|
||||
|
||||
public CommandRegistrationHandler(List<CommandCallListener> callListeners) {
|
||||
this.callListeners = ImmutableList.copyOf(callListeners);
|
||||
}
|
||||
|
||||
public <CI> void register(CommandManager manager, CommandRegistration<CI> registration, CI instance) {
|
||||
registration.containerInstance(instance)
|
||||
.commandManager(manager)
|
||||
.listeners(callListeners);
|
||||
if (registration instanceof CommandPermissionsConditionGenerator.Registration) {
|
||||
((CommandPermissionsConditionGenerator.Registration) registration).commandPermissionsConditionGenerator(
|
||||
PERM_GEN
|
||||
);
|
||||
}
|
||||
registration.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.command;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.sk89q.worldedit.extension.platform.PlatformCommandManager;
|
||||
import com.sk89q.worldedit.internal.util.Substring;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import org.enginehub.piston.Command;
|
||||
import org.enginehub.piston.exception.CommandException;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
import org.enginehub.piston.inject.Key;
|
||||
import org.enginehub.piston.part.SubCommandPart;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CommandUtil {
|
||||
|
||||
public static Map<String, Command> getSubCommands(Command currentCommand) {
|
||||
return currentCommand.getParts().stream()
|
||||
.filter(p -> p instanceof SubCommandPart)
|
||||
.flatMap(p -> ((SubCommandPart) p).getCommands().stream())
|
||||
.collect(Collectors.toMap(Command::getName, Function.identity()));
|
||||
}
|
||||
|
||||
private static String clean(String input) {
|
||||
return PlatformCommandManager.COMMAND_CLEAN_PATTERN.matcher(input).replaceAll("");
|
||||
}
|
||||
|
||||
private static final Comparator<Command> BY_CLEAN_NAME =
|
||||
Comparator.comparing(c -> clean(c.getName()));
|
||||
|
||||
public static Comparator<Command> byCleanName() {
|
||||
return BY_CLEAN_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix {@code suggestions} to replace the last space-separated word in {@code arguments}.
|
||||
*/
|
||||
public static List<String> fixSuggestions(String arguments, List<Substring> suggestions) {
|
||||
Substring lastArg = Iterables.getLast(CommandArgParser.spaceSplit(arguments));
|
||||
return suggestions.stream()
|
||||
.map(suggestion -> CommandUtil.suggestLast(lastArg, suggestion))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the last word of a command, mutate the suggestion to replace the last word, if
|
||||
* possible.
|
||||
*/
|
||||
private static Optional<String> suggestLast(Substring last, Substring suggestion) {
|
||||
if (suggestion.getStart() == last.getEnd()) {
|
||||
// this suggestion is for the next argument.
|
||||
if (last.getSubstring().isEmpty()) {
|
||||
return Optional.of(suggestion.getSubstring());
|
||||
}
|
||||
return Optional.of(last.getSubstring() + " " + suggestion.getSubstring());
|
||||
}
|
||||
StringBuilder builder = new StringBuilder(last.getSubstring());
|
||||
int start = suggestion.getStart() - last.getStart();
|
||||
int end = suggestion.getEnd() - last.getStart();
|
||||
if (start < 0) {
|
||||
// Quoted suggestion, can't complete it here.
|
||||
return Optional.empty();
|
||||
}
|
||||
checkState(end <= builder.length(),
|
||||
"Suggestion ends too late, last=%s, suggestion=", last, suggestion);
|
||||
builder.replace(start, end, suggestion.getSubstring());
|
||||
return Optional.of(builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Require {@code condition} to be {@code true}, otherwise throw a {@link CommandException}
|
||||
* with the given message.
|
||||
*
|
||||
* @param condition the condition to check
|
||||
* @param message the message for failure
|
||||
*/
|
||||
public static void checkCommandArgument(boolean condition, String message) {
|
||||
checkCommandArgument(condition, TextComponent.of(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Require {@code condition} to be {@code true}, otherwise throw a {@link CommandException}
|
||||
* with the given message.
|
||||
*
|
||||
* @param condition the condition to check
|
||||
* @param message the message for failure
|
||||
*/
|
||||
public static void checkCommandArgument(boolean condition, Component message) {
|
||||
if (!condition) {
|
||||
throw new CommandException(message, ImmutableList.of());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T requireIV(Key<T> type, String name, InjectedValueAccess injectedValueAccess) {
|
||||
return injectedValueAccess.injectedValue(type).orElseThrow(() ->
|
||||
new IllegalStateException("No injected value for " + name + " (type " + type + ")")
|
||||
);
|
||||
}
|
||||
|
||||
private CommandUtil() {
|
||||
}
|
||||
}
|
@ -357,7 +357,7 @@ public class WorldEditBinding {
|
||||
String input = context.next();
|
||||
if (input != null) {
|
||||
|
||||
if (MathMan.isInteger(input)) return BiomeTypes.get(Integer.parseInt(input));
|
||||
if (MathMan.isInteger(input)) return BiomeTypes.register(Integer.parseInt(input));
|
||||
|
||||
BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager()
|
||||
.queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry();
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.command.exception;
|
||||
|
||||
|
||||
import org.enginehub.piston.exception.CommandException;
|
||||
import org.enginehub.piston.exception.CommandExecutionException;
|
||||
|
||||
/**
|
||||
* Used to convert a recognized {@link Throwable} into an appropriate
|
||||
* {@link CommandException}.
|
||||
*
|
||||
* <p>Methods may throw relevant exceptions that are not caught by the command manager,
|
||||
* but translate into reasonable exceptions for an application. However, unknown exceptions are
|
||||
* normally simply wrapped in a {@link CommandExecutionException} and bubbled up. Only
|
||||
* normal {@link CommandException}s will be printed correctly, so a converter translates
|
||||
* one of these unknown exceptions into an appropriate {@link CommandException}.</p>
|
||||
*
|
||||
* <p>This also allows the code calling the command to not need be aware of these
|
||||
* application-specific exceptions, as they will all be converted to
|
||||
* {@link CommandException}s that are handled normally.</p>
|
||||
*/
|
||||
public interface ExceptionConverter {
|
||||
|
||||
/**
|
||||
* Attempt to convert the given throwable into a {@link CommandException}.
|
||||
*
|
||||
* <p>If the exception is not recognized, then nothing should be thrown.</p>
|
||||
*
|
||||
* @param t the throwable
|
||||
* @throws CommandException a command exception
|
||||
*/
|
||||
void convert(Throwable t) throws CommandException;
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.command.exception;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.enginehub.piston.exception.CommandException;
|
||||
import org.enginehub.piston.exception.CommandExecutionException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link ExceptionConverter} that automatically calls
|
||||
* the correct method defined on this object.
|
||||
*
|
||||
* <p>Only public methods will be used. Methods will be called in order of decreasing
|
||||
* levels of inheritance (between classes where one inherits the other). For two
|
||||
* different inheritance branches, the order between them is undefined.</p>
|
||||
*/
|
||||
public abstract class ExceptionConverterHelper implements ExceptionConverter {
|
||||
|
||||
private final List<ExceptionHandler> handlers;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ExceptionConverterHelper() {
|
||||
List<ExceptionHandler> handlers = new ArrayList<>();
|
||||
|
||||
for (Method method : this.getClass().getMethods()) {
|
||||
if (method.getAnnotation(ExceptionMatch.class) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?>[] parameters = method.getParameterTypes();
|
||||
if (parameters.length == 1) {
|
||||
Class<?> cls = parameters[0];
|
||||
if (Throwable.class.isAssignableFrom(cls)) {
|
||||
handlers.add(new ExceptionHandler(
|
||||
(Class<? extends Throwable>) cls, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(handlers);
|
||||
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convert(Throwable t) throws CommandException {
|
||||
Class<?> throwableClass = t.getClass();
|
||||
for (ExceptionHandler handler : handlers) {
|
||||
if (handler.cls.isAssignableFrom(throwableClass)) {
|
||||
try {
|
||||
handler.method.invoke(this, t);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof CommandException) {
|
||||
throw (CommandException) e.getCause();
|
||||
}
|
||||
if (e.getCause() instanceof com.sk89q.minecraft.util.commands.CommandException) {
|
||||
throw new CommandException(e.getCause(), ImmutableList.of());
|
||||
}
|
||||
throw new CommandExecutionException(e, ImmutableList.of());
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new CommandExecutionException(e, ImmutableList.of());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExceptionHandler implements Comparable<ExceptionHandler> {
|
||||
final Class<? extends Throwable> cls;
|
||||
final Method method;
|
||||
|
||||
private ExceptionHandler(Class<? extends Throwable> cls, Method method) {
|
||||
this.cls = cls;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ExceptionHandler o) {
|
||||
if (cls.equals(o.cls)) {
|
||||
return 0;
|
||||
} else if (cls.isAssignableFrom(o.cls)) {
|
||||
return 1;
|
||||
} else if (o.cls.isAssignableFrom(cls)) {
|
||||
return -1;
|
||||
} else {
|
||||
return cls.getCanonicalName().compareTo(o.cls.getCanonicalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.command.exception;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Denotes a match of an exception.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExceptionMatch {
|
||||
|
||||
}
|
@ -17,11 +17,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.command;
|
||||
package com.sk89q.worldedit.internal.command.exception;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.worldedit.DisallowedItemException;
|
||||
import com.sk89q.worldedit.EmptyClipboardException;
|
||||
import com.sk89q.worldedit.IncompleteRegionException;
|
||||
@ -37,11 +36,12 @@ import com.sk89q.worldedit.command.InsufficientArgumentsException;
|
||||
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.util.command.parametric.ExceptionConverterHelper;
|
||||
import com.sk89q.worldedit.util.command.parametric.ExceptionMatch;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.io.file.FileSelectionAbortedException;
|
||||
import com.sk89q.worldedit.util.io.file.FilenameResolutionException;
|
||||
import com.sk89q.worldedit.util.io.file.InvalidFilenameException;
|
||||
import org.enginehub.piston.exception.CommandException;
|
||||
import org.enginehub.piston.exception.UsageException;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -59,105 +59,115 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper {
|
||||
this.worldEdit = worldEdit;
|
||||
}
|
||||
|
||||
private CommandException newCommandException(String message, Throwable cause) {
|
||||
return new CommandException(TextComponent.of(String.valueOf(message)), cause, ImmutableList.of());
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(NumberFormatException e) throws CommandException {
|
||||
final Matcher matcher = numberFormat.matcher(e.getMessage());
|
||||
|
||||
if (matcher.matches()) {
|
||||
throw new CommandException("Number expected; string \"" + matcher.group(1)
|
||||
+ "\" given.");
|
||||
throw newCommandException("Number expected; string \"" + matcher.group(1)
|
||||
+ "\" given.", e);
|
||||
} else {
|
||||
throw new CommandException("Number expected; string given.");
|
||||
throw newCommandException("Number expected; string given.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(IncompleteRegionException e) throws CommandException {
|
||||
throw new CommandException("Make a region selection first.");
|
||||
throw newCommandException("Make a region selection first.", e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(UnknownItemException e) throws CommandException {
|
||||
throw new CommandException("Block name '" + e.getID() + "' was not recognized.");
|
||||
throw newCommandException("Block name '" + e.getID() + "' was not recognized.", e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(InvalidItemException e) throws CommandException {
|
||||
throw new CommandException(e.getMessage());
|
||||
throw newCommandException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(DisallowedItemException e) throws CommandException {
|
||||
throw new CommandException("Block '" + e.getID()
|
||||
+ "' not allowed (see WorldEdit configuration).");
|
||||
throw newCommandException("Block '" + e.getID()
|
||||
+ "' not allowed (see WorldEdit configuration).", e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(MaxChangedBlocksException e) throws CommandException {
|
||||
throw new CommandException("Max blocks changed in an operation reached ("
|
||||
+ e.getBlockLimit() + ").");
|
||||
throw newCommandException("Max blocks changed in an operation reached ("
|
||||
+ e.getBlockLimit() + ").", e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(MaxBrushRadiusException e) throws CommandException {
|
||||
throw new CommandException("Maximum brush radius (in configuration): " + worldEdit.getConfiguration().maxBrushRadius);
|
||||
throw newCommandException("Maximum brush radius (in configuration): " + worldEdit.getConfiguration().maxBrushRadius, e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(MaxRadiusException e) throws CommandException {
|
||||
throw new CommandException("Maximum radius (in configuration): " + worldEdit.getConfiguration().maxRadius);
|
||||
throw newCommandException("Maximum radius (in configuration): " + worldEdit.getConfiguration().maxRadius, e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(UnknownDirectionException e) throws CommandException {
|
||||
throw new CommandException("Unknown direction: " + e.getDirection());
|
||||
throw newCommandException("Unknown direction: " + e.getDirection(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(InsufficientArgumentsException e) throws CommandException {
|
||||
throw new CommandException(e.getMessage());
|
||||
throw newCommandException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(RegionOperationException e) throws CommandException {
|
||||
throw new CommandException(e.getMessage());
|
||||
throw newCommandException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(ExpressionException e) throws CommandException {
|
||||
throw new CommandException(e.getMessage());
|
||||
throw newCommandException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(EmptyClipboardException e) throws CommandException {
|
||||
throw new CommandException("Your clipboard is empty. Use //copy first.");
|
||||
throw newCommandException("Your clipboard is empty. Use //copy first.", e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(InvalidFilenameException e) throws CommandException {
|
||||
throw new CommandException("Filename '" + e.getFilename() + "' invalid: "
|
||||
+ e.getMessage());
|
||||
throw newCommandException("Filename '" + e.getFilename() + "' invalid: "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(FilenameResolutionException e) throws CommandException {
|
||||
throw new CommandException(
|
||||
"File '" + e.getFilename() + "' resolution error: " + e.getMessage());
|
||||
throw newCommandException(
|
||||
"File '" + e.getFilename() + "' resolution error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(InvalidToolBindException e) throws CommandException {
|
||||
throw new CommandException("Can't bind tool to " + e.getItemType().getName() + ": " + e.getMessage());
|
||||
throw newCommandException("Can't bind tool to " + e.getItemType().getName() + ": " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(FileSelectionAbortedException e) throws CommandException {
|
||||
throw new CommandException("File selection aborted.");
|
||||
throw newCommandException("File selection aborted.", e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(WorldEditException e) throws CommandException {
|
||||
throw new CommandException(e.getMessage(), e);
|
||||
throw newCommandException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Prevent investigation into UsageExceptions
|
||||
@ExceptionMatch
|
||||
public void convert(UsageException e) throws CommandException {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
@ -32,6 +32,8 @@ 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.session.request.Request;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -159,7 +161,18 @@ public class Expression {
|
||||
}
|
||||
|
||||
private double evaluateRootTimed(int timeout) throws EvaluationException {
|
||||
Future<Double> result = evalThread.submit(this::evaluateRoot);
|
||||
Request request = Request.request();
|
||||
Future<Double> result = evalThread.submit(() -> {
|
||||
Request local = Request.request();
|
||||
local.setSession(request.getSession());
|
||||
local.setWorld(request.getWorld());
|
||||
local.setEditSession(request.getEditSession());
|
||||
try {
|
||||
return Expression.this.evaluateRoot();
|
||||
} finally {
|
||||
Request.reset();
|
||||
}
|
||||
});
|
||||
try {
|
||||
return result.get(timeout, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -54,7 +54,7 @@ public class Function extends Node {
|
||||
return invokeMethod(method, args);
|
||||
}
|
||||
|
||||
public static double invokeMethod(Method method, Object[] args) throws EvaluationException {
|
||||
protected static double invokeMethod(Method method, Object[] args) throws EvaluationException {
|
||||
try {
|
||||
return (Double) method.invoke(null, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
|
@ -30,6 +30,48 @@ public final class MCDirections {
|
||||
}
|
||||
|
||||
public static Direction fromHanging(int i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return Direction.DOWN;
|
||||
case 1:
|
||||
return Direction.UP;
|
||||
case 2:
|
||||
return Direction.NORTH;
|
||||
case 3:
|
||||
return Direction.SOUTH;
|
||||
case 4:
|
||||
return Direction.WEST;
|
||||
case 5:
|
||||
return Direction.EAST;
|
||||
default:
|
||||
return Direction.DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static int toHanging(Direction direction) {
|
||||
switch (direction) {
|
||||
case DOWN:
|
||||
return 0;
|
||||
case UP:
|
||||
return 1;
|
||||
case NORTH:
|
||||
return 2;
|
||||
case SOUTH:
|
||||
return 3;
|
||||
case WEST:
|
||||
return 4;
|
||||
case EAST:
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static Direction fromPre13Hanging(int i) {
|
||||
return fromHorizontalHanging(i);
|
||||
}
|
||||
|
||||
public static Direction fromHorizontalHanging(int i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return Direction.SOUTH;
|
||||
@ -44,7 +86,7 @@ public final class MCDirections {
|
||||
}
|
||||
}
|
||||
|
||||
public static int toHanging(Direction direction) {
|
||||
public static int toHorizontalHanging(Direction direction) {
|
||||
switch (direction) {
|
||||
case SOUTH:
|
||||
return 0;
|
||||
@ -68,15 +110,6 @@ public final class MCDirections {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte toLegacyHanging(int i) {
|
||||
switch (i) {
|
||||
case 0: return (byte) 2;
|
||||
case 1: return (byte) 1;
|
||||
case 2: return (byte) 0;
|
||||
default: return (byte) 3;
|
||||
}
|
||||
}
|
||||
|
||||
public static Direction fromRotation(int i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
|
@ -29,6 +29,7 @@ import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* An abstract implementation of a factory for internal usage.
|
||||
@ -45,10 +46,13 @@ public abstract class AbstractFactory<E> {
|
||||
* Create a new factory.
|
||||
*
|
||||
* @param worldEdit the WorldEdit instance
|
||||
* @param defaultParser the parser to fall back to
|
||||
*/
|
||||
protected AbstractFactory(WorldEdit worldEdit) {
|
||||
protected AbstractFactory(WorldEdit worldEdit, InputParser<E> defaultParser) {
|
||||
checkNotNull(worldEdit);
|
||||
checkNotNull(defaultParser);
|
||||
this.worldEdit = worldEdit;
|
||||
this.parsers.add(defaultParser);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,6 +80,12 @@ public abstract class AbstractFactory<E> {
|
||||
throw new NoMatchException("No match for '" + input + "'");
|
||||
}
|
||||
|
||||
public List<String> getSuggestions(String input) {
|
||||
return parsers.stream().flatMap(
|
||||
p -> p.getSuggestions(input)
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an InputParser to this factory
|
||||
*
|
||||
@ -84,6 +94,6 @@ public abstract class AbstractFactory<E> {
|
||||
public void register(InputParser<E> inputParser) {
|
||||
checkNotNull(inputParser);
|
||||
|
||||
parsers.add(inputParser);
|
||||
parsers.add(parsers.size() - 1, inputParser);
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,7 @@ import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Input parser interface for {@link AbstractFactory}.
|
||||
@ -43,11 +42,11 @@ public abstract class InputParser<E> {
|
||||
public abstract E parseFromInput(String input, ParserContext context) throws InputParseException;
|
||||
|
||||
/**
|
||||
* Gets a list of suggestions of input to this parser.
|
||||
* Gets a stream of suggestions of input to this parser.
|
||||
*
|
||||
* @return a list of suggestions
|
||||
* @return a stream of suggestions
|
||||
*/
|
||||
public List<String> getSuggestions() {
|
||||
return Collections.emptyList();
|
||||
public Stream<String> getSuggestions(String input) {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,13 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.registry;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* An input parser that only performs a single function from aliases.
|
||||
@ -65,7 +66,16 @@ public abstract class SimpleInputParser<E> extends InputParser<E> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSuggestions() {
|
||||
return Lists.newArrayList(getPrimaryMatcher());
|
||||
public Stream<String> getSuggestions(String input) {
|
||||
if (input.isEmpty()) {
|
||||
return Stream.of(getPrimaryMatcher());
|
||||
}
|
||||
final String prefix = input.toLowerCase(Locale.ROOT);
|
||||
for (String alias : getMatchedAliases()) {
|
||||
if (alias.startsWith(prefix)) {
|
||||
return Stream.of(alias);
|
||||
}
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.util;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||
import com.sk89q.minecraft.util.commands.NestedCommand;
|
||||
import com.sk89q.worldedit.command.*;
|
||||
@ -79,10 +79,10 @@ public final class DocumentationPrinter {
|
||||
if (!f.getName().matches("^.*\\.java$")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
String className = "com.sk89q.worldedit.commands."
|
||||
+ f.getName().substring(0, f.getName().lastIndexOf("."));
|
||||
|
||||
|
||||
Class<?> cls;
|
||||
try {
|
||||
cls = Class.forName(className, true,
|
||||
@ -90,7 +90,7 @@ public final class DocumentationPrinter {
|
||||
} catch (ClassNotFoundException e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
classes.add(cls);
|
||||
}*/
|
||||
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 java.util.Objects;
|
||||
|
||||
/**
|
||||
* An explicit substring. Provides the range from which it was taken.
|
||||
*/
|
||||
public final class Substring {
|
||||
|
||||
/**
|
||||
* Take a substring from {@code original}, and {@link #wrap(String, int, int)} it into
|
||||
* a Substring.
|
||||
*/
|
||||
public static Substring from(String original, int start) {
|
||||
return wrap(original.substring(start), start, original.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a substring from {@code original}, and {@link #wrap(String, int, int)} it into
|
||||
* a Substring.
|
||||
*/
|
||||
public static Substring from(String original, int start, int end) {
|
||||
return wrap(original.substring(start, end), start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given parameters into a Substring instance.
|
||||
*/
|
||||
public static Substring wrap(String substring, int start, int end) {
|
||||
return new Substring(substring, start, end);
|
||||
}
|
||||
|
||||
private final String substring;
|
||||
private final int start;
|
||||
private final int end;
|
||||
|
||||
private Substring(String substring, int start, int end) {
|
||||
this.substring = substring;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public String getSubstring() {
|
||||
return substring;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Substring substring1 = (Substring) o;
|
||||
return start == substring1.start &&
|
||||
end == substring1.end &&
|
||||
substring.equals(substring1.substring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(substring, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Substring{" +
|
||||
"substring='" + substring + "'" +
|
||||
",start=" + start +
|
||||
",end=" + end +
|
||||
"}";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user