Allow over/undersizes schematics to be saved and pasted (#2944)

Co-authored-by: NotMyFault <mc.cache@web.de>
This commit is contained in:
dordsor21 2021-01-03 22:46:53 +00:00 committed by GitHub
parent 01dd2d8097
commit 4c0bc79e49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 226 additions and 182 deletions

View File

@ -38,7 +38,7 @@ public interface BackupManager {
/** /**
* This will perform an automatic backup of the plot iff the plot has an owner, * This will perform an automatic backup of the plot iff the plot has an owner,
* automatic backups are enabled and the plot is not merged. * automatic backups are enabled.
* Otherwise it will complete immediately. * Otherwise it will complete immediately.
* *
* @param player Player that triggered the backup * @param player Player that triggered the backup
@ -60,7 +60,7 @@ public interface BackupManager {
/** /**
* This will perform an automatic backup of the plot iff the plot has an owner, * This will perform an automatic backup of the plot iff the plot has an owner,
* automatic backups are enabled and the plot is not merged. * automatic backups are enabled.
* Otherwise it will complete immediately. * Otherwise it will complete immediately.
* *
* @param player Player that triggered the backup * @param player Player that triggered the backup

View File

@ -77,7 +77,7 @@ import java.util.concurrent.TimeUnit;
} }
@Override @Nonnull public BackupProfile getProfile(@Nonnull final Plot plot) { @Override @Nonnull public BackupProfile getProfile(@Nonnull final Plot plot) {
if (plot.hasOwner() && !plot.isMerged()) { if (plot.hasOwner()) {
try { try {
return backupProfileCache.get(new PlotCacheKey(plot), () -> this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot)); return backupProfileCache.get(new PlotCacheKey(plot), () -> this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot));
} catch (ExecutionException e) { } catch (ExecutionException e) {

View File

@ -99,10 +99,6 @@ public class Download extends SubCommand {
player.sendMessage(TranslatableCaption.of("permission.no_plot_perms")); player.sendMessage(TranslatableCaption.of("permission.no_plot_perms"));
return false; return false;
} }
if (plot.isMerged()) {
player.sendMessage(TranslatableCaption.of("web.plot_merged"));
return false;
}
if (plot.getRunning() > 0) { if (plot.getRunning() > 0) {
player.sendMessage(TranslatableCaption.of("errors.wait_for_timer")); player.sendMessage(TranslatableCaption.of("errors.wait_for_timer"));
return false; return false;

View File

@ -116,10 +116,6 @@ public class SchematicCmd extends SubCommand {
player.sendMessage(TranslatableCaption.of("error.task_in_process")); player.sendMessage(TranslatableCaption.of("error.task_in_process"));
return false; return false;
} }
if (plot.isMerged()) {
player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_merged"));
return false;
}
final String location = args[1]; final String location = args[1];
this.running = true; this.running = true;
TaskManager.runTaskAsync(() -> { TaskManager.runTaskAsync(() -> {
@ -154,7 +150,7 @@ public class SchematicCmd extends SubCommand {
); );
return; return;
} }
this.schematicHandler.paste(schematic, plot, 0, 1, 0, false, player, new RunnableVal<Boolean>() { this.schematicHandler.paste(schematic, plot, 0, plot.getArea().getMinBuildHeight(), 0, false, player, new RunnableVal<Boolean>() {
@Override public void run(Boolean value) { @Override public void run(Boolean value) {
SchematicCmd.this.running = false; SchematicCmd.this.running = false;
if (value) { if (value) {

View File

@ -403,6 +403,10 @@ public class Settings extends Config {
@Comment( @Comment(
"Whether schematic based road generation should paste schematic on top of roads, or from Y=1") "Whether schematic based road generation should paste schematic on top of roads, or from Y=1")
public static boolean PASTE_ROAD_ON_TOP = true; public static boolean PASTE_ROAD_ON_TOP = true;
@Comment({"If schematics that do not match a plot's size should be pasted anyway",
" - This will still only paste a schematic with a plot's bounds.",
" - If a schematic is too big, it will cut off, and if too small, will not full the plot."})
public static boolean PASTE_MISMATCHES = true;
} }

View File

@ -1735,7 +1735,7 @@ public class Plot {
/** /**
* Gets the top loc of a plot (if mega, returns top loc of that mega plot) - If you would like each plot treated as * Gets the top loc of a plot (if mega, returns top loc of that mega plot) - If you would like each plot treated as
* a small plot use getPlotTopLocAbs(...) * a small plot use {@link #getTopAbs()}
* *
* @return Location top of mega plot * @return Location top of mega plot
*/ */
@ -1754,9 +1754,8 @@ public class Plot {
} }
/** /**
* Gets the bottom location for a plot.<br> * Gets the bot loc of a plot (if mega, returns bot loc of that mega plot) - If you would like each plot treated as
* - Does not respect mega plots<br> * a small plot use {@link #getBottomAbs()}
* - Merged plots, only the road will be considered part of the plot<br>
* *
* @return Location bottom of mega plot * @return Location bottom of mega plot
*/ */
@ -2362,6 +2361,8 @@ public class Plot {
bot = PlotId.of(bot.getX() - 1, bot.getX()); bot = PlotId.of(bot.getX() - 1, bot.getX());
} }
} }
int minHeight = getArea().getMinBuildHeight();
int maxHeight = getArea().getMaxBuildHeight();
Location gtopabs = this.area.getPlotAbs(top).getTopAbs(); Location gtopabs = this.area.getPlotAbs(top).getTopAbs();
Location gbotabs = this.area.getPlotAbs(bot).getBottomAbs(); Location gbotabs = this.area.getPlotAbs(bot).getBottomAbs();
visited.addAll(Lists.newArrayList((Iterable<? extends PlotId>) PlotId.PlotRangeIterator.range(bot, top))); visited.addAll(Lists.newArrayList((Iterable<? extends PlotId>) PlotId.PlotRangeIterator.range(bot, top)));
@ -2372,12 +2373,12 @@ public class Plot {
Location toploc = plot.getExtendedTopAbs(); Location toploc = plot.getExtendedTopAbs();
Location botabs = plot.getBottomAbs(); Location botabs = plot.getBottomAbs();
Location topabs = plot.getTopAbs(); Location topabs = plot.getTopAbs();
BlockVector3 pos1 = BlockVector3.at(botabs.getX(), 0, topabs.getZ() + 1); BlockVector3 pos1 = BlockVector3.at(botabs.getX(), minHeight, topabs.getZ() + 1);
BlockVector3 pos2 = BlockVector3.at(topabs.getX(), Plot.MAX_HEIGHT - 1, toploc.getZ()); BlockVector3 pos2 = BlockVector3.at(topabs.getX(), maxHeight, toploc.getZ());
regions.add(new CuboidRegion(pos1, pos2)); regions.add(new CuboidRegion(pos1, pos2));
if (plot.isMerged(Direction.SOUTHEAST)) { if (plot.isMerged(Direction.SOUTHEAST)) {
pos1 = BlockVector3.at(topabs.getX() + 1, 0, topabs.getZ() + 1); pos1 = BlockVector3.at(topabs.getX() + 1, minHeight, topabs.getZ() + 1);
pos2 = BlockVector3.at(toploc.getX(), Plot.MAX_HEIGHT - 1, toploc.getZ()); pos2 = BlockVector3.at(toploc.getX(), maxHeight, toploc.getZ());
regions.add(new CuboidRegion(pos1, pos2)); regions.add(new CuboidRegion(pos1, pos2));
// intersection // intersection
} }
@ -2391,19 +2392,19 @@ public class Plot {
Location toploc = plot.getExtendedTopAbs(); Location toploc = plot.getExtendedTopAbs();
Location botabs = plot.getBottomAbs(); Location botabs = plot.getBottomAbs();
Location topabs = plot.getTopAbs(); Location topabs = plot.getTopAbs();
BlockVector3 pos1 = BlockVector3.at(topabs.getX() + 1, 0, botabs.getZ()); BlockVector3 pos1 = BlockVector3.at(topabs.getX() + 1, minHeight, botabs.getZ());
BlockVector3 pos2 = BlockVector3.at(toploc.getX(), Plot.MAX_HEIGHT - 1, topabs.getZ()); BlockVector3 pos2 = BlockVector3.at(toploc.getX(), maxHeight, topabs.getZ());
regions.add(new CuboidRegion(pos1, pos2)); regions.add(new CuboidRegion(pos1, pos2));
if (plot.isMerged(Direction.SOUTHEAST)) { if (plot.isMerged(Direction.SOUTHEAST)) {
pos1 = BlockVector3.at(topabs.getX() + 1, 0, topabs.getZ() + 1); pos1 = BlockVector3.at(topabs.getX() + 1, minHeight, topabs.getZ() + 1);
pos2 = BlockVector3.at(toploc.getX(), Plot.MAX_HEIGHT - 1, toploc.getZ()); pos2 = BlockVector3.at(toploc.getX(), maxHeight, toploc.getZ());
regions.add(new CuboidRegion(pos1, pos2)); regions.add(new CuboidRegion(pos1, pos2));
// intersection // intersection
} }
} }
} }
BlockVector3 pos1 = BlockVector3.at(gbotabs.getX(), 0, gbotabs.getZ()); BlockVector3 pos1 = BlockVector3.at(gbotabs.getX(), minHeight, gbotabs.getZ());
BlockVector3 pos2 = BlockVector3.at(gtopabs.getX(), Plot.MAX_HEIGHT - 1, gtopabs.getZ()); BlockVector3 pos2 = BlockVector3.at(gtopabs.getX(), maxHeight, gtopabs.getZ());
regions.add(new CuboidRegion(pos1, pos2)); regions.add(new CuboidRegion(pos1, pos2));
} }
return regions; return regions;

