2014-03-30 04:05:09 +00:00
|
|
|
/*
|
|
|
|
* WorldEdit, a Minecraft world manipulation toolkit
|
|
|
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
|
|
|
* Copyright (C) WorldEdit team and contributors
|
|
|
|
*
|
2020-08-25 01:31:47 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
2014-03-30 04:05:09 +00:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
2020-08-25 01:31:47 +00:00
|
|
|
* 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 General Public License for more details.
|
2014-03-30 04:05:09 +00:00
|
|
|
*
|
2020-08-25 01:31:47 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2014-03-30 04:05:09 +00:00
|
|
|
*/
|
|
|
|
|
2014-03-30 19:03:12 +00:00
|
|
|
package com.sk89q.worldedit.extent;
|
2014-03-30 04:05:09 +00:00
|
|
|
|
2019-11-17 22:47:56 +00:00
|
|
|
import com.boydti.fawe.FaweCache;
|
2019-10-31 13:12:23 +00:00
|
|
|
import com.boydti.fawe.beta.Filter;
|
2019-10-23 16:58:36 +00:00
|
|
|
import com.boydti.fawe.beta.IBatchProcessor;
|
2020-07-14 02:50:59 +00:00
|
|
|
import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock;
|
2021-01-11 19:29:16 +00:00
|
|
|
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
|
2020-09-28 10:13:02 +00:00
|
|
|
import com.boydti.fawe.config.Settings;
|
2020-01-06 08:36:16 +00:00
|
|
|
import com.boydti.fawe.object.changeset.AbstractChangeSet;
|
2019-06-28 18:20:48 +00:00
|
|
|
import com.boydti.fawe.object.clipboard.WorldCopyClipboard;
|
2019-07-18 16:07:31 +00:00
|
|
|
import com.boydti.fawe.object.exception.FaweException;
|
2019-11-17 22:47:56 +00:00
|
|
|
import com.boydti.fawe.object.extent.NullExtent;
|
|
|
|
import com.boydti.fawe.util.ExtentTraverser;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
2019-04-03 06:00:59 +00:00
|
|
|
import com.sk89q.worldedit.WorldEditException;
|
2014-04-28 06:34:39 +00:00
|
|
|
import com.sk89q.worldedit.entity.BaseEntity;
|
|
|
|
import com.sk89q.worldedit.entity.Entity;
|
2019-11-06 09:29:20 +00:00
|
|
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.function.RegionMaskingFilter;
|
|
|
|
import com.sk89q.worldedit.function.block.BlockReplace;
|
2019-07-18 20:49:29 +00:00
|
|
|
import com.sk89q.worldedit.function.generator.CavesGen;
|
|
|
|
import com.sk89q.worldedit.function.generator.GenBase;
|
|
|
|
import com.sk89q.worldedit.function.generator.OreGen;
|
|
|
|
import com.sk89q.worldedit.function.generator.Resource;
|
|
|
|
import com.sk89q.worldedit.function.generator.SchemGen;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.function.mask.BlockMask;
|
|
|
|
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
2019-04-03 06:00:59 +00:00
|
|
|
import com.sk89q.worldedit.function.mask.Mask;
|
2019-04-03 11:28:57 +00:00
|
|
|
import com.sk89q.worldedit.function.operation.Operation;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.function.operation.Operations;
|
|
|
|
import com.sk89q.worldedit.function.pattern.BlockPattern;
|
2019-04-03 11:28:57 +00:00
|
|
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
2019-04-03 06:00:59 +00:00
|
|
|
import com.sk89q.worldedit.math.BlockVector2;
|
2018-12-23 16:19:33 +00:00
|
|
|
import com.sk89q.worldedit.math.BlockVector3;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.math.MathUtils;
|
2019-04-04 10:28:41 +00:00
|
|
|
import com.sk89q.worldedit.math.MutableBlockVector3;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.math.Vector3;
|
|
|
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.regions.Region;
|
2019-04-03 06:00:59 +00:00
|
|
|
import com.sk89q.worldedit.registry.state.PropertyGroup;
|
|
|
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
|
|
|
import com.sk89q.worldedit.util.Countable;
|
2018-12-23 16:19:33 +00:00
|
|
|
import com.sk89q.worldedit.util.Location;
|
2019-08-10 06:01:42 +00:00
|
|
|
import com.sk89q.worldedit.world.World;
|
2019-07-18 16:07:31 +00:00
|
|
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
2019-07-11 15:32:14 +00:00
|
|
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.world.block.BlockState;
|
2019-04-03 06:00:59 +00:00
|
|
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockType;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
2020-03-31 23:28:21 +00:00
|
|
|
|
2018-08-16 15:54:13 +00:00
|
|
|
import java.util.ArrayList;
|
2018-08-12 14:03:07 +00:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
2019-07-11 15:32:14 +00:00
|
|
|
import java.util.Set;
|
2019-08-06 15:25:53 +00:00
|
|
|
import java.util.UUID;
|
2018-08-12 14:03:07 +00:00
|
|
|
import java.util.concurrent.ThreadLocalRandom;
|
2019-09-22 01:00:45 +00:00
|
|
|
import javax.annotation.Nullable;
|
2019-07-11 15:32:14 +00:00
|
|
|
|
2020-07-14 02:50:59 +00:00
|
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
|
2014-03-30 04:05:09 +00:00
|
|
|
/**
|
2014-04-03 06:42:11 +00:00
|
|
|
* A world, portion of a world, clipboard, or other object that can have blocks
|
|
|
|
* set or entities placed.
|
|
|
|
*
|
|
|
|
* @see InputExtent the get____() portion
|
|
|
|
* @see OutputExtent the set____() portion
|
2014-03-30 04:05:09 +00:00
|
|
|
*/
|
2014-04-03 06:42:11 +00:00
|
|
|
public interface Extent extends InputExtent, OutputExtent {
|
2014-04-04 00:52:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the minimum point in the extent.
|
2014-07-26 07:29:12 +00:00
|
|
|
*
|
|
|
|
* <p>If the extent is unbounded, then a large (negative) value may
|
|
|
|
* be returned.</p>
|
2014-04-04 00:52:53 +00:00
|
|
|
*
|
|
|
|
* @return the minimum point
|
|
|
|
*/
|
2018-12-23 16:19:33 +00:00
|
|
|
BlockVector3 getMinimumPoint();
|
2014-04-04 00:52:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the maximum point in the extent.
|
2014-07-26 07:29:12 +00:00
|
|
|
*
|
|
|
|
* <p>If the extent is unbounded, then a large (positive) value may
|
|
|
|
* be returned.</p>
|
2014-04-04 00:52:53 +00:00
|
|
|
*
|
|
|
|
* @return the maximum point
|
|
|
|
*/
|
2018-12-23 16:19:33 +00:00
|
|
|
BlockVector3 getMaximumPoint();
|
2014-04-04 00:52:53 +00:00
|
|
|
|
2014-07-11 05:22:35 +00:00
|
|
|
/**
|
|
|
|
* Get a list of all entities within the given region.
|
2014-07-26 07:29:12 +00:00
|
|
|
*
|
|
|
|
* <p>If the extent is not wholly loaded (i.e. a world being simulated in the
|
2014-07-11 05:22:35 +00:00
|
|
|
* game will not have every chunk loaded), then this list may not be
|
2014-07-26 07:29:12 +00:00
|
|
|
* incomplete.</p>
|
2014-07-11 05:22:35 +00:00
|
|
|
*
|
2014-07-29 21:23:00 +00:00
|
|
|
* @param region the region in which entities must be contained
|
2014-07-11 05:22:35 +00:00
|
|
|
* @return a list of entities
|
|
|
|
*/
|
2018-08-12 14:03:07 +00:00
|
|
|
default List<? extends Entity> getEntities(Region region) {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
2014-07-11 05:22:35 +00:00
|
|
|
|
2014-04-28 06:34:39 +00:00
|
|
|
/**
|
|
|
|
* Get a list of all entities.
|
2014-07-26 07:29:12 +00:00
|
|
|
*
|
|
|
|
* <p>If the extent is not wholly loaded (i.e. a world being simulated in the
|
2014-04-28 06:34:39 +00:00
|
|
|
* game will not have every chunk loaded), then this list may not be
|
2014-07-26 07:29:12 +00:00
|
|
|
* incomplete.</p>
|
2014-04-28 06:34:39 +00:00
|
|
|
*
|
|
|
|
* @return a list of entities
|
|
|
|
*/
|
2018-08-12 14:03:07 +00:00
|
|
|
default List<? extends Entity> getEntities() {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
2014-04-28 06:34:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an entity at the given location.
|
|
|
|
*
|
|
|
|
* @param entity the entity
|
2014-07-10 21:50:40 +00:00
|
|
|
* @param location the location
|
2014-04-28 06:34:39 +00:00
|
|
|
* @return a reference to the created entity, or null if the entity could not be created
|
|
|
|
*/
|
2020-10-05 17:41:41 +00:00
|
|
|
@Nullable
|
|
|
|
default Entity createEntity(Location location, BaseEntity entity) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:25:53 +00:00
|
|
|
/**
|
|
|
|
* Create an entity at the given location.
|
|
|
|
*
|
2019-10-23 16:58:36 +00:00
|
|
|
* @param x the x coordinate
|
|
|
|
* @param y the y coordinate
|
|
|
|
* @param z the z coordinate
|
|
|
|
* @param uuid the unique identifier of the entity
|
2019-08-06 15:25:53 +00:00
|
|
|
*/
|
2020-01-15 00:44:09 +00:00
|
|
|
default void removeEntity(int x, int y, int z, UUID uuid) {}
|
2019-08-06 15:25:53 +00:00
|
|
|
|
2019-07-18 16:07:31 +00:00
|
|
|
/*
|
|
|
|
Queue based methods
|
|
|
|
TODO NOT IMPLEMENTED:
|
|
|
|
*/
|
|
|
|
default boolean isQueueEnabled() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
default void enableQueue() {
|
2020-10-05 17:41:41 +00:00
|
|
|
if (!isQueueEnabled()) {
|
|
|
|
throw FaweException._enableQueue;
|
|
|
|
}
|
2019-07-18 16:07:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default void disableQueue() {
|
2020-10-05 17:41:41 +00:00
|
|
|
if (isQueueEnabled()) {
|
|
|
|
throw FaweException._disableQueue;
|
|
|
|
}
|
2019-07-18 16:07:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
World based methods
|
|
|
|
TODO NOT IMPLEMENTED:
|
|
|
|
*/
|
|
|
|
|
|
|
|
default boolean isWorld() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
default boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) {
|
Re-Implement //regen (#598)
* start reimplementing regen command
* start reimplementing regen command
* Formatting and logic tweaks.
Regen will now throw exceptions but they are not caught yet.
ConversionSessions will now be closed when no longer being used instead of left open.
* fix //regen crashing server
* added //regen support for 1.16.1 and 1.15.2
* cleanup
* Update the issue template
* improve performance of regen by a factor of 40, approx 1.2 millon blocks/second
* Update the issue template
* Update the issue template & add a config (#640)
* Update the issue template
* Add a config.yml to the issue template
* improve performance of regen by a factor of 40, approx 1.2 millon blocks/second
* Fix entity rotation (#642)
* fix entity rotation
fixes #577
Co-authored-by: wea_ondara <wea_ondara@alpenblock.net>
* Fix toggle permission (#644)
* Fixes #529
* fix superperms perm toggling
Co-authored-by: @weaondara <wea_ondara@alpenblock.net>
* Fix #647
* Squash errors and debug to aid fixing #652 properly
* cleanup imports
* cleanup imports 2
* cleanup imports 3
* cleanup imports 4
* add patch by @SirYwell
* aysnc world gen with features and stuff and some minor issues
* optimizations, full chunkstatus, block populators
* optimizations, cleanup
* optimizations
* fix feature regeneration, fix temp folder deletion
* cleanup
* make chunk gen multithreaded
* fix precomputation of chunk lists for RegionLimitedWorldAccess again
* added regenerator abstraction, fix aioobe while running through chunk stati
* remove override for getChunkAt in freshnmsworld
* don't use concurrent chunk gen if custom chunk generators do not support it
* distinct between generator types
* improve regen speed for overworlds
* mix
* Add message that regen might take a while
* use a shared map for FastAreaLazy, cleanup imports
* use custom concurrency levels for chunk stati and process accordingly
* implement new regen in 1.15.2 and 1.16.1 as well
Conflicts:
worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java
worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java
* woops
* further abstraction, finalized regen impl
* Formatting
* Fix some typos, remove debug
* replace wildcard imports
* cleanup debug
* braces
* serr -> logger
* move regen impls to seperate classes
* fix world init for 1.16.1
* fix world init for 1.15.2
* fix world init for 1.15.2 #2
* use original world name for regeneration
* Update Regen_v1_15_R2.java
* Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java
Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
* Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java
Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
* Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java
Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
* improve documentation, use parallel task count for fawe settings
* fix compile
Co-authored-by: wea_ondara <wea_ondara@alpenblock.net>
Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com>
Co-authored-by: dordsor21 <dordsor21@gmail.com>
2020-10-10 09:57:12 +00:00
|
|
|
return false;
|
2019-07-18 16:07:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Shifting operations down the pipeline from EditSession -> Extent
|
|
|
|
- This allows certain extents (e.g. multithreaded extent) to override and optimize as needed
|
|
|
|
- The EditSession shouldn't need to worry about implementation details
|
|
|
|
- TODO: actually optimize these
|
|
|
|
*/
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY) {
|
|
|
|
maxY = Math.min(maxY, Math.max(0, maxY));
|
|
|
|
minY = Math.max(0, minY);
|
|
|
|
for (int y = maxY; y >= minY; --y) {
|
2019-05-12 13:32:04 +00:00
|
|
|
BlockState block = getBlock(x, y, z);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (block.getBlockType().getMaterial().isMovementBlocker()) {
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return minY;
|
|
|
|
}
|
|
|
|
|
2019-04-04 10:28:41 +00:00
|
|
|
default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY, Mask filter) {
|
|
|
|
maxY = Math.min(maxY, Math.max(0, maxY));
|
|
|
|
minY = Math.max(0, minY);
|
|
|
|
for (int y = maxY; y >= minY; --y) {
|
2020-09-11 19:13:31 +00:00
|
|
|
if (filter.test(MutableBlockVector3.get(x, y, z))) {
|
2019-04-04 10:28:41 +00:00
|
|
|
return y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return minY;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
default int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
|
|
|
|
int clearanceAbove = maxY - y;
|
|
|
|
int clearanceBelow = y - minY;
|
|
|
|
int clearance = Math.min(clearanceAbove, clearanceBelow);
|
|
|
|
|
2019-05-12 13:32:04 +00:00
|
|
|
BlockState block = getBlock(x, y, z);
|
2018-08-12 14:03:07 +00:00
|
|
|
boolean state = !block.getBlockType().getMaterial().isMovementBlocker();
|
|
|
|
int data1 = PropertyGroup.LEVEL.get(block);
|
|
|
|
int data2 = data1;
|
|
|
|
int offset = state ? 0 : 1;
|
|
|
|
for (int d = 0; d <= clearance; d++) {
|
|
|
|
int y1 = y + d;
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, y1, z);
|
2019-06-25 17:07:47 +00:00
|
|
|
if (block.getBlockType().getMaterial().isMovementBlocker() == state) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return ((y1 - offset) << 4) - (15 - (state ? PropertyGroup.LEVEL.get(block) : data1));
|
|
|
|
}
|
|
|
|
data1 = PropertyGroup.LEVEL.get(block);
|
|
|
|
int y2 = y - d;
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, y2, z);
|
2019-06-25 17:07:47 +00:00
|
|
|
if (block.getBlockType().getMaterial().isMovementBlocker() == state) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return ((y2 + offset) << 4) - (15 - (state ? PropertyGroup.LEVEL.get(block) : data2));
|
|
|
|
}
|
|
|
|
data2 = PropertyGroup.LEVEL.get(block);
|
|
|
|
}
|
|
|
|
if (clearanceAbove != clearanceBelow) {
|
|
|
|
if (clearanceAbove < clearanceBelow) {
|
|
|
|
for (int layer = y - clearance - 1; layer >= minY; layer--) {
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, layer, z);
|
2019-06-25 17:07:47 +00:00
|
|
|
if (block.getBlockType().getMaterial().isMovementBlocker() == state) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return ((layer + offset) << 4) + 0;
|
|
|
|
}
|
|
|
|
data1 = PropertyGroup.LEVEL.get(block);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, layer, z);
|
2019-06-25 17:07:47 +00:00
|
|
|
if (block.getBlockType().getMaterial().isMovementBlocker() == state) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return ((layer - offset) << 4) - (15 - (state ? PropertyGroup.LEVEL.get(block) : data2));
|
|
|
|
}
|
|
|
|
data2 = PropertyGroup.LEVEL.get(block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (state ? minY : maxY) << 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
|
|
|
|
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, minY, maxY, ignoreAir);
|
|
|
|
}
|
|
|
|
|
|
|
|
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
|
|
|
|
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, minY, maxY);
|
|
|
|
}
|
|
|
|
|
|
|
|
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
|
|
|
|
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, true);
|
|
|
|
}
|
|
|
|
|
2019-04-04 10:28:41 +00:00
|
|
|
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
|
|
|
|
y = Math.max(minY, Math.min(maxY, y));
|
|
|
|
int clearanceAbove = maxY - y;
|
|
|
|
int clearanceBelow = y - minY;
|
|
|
|
int clearance = Math.min(clearanceAbove, clearanceBelow);
|
2020-09-11 19:13:31 +00:00
|
|
|
boolean state = !mask.test(MutableBlockVector3.get(x, y, z));
|
2019-04-04 10:28:41 +00:00
|
|
|
int offset = state ? 0 : 1;
|
|
|
|
for (int d = 0; d <= clearance; d++) {
|
|
|
|
int y1 = y + d;
|
2020-10-05 17:41:41 +00:00
|
|
|
if (mask.test(MutableBlockVector3.get(x, y1, z)) != state) {
|
|
|
|
return y1 - offset;
|
|
|
|
}
|
2019-04-04 10:28:41 +00:00
|
|
|
int y2 = y - d;
|
2020-10-05 17:41:41 +00:00
|
|
|
if (mask.test(MutableBlockVector3.get(x, y2, z)) != state) {
|
|
|
|
return y2 + offset;
|
|
|
|
}
|
2019-04-04 10:28:41 +00:00
|
|
|
}
|
|
|
|
if (clearanceAbove != clearanceBelow) {
|
|
|
|
if (clearanceAbove < clearanceBelow) {
|
|
|
|
for (int layer = y - clearance - 1; layer >= minY; layer--) {
|
2020-10-05 17:41:41 +00:00
|
|
|
if (mask.test(MutableBlockVector3.get(x, layer, z)) != state) {
|
|
|
|
return layer + offset;
|
|
|
|
}
|
2019-04-04 10:28:41 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
|
2020-10-05 17:41:41 +00:00
|
|
|
if (mask.test(MutableBlockVector3.get(x, layer, z)) != state) {
|
|
|
|
return layer - offset;
|
|
|
|
}
|
2019-04-04 10:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-25 17:07:47 +00:00
|
|
|
return state ? failedMin : failedMax;
|
2019-04-04 10:28:41 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, boolean ignoreAir) {
|
|
|
|
y = Math.max(minY, Math.min(maxY, y));
|
|
|
|
int clearanceAbove = maxY - y;
|
|
|
|
int clearanceBelow = y - minY;
|
|
|
|
int clearance = Math.min(clearanceAbove, clearanceBelow);
|
2019-09-22 01:00:45 +00:00
|
|
|
BlockState block = getBlock(x, y, z);
|
2018-08-12 14:03:07 +00:00
|
|
|
boolean state = !block.getBlockType().getMaterial().isMovementBlocker();
|
|
|
|
int offset = state ? 0 : 1;
|
|
|
|
for (int d = 0; d <= clearance; d++) {
|
|
|
|
int y1 = y + d;
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, y1, z);
|
2020-10-05 17:41:41 +00:00
|
|
|
if (block.getMaterial().isMovementBlocker() == state && block.getBlockType() != BlockTypes.__RESERVED__) {
|
|
|
|
return y1 - offset;
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
int y2 = y - d;
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, y2, z);
|
2020-10-05 17:41:41 +00:00
|
|
|
if (block.getMaterial().isMovementBlocker() == state && block.getBlockType() != BlockTypes.__RESERVED__) {
|
|
|
|
return y2 + offset;
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
if (clearanceAbove != clearanceBelow) {
|
|
|
|
if (clearanceAbove < clearanceBelow) {
|
|
|
|
for (int layer = y - clearance - 1; layer >= minY; layer--) {
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, layer, z);
|
2020-10-05 17:41:41 +00:00
|
|
|
if (block.getMaterial().isMovementBlocker() == state && block.getBlockType() != BlockTypes.__RESERVED__) {
|
|
|
|
return layer + offset;
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, layer, z);
|
2020-10-05 17:41:41 +00:00
|
|
|
if (block.getMaterial().isMovementBlocker() == state && block.getBlockType() != BlockTypes.__RESERVED__) {
|
|
|
|
return layer - offset;
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int result = state ? failedMin : failedMax;
|
2020-10-05 17:41:41 +00:00
|
|
|
if (result > 0 && !ignoreAir) {
|
2019-05-12 13:32:04 +00:00
|
|
|
block = getBlock(x, result, z);
|
2018-08-12 14:03:07 +00:00
|
|
|
return block.getBlockType().getMaterial().isAir() ? -1 : result;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
default void addCaves(Region region) throws WorldEditException {
|
|
|
|
generate(region, new CavesGen(8));
|
|
|
|
}
|
|
|
|
|
|
|
|
default void generate(Region region, GenBase gen) throws WorldEditException {
|
2018-12-23 16:19:33 +00:00
|
|
|
for (BlockVector2 chunkPos : region.getChunks()) {
|
2018-08-12 14:03:07 +00:00
|
|
|
gen.generate(chunkPos, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-25 17:07:47 +00:00
|
|
|
default void addSchems(Region region, Mask mask, List<ClipboardHolder> clipboards, int rarity, boolean rotate) throws WorldEditException {
|
2018-08-12 14:03:07 +00:00
|
|
|
spawnResource(region, new SchemGen(mask, this, clipboards, rotate), rarity, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
default void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {
|
|
|
|
ThreadLocalRandom random = ThreadLocalRandom.current();
|
2018-12-23 16:19:33 +00:00
|
|
|
for (BlockVector2 chunkPos : region.getChunks()) {
|
2018-08-12 14:03:07 +00:00
|
|
|
for (int i = 0; i < frequency; i++) {
|
|
|
|
if (random.nextInt(100) > rarity) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-09-13 21:38:29 +00:00
|
|
|
int x = (chunkPos.getBlockX() << 4) + random.nextInt(16);
|
|
|
|
int z = (chunkPos.getBlockZ() << 4) + random.nextInt(16);
|
2018-08-12 14:03:07 +00:00
|
|
|
gen.spawn(random, x, z);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
default boolean contains(BlockVector3 pt) {
|
|
|
|
BlockVector3 min = getMinimumPoint();
|
|
|
|
BlockVector3 max = getMaximumPoint();
|
2019-07-06 00:46:48 +00:00
|
|
|
return pt.containedWithin(min, max);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
2019-06-25 17:07:47 +00:00
|
|
|
default void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException {
|
2018-08-12 14:03:07 +00:00
|
|
|
spawnResource(region, new OreGen(this, mask, material, size, minY, maxY), rarity, frequency);
|
|
|
|
}
|
|
|
|
|
2019-06-25 17:07:47 +00:00
|
|
|
default void addOres(Region region, Mask mask) throws WorldEditException {
|
2018-08-12 14:03:07 +00:00
|
|
|
addOre(region, mask, BlockTypes.DIRT.getDefaultState(), 33, 10, 100, 0, 255);
|
|
|
|
addOre(region, mask, BlockTypes.GRAVEL.getDefaultState(), 33, 8, 100, 0, 255);
|
|
|
|
addOre(region, mask, BlockTypes.ANDESITE.getDefaultState(), 33, 10, 100, 0, 79);
|
|
|
|
addOre(region, mask, BlockTypes.DIORITE.getDefaultState(), 33, 10, 100, 0, 79);
|
|
|
|
addOre(region, mask, BlockTypes.GRANITE.getDefaultState(), 33, 10, 100, 0, 79);
|
|
|
|
addOre(region, mask, BlockTypes.COAL_ORE.getDefaultState(), 17, 20, 100, 0, 127);
|
|
|
|
addOre(region, mask, BlockTypes.IRON_ORE.getDefaultState(), 9, 20, 100, 0, 63);
|
|
|
|
addOre(region, mask, BlockTypes.GOLD_ORE.getDefaultState(), 9, 2, 100, 0, 31);
|
|
|
|
addOre(region, mask, BlockTypes.REDSTONE_ORE.getDefaultState(), 8, 8, 100, 0, 15);
|
|
|
|
addOre(region, mask, BlockTypes.DIAMOND_ORE.getDefaultState(), 8, 1, 100, 0, 15);
|
|
|
|
addOre(region, mask, BlockTypes.LAPIS_ORE.getDefaultState(), 7, 1, 100, 0, 15);
|
|
|
|
addOre(region, mask, BlockTypes.EMERALD_ORE.getDefaultState(), 5, 1, 100, 4, 31);
|
|
|
|
}
|
|
|
|
|
2018-08-16 15:54:13 +00:00
|
|
|
/**
|
|
|
|
* Get the block distribution inside a region.
|
|
|
|
*
|
|
|
|
* @param region a region
|
|
|
|
* @return the results
|
|
|
|
*/
|
|
|
|
default List<Countable<BlockType>> getBlockDistribution(final Region region) {
|
|
|
|
int[] counter = new int[BlockTypes.size()];
|
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
for (final BlockVector3 pt : region) {
|
2019-07-06 00:46:48 +00:00
|
|
|
BlockType type = getBlock(pt).getBlockType();
|
2020-12-11 16:13:09 +00:00
|
|
|
if (type == BlockTypes.__RESERVED__) {
|
|
|
|
counter[1]++;
|
|
|
|
continue;
|
|
|
|
}
|
2018-08-16 15:54:13 +00:00
|
|
|
counter[type.getInternalId()]++;
|
|
|
|
}
|
|
|
|
List<Countable<BlockType>> distribution = new ArrayList<>();
|
|
|
|
for (int i = 0; i < counter.length; i++) {
|
|
|
|
int count = counter[i];
|
|
|
|
if (count != 0) {
|
|
|
|
distribution.add(new Countable<>(BlockTypes.get(i), count));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Collections.sort(distribution);
|
|
|
|
return distribution;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the block distribution (with data values) inside a region.
|
|
|
|
*
|
|
|
|
* @param region a region
|
|
|
|
* @return the results
|
|
|
|
*/
|
2019-04-03 11:28:57 +00:00
|
|
|
default List<Countable<BlockState>> getBlockDistributionWithData(final Region region) {
|
2018-08-16 15:54:13 +00:00
|
|
|
int[][] counter = new int[BlockTypes.size()][];
|
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
for (final BlockVector3 pt : region) {
|
2019-04-03 11:28:57 +00:00
|
|
|
BlockState blk = this.getBlock(pt);
|
2018-08-16 15:54:13 +00:00
|
|
|
BlockType type = blk.getBlockType();
|
2020-12-11 16:13:09 +00:00
|
|
|
if (type == BlockTypes.__RESERVED__) {
|
|
|
|
int[] stateCounter = counter[1];
|
|
|
|
if (stateCounter == null) {
|
|
|
|
counter[1] = stateCounter = new int[BlockTypes.AIR.getMaxStateId() + 1];
|
|
|
|
}
|
|
|
|
stateCounter[BlockTypes.AIR.getDefaultState().getInternalPropertiesId()]++;
|
|
|
|
}
|
2018-08-16 15:54:13 +00:00
|
|
|
int[] stateCounter = counter[type.getInternalId()];
|
|
|
|
if (stateCounter == null) {
|
|
|
|
counter[type.getInternalId()] = stateCounter = new int[type.getMaxStateId() + 1];
|
|
|
|
}
|
|
|
|
stateCounter[blk.getInternalPropertiesId()]++;
|
|
|
|
}
|
2019-04-03 11:28:57 +00:00
|
|
|
List<Countable<BlockState>> distribution = new ArrayList<>();
|
2018-08-16 15:54:13 +00:00
|
|
|
for (int typeId = 0; typeId < counter.length; typeId++) {
|
|
|
|
BlockType type = BlockTypes.get(typeId);
|
|
|
|
int[] stateCount = counter[typeId];
|
|
|
|
if (stateCount != null) {
|
|
|
|
for (int propId = 0; propId < stateCount.length; propId++) {
|
|
|
|
int count = stateCount[propId];
|
|
|
|
if (count != 0) {
|
2019-04-03 11:28:57 +00:00
|
|
|
BlockState state = type.withPropertyId(propId);
|
2018-08-16 15:54:13 +00:00
|
|
|
distribution.add(new Countable<>(state, count));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Collections.reverse(distribution);
|
|
|
|
return distribution;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Nullable
|
|
|
|
@Override
|
|
|
|
default Operation commit() {
|
|
|
|
return null;
|
|
|
|
}
|
2014-04-28 06:34:39 +00:00
|
|
|
|
2019-07-11 15:32:14 +00:00
|
|
|
default boolean cancel() {
|
2019-11-17 22:47:56 +00:00
|
|
|
ExtentTraverser<Extent> traverser = new ExtentTraverser<>(this);
|
|
|
|
|
2020-01-29 20:01:38 +00:00
|
|
|
NullExtent nullExtent = new NullExtent(this, FaweCache.MANUAL);
|
2019-11-17 22:47:56 +00:00
|
|
|
|
|
|
|
ExtentTraverser<Extent> next = traverser.next();
|
|
|
|
if (next != null) {
|
|
|
|
Extent child = next.get();
|
2020-10-05 17:41:41 +00:00
|
|
|
if (child instanceof NullExtent) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-11-17 22:47:56 +00:00
|
|
|
traverser.setNext(nullExtent);
|
|
|
|
child.cancel();
|
|
|
|
}
|
|
|
|
addProcessor(nullExtent);
|
2019-07-16 19:10:39 +00:00
|
|
|
return true;
|
2019-07-11 15:32:14 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
default int getMaxY() {
|
|
|
|
return 255;
|
|
|
|
}
|
2019-06-28 18:20:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Lazily copy a region
|
|
|
|
*
|
|
|
|
* @param region
|
|
|
|
* @return
|
|
|
|
*/
|
2019-11-06 09:29:20 +00:00
|
|
|
default Clipboard lazyCopy(Region region) {
|
|
|
|
WorldCopyClipboard faweClipboard = new WorldCopyClipboard(() -> this, region);
|
|
|
|
faweClipboard.setOrigin(region.getMinimumPoint());
|
|
|
|
return faweClipboard;
|
2019-06-28 18:20:48 +00:00
|
|
|
}
|
2019-07-11 15:32:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Count the number of blocks of a list of types in a region.
|
|
|
|
*
|
|
|
|
* @param region the region
|
|
|
|
* @param searchBlocks the list of blocks to search
|
|
|
|
* @return the number of blocks that matched the block
|
|
|
|
*/
|
|
|
|
default int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
|
|
|
|
BlockMask mask = new BlockMask(this, searchBlocks);
|
|
|
|
return countBlocks(region, mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Count the number of blocks of a list of types in a region.
|
|
|
|
*
|
|
|
|
* @param region the region
|
|
|
|
* @param searchMask mask to match
|
|
|
|
* @return the number of blocks that matched the mask
|
|
|
|
*/
|
|
|
|
default int countBlocks(Region region, Mask searchMask) {
|
2021-04-04 17:56:57 +00:00
|
|
|
RegionVisitor visitor = new RegionVisitor(region, searchMask::test);
|
2019-07-11 15:32:14 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
return visitor.getAffected();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets all the blocks inside a region to a given block type.
|
|
|
|
*
|
|
|
|
* @param region the region
|
|
|
|
* @param block the block
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
default <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException {
|
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(block);
|
2020-01-21 19:30:13 +00:00
|
|
|
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
2019-07-11 15:32:14 +00:00
|
|
|
|
2019-07-22 09:05:14 +00:00
|
|
|
int changes = 0;
|
|
|
|
for (BlockVector3 pos : region) {
|
|
|
|
if (setBlock(pos, block)) {
|
|
|
|
changes++;
|
2019-07-11 15:32:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets all the blocks inside a region to a given pattern.
|
|
|
|
*
|
|
|
|
* @param region the region
|
|
|
|
* @param pattern the pattern that provides the replacement block
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
default int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
|
|
|
if (pattern instanceof BlockPattern) {
|
|
|
|
return setBlocks(region, ((BlockPattern) pattern).getBlock());
|
|
|
|
}
|
|
|
|
if (pattern instanceof BlockStateHolder) {
|
|
|
|
return setBlocks(region, (BlockStateHolder) pattern);
|
|
|
|
}
|
2019-07-22 09:05:14 +00:00
|
|
|
int count = 0;
|
|
|
|
for (BlockVector3 pos : region) {
|
|
|
|
if (pattern.apply(this, pos, pos)) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
2019-07-11 15:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces all the blocks matching a given filter, within a given region, to a block
|
|
|
|
* returned by a given pattern.
|
|
|
|
*
|
|
|
|
* @param region the region to replace the blocks within
|
|
|
|
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
|
|
|
|
* @param replacement the replacement block
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
default <B extends BlockStateHolder<B>> int replaceBlocks(Region region, Set<BaseBlock> filter, B replacement) throws MaxChangedBlocksException {
|
2019-11-19 21:23:47 +00:00
|
|
|
return replaceBlocks(region, filter, (Pattern) replacement);
|
2019-07-11 15:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces all the blocks matching a given filter, within a given region, to a block
|
|
|
|
* returned by a given pattern.
|
|
|
|
*
|
|
|
|
* @param region the region to replace the blocks within
|
|
|
|
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
|
|
|
|
* @param pattern the pattern that provides the new blocks
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
default int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
Mask mask = filter == null ? new ExistingBlockMask(this) : new BlockMask(this, filter);
|
|
|
|
return replaceBlocks(region, mask, pattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces all the blocks matching a given mask, within a given region, to a block
|
|
|
|
* returned by a given pattern.
|
|
|
|
*
|
|
|
|
* @param region the region to replace the blocks within
|
|
|
|
* @param mask the mask that blocks must match
|
|
|
|
* @param pattern the pattern that provides the new blocks
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
default int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(mask);
|
|
|
|
checkNotNull(pattern);
|
|
|
|
|
|
|
|
BlockReplace replace = new BlockReplace(this, pattern);
|
2020-01-04 10:11:13 +00:00
|
|
|
RegionMaskingFilter filter = new RegionMaskingFilter(this, mask, replace);
|
2019-07-11 15:32:14 +00:00
|
|
|
RegionVisitor visitor = new RegionVisitor(region, filter);
|
|
|
|
Operations.completeLegacy(visitor);
|
|
|
|
return visitor.getAffected();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the blocks at the center of the given region to the given pattern.
|
|
|
|
* If the center sits between two blocks on a certain axis, then two blocks
|
|
|
|
* will be placed to mark the center.
|
|
|
|
*
|
|
|
|
* @param region the region to find the center of
|
|
|
|
* @param pattern the replacement pattern
|
|
|
|
* @return the number of blocks placed
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
default int center(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
|
|
|
|
|
|
|
Vector3 center = region.getCenter();
|
|
|
|
Region centerRegion = new CuboidRegion(
|
2019-08-10 06:01:42 +00:00
|
|
|
this instanceof World ? (World) this : null, // Causes clamping of Y range
|
2019-07-11 15:32:14 +00:00
|
|
|
BlockVector3.at(((int) center.getX()), ((int) center.getY()), ((int) center.getZ())),
|
|
|
|
BlockVector3.at(MathUtils.roundHalfUp(center.getX()),
|
|
|
|
center.getY(), MathUtils.roundHalfUp(center.getZ())));
|
|
|
|
return setBlocks(centerRegion, pattern);
|
|
|
|
}
|
|
|
|
|
2019-07-11 16:10:32 +00:00
|
|
|
default int setBlocks(final Set<BlockVector3> vset, final Pattern pattern) {
|
2019-07-22 09:05:14 +00:00
|
|
|
if (vset instanceof Region) {
|
|
|
|
return setBlocks((Region) vset, pattern);
|
|
|
|
}
|
|
|
|
int count = 0;
|
|
|
|
for (BlockVector3 pos : vset) {
|
|
|
|
if (pattern.apply(this, pos, pos)) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2020-05-27 10:45:08 +00:00
|
|
|
default boolean relight(int x, int y, int z) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
default boolean relightBlock(int x, int y, int z) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
default boolean relightSky(int x, int y, int z) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-23 04:23:52 +00:00
|
|
|
/**
|
|
|
|
* Have an extent processed
|
|
|
|
* - Either block (Extent) processing or chunk processing
|
|
|
|
* @param processor
|
|
|
|
* @return processed Extent
|
|
|
|
*/
|
|
|
|
default Extent addProcessor(IBatchProcessor processor) {
|
|
|
|
return processor.construct(this);
|
|
|
|
}
|
|
|
|
|
2020-09-28 10:13:02 +00:00
|
|
|
default Extent addPostProcessor(IBatchProcessor processor) {
|
2021-01-11 19:29:16 +00:00
|
|
|
if (processor.getScope() == ProcessorScope.READING_SET_BLOCKS) {
|
|
|
|
throw new IllegalArgumentException("You cannot alter blocks in a PostProcessor");
|
|
|
|
}
|
2020-09-28 10:13:02 +00:00
|
|
|
return processor.construct(this);
|
|
|
|
}
|
|
|
|
|
2020-01-06 08:36:16 +00:00
|
|
|
default Extent enableHistory(AbstractChangeSet changeSet) {
|
2020-09-28 10:13:02 +00:00
|
|
|
if (Settings.IMP.EXPERIMENTAL.SEND_BEFORE_HISTORY) {
|
|
|
|
return addPostProcessor(changeSet);
|
|
|
|
} else {
|
|
|
|
return addProcessor(changeSet);
|
|
|
|
}
|
2019-10-23 04:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default Extent disableHistory() {
|
|
|
|
return this;
|
|
|
|
}
|
2019-10-31 13:12:23 +00:00
|
|
|
|
2019-11-11 16:49:13 +00:00
|
|
|
default <T extends Filter> T apply(Region region, T filter, boolean full) {
|
2019-10-31 13:12:23 +00:00
|
|
|
return apply((Iterable<BlockVector3>) region, filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
default <T extends Filter> T apply(Iterable<BlockVector3> positions, T filter) {
|
|
|
|
ExtentFilterBlock block = new ExtentFilterBlock(this);
|
|
|
|
for (BlockVector3 pos : positions) {
|
|
|
|
filter.applyBlock(block.init(pos));
|
|
|
|
}
|
|
|
|
return filter;
|
|
|
|
}
|
2014-03-30 04:05:09 +00:00
|
|
|
}
|