PlotSquared/Core/src/main/java/com/plotsquared/core/util/RegionManager.java

422 lines
17 KiB
Java
Raw Normal View History

/*
* _____ _ _ _____ _
* | __ \| | | | / ____| | |
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
* | |
* |_|
* PlotSquared plot management system for Minecraft
2022-01-02 22:22:19 +01:00
* 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
2020-08-15 14:59:29 +02:00
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.util;
import com.google.inject.Inject;
import com.plotsquared.core.PlotSquared;
2020-09-11 13:59:40 +02:00
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
2020-09-11 13:59:40 +02:00
import com.plotsquared.core.inject.factory.ProgressSubscriberFactory;
import com.plotsquared.core.location.Location;
2020-09-11 13:59:40 +02:00
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotManager;
import com.plotsquared.core.queue.BasicQueueCoordinator;
import com.plotsquared.core.queue.GlobalBlockQueue;
import com.plotsquared.core.queue.QueueCoordinator;
import com.plotsquared.core.util.task.TaskManager;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
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 java.io.File;
import java.util.Collection;
import java.util.Set;
public abstract class RegionManager {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + RegionManager.class.getSimpleName());
public static RegionManager manager = null;
2021-09-15 14:19:25 +02:00
protected final WorldUtil worldUtil;
private final GlobalBlockQueue blockQueue;
2020-10-09 17:34:59 +02:00
private final ProgressSubscriberFactory subscriberFactory;
2020-10-09 18:47:51 +02:00
@Inject
public RegionManager(
@NonNull WorldUtil worldUtil,
@NonNull GlobalBlockQueue blockQueue,
@NonNull ProgressSubscriberFactory subscriberFactory
) {
this.worldUtil = worldUtil;
this.blockQueue = blockQueue;
2020-10-09 17:34:59 +02:00
this.subscriberFactory = subscriberFactory;
}
public static BlockVector2 getRegion(Location location) {
int x = location.getX() >> 9;
int z = location.getZ() >> 9;
return BlockVector2.at(x, z);
}
/**
* 0 = Entity
* 1 = Animal
* 2 = Monster
* 3 = Mob
* 4 = Boat
* 5 = Misc
*
* @param plot plot
* @return array of counts of entity types
*/
public abstract int[] countEntities(Plot plot);
2020-07-24 17:24:53 +02:00
public void deleteRegionFiles(final String world, final Collection<BlockVector2> chunks, final Runnable whenDone) {
TaskManager.runTaskAsync(() -> {
for (BlockVector2 loc : chunks) {
2020-07-24 17:24:53 +02:00
String directory = world + File.separator + "region" + File.separator + "r." + loc.getX() + "." + loc.getZ() + ".mca";
File file = new File(PlotSquared.platform().worldContainer(), directory);
LOGGER.info("- Deleting file: {} (max 1024 chunks)", file.getName());
if (file.exists()) {
file.delete();
}
}
TaskManager.runTask(whenDone);
});
}
/**
* Set a number of cuboids to a certain block between two y values.
*
* @param area plot area
* @param regions cuboid regions
* @param blocks pattern
* @param minY y to set from
* @param maxY y to set to
2020-09-11 13:59:40 +02:00
* @param actor the actor associated with the cuboid set
* @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
* otherwise writes to the queue but does not enqueue.
* @return {@code true} if not enqueued, otherwise whether the created queue enqueued.
*/
public boolean setCuboids(
final @NonNull PlotArea area,
final @NonNull Set<CuboidRegion> regions,
final @NonNull Pattern blocks,
int minY,
int maxY,
@Nullable PlotPlayer<?> actor,
@Nullable QueueCoordinator queue
) {
boolean enqueue = false;
if (queue == null) {
queue = area.getQueue();
enqueue = true;
2020-09-11 13:59:40 +02:00
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
2020-10-09 17:34:59 +02:00
queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
2020-09-11 13:59:40 +02:00
}
}
for (CuboidRegion region : regions) {
Location pos1 = Location.at(
area.getWorldName(),
region.getMinimumPoint().getX(),
minY,
region.getMinimumPoint().getZ()
);
Location pos2 = Location.at(
area.getWorldName(),
region.getMaximumPoint().getX(),
maxY,
region.getMaximumPoint().getZ()
);
queue.setCuboid(pos1, pos2, blocks);
}
return !enqueue || queue.enqueue();
}
/**
* Notify any plugins that may want to modify clear behaviour that a clear is occuring
*
* @param manager plot manager
* @return {@code true} if the notified will accept the clear task
*/
public boolean notifyClear(PlotManager manager) {
return false;
}
/**
* Only called when {@link RegionManager#notifyClear(PlotManager)} returns true in specific PlotManagers
*
* @param plot plot
* @param whenDone task to run when complete
* @param manager plot manager
2020-09-11 13:59:40 +02:00
* @param actor the player running the clear
* @return {@code true} if the clear worked. {@code false} if someone went wrong so P2 can then handle the clear
*/
public abstract boolean handleClear(
@NonNull Plot plot,
final @Nullable Runnable whenDone,
@NonNull PlotManager manager,
@Nullable PlotPlayer<?> actor
);
/**
* Copy a region to a new location (in the same world)
*
* @param pos1 position 1
* @param pos2 position 2
* @param newPos position to move pos1 to
2020-09-11 13:59:40 +02:00
* @param actor the actor associated with the region copy
* @param whenDone task to run when complete
* @return success or not
*/
public boolean copyRegion(
final @NonNull Location pos1,
final @NonNull Location pos2,
final @NonNull Location newPos,
final @Nullable PlotPlayer<?> actor,
final @NonNull Runnable whenDone
) {
final int relX = newPos.getX() - pos1.getX();
final int relZ = newPos.getZ() - pos1.getZ();
final com.sk89q.worldedit.world.World oldWorld = worldUtil.getWeWorld(pos1.getWorldName());
2020-07-24 17:24:53 +02:00
final com.sk89q.worldedit.world.World newWorld = worldUtil.getWeWorld(newPos.getWorldName());
final QueueCoordinator copyFrom = blockQueue.getNewQueue(oldWorld);
2020-07-24 17:24:53 +02:00
final BasicQueueCoordinator copyTo = (BasicQueueCoordinator) blockQueue.getNewQueue(newWorld);
setCopyFromToConsumer(pos1, pos2, relX, relZ, oldWorld, copyFrom, copyTo, false);
copyFrom.setCompleteTask(copyTo::enqueue);
2020-09-11 13:59:40 +02:00
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
2020-10-09 18:47:51 +02:00
copyFrom.addProgressSubscriber(subscriberFactory
.createFull(
actor,
Settings.QUEUE.NOTIFY_INTERVAL,
Settings.QUEUE.NOTIFY_WAIT,
TranslatableCaption.of("swap.progress_region_copy")
));
2020-09-11 13:59:40 +02:00
}
2020-07-24 17:24:53 +02:00
copyFrom
.addReadChunks(new CuboidRegion(
BlockVector3.at(pos1.getX(), 0, pos1.getZ()),
BlockVector3.at(pos2.getX(), 0, pos2.getZ())
).getChunks());
copyTo.setCompleteTask(whenDone);
2020-09-11 13:59:40 +02:00
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
2020-10-09 18:47:51 +02:00
copyTo.addProgressSubscriber(subscriberFactory
.createFull(
actor,
Settings.QUEUE.NOTIFY_INTERVAL,
Settings.QUEUE.NOTIFY_WAIT,
TranslatableCaption.of("swap.progress_region_paste")
));
2020-09-11 13:59:40 +02:00
}
return copyFrom.enqueue();
}
/**
* Assumptions:<br>
* - pos1 and pos2 are in the same plot<br>
* It can be harmful to the world if parameters outside this scope are provided
*
* @param pos1 position 1
* @param pos2 position 2
* @param ignoreAugment if to bypass synchronisation ish thing
* @param whenDone task to run when regeneration completed
* @return success or not
*/
2020-07-24 17:24:53 +02:00
public abstract boolean regenerateRegion(Location pos1, Location pos2, boolean ignoreAugment, Runnable whenDone);
public abstract void clearAllEntities(Location pos1, Location pos2);
2020-09-11 13:59:40 +02:00
/**
2021-05-11 19:26:39 +02:00
* Swap two regions within the same world
2020-09-11 13:59:40 +02:00
*
* @param pos1 position 1
* @param pos2 position 2
* @param swapPos position to swap with
* @param actor the actor associated with the region copy
* @param whenDone task to run when complete
*/
public void swap(
Location pos1,
Location pos2,
Location swapPos,
final @Nullable PlotPlayer<?> actor,
final Runnable whenDone
) {
int relX = swapPos.getX() - pos1.getX();
int relZ = swapPos.getZ() - pos1.getZ();
World world1 = worldUtil.getWeWorld(pos1.getWorldName());
World world2 = worldUtil.getWeWorld(swapPos.getWorldName());
QueueCoordinator fromQueue1 = blockQueue.getNewQueue(world1);
QueueCoordinator fromQueue2 = blockQueue.getNewQueue(world2);
fromQueue1.setUnloadAfter(false);
fromQueue2.setUnloadAfter(false);
2020-07-24 17:24:53 +02:00
fromQueue1.addReadChunks(new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()).getChunks());
fromQueue2.addReadChunks(new CuboidRegion(
swapPos.getBlockVector3(),
BlockVector3.at(swapPos.getX() + pos2.getX() - pos1.getX(), 0, swapPos.getZ() + pos2.getZ() - pos1.getZ())
).getChunks());
QueueCoordinator toQueue1 = blockQueue.getNewQueue(world1);
QueueCoordinator toQueue2 = blockQueue.getNewQueue(world2);
setCopyFromToConsumer(pos1, pos2, relX, relZ, world1, fromQueue1, toQueue2, true);
setCopyFromToConsumer(pos1.add(relX, 0, relZ), pos2.add(relX, 0, relZ), -relX, -relZ, world1, fromQueue2, toQueue1,
true
);
2022-01-20 21:01:38 +01:00
toQueue2.setCompleteTask(whenDone);
2020-09-11 13:59:40 +02:00
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
toQueue2.addProgressSubscriber(subscriberFactory.createFull(
actor,
Settings.QUEUE.NOTIFY_INTERVAL,
Settings.QUEUE.NOTIFY_WAIT,
TranslatableCaption.of("swap.progress_region2_paste")
));
}
toQueue1.setCompleteTask(toQueue2::enqueue);
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
toQueue1.addProgressSubscriber(subscriberFactory.createFull(
actor,
Settings.QUEUE.NOTIFY_INTERVAL,
Settings.QUEUE.NOTIFY_WAIT,
TranslatableCaption.of("swap.progress_region1_paste")
));
2020-09-11 13:59:40 +02:00
}
fromQueue2.setCompleteTask(toQueue1::enqueue);
2020-09-11 13:59:40 +02:00
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
2020-10-09 18:47:51 +02:00
fromQueue2.addProgressSubscriber(subscriberFactory
.createFull(
actor,
Settings.QUEUE.NOTIFY_INTERVAL,
Settings.QUEUE.NOTIFY_WAIT,
TranslatableCaption.of("swap.progress_region2_copy")
));
2020-09-11 13:59:40 +02:00
}
fromQueue1.setCompleteTask(fromQueue2::enqueue);
2020-09-11 13:59:40 +02:00
if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
fromQueue1.addProgressSubscriber(subscriberFactory
.createFull(
actor,
Settings.QUEUE.NOTIFY_INTERVAL,
Settings.QUEUE.NOTIFY_WAIT,
TranslatableCaption.of("swap.progress_region1_copy")
));
2020-09-11 13:59:40 +02:00
}
fromQueue1.enqueue();
}
private void setCopyFromToConsumer(
final Location pos1,
final Location pos2,
int relX,
int relZ,
final World world1,
final QueueCoordinator fromQueue,
final QueueCoordinator toQueue,
boolean removeEntities
) {
fromQueue.setChunkConsumer(chunk -> {
int cx = chunk.getX();
int cz = chunk.getZ();
int cbx = cx << 4;
int cbz = cz << 4;
int bx = Math.max(pos1.getX(), cbx) & 15;
int bz = Math.max(pos1.getZ(), cbz) & 15;
int tx = Math.min(pos2.getX(), cbx + 15) & 15;
int tz = Math.min(pos2.getZ(), cbz + 15) & 15;
for (int y = 0; y < 256; y++) {
for (int x = bx; x <= tx; x++) {
for (int z = bz; z <= tz; z++) {
int rx = cbx + x;
int rz = cbz + z;
BlockVector3 loc = BlockVector3.at(rx, y, rz);
toQueue.setBlock(rx + relX, y, rz + relZ, world1.getFullBlock(loc));
toQueue.setBiome(rx + relX, y, rz + relZ, world1.getBiome(loc));
}
}
}
2020-07-24 17:24:53 +02:00
Region region = new CuboidRegion(BlockVector3.at(cbx + bx, 0, cbz + bz), BlockVector3.at(cbx + tx, 255, cbz + tz));
toQueue.addEntities(world1.getEntities(region));
if (removeEntities) {
for (Entity entity : world1.getEntities(region)) {
entity.remove();
}
}
});
}
public void setBiome(
final CuboidRegion region,
final int extendBiome,
final BiomeType biome,
final String world,
final Runnable whenDone
) {
2020-07-24 17:24:53 +02:00
Location pos1 = Location
.at(
world,
region.getMinimumPoint().getX() - extendBiome,
region.getMinimumPoint().getY(),
region.getMinimumPoint().getZ() - extendBiome
);
2020-07-24 17:24:53 +02:00
Location pos2 = Location
.at(
world,
region.getMaximumPoint().getX() + extendBiome,
region.getMaximumPoint().getY(),
region.getMaximumPoint().getZ() + extendBiome
);
final QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(world));
final int minX = pos1.getX();
final int minZ = pos1.getZ();
final int maxX = pos2.getX();
final int maxZ = pos2.getZ();
queue.addReadChunks(region.getChunks());
queue.setChunkConsumer(blockVector2 -> {
final int cx = blockVector2.getX() << 4;
final int cz = blockVector2.getZ() << 4;
WorldUtil.setBiome(
world,
Math.max(minX, cx),
Math.max(minZ, cz),
Math.min(maxX, cx + 15),
Math.min(maxZ, cz + 15),
biome
);
worldUtil.refreshChunk(blockVector2.getBlockX(), blockVector2.getBlockZ(), world);
});
queue.setCompleteTask(whenDone);
queue.enqueue();
}
}