View File

@ -25,12 +25,13 @@
*/ */
package com.plotsquared.core.util; package com.plotsquared.core.util;
import com.google.inject.Inject;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.google.inject.Inject;
import com.plotsquared.core.PlotSquared; import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.generator.ClassicPlotWorld; import com.plotsquared.core.generator.ClassicPlotWorld;
import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.inject.factory.ProgressSubscriberFactory;
import com.plotsquared.core.location.Location; import com.plotsquared.core.location.Location;
@ -64,8 +65,12 @@ import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicReader;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionIntersection;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -90,9 +95,6 @@ import java.net.URLConnection;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -114,7 +116,6 @@ public abstract class SchematicHandler {
private static final Logger logger = LoggerFactory.getLogger("P2/" + SchematicHandler.class.getSimpleName()); private static final Logger logger = LoggerFactory.getLogger("P2/" + SchematicHandler.class.getSimpleName());
private static final Gson GSON = new Gson(); private static final Gson GSON = new Gson();
private static final Path TEMP_DIR = Paths.get("TODO-PATH");
public static SchematicHandler manager; public static SchematicHandler manager;
private final WorldUtil worldUtil; private final WorldUtil worldUtil;
private boolean exportAll = false; private boolean exportAll = false;
@ -280,84 +281,104 @@ public abstract class SchematicHandler {
final boolean autoHeight, final boolean autoHeight,
final PlotPlayer<?> actor, final PlotPlayer<?> actor,
final RunnableVal<Boolean> whenDone) { final RunnableVal<Boolean> whenDone) {
if (whenDone != null) {
TaskManager.runTask(() -> { whenDone.value = false;
if (whenDone != null) { }
whenDone.value = false; if (schematic == null) {
} TaskManager.runTask(whenDone);
if (schematic == null) { return;
}
try {
BlockVector3 dimension = schematic.getClipboard().getDimensions();
final int WIDTH = dimension.getX();
final int LENGTH = dimension.getZ();
final int HEIGHT = dimension.getY();
// Validate dimensions
CuboidRegion region = plot.getLargestRegion();
boolean sizeMismatch =
((region.getMaximumPoint().getX() - region.getMinimumPoint().getX() + xOffset + 1) < WIDTH) || (
(region.getMaximumPoint().getZ() - region.getMinimumPoint().getZ() + zOffset + 1) < LENGTH) || (HEIGHT
> 256);
if (!Settings.Schematics.PASTE_MISMATCHES && sizeMismatch) {
actor.sendMessage(TranslatableCaption.of("schematics.schematic_size_mismatch"));
TaskManager.runTask(whenDone); TaskManager.runTask(whenDone);
return; return;
} }
try { // block type and data arrays
BlockVector3 dimension = schematic.getClipboard().getDimensions(); final Clipboard blockArrayClipboard = schematic.getClipboard();
final int WIDTH = dimension.getX(); // Calculate the optimal height to paste the schematic at
final int LENGTH = dimension.getZ(); final int y_offset_actual;
final int HEIGHT = dimension.getY(); if (autoHeight) {
// Validate dimensions if (HEIGHT >= 256) {
CuboidRegion region = plot.getLargestRegion();
if (((region.getMaximumPoint().getX() - region.getMinimumPoint().getX() + xOffset + 1) < WIDTH) || (
(region.getMaximumPoint().getZ() - region.getMinimumPoint().getZ() + zOffset + 1) < LENGTH) || (HEIGHT > 256)) {
TaskManager.runTask(whenDone);
return;
}
// block type and data arrays
final Clipboard blockArrayClipboard = schematic.getClipboard();
// Calculate the optimal height to paste the schematic at
final int y_offset_actual;
if (autoHeight) {
if (HEIGHT >= 256) {
y_offset_actual = yOffset;
} else {
PlotArea pw = plot.getArea();
if (pw instanceof ClassicPlotWorld) {
y_offset_actual = yOffset + ((ClassicPlotWorld) pw).PLOT_HEIGHT;
} else {
y_offset_actual = yOffset + 1 + this.worldUtil
.getHighestBlockSynchronous(plot.getWorldName(), region.getMinimumPoint().getX() + 1,
region.getMinimumPoint().getZ() + 1);
}
}
} else {
y_offset_actual = yOffset; y_offset_actual = yOffset;
} } else {
PlotArea pw = plot.getArea();
final Location pos1 = Location if (pw instanceof ClassicPlotWorld) {
.at(plot.getWorldName(), region.getMinimumPoint().getX() + xOffset, y_offset_actual, region.getMinimumPoint().getZ() + zOffset); y_offset_actual = yOffset + pw.getMinBuildHeight() + ((ClassicPlotWorld) pw).PLOT_HEIGHT;
} else {
final int p1x = pos1.getX(); y_offset_actual = yOffset + 1 + this.worldUtil
final int p1z = pos1.getZ(); .getHighestBlockSynchronous(plot.getWorldName(), region.getMinimumPoint().getX() + 1,
// Paste schematic here region.getMinimumPoint().getZ() + 1);
final QueueCoordinator queue = plot.getArea().getQueue();
for (int ry = 0; ry < Math.min(256, HEIGHT); ry++) {
int yy = y_offset_actual + ry;
if (yy > 255) {
continue;
} }
for (int rz = 0; rz <= blockArrayClipboard.getDimensions().getZ(); rz++) { }
for (int rx = 0; rx < blockArrayClipboard.getDimensions().getX(); rx++) { } else {
int xx = p1x + rx; y_offset_actual = yOffset;
int zz = p1z + rz; }
BaseBlock id = blockArrayClipboard.getFullBlock(BlockVector3.at(rx, ry, rz));
queue.setBlock(xx, yy, zz, id); final int p1x;
if (ry == 0) { final int p1z;
BiomeType biome = blockArrayClipboard.getBiome(BlockVector3.at(rx, ry, rz)); final int p2x;
queue.setBiome(xx, yy, zz, biome); final int p2z;
} final Region allRegion;
if (!sizeMismatch || plot.getRegions().size() == 1) {
p1x = region.getMinimumPoint().getX() + xOffset;
p1z = region.getMinimumPoint().getZ() + zOffset;
p2x = region.getMaximumPoint().getX() + xOffset;
p2z = region.getMaximumPoint().getZ() + zOffset;
allRegion = region;
} else {
Location[] corners = plot.getCorners();
p1x = corners[0].getX() + xOffset;
p1z = corners[0].getZ() + zOffset;
p2x = corners[1].getX() + xOffset;
p2z = corners[1].getZ() + zOffset;
allRegion = new RegionIntersection(null, plot.getRegions().toArray(new CuboidRegion[] {}));
}
// Paste schematic here
final QueueCoordinator queue = plot.getArea().getQueue();
for (int ry = 0; ry < Math.min(256, HEIGHT); ry++) {
int yy = y_offset_actual + ry;
if (yy > 255 || yy < 0) {
continue;
}
for (int rz = 0; rz <= blockArrayClipboard.getDimensions().getZ(); rz++) {
for (int rx = 0; rx < blockArrayClipboard.getDimensions().getX(); rx++) {
int xx = p1x + rx;
int zz = p1z + rz;
if (sizeMismatch && (xx < p1x || xx > p2x || zz < p1z || zz > p2z || !allRegion.contains(BlockVector3.at(xx, ry, zz)))) {
continue;
}
BlockVector3 loc = BlockVector3.at(rx, ry, rz);
BaseBlock id = blockArrayClipboard.getFullBlock(loc);
queue.setBlock(xx, yy, zz, id);
if (ry == 0) {
BiomeType biome = blockArrayClipboard.getBiome(loc);
queue.setBiome(xx, yy, zz, biome);
} }
} }
} }
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
}
queue.setCompleteTask(whenDone);
queue.enqueue();
} catch (Exception e) {
e.printStackTrace();
TaskManager.runTask(whenDone);
} }
}); if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
}
whenDone.value = true;
queue.setCompleteTask(whenDone);
queue.enqueue();
} catch (Exception e) {
e.printStackTrace();
TaskManager.runTask(whenDone);
}
} }
public abstract boolean restoreTile(QueueCoordinator queue, CompoundTag tag, int x, int y, int z); public abstract boolean restoreTile(QueueCoordinator queue, CompoundTag tag, int x, int y, int z);
@ -595,13 +616,17 @@ public abstract class SchematicHandler {
@Nonnull final Set<CuboidRegion> regions) { @Nonnull final Set<CuboidRegion> regions) {
CompletableFuture<CompoundTag> completableFuture = new CompletableFuture<>(); CompletableFuture<CompoundTag> completableFuture = new CompletableFuture<>();
TaskManager.runTaskAsync(() -> { TaskManager.runTaskAsync(() -> {
// Main positions World world = this.worldUtil.getWeWorld(worldName);
// All positions
CuboidRegion aabb = RegionUtil.getAxisAlignedBoundingBox(regions); CuboidRegion aabb = RegionUtil.getAxisAlignedBoundingBox(regions);
aabb.setWorld(this.worldUtil.getWeWorld(worldName)); aabb.setWorld(world);
RegionIntersection intersection = new RegionIntersection(new ArrayList<>(regions));
final int width = aabb.getWidth(); final int width = aabb.getWidth();
int height = aabb.getHeight(); int height = aabb.getHeight();
final int length = aabb.getLength(); final int length = aabb.getLength();
final boolean multipleRegions = regions.size() > 1;
Map<String, Tag> schematic = initSchematic((short) width, (short) height, (short) length); Map<String, Tag> schematic = initSchematic((short) width, (short) height, (short) length);
@ -612,73 +637,43 @@ public abstract class SchematicHandler {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length); ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length);
ByteArrayOutputStream biomeBuffer = new ByteArrayOutputStream(width * length); ByteArrayOutputStream biomeBuffer = new ByteArrayOutputStream(width * length);
// Queue // Queue
final ArrayDeque<CuboidRegion> queue = new ArrayDeque<>(regions); TaskManager.runTask(() -> {
TaskManager.runTask(new Runnable() { final BlockVector3 minimum = aabb.getMinimumPoint();
@Override public void run() { final BlockVector3 maximum = aabb.getMaximumPoint();
if (queue.isEmpty()) {
TaskManager.runTaskAsync(() -> {
writeSchematicData(schematic, palette, biomePalette, tileEntities, buffer, biomeBuffer);
completableFuture.complete(new CompoundTag(schematic));
});
return;
}
final Runnable regionTask = this;
CuboidRegion region = queue.poll();
final BlockVector3 minimum = region.getMinimumPoint(); final int minX = minimum.getX();
final BlockVector3 maximum = region.getMaximumPoint(); final int minZ = minimum.getZ();
final int minY = minimum.getY();
final int minX = minimum.getX(); final int maxX = maximum.getX();
final int minZ = minimum.getZ(); final int maxZ = maximum.getZ();
final int minY = minimum.getY(); final int maxY = maximum.getY();
final int maxX = maximum.getX(); final Runnable yTask = new YieldRunnable() {
final int maxZ = maximum.getZ(); int currentY = minY;
final int maxY = maximum.getY(); int currentX = minX;
int currentZ = minZ;
final Runnable yTask = new YieldRunnable() { @Override
int currentY = minY; public void run() {
int currentX = minX; long start = System.currentTimeMillis();
int currentZ = minZ; int lastBiome = 0;
@Override public void run() { for (; currentY <= maxY; currentY++) {
long start = System.currentTimeMillis(); int relativeY = currentY - minY;
for (; currentY <= maxY; currentY++) { for (; currentZ <= maxZ; currentZ++) {
int relativeY = currentY - minY; int relativeZ = currentZ - minZ;
for (; currentZ <= maxZ; currentZ++) { for (; currentX <= maxX; currentX++) {
int relativeZ = currentZ - minZ; // if too much time was spent here, we yield this task
for (; currentX <= maxX; currentX++) { // note that current(X/Y/Z) aren't incremented, so the same position
// if too much time was spent here, we yield this task // as *right now* will be visited again
// note that current(X/Y/Z) aren't incremented, so the same position if (System.currentTimeMillis() - start > 40) {
// as *right now* will be visited again this.yield();
if (System.currentTimeMillis() - start > 40) { return;
this.yield(); }
return; int relativeX = currentX - minX;
} BlockVector3 point = BlockVector3.at(currentX, currentY, currentZ);
int relativeX = currentX - minX; if (multipleRegions && !intersection.contains(point)) {
BlockVector3 point = BlockVector3.at(currentX, currentY, currentZ); String blockKey = BlockTypes.AIR.getDefaultState().getAsString();
BaseBlock block = aabb.getWorld().getFullBlock(point);
if (block.getNbtData() != null) {
Map<String, Tag> values = new HashMap<>();
for (Map.Entry<String, Tag> entry : block.getNbtData().getValue().entrySet()) {
values.put(entry.getKey(), entry.getValue());
}
// Positions are kept in NBT, we don't want that.
values.remove("x");
values.remove("y");
values.remove("z");
values.put("Id", new StringTag(block.getNbtId()));
// Remove 'id' if it exists. We want 'Id'.
// Do this after we get "getNbtId" cos otherwise "getNbtId" doesn't work.
// Dum.
values.remove("id");
values.put("Pos", new IntArrayTag(new int[] {relativeX, relativeY, relativeZ}));
tileEntities.add(new CompoundTag(values));
}
String blockKey = block.toImmutableState().getAsString();
int blockId; int blockId;
if (palette.containsKey(blockKey)) { if (palette.containsKey(blockKey)) {
blockId = palette.get(blockKey); blockId = palette.get(blockKey);
@ -686,7 +681,6 @@ public abstract class SchematicHandler {
blockId = palette.size(); blockId = palette.size();
palette.put(blockKey, palette.size()); palette.put(blockKey, palette.size());
} }
while ((blockId & -128) != 0) { while ((blockId & -128) != 0) {
buffer.write(blockId & 127 | 128); buffer.write(blockId & 127 | 128);
blockId >>>= 7; blockId >>>= 7;
@ -696,31 +690,83 @@ public abstract class SchematicHandler {
if (relativeY > 0) { if (relativeY > 0) {
continue; continue;
} }
BlockVector2 pt = BlockVector2.at(currentX, currentZ);
BiomeType biome = aabb.getWorld().getBiome(pt); // Write the last biome if we're not getting it from the plot;
String biomeStr = biome.getId(); int biomeId = lastBiome;
int biomeId;
if (biomePalette.containsKey(biomeStr)) {
biomeId = biomePalette.get(biomeStr);
} else {
biomeId = biomePalette.size();
biomePalette.put(biomeStr, biomeId);
}
while ((biomeId & -128) != 0) { while ((biomeId & -128) != 0) {
biomeBuffer.write(biomeId & 127 | 128); biomeBuffer.write(biomeId & 127 | 128);
biomeId >>>= 7; biomeId >>>= 7;
} }
biomeBuffer.write(biomeId); biomeBuffer.write(biomeId);
continue;
} }
currentX = minX; // reset manually as not using local variable BaseBlock block = aabb.getWorld().getFullBlock(point);
if (block.getNbtData() != null) {
Map<String, Tag> values = new HashMap<>();
for (Map.Entry<String, Tag> entry : block.getNbtData().getValue().entrySet()) {
values.put(entry.getKey(), entry.getValue());
}
// Positions are kept in NBT, we don't want that.
values.remove("x");
values.remove("y");
values.remove("z");
values.put("Id", new StringTag(block.getNbtId()));
// Remove 'id' if it exists. We want 'Id'.
// Do this after we get "getNbtId" cos otherwise "getNbtId" doesn't work.
// Dum.
values.remove("id");
values.put("Pos", new IntArrayTag(new int[] {relativeX, relativeY, relativeZ}));
tileEntities.add(new CompoundTag(values));
}
String blockKey = block.toImmutableState().getAsString();
int blockId;
if (palette.containsKey(blockKey)) {
blockId = palette.get(blockKey);
} else {
blockId = palette.size();
palette.put(blockKey, palette.size());
}
while ((blockId & -128) != 0) {
buffer.write(blockId & 127 | 128);
blockId >>>= 7;
}
buffer.write(blockId);
if (relativeY > 0) {
continue;
}
BlockVector2 pt = BlockVector2.at(currentX, currentZ);
BiomeType biome = aabb.getWorld().getBiome(pt);
String biomeStr = biome.getId();
int biomeId;
if (biomePalette.containsKey(biomeStr)) {
biomeId = lastBiome = biomePalette.get(biomeStr);
} else {
biomeId = lastBiome = biomePalette.size();
biomePalette.put(biomeStr, biomeId);
}
while ((biomeId & -128) != 0) {
biomeBuffer.write(biomeId & 127 | 128);
biomeId >>>= 7;
}
biomeBuffer.write(biomeId);
} }
currentZ = minZ; // reset manually as not using local variable currentX = minX; // reset manually as not using local variable
} }
regionTask.run(); currentZ = minZ; // reset manually as not using local variable
} }
}; TaskManager.runTaskAsync(() -> {
yTask.run(); writeSchematicData(schematic, palette, biomePalette, tileEntities, buffer, biomeBuffer);
} completableFuture.complete(new CompoundTag(schematic));
});
}
};
yTask.run();
}); });
}); });
return completableFuture; return completableFuture;

View File

@ -165,6 +165,7 @@
"plotareatype.plot_area_type_partial": "<gray>Vanilla with clusters of plots</gray>", "plotareatype.plot_area_type_partial": "<gray>Vanilla with clusters of plots</gray>",
"schematics.schematic_too_large": "<prefix><red>The plot is too large for this action!</red>", "schematics.schematic_too_large": "<prefix><red>The plot is too large for this action!</red>",
"schematics.schematic_size_mismatch": "<prefix><red>The schematic's size does not match the plot's. If you want to paste anyway, enable schematics.paste-mismatches in settings.yml</red>",
"schematics.schematic_invalid": "<prefix><red>That is not a valid schematic. Reason: </red><gray><reason>.</gray>", "schematics.schematic_invalid": "<prefix><red>That is not a valid schematic. Reason: </red><gray><reason>.</gray>",
"schematics.schematic_invalid_named": "<prefix><red><schemname> is not a valid schematic. Reason: </red><gray><reason>.</gray>", "schematics.schematic_invalid_named": "<prefix><red><schemname> is not a valid schematic. Reason: </red><gray><reason>.</gray>",
"schematics.schematic_paste_merged": "<prefix><red>Schematics cannot be pasted onto merged plots. Please unmerge the plot before performing the paste.</red>", "schematics.schematic_paste_merged": "<prefix><red>Schematics cannot be pasted onto merged plots. Please unmerge the plot before performing the paste.</red>",