More support for 3D biomes (#608)

* More support for 3D biomes

* Resolved merge conflicts
This commit is contained in:
Matt
2020-09-12 09:31:42 -04:00
committed by GitHub
parent de199a0e59
commit d00899e177
40 changed files with 337 additions and 191 deletions

View File

@ -32,7 +32,6 @@ import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
@ -40,15 +39,12 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Range;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
@ -88,10 +84,21 @@ public class AbstractDelegateExtent implements Extent {
return extent.getBlock(position.getX(), position.getY(), position.getZ());
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
return extent.getFullBlock(x, y, z);
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
return extent.getFullBlock(position.getX(), position.getY(), position.getZ());
}
/*
Queue based methods
TODO NOT IMPLEMENTED: IQueueExtent and such need to implement these
*/
@Override
public boolean isQueueEnabled() {
return extent.isQueueEnabled();
@ -122,10 +129,10 @@ public class AbstractDelegateExtent implements Extent {
new ExtentTraverser<>(this).setNext(new ForgetfulExtentBuffer(extent));
}
}
/*
History
*/
public void setChangeSet(AbstractChangeSet changeSet) {
if (extent instanceof HistoryExtent) {
HistoryExtent history = ((HistoryExtent) extent);
@ -173,13 +180,13 @@ public class AbstractDelegateExtent implements Extent {
}
@Override
public BiomeType getBiome(BlockVector2 position) {
return extent.getBiome(position);
public boolean fullySupports3DBiomes() {
return extent.fullySupports3DBiomes();
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
return extent.getFullBlock(x, y, z);
public BiomeType getBiome(BlockVector3 position) {
return extent.getBiome(position);
}
@Override
@ -210,8 +217,8 @@ public class AbstractDelegateExtent implements Extent {
}
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
return extent.setBiome(position.getX(), 0, position.getZ(), biome);
public boolean setBiome(BlockVector3 position, BiomeType biome) {
return extent.setBiome(position.getX(), position.getY(), position.getZ(), biome);
}
@Override

View File

@ -22,12 +22,11 @@ package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.history.change.BiomeChange;
import com.sk89q.worldedit.history.change.BiomeChange3D;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
@ -68,9 +67,9 @@ public class ChangeSetExtent extends AbstractDelegateExtent {
}
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
public boolean setBiome(BlockVector3 position, BiomeType biome) {
BiomeType previous = getBiome(position);
changeSet.add(new BiomeChange(position, previous, biome));
changeSet.add(new BiomeChange3D(position, previous, biome));
return super.setBiome(position, biome);
}

View File

@ -97,6 +97,11 @@ public class NullExtent implements Extent {
return false;
}
@Override
public boolean fullySupports3DBiomes() {
return false;
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
return false;

View File

@ -67,6 +67,22 @@ public interface OutputExtent {
boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException;
/**
* Check if this extent fully supports 3D biomes.
*
* <p>
* If {@code false}, the extent only visually reads biomes from {@code y = 0}.
* The biomes will still be set in 3D, but the client will only see the one at
* {@code y = 0}. It is up to the caller to determine if they want to set that
* biome instead, or simply warn the actor.
* </p>
*
* @return if the extent fully supports 3D biomes
*/
default boolean fullySupports3DBiomes() {
return true;
}
/**
* Set the biome.
*
@ -77,7 +93,7 @@ public interface OutputExtent {
*/
@Deprecated
default boolean setBiome(BlockVector2 position, BiomeType biome) {
return setBiome(position.getX(), 0, position.getBlockZ(), biome);
return setBiome(position.toBlockVector3(), biome);
}
@NonAbstractForCompatibility(
@ -149,5 +165,4 @@ public interface OutputExtent {
* @return an operation or null if there is none to execute
*/
@Nullable Operation commit();
}

View File

@ -23,7 +23,6 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.pattern.BiomePattern;
import com.sk89q.worldedit.function.pattern.Pattern;
@ -41,6 +40,7 @@ import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@ -54,13 +54,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern, BiomePattern {
private final Map<BlockVector3, BaseBlock> buffer = new LinkedHashMap<>();
private final Map<BlockVector2, BiomeType> biomeBuffer = new LinkedHashMap<>();
private final Map<BlockVector3, BiomeType> biomeBuffer = new LinkedHashMap<>();
private final Mask mask;
private final Mask2D biomeMask;
private BlockVector3 min = null;
private BlockVector2 min2d = null;
private BlockVector3 max = null;
private BlockVector2 max2d = null;
/**
* Create a new extent buffer that will buffer every change.
@ -87,8 +84,6 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
super(delegate);
checkNotNull(mask);
this.mask = mask;
Mask2D bmask = mask.toMask2D();
this.biomeMask = bmask == null ? Masks.alwaysTrue2D() : bmask;
}
@Override
@ -107,7 +102,7 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
max = max.getMaximum(location);
}
if (mask.test( location)) {
if (mask.test(location)) {
buffer.put(location, block.toBaseBlock());
return true;
} else {
@ -116,22 +111,22 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
}
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
public boolean setBiome(BlockVector3 position, BiomeType biome) {
// Update minimum
if (min2d == null) {
min2d = position;
if (min == null) {
min = position;
} else {
min2d = min2d.getMinimum(position);
min = min.getMinimum(position);
}
// Update maximum
if (max2d == null) {
max2d = position;
if (max == null) {
max = position;
} else {
max2d = max2d.getMaximum(position);
max = max.getMaximum(position);
}
if (biomeMask.test(position)) {
if (mask.test(position)) {
biomeBuffer.put(position, biome);
return true;
} else {
@ -142,25 +137,26 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
@Override
public boolean setBiome(int x, int y, int z, BiomeType biome) {
// Update minimum
if (min2d == null) {
min2d = BlockVector2.at(x, z);
if (min == null) {
min = BlockVector3.at(x, y, z);
} else {
min2d = min2d.getMinimum(BlockVector2.at(x,z));
min = min.getMinimum(BlockVector3.at(x, y, z));
}
// Update maximum
if (max2d == null) {
max2d = BlockVector2.at(x,z);
if (max == null) {
max = BlockVector3.at(x, y, z);
} else {
max2d = max2d.getMaximum(BlockVector2.at(x,z));
max = max.getMaximum(BlockVector3.at(x, y, z));
}
if (biomeMask.test(BlockVector2.at(x,z))) {
biomeBuffer.put(BlockVector2.at(x,z), biome);
if (mask.test(BlockVector3.at(x, y, z))) {
biomeBuffer.put(BlockVector3.at(x, y, z), biome);
return true;
} else {
return getExtent().setBiome(x, y, z, biome);
} }
}
}
@Override
@ -174,7 +170,7 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
}
@Override
public BiomeType apply(BlockVector2 pos) {
public BiomeType applyBiome(BlockVector3 pos) {
BiomeType biome = biomeBuffer.get(pos);
if (biome != null) {
return biome;
@ -222,7 +218,10 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
@Override
public Iterable<BlockVector2> asFlatRegion() {
return biomeBuffer.keySet();
return biomeBuffer.keySet()
.stream()
.map(BlockVector3::toBlockVector2)
.collect(Collectors.toSet());
}
};
}

View File

@ -42,10 +42,10 @@ import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.stream.Collectors;
/**
* Stores block data as a multi-dimensional array of {@link BlockState}s and
@ -174,16 +174,17 @@ public class BlockArrayClipboard implements Clipboard {
}
@Override
public BiomeType getBiome(BlockVector2 position) {
BlockVector2 v = position.subtract(region.getMinimumPoint().toBlockVector2());
return getParent().getBiomeType(v.getX(), 0, v.getZ());
public BiomeType getBiome(BlockVector3 position) {
BlockVector3 v = position.subtract(region.getMinimumPoint());
return getParent().getBiomeType(v.getX(), v.getY(), v.getZ());
}
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
public boolean setBiome(BlockVector3 position, BiomeType biome) {
int x = position.getBlockX() - origin.getX();
int y = position.getBlockY() - origin.getY();
int z = position.getBlockZ() - origin.getZ();
return getParent().setBiome(x, 0, z, biome);
return getParent().setBiome(x, y, z, biome);
}
@Override

View File

@ -41,7 +41,7 @@ import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.visitor.Order;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.MutableBlockVector2;
import com.sk89q.worldedit.math.MutableBlockVector3;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.Regions;
@ -311,16 +311,17 @@ public interface Clipboard extends Extent, Iterable<BlockVector3>, Closeable {
pasteBiomes &= Clipboard.this.hasBiomes();
MutableBlockVector2 mpos2d = new MutableBlockVector2();
mpos2d.setComponents(Integer.MIN_VALUE, Integer.MIN_VALUE);
MutableBlockVector3 blockVector3 = new MutableBlockVector3();
blockVector3.setComponents(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
for (BlockVector3 pos : this) {
BaseBlock block = pos.getFullBlock(this);
int xx = pos.getX() + relx;
int zz = pos.getZ() + relz;
int yy = pos.getZ() + relz;
int zz = pos.getY() + rely;
if (hasBiomes() && pos.getBlockY() == 0) {
if (pasteBiomes && (xx != mpos2d.getBlockX() || zz != mpos2d.getBlockZ())) {
mpos2d.setComponents(xx, zz);
extent.setBiome(mpos2d, Clipboard.this.getBiome(BlockVector2.at(pos.getX(), pos.getZ())));
if (pasteBiomes && (xx != blockVector3.getBlockX() || zz != blockVector3.getBlockZ())) {
blockVector3.setComponents(xx, yy, zz);
extent.setBiome(blockVector3, Clipboard.this.getBiome(BlockVector3.at(pos.getX(), pos.getY(), pos.getZ())));
}
}
if (!pasteAir && block.getBlockType().getMaterial().isAir()) {

View File

@ -37,7 +37,6 @@ import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.visitor.Order;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BiomeType;
@ -287,7 +286,7 @@ public class FastSchematicWriter implements ClipboardWriter {
int z0 = min.getBlockZ() + z;
for (int x = 0; x < width; x++, i++) {
int x0 = min.getBlockX() + x;
BlockVector2 pt = BlockVector2.at(x0, z0);
BlockVector3 pt = BlockVector3.at(x0, min.getBlockY(), z0);
BiomeType biome = clipboard.getBiome(pt);
task.applyInt(i, biome.getInternalId());
}

View File

@ -35,7 +35,6 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
@ -155,11 +154,7 @@ public class SpongeSchematicWriter implements ClipboardWriter {
values.remove("z");
values.put("Id", new StringTag(block.getNbtId()));
values.put("Pos", new IntArrayTag(new int[]{
x,
y,
z
}));
values.put("Pos", new IntArrayTag(new int[] { x, y, z }));
tileEntities.add(new CompoundTag(values));
}
@ -218,7 +213,7 @@ public class SpongeSchematicWriter implements ClipboardWriter {
int z0 = min.getBlockZ() + z;
for (int x = 0; x < width; x++) {
int x0 = min.getBlockX() + x;
BlockVector2 pt = BlockVector2.at(x0, z0);
BlockVector3 pt = BlockVector3.at(x0, min.getBlockY(), z0);
BiomeType biome = clipboard.getBiome(pt);
String biomeKey = biome.getId();

View File

@ -0,0 +1,50 @@
/*
* 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.extent.world;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
/**
* Handles quirks when placing biomes.
*/
public class BiomeQuirkExtent extends AbstractDelegateExtent {
/**
* Create a new instance.
*
* @param extent the extent
*/
public BiomeQuirkExtent(Extent extent) {
super(extent);
}
@Override
public boolean setBiome(BlockVector3 position, BiomeType biome) {
boolean success = false;
if (!fullySupports3DBiomes()) {
// Also place at Y = 0 for proper handling
success = super.setBiome(position.withY(0), biome);
}
return success || super.setBiome(position, biome);
}
}