Implement tile entities to generation using Populators

- Fixes #3051
This commit is contained in:
dordsor21 2022-06-09 20:49:43 +01:00
parent a79c474957
commit 90d42b8b9f
No known key found for this signature in database
GPG Key ID: 1E53E88969FFCF0B
10 changed files with 651 additions and 98 deletions

View File

@ -25,17 +25,18 @@
*/
package com.plotsquared.bukkit.generator;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.bukkit.queue.LimitedRegionWrapperQueue;
import com.plotsquared.core.generator.HybridPlotWorld;
import com.plotsquared.core.generator.IndependentPlotGenerator;
import com.plotsquared.core.location.ChunkWrapper;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.location.UncheckedWorldLocation;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.queue.QueueCoordinator;
import com.plotsquared.core.plot.world.SinglePlotArea;
import com.plotsquared.core.queue.ScopedQueueCoordinator;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.LimitedRegion;
import org.bukkit.generator.WorldInfo;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Random;
@ -43,35 +44,48 @@ import java.util.Random;
final class BlockStatePopulator extends BlockPopulator {
private final IndependentPlotGenerator plotGenerator;
private final PlotAreaManager plotAreaManager;
private QueueCoordinator queue;
public BlockStatePopulator(
final @NonNull IndependentPlotGenerator plotGenerator
) {
this.plotGenerator = plotGenerator;
}
/**
* @deprecated Use {@link BlockStatePopulator#BlockStatePopulator(IndependentPlotGenerator)} as plotAreManager is unused
*/
@Deprecated(forRemoval = true, since = "TODO")
public BlockStatePopulator(
final @NonNull IndependentPlotGenerator plotGenerator,
final @NonNull PlotAreaManager plotAreaManager
) {
this.plotGenerator = plotGenerator;
this.plotAreaManager = plotAreaManager;
}
@Override
public void populate(final @NonNull World world, final @NonNull Random random, final @NonNull Chunk source) {
if (this.queue == null) {
this.queue = PlotSquared.platform().globalBlockQueue().getNewQueue(new BukkitWorld(world));
}
final PlotArea area = this.plotAreaManager.getPlotArea(world.getName(), null);
if (area == null) {
public void populate(
@NonNull final WorldInfo worldInfo,
@NonNull final Random random,
final int chunkX,
final int chunkZ,
@NonNull final LimitedRegion limitedRegion
) {
PlotArea area = UncheckedWorldLocation.at(worldInfo.getName(), chunkX << 4, 0, chunkZ << 4).getPlotArea();
if (area == null || (area instanceof HybridPlotWorld hpw && !hpw.populationNeeded()) || area instanceof SinglePlotArea) {
return;
}
final ChunkWrapper wrap = new ChunkWrapper(area.getWorldName(), source.getX(), source.getZ());
final ScopedQueueCoordinator chunk = this.queue.getForChunk(wrap.x, wrap.z,
com.plotsquared.bukkit.util.BukkitWorld.getMinWorldHeight(world),
com.plotsquared.bukkit.util.BukkitWorld.getMaxWorldHeight(world) - 1
LimitedRegionWrapperQueue wrapped = new LimitedRegionWrapperQueue(limitedRegion);
// It is possible for the region to be larger than the chunk, but there is not reason for P2 to need to populate
// outside of the actual chunk area.
Location min = UncheckedWorldLocation.at(worldInfo.getName(), chunkX << 4, worldInfo.getMinHeight(), chunkZ << 4);
Location max = UncheckedWorldLocation.at(
worldInfo.getName(),
(chunkX << 4) + 15,
worldInfo.getMaxHeight(),
(chunkZ << 4) + 15
);
if (this.plotGenerator.populateChunk(chunk, area)) {
this.queue.enqueue();
}
ScopedQueueCoordinator offsetChunkQueue = new ScopedQueueCoordinator(wrapped, min, max);
this.plotGenerator.populateChunk(offsetChunkQueue, area);
}
}

View File

@ -0,0 +1,78 @@
/*
* _____ _ _ _____ _
* | __ \| | | | / ____| | |
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
* | |
* |_|
* PlotSquared plot management system for Minecraft
* Copyright (C) 2014 - 2022 IntellectualSites
*
* 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
* (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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.bukkit.generator;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.generator.HybridPlotWorld;
import com.plotsquared.core.generator.IndependentPlotGenerator;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.location.UncheckedWorldLocation;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.world.SinglePlotArea;
import com.plotsquared.core.queue.QueueCoordinator;
import com.plotsquared.core.queue.ScopedQueueCoordinator;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.util.SideEffectSet;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.generator.BlockPopulator;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
final class BlockStatePopulator116 extends BlockPopulator {
private final IndependentPlotGenerator plotGenerator;
public BlockStatePopulator116(
final @NonNull IndependentPlotGenerator plotGenerator
) {
this.plotGenerator = plotGenerator;
}
@Override
public void populate(@NotNull final World world, @NotNull final Random random, @NotNull final Chunk source) {
int chunkMinX = source.getX() << 4;
int chunkMinZ = source.getZ() << 4;
PlotArea area = Location.at(world.getName(), chunkMinX, 0, chunkMinZ).getPlotArea();
if (area == null || (area instanceof HybridPlotWorld hpw && !hpw.populationNeeded()) || area instanceof SinglePlotArea) {
return;
}
QueueCoordinator queue = PlotSquared.platform().globalBlockQueue().getNewQueue(new BukkitWorld(world));
queue.setForceSync(true);
queue.setSideEffectSet(SideEffectSet.none());
queue.setBiomesEnabled(false);
queue.setChunkObject(source);
Location min = UncheckedWorldLocation.at(world.getName(), chunkMinX, world.getMinHeight(), chunkMinZ);
Location max = UncheckedWorldLocation.at(world.getName(), chunkMinX + 15, world.getMaxHeight(), chunkMinZ + 15);
ScopedQueueCoordinator offsetChunkQueue = new ScopedQueueCoordinator(queue, min, max);
this.plotGenerator.populateChunk(offsetChunkQueue, area);
queue.enqueue();
}
}

View File

@ -37,6 +37,8 @@ import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.queue.ScopedQueueCoordinator;
import com.plotsquared.core.util.ChunkManager;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.math.BlockVector2;
import org.bukkit.World;
import org.bukkit.block.Biome;
@ -49,8 +51,7 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
public class BukkitPlotGenerator extends ChunkGenerator
implements GeneratorWrapper<ChunkGenerator> {
public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrapper<ChunkGenerator> {
@SuppressWarnings("unused")
public final boolean PAPER_ASYNC_SAFE = true;
@ -73,7 +74,12 @@ public class BukkitPlotGenerator extends ChunkGenerator
this.plotGenerator = generator;
this.platformGenerator = this;
this.populators = new ArrayList<>();
this.populators.add(new BlockStatePopulator(this.plotGenerator, this.plotAreaManager));
int minecraftMinorVersion = PlotSquared.platform().serverVersion()[1];
if (minecraftMinorVersion >= 17) {
this.populators.add(new BlockStatePopulator(this.plotGenerator));
} else {
this.populators.add(new BlockStatePopulator116(this.plotGenerator));
}
this.full = true;
}
@ -159,7 +165,6 @@ public class BukkitPlotGenerator extends ChunkGenerator
@NonNull World world, @NonNull Random random, int x, int z,
@NonNull BiomeGrid biome
) {
int minY = BukkitWorld.getMinWorldHeight(world);
int maxY = BukkitWorld.getMaxWorldHeight(world);
GenChunk result = new GenChunk(minY, maxY);

View File

@ -0,0 +1,106 @@
/*
* _____ _ _ _____ _
* | __ \| | | | / ____| | |
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
* | |
* |_|
* PlotSquared plot management system for Minecraft
* Copyright (C) 2014 - 2022 IntellectualSites
*
* 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
* (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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.bukkit.queue;
import com.plotsquared.bukkit.schematic.StateWrapper;
import com.plotsquared.core.queue.DelegateQueueCoordinator;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.generator.LimitedRegion;
import org.checkerframework.checker.nullness.qual.NonNull;
public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
private final LimitedRegion limitedRegion;
public LimitedRegionWrapperQueue(LimitedRegion limitedRegion) {
super(null);
this.limitedRegion = limitedRegion;
}
@Override
public boolean setBlock(final int x, final int y, final int z, @NonNull final Pattern pattern) {
return setBlock(x, y, z, pattern.applyBlock(BlockVector3.at(x, y, z)));
}
@Override
public boolean setBlock(final int x, final int y, final int z, @NonNull final BaseBlock id) {
boolean result = setBlock(x, y, z, id.toImmutableState());
if (result && id.hasNbtData()) {
CompoundTag tag = id.getNbtData();
StateWrapper sw = new StateWrapper(tag);
sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
}
return result;
}
@Override
public boolean setBlock(final int x, final int y, final int z, @NonNull final BlockState id) {
try {
limitedRegion.setType(x, y, z, BukkitAdapter.adapt(id.getBlockType()));
limitedRegion.setBlockData(x, y, z, BukkitAdapter.adapt(id));
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
public boolean setEntity(@NonNull final Entity entity) {
try {
EntityType type = BukkitAdapter.adapt(entity.getState().getType());
double x = entity.getLocation().getX();
double y = entity.getLocation().getY();
double z = entity.getLocation().getZ();
Location location = new Location(limitedRegion.getWorld(), x, y, z);
limitedRegion.spawnEntity(location, type);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
public boolean setTile(final int x, final int y, final int z, @NonNull final CompoundTag tag) {
StateWrapper sw = new StateWrapper(tag);
return sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
}
@Override
public boolean isSettingTiles() {
return true;
}
}

View File

@ -35,11 +35,13 @@ import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.item.ItemType;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Container;
import org.bukkit.block.Sign;
import org.bukkit.block.Skull;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
@ -57,6 +59,10 @@ public class StateWrapper {
public org.bukkit.block.BlockState state = null;
public CompoundTag tag = null;
/**
* @deprecated in favour of using WE methods for obtaining NBT
*/
@Deprecated(forRemoval = true, since = "TODO")
public StateWrapper(org.bukkit.block.BlockState state) {
this.state = state;
}
@ -230,10 +236,27 @@ public class StateWrapper {
}
return false;
}
case "skull" -> {
if (state instanceof Skull skull) {
CompoundTag skullOwner = ((CompoundTag) this.tag.getValue().get("SkullOwner"));
if (skullOwner == null) {
return true;
}
String player = skullOwner.getString("Name");
skull.setOwningPlayer(Bukkit.getOfflinePlayer(player));
skull.update(true);
return true;
}
return false;
}
}
return false;
}
/**
* @deprecated in favour of using WE methods for obtaining NBT
*/
@Deprecated(forRemoval = true, since = "TODO")
public CompoundTag getTag() {
if (this.tag != null) {
return this.tag;

View File

@ -35,13 +35,23 @@ import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotId;
import com.plotsquared.core.queue.ScopedQueueCoordinator;
import com.plotsquared.core.util.MathMan;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.RegionOperationException;
import com.sk89q.worldedit.world.NullWorld;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class HybridGen extends IndependentPlotGenerator {
private static final CuboidRegion CHUNK = new CuboidRegion(BlockVector3.ZERO, BlockVector3.at(15, 396, 15));
private final HybridPlotWorldFactory hybridPlotWorldFactory;
@Inject
@ -55,12 +65,17 @@ public class HybridGen extends IndependentPlotGenerator {
}
private void placeSchem(
HybridPlotWorld world, ScopedQueueCoordinator result, short relativeX,
short relativeZ, int x, int z, boolean isRoad
HybridPlotWorld world,
ScopedQueueCoordinator result,
short relativeX,
short relativeZ,
int x,
int z,
boolean isRoad,
boolean isPopulating
) {
int minY; // Math.min(world.PLOT_HEIGHT, world.ROAD_HEIGHT);
if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad
&& Settings.Schematics.PASTE_ON_TOP)) {
if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) {
minY = world.SCHEM_Y;
} else {
minY = world.getMinBuildHeight();
@ -69,10 +84,12 @@ public class HybridGen extends IndependentPlotGenerator {
if (blocks != null) {
for (int y = 0; y < blocks.length; y++) {
if (blocks[y] != null) {
if (!isPopulating || blocks[y].hasNbtData()) {
result.setBlock(x, minY + y, z, blocks[y]);
}
}
}
}
BiomeType biome = world.G_SCH_B.get(MathMan.pair(relativeX, relativeZ));
if (biome != null) {
result.setBiome(x, z, biome);
@ -128,19 +145,17 @@ public class HybridGen extends IndependentPlotGenerator {
}
relativeX[i] = v;
if (hybridPlotWorld.ROAD_WIDTH != 0) {
insideRoadX[i] =
v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER;
insideWallX[i] =
v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER;
insideRoadX[i] = v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER;
insideWallX[i] = v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER;
}
}
// The Z-coordinate of a given Z coordinate, relative to the
// plot (Counting from the corner with the least positive
// coordinates)
short[] relativeZ = new short[16];
// Whether or not the given Z coordinate belongs to the road
// Whether the given Z coordinate belongs to the road
boolean[] insideRoadZ = new boolean[16];
// Whether or not the given Z coordinate belongs to the wall
// Whether the given Z coordinate belongs to the wall
boolean[] insideWallZ = new boolean[16];
for (short i = 0; i < 16; i++) {
short v = (short) (relativeOffsetZ + i);
@ -149,10 +164,8 @@ public class HybridGen extends IndependentPlotGenerator {
}
relativeZ[i] = v;
if (hybridPlotWorld.ROAD_WIDTH != 0) {
insideRoadZ[i] =
v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER;
insideWallZ[i] =
v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER;
insideRoadZ[i] = v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER;
insideWallZ[i] = v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER;
}
}
// generation
@ -165,7 +178,7 @@ public class HybridGen extends IndependentPlotGenerator {
result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern());
}
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true);
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false);
}
}
} else if (insideWallX[x]) {
@ -176,9 +189,7 @@ public class HybridGen extends IndependentPlotGenerator {
result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern());
}
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z,
true
);
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false);
}
} else {
// wall
@ -187,14 +198,10 @@ public class HybridGen extends IndependentPlotGenerator {
}
if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
if (hybridPlotWorld.PLACE_TOP_BLOCK) {
result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z,
hybridPlotWorld.WALL_BLOCK.toPattern()
);
result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern());
}
} else {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z,
true
);
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false);
}
}
}
@ -206,9 +213,7 @@ public class HybridGen extends IndependentPlotGenerator {
result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern());
}
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z,
true
);
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false);
}
} else if (insideWallZ[z]) {
// wall
@ -217,27 +222,19 @@ public class HybridGen extends IndependentPlotGenerator {
}
if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
if (hybridPlotWorld.PLACE_TOP_BLOCK) {
result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z,
hybridPlotWorld.WALL_BLOCK.toPattern()
);
result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern());
}
} else {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z,
true
);
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false);
}
} else {
// plot
for (int y = startY; y < hybridPlotWorld.PLOT_HEIGHT; y++) {
result.setBlock(x, y, z, hybridPlotWorld.MAIN_BLOCK.toPattern());
}
result.setBlock(x, hybridPlotWorld.PLOT_HEIGHT, z,
hybridPlotWorld.TOP_BLOCK.toPattern()
);
result.setBlock(x, hybridPlotWorld.PLOT_HEIGHT, z, hybridPlotWorld.TOP_BLOCK.toPattern());
if (hybridPlotWorld.PLOT_SCHEMATIC) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z,
false
);
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, false);
}
}
}
@ -245,6 +242,150 @@ public class HybridGen extends IndependentPlotGenerator {
}
}
@Override
public boolean populateChunk(final ScopedQueueCoordinator result, final PlotArea settings) {
HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings;
if (!hybridPlotWorld.populationNeeded()) {
return false;
}
// Coords
Location min = result.getMin();
int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X;
int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z;
// The relative X-coordinate (within the plot) of the minimum X coordinate
// contained in the scoped queue
short relativeOffsetX;
if (bx < 0) {
relativeOffsetX = (short) (hybridPlotWorld.SIZE + (bx % hybridPlotWorld.SIZE));
} else {
relativeOffsetX = (short) (bx % hybridPlotWorld.SIZE);
}
// The relative Z-coordinate (within the plot) of the minimum Z coordinate
// contained in the scoped queue
short relativeOffsetZ;
if (bz < 0) {
relativeOffsetZ = (short) (hybridPlotWorld.SIZE + (bz % hybridPlotWorld.SIZE));
} else {
relativeOffsetZ = (short) (bz % hybridPlotWorld.SIZE);
}
boolean allRoad = true;
boolean overlap = false;
// The X-coordinate of a given X coordinate, relative to the
// plot (Counting from the corner with the least positive
// coordinates)
short[] relativeX = new short[16];
boolean[] insideRoadX = new boolean[16];
boolean[] insideWallX = new boolean[16];
short offsetX = relativeOffsetX;
while (offsetX >= hybridPlotWorld.SIZE) {
offsetX -= hybridPlotWorld.SIZE;
}
for (short i = 0; i < 16; i++) {
if (offsetX >= hybridPlotWorld.SIZE) {
offsetX -= hybridPlotWorld.SIZE;
overlap = true;
}
relativeX[i] = offsetX;
if (hybridPlotWorld.ROAD_WIDTH != 0) {
boolean insideRoad = offsetX < hybridPlotWorld.PATH_WIDTH_LOWER || offsetX > hybridPlotWorld.PATH_WIDTH_UPPER;
boolean insideWall = offsetX == hybridPlotWorld.PATH_WIDTH_LOWER || offsetX == hybridPlotWorld.PATH_WIDTH_UPPER;
insideRoadX[i] = insideRoad;
insideWallX[i] = insideWall;
allRoad &= insideRoad && insideWall;
}
offsetX++;
}
// The Z-coordinate of a given Z coordinate, relative to the
// plot (Counting from the corner with the least positive
// coordinates)
short[] relativeZ = new short[16];
boolean[] insideRoadZ = new boolean[16];
boolean[] insideWallZ = new boolean[16];
short offsetZ = relativeOffsetZ;
while (offsetZ >= hybridPlotWorld.SIZE) {
offsetZ -= hybridPlotWorld.SIZE;
overlap = true;
}
for (short i = 0; i < 16; i++) {
if (offsetZ >= hybridPlotWorld.SIZE) {
offsetZ -= hybridPlotWorld.SIZE;
}
relativeZ[i] = offsetZ;
if (hybridPlotWorld.ROAD_WIDTH != 0) {
boolean insideRoad = offsetZ < hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ > hybridPlotWorld.PATH_WIDTH_UPPER;
boolean insideWall = offsetZ == hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ == hybridPlotWorld.PATH_WIDTH_UPPER;
insideRoadZ[i] = insideRoad;
insideWallZ[i] = insideWall;
allRoad &= insideRoad && insideWall;
}
offsetZ++;
}
for (short x = 0; x < 16; x++) {
if (insideRoadX[x]) {
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
for (short z = 0; z < 16; z++) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true);
}
}
} else if (insideWallX[x]) {
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
for (short z = 0; z < 16; z++) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true);
}
}
} else {
for (short z = 0; z < 16; z++) {
if (insideRoadZ[z] || insideWallZ[z]) {
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true);
}
} else if (hybridPlotWorld.PLOT_SCHEMATIC) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, true);
}
}
}
}
if (!allRoad && hybridPlotWorld.getPlotSchematicEntities() != null && !hybridPlotWorld
.getPlotSchematicEntities()
.isEmpty()) {
CuboidRegion region = CHUNK.clone();
try {
region.shift(hybridPlotWorld
.getPlotSchematicMinPoint()
.add(relativeOffsetX, 0, relativeOffsetZ)
.subtract(hybridPlotWorld.PATH_WIDTH_LOWER + 1, 0, hybridPlotWorld.PATH_WIDTH_LOWER + 1));
for (Entity entity : hybridPlotWorld.getPlotSchematicEntities()) {
if (region.contains(entity.getLocation().toVector().toBlockPoint())) {
Vector3 pos = (entity.getLocation().toVector()
.subtract(region.getMinimumPoint().withY(hybridPlotWorld.getPlotSchematicMinPoint().getY()).toVector3()))
.add(min.getBlockVector3().withY(hybridPlotWorld.SCHEM_Y).toVector3());
result.setEntity(new PopulatingEntity(
entity,
new com.sk89q.worldedit.util.Location(NullWorld.getInstance(), pos)
));
}
}
} catch (RegionOperationException e) {
throw new RuntimeException(e);
}
if (overlap) {
try {
region.shift(BlockVector3.at(-hybridPlotWorld.SIZE, 0, -hybridPlotWorld.SIZE));
for (Entity entity : hybridPlotWorld.getPlotSchematicEntities()) {
if (region.contains(entity.getLocation().toVector().toBlockPoint())) {
result.setEntity(entity);
}
}
} catch (RegionOperationException e) {
throw new RuntimeException(e);
}
}
}
return true;
}
@Override
public PlotArea getNewPlotArea(String world, String id, PlotId min, PlotId max) {
return this.hybridPlotWorldFactory.create(world, id, this, min, max);
@ -255,4 +396,49 @@ public class HybridGen extends IndependentPlotGenerator {
// All initialization is done in the PlotArea class
}
private static final class PopulatingEntity implements Entity {
private final Entity parent;
private com.sk89q.worldedit.util.Location location;
private PopulatingEntity(Entity parent, com.sk89q.worldedit.util.Location location) {
this.parent = parent;
this.location = location;
}
@Nullable
@Override
public BaseEntity getState() {
return parent.getState();
}
@Override
public boolean remove() {
return parent.remove();
}
@Override
public com.sk89q.worldedit.util.Location getLocation() {
return location;
}
@Override
public boolean setLocation(final com.sk89q.worldedit.util.Location location) {
this.location = location;
return true;
}
@Override
public Extent getExtent() {
return parent.getExtent();
}
@Nullable
@Override
public <T> T getFacet(final Class<? extends T> cls) {
return parent.getFacet(cls);
}
}
}

