mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2024-11-22 05:06:44 +01:00
Allow over/undersizes schematics to be saved and pasted (#2944)
Co-authored-by: NotMyFault <mc.cache@web.de>
This commit is contained in:
parent
01dd2d8097
commit
4c0bc79e49
@ -38,7 +38,7 @@ public interface BackupManager {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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,
|
||||
* automatic backups are enabled and the plot is not merged.
|
||||
* automatic backups are enabled.
|
||||
* Otherwise it will complete immediately.
|
||||
*
|
||||
* @param player Player that triggered the backup
|
||||
|
@ -77,7 +77,7 @@ import java.util.concurrent.TimeUnit;
|
||||
}
|
||||
|
||||
@Override @Nonnull public BackupProfile getProfile(@Nonnull final Plot plot) {
|
||||
if (plot.hasOwner() && !plot.isMerged()) {
|
||||
if (plot.hasOwner()) {
|
||||
try {
|
||||
return backupProfileCache.get(new PlotCacheKey(plot), () -> this.playerBackupProfileFactory.create(plot.getOwnerAbs(), plot));
|
||||
} catch (ExecutionException e) {
|
||||
|
@ -99,10 +99,6 @@ public class Download extends SubCommand {
|
||||
player.sendMessage(TranslatableCaption.of("permission.no_plot_perms"));
|
||||
return false;
|
||||
}
|
||||
if (plot.isMerged()) {
|
||||
player.sendMessage(TranslatableCaption.of("web.plot_merged"));
|
||||
return false;
|
||||
}
|
||||
if (plot.getRunning() > 0) {
|
||||
player.sendMessage(TranslatableCaption.of("errors.wait_for_timer"));
|
||||
return false;
|
||||
|
@ -116,10 +116,6 @@ public class SchematicCmd extends SubCommand {
|
||||
player.sendMessage(TranslatableCaption.of("error.task_in_process"));
|
||||
return false;
|
||||
}
|
||||
if (plot.isMerged()) {
|
||||
player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_merged"));
|
||||
return false;
|
||||
}
|
||||
final String location = args[1];
|
||||
this.running = true;
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
@ -154,7 +150,7 @@ public class SchematicCmd extends SubCommand {
|
||||
);
|
||||
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) {
|
||||
SchematicCmd.this.running = false;
|
||||
if (value) {
|
||||
|
@ -403,6 +403,10 @@ public class Settings extends Config {
|
||||
@Comment(
|
||||
"Whether schematic based road generation should paste schematic on top of roads, or from Y=1")
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
* a small plot use getPlotTopLocAbs(...)
|
||||
* a small plot use {@link #getTopAbs()}
|
||||
*
|
||||
* @return Location top of mega plot
|
||||
*/
|
||||
@ -1754,9 +1754,8 @@ public class Plot {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bottom location for a plot.<br>
|
||||
* - Does not respect mega plots<br>
|
||||
* - Merged plots, only the road will be considered part of the 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
|
||||
* a small plot use {@link #getBottomAbs()}
|
||||
*
|
||||
* @return Location bottom of mega plot
|
||||
*/
|
||||
@ -2362,6 +2361,8 @@ public class Plot {
|
||||
bot = PlotId.of(bot.getX() - 1, bot.getX());
|
||||
}
|
||||
}
|
||||
int minHeight = getArea().getMinBuildHeight();
|
||||
int maxHeight = getArea().getMaxBuildHeight();
|
||||
Location gtopabs = this.area.getPlotAbs(top).getTopAbs();
|
||||
Location gbotabs = this.area.getPlotAbs(bot).getBottomAbs();
|
||||
visited.addAll(Lists.newArrayList((Iterable<? extends PlotId>) PlotId.PlotRangeIterator.range(bot, top)));
|
||||
@ -2372,12 +2373,12 @@ public class Plot {
|
||||
Location toploc = plot.getExtendedTopAbs();
|
||||
Location botabs = plot.getBottomAbs();
|
||||
Location topabs = plot.getTopAbs();
|
||||
BlockVector3 pos1 = BlockVector3.at(botabs.getX(), 0, topabs.getZ() + 1);
|
||||
BlockVector3 pos2 = BlockVector3.at(topabs.getX(), Plot.MAX_HEIGHT - 1, toploc.getZ());
|
||||
BlockVector3 pos1 = BlockVector3.at(botabs.getX(), minHeight, topabs.getZ() + 1);
|
||||
BlockVector3 pos2 = BlockVector3.at(topabs.getX(), maxHeight, toploc.getZ());
|
||||
regions.add(new CuboidRegion(pos1, pos2));
|
||||
if (plot.isMerged(Direction.SOUTHEAST)) {
|
||||
pos1 = BlockVector3.at(topabs.getX() + 1, 0, topabs.getZ() + 1);
|
||||
pos2 = BlockVector3.at(toploc.getX(), Plot.MAX_HEIGHT - 1, toploc.getZ());
|
||||
pos1 = BlockVector3.at(topabs.getX() + 1, minHeight, topabs.getZ() + 1);
|
||||
pos2 = BlockVector3.at(toploc.getX(), maxHeight, toploc.getZ());
|
||||
regions.add(new CuboidRegion(pos1, pos2));
|
||||
// intersection
|
||||
}
|
||||
@ -2391,19 +2392,19 @@ public class Plot {
|
||||
Location toploc = plot.getExtendedTopAbs();
|
||||
Location botabs = plot.getBottomAbs();
|
||||
Location topabs = plot.getTopAbs();
|
||||
BlockVector3 pos1 = BlockVector3.at(topabs.getX() + 1, 0, botabs.getZ());
|
||||
BlockVector3 pos2 = BlockVector3.at(toploc.getX(), Plot.MAX_HEIGHT - 1, topabs.getZ());
|
||||
BlockVector3 pos1 = BlockVector3.at(topabs.getX() + 1, minHeight, botabs.getZ());
|
||||
BlockVector3 pos2 = BlockVector3.at(toploc.getX(), maxHeight, topabs.getZ());
|
||||
regions.add(new CuboidRegion(pos1, pos2));
|
||||
if (plot.isMerged(Direction.SOUTHEAST)) {
|
||||
pos1 = BlockVector3.at(topabs.getX() + 1, 0, topabs.getZ() + 1);
|
||||
pos2 = BlockVector3.at(toploc.getX(), Plot.MAX_HEIGHT - 1, toploc.getZ());
|
||||
pos1 = BlockVector3.at(topabs.getX() + 1, minHeight, topabs.getZ() + 1);
|
||||
pos2 = BlockVector3.at(toploc.getX(), maxHeight, toploc.getZ());
|
||||
regions.add(new CuboidRegion(pos1, pos2));
|
||||
// intersection
|
||||
}
|
||||
}
|
||||
}
|
||||
BlockVector3 pos1 = BlockVector3.at(gbotabs.getX(), 0, gbotabs.getZ());
|
||||
BlockVector3 pos2 = BlockVector3.at(gtopabs.getX(), Plot.MAX_HEIGHT - 1, gtopabs.getZ());
|
||||
BlockVector3 pos1 = BlockVector3.at(gbotabs.getX(), minHeight, gbotabs.getZ());
|
||||
BlockVector3 pos2 = BlockVector3.at(gtopabs.getX(), maxHeight, gtopabs.getZ());
|
||||
regions.add(new CuboidRegion(pos1, pos2));
|
||||
}
|
||||
return regions;
|
||||
|
@ -25,12 +25,13 @@
|
||||
*/
|
||||
package com.plotsquared.core.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.inject.Inject;
|
||||
import com.plotsquared.core.PlotSquared;
|
||||
import com.plotsquared.core.configuration.Settings;
|
||||
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
||||
import com.plotsquared.core.generator.ClassicPlotWorld;
|
||||
import com.plotsquared.core.inject.factory.ProgressSubscriberFactory;
|
||||
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.BlockVector3;
|
||||
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.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -90,9 +95,6 @@ import java.net.URLConnection;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
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.Arrays;
|
||||
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 Gson GSON = new Gson();
|
||||
private static final Path TEMP_DIR = Paths.get("TODO-PATH");
|
||||
public static SchematicHandler manager;
|
||||
private final WorldUtil worldUtil;
|
||||
private boolean exportAll = false;
|
||||
@ -280,8 +281,6 @@ public abstract class SchematicHandler {
|
||||
final boolean autoHeight,
|
||||
final PlotPlayer<?> actor,
|
||||
final RunnableVal<Boolean> whenDone) {
|
||||
|
||||
TaskManager.runTask(() -> {
|
||||
if (whenDone != null) {
|
||||
whenDone.value = false;
|
||||
}
|
||||
@ -296,8 +295,12 @@ public abstract class SchematicHandler {
|
||||
final int HEIGHT = dimension.getY();
|
||||
// Validate dimensions
|
||||
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)) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -311,7 +314,7 @@ public abstract class SchematicHandler {
|
||||
} else {
|
||||
PlotArea pw = plot.getArea();
|
||||
if (pw instanceof ClassicPlotWorld) {
|
||||
y_offset_actual = yOffset + ((ClassicPlotWorld) pw).PLOT_HEIGHT;
|
||||
y_offset_actual = yOffset + pw.getMinBuildHeight() + ((ClassicPlotWorld) pw).PLOT_HEIGHT;
|
||||
} else {
|
||||
y_offset_actual = yOffset + 1 + this.worldUtil
|
||||
.getHighestBlockSynchronous(plot.getWorldName(), region.getMinimumPoint().getX() + 1,
|
||||
@ -322,27 +325,45 @@ public abstract class SchematicHandler {
|
||||
y_offset_actual = yOffset;
|
||||
}
|
||||
|
||||
final Location pos1 = Location
|
||||
.at(plot.getWorldName(), region.getMinimumPoint().getX() + xOffset, y_offset_actual, region.getMinimumPoint().getZ() + zOffset);
|
||||
|
||||
final int p1x = pos1.getX();
|
||||
final int p1z = pos1.getZ();
|
||||
final int p1x;
|
||||
final int p1z;
|
||||
final int p2x;
|
||||
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) {
|
||||
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;
|
||||
BaseBlock id = blockArrayClipboard.getFullBlock(BlockVector3.at(rx, ry, 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(BlockVector3.at(rx, ry, rz));
|
||||
BiomeType biome = blockArrayClipboard.getBiome(loc);
|
||||
queue.setBiome(xx, yy, zz, biome);
|
||||
}
|
||||
}
|
||||
@ -351,13 +372,13 @@ public abstract class SchematicHandler {
|
||||
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);
|
||||
@ -595,13 +616,17 @@ public abstract class SchematicHandler {
|
||||
@Nonnull final Set<CuboidRegion> regions) {
|
||||
CompletableFuture<CompoundTag> completableFuture = new CompletableFuture<>();
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
// Main positions
|
||||
World world = this.worldUtil.getWeWorld(worldName);
|
||||
// All positions
|
||||
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();
|
||||
int height = aabb.getHeight();
|
||||
final int length = aabb.getLength();
|
||||
final boolean multipleRegions = regions.size() > 1;
|
||||
|
||||
Map<String, Tag> schematic = initSchematic((short) width, (short) height, (short) length);
|
||||
|
||||
@ -612,21 +637,9 @@ public abstract class SchematicHandler {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length);
|
||||
ByteArrayOutputStream biomeBuffer = new ByteArrayOutputStream(width * length);
|
||||
// Queue
|
||||
final ArrayDeque<CuboidRegion> queue = new ArrayDeque<>(regions);
|
||||
TaskManager.runTask(new Runnable() {
|
||||
@Override public void run() {
|
||||
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 BlockVector3 maximum = region.getMaximumPoint();
|
||||
TaskManager.runTask(() -> {
|
||||
final BlockVector3 minimum = aabb.getMinimumPoint();
|
||||
final BlockVector3 maximum = aabb.getMaximumPoint();
|
||||
|
||||
final int minX = minimum.getX();
|
||||
final int minZ = minimum.getZ();
|
||||
@ -640,8 +653,11 @@ public abstract class SchematicHandler {
|
||||
int currentY = minY;
|
||||
int currentX = minX;
|
||||
int currentZ = minZ;
|
||||
@Override public void run() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long start = System.currentTimeMillis();
|
||||
int lastBiome = 0;
|
||||
for (; currentY <= maxY; currentY++) {
|
||||
int relativeY = currentY - minY;
|
||||
for (; currentZ <= maxZ; currentZ++) {
|
||||
@ -656,6 +672,34 @@ public abstract class SchematicHandler {
|
||||
}
|
||||
int relativeX = currentX - minX;
|
||||
BlockVector3 point = BlockVector3.at(currentX, currentY, currentZ);
|
||||
if (multipleRegions && !intersection.contains(point)) {
|
||||
String blockKey = BlockTypes.AIR.getDefaultState().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;
|
||||
}
|
||||
|
||||
// Write the last biome if we're not getting it from the plot;
|
||||
int biomeId = lastBiome;
|
||||
while ((biomeId & -128) != 0) {
|
||||
biomeBuffer.write(biomeId & 127 | 128);
|
||||
biomeId >>>= 7;
|
||||
}
|
||||
biomeBuffer.write(biomeId);
|
||||
continue;
|
||||
}
|
||||
BaseBlock block = aabb.getWorld().getFullBlock(point);
|
||||
if (block.getNbtData() != null) {
|
||||
Map<String, Tag> values = new HashMap<>();
|
||||
@ -701,9 +745,9 @@ public abstract class SchematicHandler {
|
||||
String biomeStr = biome.getId();
|
||||
int biomeId;
|
||||
if (biomePalette.containsKey(biomeStr)) {
|
||||
biomeId = biomePalette.get(biomeStr);
|
||||
biomeId = lastBiome = biomePalette.get(biomeStr);
|
||||
} else {
|
||||
biomeId = biomePalette.size();
|
||||
biomeId = lastBiome = biomePalette.size();
|
||||
biomePalette.put(biomeStr, biomeId);
|
||||
}
|
||||
while ((biomeId & -128) != 0) {
|
||||
@ -716,11 +760,13 @@ public abstract class SchematicHandler {
|
||||
}
|
||||
currentZ = minZ; // reset manually as not using local variable
|
||||
}
|
||||
regionTask.run();
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
writeSchematicData(schematic, palette, biomePalette, tileEntities, buffer, biomeBuffer);
|
||||
completableFuture.complete(new CompoundTag(schematic));
|
||||
});
|
||||
}
|
||||
};
|
||||
yTask.run();
|
||||
}
|
||||
});
|
||||
});
|
||||
return completableFuture;
|
||||
|
@ -165,6 +165,7 @@
|
||||
"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_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_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>",
|
||||
|
Loading…
Reference in New Issue
Block a user