From 98a07dad1b3f23f00315ea5200202abfc35ab85f Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 15 May 2022 10:58:04 +0100 Subject: [PATCH] Fix plot analysis (#3618) * Fix plot analysis - Stop using deprecated ChunkQueueCoordinator and create a new purpose-built coordinator - Generation is chunk-by-chunk thus the old blocks cache needs to be filled accordingly - Remove the **four** System#gc calls - Fixes #3464 - Fix really weird dumb... maths? in ExpiryTask - Fixes #3600 * Add since annotation * Address comments and maxY should be inclusive * Annotate new queue as internal use only --- .../core/generator/HybridUtils.java | 62 +++---- .../core/plot/expiration/ExpiryTask.java | 4 +- ...BlockArrayCacheScopedQueueCoordinator.java | 175 ++++++++++++++++++ 3 files changed, 207 insertions(+), 34 deletions(-) create mode 100644 Core/src/main/java/com/plotsquared/core/queue/BlockArrayCacheScopedQueueCoordinator.java diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java b/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java index 0eab11c71..a1b688151 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java @@ -41,7 +41,7 @@ import com.plotsquared.core.plot.flag.GlobalFlagContainer; import com.plotsquared.core.plot.flag.PlotFlag; import com.plotsquared.core.plot.flag.implementations.AnalysisFlag; import com.plotsquared.core.plot.world.PlotAreaManager; -import com.plotsquared.core.queue.ChunkQueueCoordinator; +import com.plotsquared.core.queue.BlockArrayCacheScopedQueueCoordinator; import com.plotsquared.core.queue.GlobalBlockQueue; import com.plotsquared.core.queue.QueueCoordinator; import com.plotsquared.core.util.ChunkManager; @@ -83,6 +83,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class HybridUtils { private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + HybridUtils.class.getSimpleName()); + private static final BlockState AIR = BlockTypes.AIR.getDefaultState(); public static HybridUtils manager; public static Set regions; @@ -136,6 +137,11 @@ public class HybridUtils { * */ TaskManager.runTaskAsync(() -> { + final PlotArea area = this.plotAreaManager.getPlotArea(world, null); + if (!(area instanceof HybridPlotWorld hpw)) { + return; + } + final BlockVector3 bot = region.getMinimumPoint(); final BlockVector3 top = region.getMaximumPoint(); @@ -152,37 +158,28 @@ public class HybridUtils { final int height = area.getMaxGenHeight() - area.getMinGenHeight() + 1; final int minHeight = area.getMinGenHeight(); - final PlotArea area = this.plotAreaManager.getPlotArea(world, null); - - if (!(area instanceof HybridPlotWorld hpw)) { - return; - } - - ChunkQueueCoordinator chunk = new ChunkQueueCoordinator(worldUtil.getWeWorld(world), bot, top, false); - hpw.getGenerator().generateChunk(chunk, hpw); - - final BlockState airBlock = BlockTypes.AIR.getDefaultState(); - final BlockState[][][] oldBlocks = chunk.getBlocks(); final BlockState[][][] newBlocks = new BlockState[height][width][length]; - for (final BlockState[][] newBlock : newBlocks) { - for (final BlockState[] blockStates : newBlock) { - Arrays.fill(blockStates, airBlock); - } - } - for (final BlockState[][] oldBlock : oldBlocks) { - for (final BlockState[] blockStates : oldBlock) { - Arrays.fill(blockStates, airBlock); - } - } - System.gc(); - System.gc(); + BlockArrayCacheScopedQueueCoordinator oldBlockQueue = new BlockArrayCacheScopedQueueCoordinator( + Location.at("", region.getMinimumPoint().withY(hpw.getMinGenHeight())), + Location.at("", region.getMaximumPoint().withY(hpw.getMaxGenHeight())) + ); + + region.getChunks().forEach(chunkPos -> { + int relChunkX = chunkPos.getX() - cbx; + int relChunkZ = chunkPos.getZ() - cbz; + oldBlockQueue.setOffsetX(relChunkX << 4); + oldBlockQueue.setOffsetZ(relChunkZ << 4); + hpw.getGenerator().generateChunk(oldBlockQueue, hpw); + }); + + final BlockState[][][] oldBlocks = oldBlockQueue.getBlockStates(); QueueCoordinator queue = area.getQueue(); queue.addReadChunks(region.getChunks()); - queue.setChunkConsumer(blockVector2 -> { - int X = blockVector2.getX(); - int Z = blockVector2.getZ(); + queue.setChunkConsumer(chunkPos -> { + int X = chunkPos.getX(); + int Z = chunkPos.getZ(); int minX; if (X == cbx) { minX = bx & 15; @@ -220,6 +217,9 @@ public class HybridUtils { for (int yIndex = 0; yIndex < height; yIndex++) { int y = yIndex + minHeight; BlockState block = queue.getBlock(xx, y, zz); + if (block == null) { + block = AIR; + } int xr = xb + x; int zr = zb + z; newBlocks[yIndex][xr][zr] = block; @@ -240,10 +240,10 @@ public class HybridUtils { for (int z = 0; z < length; z++) { Set types = new HashSet<>(); for (int yIndex = 0; yIndex < height; yIndex++) { - BlockState old = oldBlocks[yIndex][x][z]; + BlockState old = oldBlocks[yIndex][x][z]; // Nullable try { - BlockState now = newBlocks[yIndex][x][z]; - if (!old.equals(now)) { + BlockState now = newBlocks[yIndex][x][z]; // Not null + if (!now.equals(old) && !(old == null && now.getBlockType().equals(BlockTypes.AIR))) { changes[i]++; } if (now.getBlockType().getMaterial().isAir()) { @@ -301,8 +301,6 @@ public class HybridUtils { analysis.data_sd = (int) (MathMan.getSD(data, analysis.data) * 100); analysis.air_sd = (int) (MathMan.getSD(air, analysis.air) * 100); analysis.variety_sd = (int) (MathMan.getSD(variety, analysis.variety) * 100); - System.gc(); - System.gc(); whenDone.value = analysis; whenDone.run(); }); diff --git a/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpiryTask.java b/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpiryTask.java index 03a9c010b..c0e2b8a1a 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpiryTask.java +++ b/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpiryTask.java @@ -75,9 +75,9 @@ public class ExpiryTask { plots = plots != null ? plots : getPlotsToCheck(); int diff = settings.REQUIRED_PLOTS; boolean min = true; - if (settings.REQUIRED_PLOTS - plots.size() < settings.REQUIRED_PLOTS) { + if (plots.size() > settings.REQUIRED_PLOTS) { min = false; - diff = settings.REQUIRED_PLOTS - plots.size(); + diff = plots.size() - settings.REQUIRED_PLOTS; } List entireList = plots.stream().map(plot -> ExpireManager.IMP.getAge(plot, settings.DELETE_IF_OWNER_IS_UNKNOWN)) diff --git a/Core/src/main/java/com/plotsquared/core/queue/BlockArrayCacheScopedQueueCoordinator.java b/Core/src/main/java/com/plotsquared/core/queue/BlockArrayCacheScopedQueueCoordinator.java new file mode 100644 index 000000000..043e6e0ff --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/queue/BlockArrayCacheScopedQueueCoordinator.java @@ -0,0 +1,175 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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 . + */ +package com.plotsquared.core.queue; + +import com.plotsquared.core.location.Location; +import com.plotsquared.core.util.AnnotationHelper; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * {@link QueueCoordinator} that caches all blocks set to it in a given array of form BlockState[][][]. An offset can be + * applied to blocks set to it, and the scope limited. This should have blocks set to it one chunk at a time, based on the + * result of {@link BlockArrayCacheScopedQueueCoordinator#getMin()} and {@link BlockArrayCacheScopedQueueCoordinator#getMax()}. + * The min and max points of this queue are offset according to the minimum point given in the constructor, and the offsets set + * in {@link BlockArrayCacheScopedQueueCoordinator#setOffsetX(int)} and + * {@link BlockArrayCacheScopedQueueCoordinator#setOffsetZ(int)} + */ +@AnnotationHelper.ApiDescription(info = "Internal use only. Subject to change at any time and created for specific use cases.") +public class BlockArrayCacheScopedQueueCoordinator extends ScopedQueueCoordinator { + + private final BlockState[][][] blockStates; + private final int height; + private final int width; + private final int length; + private final int minY; + private final int maxY; + private final int scopeMinX; + private final int scopeMinZ; + private final int scopeMaxX; + private final int scopeMaxZ; + private int offsetX = 0; + private int offsetZ = 0; + /** + * Construct a new instance + * + * @param min Inclusive location of the minimum point to limit the scope to. + * @param max Inclusive location of the maximum point to limit the scope to. + * @since TODO + */ + public BlockArrayCacheScopedQueueCoordinator(Location min, Location max) { + super(null, min, max); + this.width = max.getX() - min.getX() + 1; + this.length = max.getZ() - min.getZ() + 1; + this.minY = min.getY(); + this.maxY = max.getY(); + this.height = maxY - minY + 1; + + this.scopeMinX = min.getX() & 15; + this.scopeMinZ = min.getZ() & 15; + this.scopeMaxX = scopeMinX + width; + this.scopeMaxZ = scopeMinZ + length; + this.blockStates = new BlockState[height][width][length]; + } + + public BlockState[][][] getBlockStates() { + return blockStates; + } + + @Override + public boolean setBlock(int x, final int y, int z, final @NonNull BlockState id) { + x += offsetX; + z += offsetZ; + if (x >= scopeMinX && x < scopeMaxX && y >= minY && y <= maxY && z >= scopeMinZ && z < scopeMaxZ) { + blockStates[y - minY][x - scopeMinX][z - scopeMinZ] = id; + } + return false; + } + + @Override + public boolean setBlock(final int x, final int y, final int z, @NonNull final Pattern pattern) { + int rx = x + offsetX; + int rz = z + offsetZ; + if (rx >= scopeMinX && rx < scopeMaxX && y >= minY && y <= maxY && rz >= scopeMinZ && rz < scopeMaxZ) { + BlockState state = pattern + .applyBlock(super.getMin().getBlockVector3().add(BlockVector3.at(x, y, z))) + .toImmutableState(); + blockStates[y - minY][rx - scopeMinX][rz - scopeMinZ] = state; + } + return false; + } + + @Override + public @NonNull Location getMin() { + return super.getMin().add(offsetX - scopeMinX, 0, offsetZ - scopeMinZ); + } + + @Override + public @NonNull Location getMax() { + return getMin().add(15, 0, 15).withY(maxY); + } + + @Override + public boolean setBlock(int x, int y, int z, final @NonNull BaseBlock id) { + x += offsetX; + z += offsetZ; + if (x >= scopeMinX && x < scopeMaxX && y >= minY && y <= maxY && z >= scopeMinZ && z < scopeMaxZ) { + blockStates[y - minY][x][z] = id.toImmutableState(); + } + return false; + } + + @Override + public @Nullable BlockState getBlock(final int x, final int y, final int z) { + if (x >= 0 && x < width && y >= minY && y <= maxY && z >= 0 && z < length) { + return blockStates[y - minY][x][z]; + } + return null; + } + + public void setOffsetX(final int offsetX) { + this.offsetX = offsetX; + } + + public void setOffsetZ(final int offsetZ) { + this.offsetZ = offsetZ; + } + + @Override + public int size() { + return height * width * length; + } + + @Override + public boolean setBiome(final int x, final int z, @NonNull final BiomeType biome) { + //do nothing + return false; + } + + @Override + public boolean setBiome(final int x, final int y, final int z, @NonNull final BiomeType biome) { + //do nothing + return false; + } + + @Override + public void fillBiome(final BiomeType biome) { + //do nothing + } + + @Override + public boolean setTile(final int x, final int y, final int z, @NonNull final CompoundTag tag) { + //do nothing + return false; + } + +}