View File

@ -40,11 +40,13 @@ import com.plotsquared.core.plot.PlotId;
import com.plotsquared.core.plot.PlotManager;
import com.plotsquared.core.plot.schematic.Schematic;
import com.plotsquared.core.queue.GlobalBlockQueue;
import com.plotsquared.core.util.AnnotationHelper;
import com.plotsquared.core.util.FileUtils;
import com.plotsquared.core.util.MathMan;
import com.plotsquared.core.util.SchematicHandler;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.CompoundTagBuilder;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.internal.helper.MCDirections;
@ -58,11 +60,13 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.annotation.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
public class HybridPlotWorld extends ClassicPlotWorld {
@ -71,6 +75,7 @@ public class HybridPlotWorld extends ClassicPlotWorld {
private static final AffineTransform transform = new AffineTransform().rotateY(90);
public boolean ROAD_SCHEMATIC_ENABLED;
public boolean PLOT_SCHEMATIC = false;
@Deprecated(forRemoval = true, since = "TODO")
public int PLOT_SCHEMATIC_HEIGHT = -1;
public short PATH_WIDTH_LOWER;
public short PATH_WIDTH_UPPER;
@ -80,6 +85,11 @@ public class HybridPlotWorld extends ClassicPlotWorld {
private Location SIGN_LOCATION;
private File root = null;
private int lastOverlayHeightError = Integer.MIN_VALUE;
private List<Entity> schem3Entities = null;
private BlockVector3 schem3MinPoint = null;
private boolean schem1PopulationNeeded = false;
private boolean schem2PopulationNeeded = false;
private boolean schem3PopulationNeeded = false;
@Inject
private SchematicHandler schematicHandler;
@ -98,6 +108,7 @@ public class HybridPlotWorld extends ClassicPlotWorld {
PlotSquared.platform().injector().injectMembers(this);
}
@Deprecated(forRemoval = true, since = "TODO")
public static byte wrap(byte data, int start) {
if ((data >= start) && (data < (start + 4))) {
data = (byte) ((((data - start) + 2) & 3) + start);
@ -105,6 +116,7 @@ public class HybridPlotWorld extends ClassicPlotWorld {
return data;
}
@Deprecated(forRemoval = true, since = "TODO")
public static byte wrap2(byte data, int start) {
if ((data >= start) && (data < (start + 2))) {
data = (byte) ((((data - start) + 1) & 1) + start);
@ -112,8 +124,6 @@ public class HybridPlotWorld extends ClassicPlotWorld {
return data;
}
// FIXME depends on block ids
// Possibly make abstract?
public static BaseBlock rotate(BaseBlock id) {
CompoundTag tag = id.getNbtData();
@ -251,6 +261,14 @@ public class HybridPlotWorld extends ClassicPlotWorld {
Schematic schematic1 = this.schematicHandler.getSchematic(schematic1File);
Schematic schematic2 = this.schematicHandler.getSchematic(schematic2File);
Schematic schematic3 = this.schematicHandler.getSchematic(schematic3File);
// If the plot schematic contains entities, then they need to be populated upon generation.
if (schematic3 != null && !schematic3.getClipboard().getEntities().isEmpty()) {
this.schem3Entities = new ArrayList<>(schematic3.getClipboard().getEntities());
this.schem3MinPoint = schematic3.getClipboard().getMinimumPoint();
this.schem3PopulationNeeded = true;
}
int shift = this.ROAD_WIDTH / 2;
int oddshift = (this.ROAD_WIDTH & 1);
@ -314,19 +332,28 @@ public class HybridPlotWorld extends ClassicPlotWorld {
for (short x = 0; x < w3; x++) {
for (short z = 0; z < l3; z++) {
for (short y = 0; y < h3; y++) {
BaseBlock id =
blockArrayClipboard3.getFullBlock(BlockVector3.at(
BaseBlock id = blockArrayClipboard3.getFullBlock(BlockVector3.at(
x + min.getBlockX(),
y + min.getBlockY(),
z + min.getBlockZ()
));
if (!id.getBlockType().getMaterial().isAir()) {
addOverlayBlock((short) (x + shift + oddshift + centerShiftX), (short) (y + plotY),
(short) (z + shift + oddshift + centerShiftZ), id, false, h3
schem3PopulationNeeded |= id.hasNbtData();
addOverlayBlock(
(short) (x + shift + oddshift + centerShiftX),
(short) (y + plotY),
(short) (z + shift + oddshift + centerShiftZ),
id,
false,
h3
);
}
}
BiomeType biome = blockArrayClipboard3.getBiome(BlockVector2.at(x + min.getBlockX(), z + min.getBlockZ()));
if (blockArrayClipboard3.hasBiomes()) {
BiomeType biome = blockArrayClipboard3.getBiome(BlockVector2.at(
x + min.getBlockX(),
z + min.getBlockZ()
));
addOverlayBiome(
(short) (x + shift + oddshift + centerShiftX),
(short) (z + shift + oddshift + centerShiftZ),
@ -334,6 +361,7 @@ public class HybridPlotWorld extends ClassicPlotWorld {
);
}
}
}
if (Settings.DEBUG) {
LOGGER.info("- plot schematic: {}", schematic3File.getPath());
@ -370,6 +398,7 @@ public class HybridPlotWorld extends ClassicPlotWorld {
z + min.getBlockZ()
));
if (!id.getBlockType().getMaterial().isAir()) {
schem1PopulationNeeded |= id.hasNbtData();
addOverlayBlock((short) (x - shift), (short) (y + roadY), (short) (z + shift + oddshift), id, false, h1);
addOverlayBlock(
(short) (z + shift + oddshift),
@ -381,11 +410,13 @@ public class HybridPlotWorld extends ClassicPlotWorld {
);
}
}
if (blockArrayClipboard1.hasBiomes()) {
BiomeType biome = blockArrayClipboard1.getBiome(BlockVector2.at(x + min.getBlockX(), z + min.getBlockZ()));
addOverlayBiome((short) (x - shift), (short) (z + shift + oddshift), biome);
addOverlayBiome((short) (z + shift + oddshift), (short) (shift - x + (oddshift - 1)), biome);
}
}
}
Clipboard blockArrayClipboard2 = schematic2.getClipboard();
BlockVector3 d2 = blockArrayClipboard2.getDimensions();
@ -406,14 +437,17 @@ public class HybridPlotWorld extends ClassicPlotWorld {
z + min.getBlockZ()
));
if (!id.getBlockType().getMaterial().isAir()) {
schem2PopulationNeeded |= id.hasNbtData();
addOverlayBlock((short) (x - shift), (short) (y + roadY), (short) (z - shift), id, false, h2);
}
}
if (blockArrayClipboard2.hasBiomes()) {
BiomeType biome = blockArrayClipboard2.getBiome(BlockVector2.at(x + min.getBlockX(), z + min.getBlockZ()));
addOverlayBiome((short) (x - shift), (short) (z - shift), biome);
}
}
}
}
public void addOverlayBlock(short x, short y, short z, BaseBlock id, boolean rotate, int height) {
if (z < 0) {
@ -456,8 +490,46 @@ public class HybridPlotWorld extends ClassicPlotWorld {
this.G_SCH_B.put(pair, id);
}
/**
* Get the entities contained within the plot schematic for generation. Intended for internal use only.
*/
@AnnotationHelper.ApiDescription(info = "Internal use only. Subject to changes at any time.")
public @Nullable List<Entity> getPlotSchematicEntities() {
return schem3Entities;
}
/**
* Get the minimum point of the plot schematic for generation. Intended for internal use only.
*/
@AnnotationHelper.ApiDescription(info = "Internal use only. Subject to changes at any time.")
public @Nullable BlockVector3 getPlotSchematicMinPoint() {
return schem3MinPoint;
}
/**
* Get if post-generation population of chunks with tiles/entities is needed for this world. Not for public API use.
*/
@AnnotationHelper.ApiDescription(info = "Internal use only. Subject to changes at any time.")
public boolean populationNeeded() {
return schem1PopulationNeeded || schem2PopulationNeeded || schem3PopulationNeeded;
}
/**
* @deprecated in favour of {@link HybridPlotWorld#getSchematicRoot()}
*/
@Deprecated(forRemoval = true, since = "TODO")
public File getRoot() {
return this.root;
}
/**
* Get the root folder for this world's generation schematics. May be null if schematics not initialised via
* {@link HybridPlotWorld#setupSchematics()}
*
* @since TODO
*/
public @Nullable File getSchematicRoot() {
return this.root;
}
}

View File

@ -47,9 +47,7 @@ public abstract class IndependentPlotGenerator {
public abstract String getName();
/**
* Use the setBlock or setBiome method of the PlotChunk result parameter to make changes.
* The PlotArea settings is the same one this was initialized with.
* The PseudoRandom random is a fast random object.
* Generate chunk block data
*
* @param result queue
* @param settings PlotArea (settings)
@ -59,10 +57,13 @@ public abstract class IndependentPlotGenerator {
public abstract void generateChunk(ScopedQueueCoordinator result, PlotArea settings);
/**
* @deprecated {@link ScopedQueueCoordinator} will be renamed in v7.
* Populates the queue representing a chunk area with tile entities and entities
*
* @param result Queue to write to
* @param settings PlotArea (settings)
* @return True if any population occured
*/
@Deprecated(forRemoval = true, since = "TODO")
public boolean populateChunk(ScopedQueueCoordinator result, PlotArea setting) {
public boolean populateChunk(ScopedQueueCoordinator result, PlotArea settings) {
return false;
}

View File

@ -41,14 +41,14 @@ import org.khelekore.prtree.SimpleMBR;
* An unmodifiable 6-tuple (world,x,y,z,yaw,pitch)
*/
@SuppressWarnings("unused")
public final class Location extends BlockLoc implements Comparable<Location> {
public class Location extends BlockLoc implements Comparable<Location> {
private final float yaw;
private final float pitch;
private final BlockVector3 blockVector3;
private final World<?> world;
private Location(
protected Location(
final @NonNull World<?> world, final @NonNull BlockVector3 blockVector3,
final float yaw, final float pitch
) {

View File

@ -0,0 +1,68 @@
/*
* _____ _ _ _____ _
* | __ \| | | | / ____| | |
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
* | |
* |_|
* PlotSquared plot management system for Minecraft
* Copyright (C) 2014 - 2022 IntellectualSites
*
* 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
* (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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.location;
import com.plotsquared.core.util.AnnotationHelper;
import com.sk89q.worldedit.math.BlockVector3;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Used internally for generation to reference locations in worlds that "don't exist yet". There is no guarantee that the world
* name provided by {@link UncheckedWorldLocation#getWorldName()} exists on the server.
*/
@AnnotationHelper.ApiDescription(info = "Internal use only. Subject to changes at any time.")
public class UncheckedWorldLocation extends Location {
private final String worldName;
private UncheckedWorldLocation(
final @NonNull String worldName, final int x, final int y, final int z
) {
super(World.nullWorld(), BlockVector3.at(x, y, z), 0f, 0f);
this.worldName = worldName;
}
/**
* Construct a new location with yaw and pitch equal to 0
*
* @param world World
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @return New location
*/
public static @NonNull UncheckedWorldLocation at(
final @NonNull String world, final int x, final int y, final int z
) {
return new UncheckedWorldLocation(world, x, y, z);
}
@Override
public @NonNull String getWorldName() {
return this.worldName;
}
}