Merge branch 'v5' into v6

# Conflicts:
#	Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java
This commit is contained in:
Alexander Söderberg 2020-07-14 19:18:08 +02:00
commit 8eb903ad72
No known key found for this signature in database
GPG Key ID: C0207FF7EA146678
6 changed files with 365 additions and 19 deletions

View File

@ -28,7 +28,7 @@ dependencies {
exclude(module: "bukkit") exclude(module: "bukkit")
} }
compile("io.papermc:paperlib:1.0.2") compile("io.papermc:paperlib:1.0.4")
implementation("net.kyori:text-adapter-bukkit:3.0.3") implementation("net.kyori:text-adapter-bukkit:3.0.3")
compile("com.github.MilkBowl:VaultAPI:1.7") { compile("com.github.MilkBowl:VaultAPI:1.7") {
exclude(module: "bukkit") exclude(module: "bukkit")
@ -96,7 +96,7 @@ task copyFiles {
shadowJar { shadowJar {
dependencies { dependencies {
include(dependency(":PlotSquared-Core")) include(dependency(":PlotSquared-Core"))
include(dependency("io.papermc:paperlib:1.0.2")) include(dependency("io.papermc:paperlib:1.0.4"))
include(dependency("net.kyori:text-adapter-bukkit:3.0.3")) include(dependency("net.kyori:text-adapter-bukkit:3.0.3"))
include(dependency("org.bstats:bstats-bukkit:1.7")) include(dependency("org.bstats:bstats-bukkit:1.7"))
include(dependency("org.khelekore:prtree:1.7.0-SNAPSHOT")) include(dependency("org.khelekore:prtree:1.7.0-SNAPSHOT"))

View File

@ -389,9 +389,11 @@ import static com.plotsquared.core.util.ReflectionUtils.getRefClass;
logger.info("[P2] (UUID) Using the offline mode UUID service"); logger.info("[P2] (UUID) Using the offline mode UUID service");
} }
final OfflinePlayerUUIDService offlinePlayerUUIDService = new OfflinePlayerUUIDService(); if (Settings.UUID.SERVICE_BUKKIT) {
this.impromptuPipeline.registerService(offlinePlayerUUIDService); final OfflinePlayerUUIDService offlinePlayerUUIDService = new OfflinePlayerUUIDService();
this.backgroundPipeline.registerService(offlinePlayerUUIDService); this.impromptuPipeline.registerService(offlinePlayerUUIDService);
this.backgroundPipeline.registerService(offlinePlayerUUIDService);
}
final SQLiteUUIDService sqLiteUUIDService = new SQLiteUUIDService("user_cache.db"); final SQLiteUUIDService sqLiteUUIDService = new SQLiteUUIDService("user_cache.db");
@ -404,7 +406,8 @@ import static com.plotsquared.core.util.ReflectionUtils.getRefClass;
} }
final LuckPermsUUIDService luckPermsUUIDService; final LuckPermsUUIDService luckPermsUUIDService;
if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) { if (Settings.UUID.SERVICE_LUCKPERMS &&
Bukkit.getPluginManager().getPlugin("LuckPerms") != null) {
luckPermsUUIDService = new LuckPermsUUIDService(); luckPermsUUIDService = new LuckPermsUUIDService();
logger.info("[P2] (UUID) Using LuckPerms as a complementary UUID service"); logger.info("[P2] (UUID) Using LuckPerms as a complementary UUID service");
} else { } else {
@ -412,7 +415,8 @@ import static com.plotsquared.core.util.ReflectionUtils.getRefClass;
} }
final BungeePermsUUIDService bungeePermsUUIDService; final BungeePermsUUIDService bungeePermsUUIDService;
if (Bukkit.getPluginManager().getPlugin("BungeePerms") != null) { if (Settings.UUID.SERVICE_BUNGEE_PERMS &&
Bukkit.getPluginManager().getPlugin("BungeePerms") != null) {
bungeePermsUUIDService = new BungeePermsUUIDService(); bungeePermsUUIDService = new BungeePermsUUIDService();
logger.info("[P2] (UUID) Using BungeePerms as a complementary UUID service"); logger.info("[P2] (UUID) Using BungeePerms as a complementary UUID service");
} else { } else {
@ -420,16 +424,16 @@ import static com.plotsquared.core.util.ReflectionUtils.getRefClass;
} }
final EssentialsUUIDService essentialsUUIDService; final EssentialsUUIDService essentialsUUIDService;
if (Bukkit.getPluginManager().getPlugin("Essentials") != null) { if (Settings.UUID.SERVICE_ESSENTIALSX && Bukkit.getPluginManager().getPlugin("Essentials") != null) {
essentialsUUIDService = new EssentialsUUIDService(); essentialsUUIDService = new EssentialsUUIDService();
logger.info("[P2] (UUID) Using Essentials as a complementary UUID service"); logger.info("[P2] (UUID) Using EssentialsX as a complementary UUID service");
} else { } else {
essentialsUUIDService = null; essentialsUUIDService = null;
} }
if (!Settings.UUID.OFFLINE) { if (!Settings.UUID.OFFLINE) {
// If running Paper we'll also try to use their profiles // If running Paper we'll also try to use their profiles
if (PaperLib.isPaper()) { if (Bukkit.getOnlineMode() && PaperLib.isPaper() && Settings.UUID.SERVICE_PAPER) {
final PaperUUIDService paperUUIDService = new PaperUUIDService(); final PaperUUIDService paperUUIDService = new PaperUUIDService();
this.impromptuPipeline.registerService(paperUUIDService); this.impromptuPipeline.registerService(paperUUIDService);
this.backgroundPipeline.registerService(paperUUIDService); this.backgroundPipeline.registerService(paperUUIDService);

View File

@ -1535,8 +1535,9 @@ import java.util.regex.Pattern;
} }
for (Block block1 : event.getBlocks()) { for (Block block1 : event.getBlocks()) {
Location bloc = BukkitUtil.getLocation(block1.getLocation()); Location bloc = BukkitUtil.getLocation(block1.getLocation());
if (bloc.isPlotArea() || bloc.add(relative.getBlockX(), if (bloc.isPlotArea() || bloc
relative.getBlockY(), relative.getBlockZ()).isPlotArea()) { .add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ())
.isPlotArea()) {
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
@ -1567,8 +1568,8 @@ import java.util.regex.Pattern;
return; return;
} }
} }
if (!plot.equals(area.getOwnedPlot(location.add( if (!plot.equals(area.getOwnedPlot(
relative.getBlockX(), relative.getBlockY(), relative.getBlockZ())))) { location.add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ())))) {
// This branch is only necessary to prevent pistons from extending // This branch is only necessary to prevent pistons from extending
// if they are: on a plot edge, facing outside the plot, and not // if they are: on a plot edge, facing outside the plot, and not
// pushing any blocks // pushing any blocks
@ -1589,8 +1590,9 @@ import java.util.regex.Pattern;
} }
for (Block block1 : event.getBlocks()) { for (Block block1 : event.getBlocks()) {
Location bloc = BukkitUtil.getLocation(block1.getLocation()); Location bloc = BukkitUtil.getLocation(block1.getLocation());
if (bloc.isPlotArea() || bloc.add(relative.getBlockX(), if (bloc.isPlotArea() || bloc
relative.getBlockY(), relative.getBlockZ()).isPlotArea()) { .add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ())
.isPlotArea()) {
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
@ -1976,7 +1978,7 @@ import java.util.regex.Pattern;
} }
if (event.getAction() == Action.RIGHT_CLICK_AIR) { if (event.getAction() == Action.RIGHT_CLICK_AIR) {
Material item = event.getMaterial(); Material item = event.getMaterial();
if (item.toString().toLowerCase().endsWith("egg")) { if (item.toString().toLowerCase().endsWith("_egg")) {
event.setCancelled(true); event.setCancelled(true);
event.setUseItemInHand(Event.Result.DENY); event.setUseItemInHand(Event.Result.DENY);
} }
@ -1988,7 +1990,7 @@ import java.util.regex.Pattern;
if (type == Material.AIR) { if (type == Material.AIR) {
type = offType; type = offType;
} }
if (type.toString().toLowerCase().endsWith("egg")) { if (type.toString().toLowerCase().endsWith("_egg")) {
Block block = player.getTargetBlockExact(5, FluidCollisionMode.SOURCE_ONLY); Block block = player.getTargetBlockExact(5, FluidCollisionMode.SOURCE_ONLY);
if (block != null && block.getType() != Material.AIR) { if (block != null && block.getType() != Material.AIR) {
Location location = BukkitUtil.getLocation(block.getLocation()); Location location = BukkitUtil.getLocation(block.getLocation());

View File

@ -0,0 +1,326 @@
/*
* _____ _ _ _____ _
* | __ \| | | | / ____| | |
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
* | |
* |_|
* PlotSquared plot management system for Minecraft
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
*/
package com.plotsquared.bukkit.queue;
import com.google.common.base.Preconditions;
import com.plotsquared.bukkit.BukkitMain;
import com.sk89q.worldedit.math.BlockVector2;
import io.papermc.lib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
* Utility that allows for the loading and coordination of chunk actions
* <p>
* The coordinator takes in collection of chunk coordinates, loads them
* and allows the caller to specify a sink for the loaded chunks. The
* coordinator will prevent the chunks from being unloaded until the sink
* has fully consumed the chunk
* <p>
* Usage:
* <pre>{@code
* final ChunkCoordinator chunkCoordinator = ChunkCoordinator.builder()
* .inWorld(Objects.requireNonNull(Bukkit.getWorld("world"))).withChunk(BlockVector2.at(0, 0))
* .withConsumer(chunk -> System.out.printf("Got chunk %d;%d", chunk.getX(), chunk.getZ()))
* .withFinalAction(() -> System.out.println("All chunks have been loaded"))
* .withThrowableConsumer(throwable -> System.err.println("Something went wrong... =("))
* .withMaxIterationTime(25L)
* .build();
* chunkCoordinator.subscribeToProgress((coordinator, progress) ->
* System.out.printf("Progress: %.1f", progress * 100.0f));
* chunkCoordinator.start();
* }</pre>
*
* @author Alexander Söderberg
* @see #builder() To create a new coordinator instance
*/
public final class ChunkCoordinator extends BukkitRunnable {
private final List<ProgressSubscriber> progressSubscribers = new LinkedList<>();
private final Queue<BlockVector2> requestedChunks;
private final Queue<Chunk> availableChunks;
private final long maxIterationTime;
private final Plugin plugin;
private final Consumer<Chunk> chunkConsumer;
private final World world;
private final Runnable whenDone;
private final Consumer<Throwable> throwableConsumer;
private final int totalSize;
private AtomicInteger expectedSize;
private int batchSize;
private ChunkCoordinator(final long maxIterationTime, final int initialBatchSize,
@NotNull final Consumer<Chunk> chunkConsumer, @NotNull final World world,
@NotNull final Collection<BlockVector2> requestedChunks, @NotNull final Runnable whenDone,
@NotNull final Consumer<Throwable> throwableConsumer) {
this.requestedChunks = new LinkedBlockingQueue<>(requestedChunks);
this.availableChunks = new LinkedBlockingQueue<>();
this.totalSize = requestedChunks.size();
this.expectedSize = new AtomicInteger(this.totalSize);
this.world = world;
this.batchSize = initialBatchSize;
this.chunkConsumer = chunkConsumer;
this.maxIterationTime = maxIterationTime;
this.whenDone = whenDone;
this.throwableConsumer = throwableConsumer;
this.plugin = JavaPlugin.getPlugin(BukkitMain.class);
}
/**
* Create a new {@link ChunkCoordinator} instance
*
* @return Coordinator builder instance
*/
@NotNull public static ChunkCoordinatorBuilder builder() {
return new ChunkCoordinatorBuilder();
}
/**
* Start the coordinator instance
*/
public void start() {
// Request initial batch
this.requestBatch();
// Wait until next tick to give the chunks a chance to be loaded
this.runTaskTimer(this.plugin, 1L, 1L);
}
@Override public void run() {
Chunk chunk = this.availableChunks.poll();
if (chunk == null) {
return;
}
long iterationTime;
int processedChunks = 0;
do {
final long start = System.currentTimeMillis();
try {
this.chunkConsumer.accept(chunk);
} catch (final Throwable throwable) {
this.throwableConsumer.accept(throwable);
}
this.freeChunk(chunk);
processedChunks++;
final long end = System.currentTimeMillis();
// Update iteration time
iterationTime = end - start;
} while (2 * iterationTime /* last chunk + next chunk */ < this.maxIterationTime
&& (chunk = availableChunks.poll()) != null);
if (processedChunks < this.batchSize) {
// Adjust batch size based on the amount of processed chunks per tick
this.batchSize = processedChunks;
}
final int expected = this.expectedSize.addAndGet(-processedChunks);
final float progress = ((float) totalSize - (float) expected) / (float) totalSize;
for (final ProgressSubscriber subscriber : this.progressSubscribers) {
subscriber.notifyProgress(this, progress);
}
if (expected <= 0) {
try {
this.whenDone.run();
} catch (final Throwable throwable) {
this.throwableConsumer.accept(throwable);
}
this.cancel();
} else {
if (this.availableChunks.size() < processedChunks) {
this.requestBatch();
}
}
}
private void requestBatch() {
BlockVector2 chunk;
for (int i = 0; i < this.batchSize && (chunk = this.requestedChunks.poll()) != null; i++) {
// This required PaperLib to be bumped to version 1.0.4 to mark the request as urgent
PaperLib.getChunkAtAsync(this.world, chunk.getX(), chunk.getZ(), true, true)
.whenComplete((chunkObject, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
// We want one less because this couldn't be processed
this.expectedSize.decrementAndGet();
} else {
this.processChunk(chunkObject);
}
});
}
}
private void processChunk(@NotNull final Chunk chunk) {
if (!chunk.isLoaded()) {
throw new IllegalArgumentException(
String.format("Chunk %d;%d is is not loaded", chunk.getX(), chunk.getZ()));
}
chunk.addPluginChunkTicket(this.plugin);
this.availableChunks.add(chunk);
}
private void freeChunk(@NotNull final Chunk chunk) {
if (!chunk.isLoaded()) {
throw new IllegalArgumentException(
String.format("Chunk %d;%d is is not loaded", chunk.getX(), chunk.getZ()));
}
chunk.removePluginChunkTicket(this.plugin);
}
/**
* Get the amount of remaining chunks (at the time of the method call)
*
* @return Snapshot view of remaining chunk count
*/
public int getRemainingChunks() {
return this.expectedSize.get();
}
/**
* Get the amount of requested chunks
*
* @return Requested chunk count
*/
public int getTotalChunks() {
return this.totalSize;
}
/**
* Subscribe to coordinator progress updates
*
* @param subscriber Subscriber
*/
public void subscribeToProgress(@NotNull final ChunkCoordinator.ProgressSubscriber subscriber) {
this.progressSubscribers.add(subscriber);
}
@FunctionalInterface
public interface ProgressSubscriber {
/**
* Notify about a progress update in the coordinator
*
* @param coordinator Coordinator instance that triggered the notification
* @param progress Progress in the range [0, 1]
*/
void notifyProgress(@NotNull final ChunkCoordinator coordinator, final float progress);
}
public static final class ChunkCoordinatorBuilder {
private final List<BlockVector2> requestedChunks = new LinkedList<>();
private Consumer<Throwable> throwableConsumer = Throwable::printStackTrace;
private World world;
private Consumer<Chunk> chunkConsumer;
private Runnable whenDone = () -> {
};
private long maxIterationTime = 60; // A little over 1 tick;
private int initialBatchSize = 4;
private ChunkCoordinatorBuilder() {
}
@NotNull public ChunkCoordinatorBuilder inWorld(@NotNull final World world) {
this.world = Preconditions.checkNotNull(world, "World may not be null");
return this;
}
@NotNull
public ChunkCoordinatorBuilder withChunk(@NotNull final BlockVector2 chunkLocation) {
this.requestedChunks
.add(Preconditions.checkNotNull(chunkLocation, "Chunk location may not be null"));
return this;
}
@NotNull public ChunkCoordinatorBuilder withChunks(
@NotNull final Collection<BlockVector2> chunkLocations) {
chunkLocations.forEach(this::withChunk);
return this;
}
@NotNull
public ChunkCoordinatorBuilder withConsumer(@NotNull final Consumer<Chunk> chunkConsumer) {
this.chunkConsumer =
Preconditions.checkNotNull(chunkConsumer, "Chunk consumer may not be null");
return this;
}
@NotNull public ChunkCoordinatorBuilder withFinalAction(@NotNull final Runnable whenDone) {
this.whenDone = Preconditions.checkNotNull(whenDone, "Final action may not be null");
return this;
}
@NotNull public ChunkCoordinatorBuilder withMaxIterationTime(final long maxIterationTime) {
Preconditions
.checkArgument(maxIterationTime > 0, "Max iteration time must be positive");
this.maxIterationTime = maxIterationTime;
return this;
}
@NotNull public ChunkCoordinatorBuilder withInitialBatchSize(final int initialBatchSize) {
Preconditions
.checkArgument(initialBatchSize > 0, "Initial batch size must be positive");
this.initialBatchSize = initialBatchSize;
return this;
}
@NotNull public ChunkCoordinatorBuilder withThrowableConsumer(
@NotNull final Consumer<Throwable> throwableConsumer) {
this.throwableConsumer =
Preconditions.checkNotNull(throwableConsumer, "Throwable consumer may not be null");
return this;
}
@NotNull public ChunkCoordinator build() {
Preconditions.checkNotNull(this.world, "No world was supplied");
Preconditions.checkNotNull(this.chunkConsumer, "No chunk consumer was supplied");
Preconditions.checkNotNull(this.whenDone, "No final action was supplied");
Preconditions
.checkNotNull(this.throwableConsumer, "No throwable consumer was supplied");
return new ChunkCoordinator(this.maxIterationTime, this.initialBatchSize,
this.chunkConsumer, this.world, this.requestedChunks, this.whenDone,
this.throwableConsumer);
}
}
}

View File

@ -42,7 +42,7 @@ import com.plotsquared.core.util.query.PlotQuery;
public class Target extends SubCommand { public class Target extends SubCommand {
public Target() { public Target() {
super(Argument.PlotID); super();
} }
@Override public boolean onCommand(PlotPlayer<?> player, String[] args) { @Override public boolean onCommand(PlotPlayer<?> player, String[] args) {
@ -51,6 +51,10 @@ public class Target extends SubCommand {
MainUtil.sendMessage(player, Captions.NOT_IN_PLOT_WORLD); MainUtil.sendMessage(player, Captions.NOT_IN_PLOT_WORLD);
return false; return false;
} }
if (args.length == 0) {
MainUtil.sendMessage(player, this.getUsage());
return false;
}
Plot target = null; Plot target = null;
if (StringMan.isEqualIgnoreCaseToAny(args[0], "near", "nearest")) { if (StringMan.isEqualIgnoreCaseToAny(args[0], "near", "nearest")) {
int distance = Integer.MAX_VALUE; int distance = Integer.MAX_VALUE;

View File

@ -260,6 +260,16 @@ public class Settings extends Config {
@Comment("Whether or not automatic background caching should be enabled. It is HIGHLY recommended to keep this turned on." @Comment("Whether or not automatic background caching should be enabled. It is HIGHLY recommended to keep this turned on."
+ " This should only be disabled if the server has a very large number of plots (>100k)") + " This should only be disabled if the server has a very large number of plots (>100k)")
public static boolean BACKGROUND_CACHING_ENABLED = true; public static boolean BACKGROUND_CACHING_ENABLED = true;
@Comment("Whether the PaperMC service is enabled")
public static boolean SERVICE_PAPER = true;
@Comment("Whether the LuckPerms service is enabled")
public static boolean SERVICE_LUCKPERMS = true;
@Comment("Whether the Bukkit service is enabled")
public static boolean SERVICE_BUKKIT = true;
@Comment("Whether the EssentialsX service is enabled")
public static boolean SERVICE_ESSENTIALSX = true;
@Comment("Whether the BungeePerms service is enabled")
public static boolean SERVICE_BUNGEE_PERMS = true;
} }