Compare commits

...

11 Commits

Author SHA1 Message Date
4e486f9be3 fix: allow cauldron in use flag to behave as expected 2025-06-14 13:55:31 +01:00
6f0fa19601 Update junit5 monorepo (#4667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 09:06:38 +00:00
0b692459e6 Update dependency gradle to v8.14.2 (#4666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-05 15:26:03 +00:00
0d410ed869 Update dependency org.checkerframework:checker-qual to v3.49.4 (#4665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:02:18 +00:00
711fba0b2a Update junit5 monorepo (#4662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-30 12:06:20 +00:00
671a27fa6f Update dependency com.diffplug.spotless to v7.0.4 (#4661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 22:39:24 +00:00
03de685dc4 Update dependency net.essentialsx:EssentialsX to v2.21.1 (#4660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-25 21:59:36 +00:00
f616885206 Update dependency net.luckperms:api to v5.5 (#4659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-25 10:29:47 +00:00
7da4eb1ab5 fix: correctly parse help messages with prefix (#4652) 2025-05-24 14:39:31 +02:00
629646ab06 fix: cancel structure locate in classic plot worlds (#4653) 2025-05-24 14:39:14 +02:00
74a1a1f954 Update dependency gradle to v8.14.1 (#4658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 17:04:52 +00:00
11 changed files with 150 additions and 67 deletions

View File

@ -29,6 +29,7 @@ import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent;
import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
import com.google.inject.Inject;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.command.Command;
import com.plotsquared.core.command.MainCommand;
import com.plotsquared.core.configuration.Settings;
@ -38,6 +39,7 @@ import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotAreaType;
import com.plotsquared.core.plot.flag.FlagContainer;
import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag;
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
@ -48,6 +50,7 @@ import com.plotsquared.core.plot.flag.types.BooleanFlag;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.PlotFlagUtil;
import io.papermc.paper.event.entity.EntityMoveEvent;
import io.papermc.paper.event.world.StructuresLocateEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
@ -458,6 +461,21 @@ public class PaperListener implements Listener {
}
}
/**
* Don't let the server die when populating cartographers (villager offering maps) in classic plot worlds
* (as those don't generate POIs)
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onStructuresLocate(StructuresLocateEvent event) {
if (!PlotSquared.get().getPlotAreaManager().hasPlotArea(event.getWorld().getName())) {
return;
}
final PlotArea area = PlotSquared.get().getPlotAreaManager().getPlotAreaByString(event.getWorld().getName());
if (area != null && area.getType() == PlotAreaType.NORMAL) {
event.setCancelled(true);
}
}
private boolean getBooleanFlagValue(
@NonNull FlagContainer container,
@NonNull Class<? extends BooleanFlag<?>> flagClass,

View File

@ -64,9 +64,11 @@ import com.plotsquared.core.plot.flag.implementations.PreventCreativeCopyFlag;
import com.plotsquared.core.plot.flag.implementations.TamedInteractFlag;
import com.plotsquared.core.plot.flag.implementations.TileDropFlag;
import com.plotsquared.core.plot.flag.implementations.UntrustedVisitFlag;
import com.plotsquared.core.plot.flag.implementations.UseFlag;
import com.plotsquared.core.plot.flag.implementations.VehicleBreakFlag;
import com.plotsquared.core.plot.flag.implementations.VehicleUseFlag;
import com.plotsquared.core.plot.flag.implementations.VillagerInteractFlag;
import com.plotsquared.core.plot.flag.types.BlockTypeWrapper;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.MathMan;
@ -78,6 +80,7 @@ import com.plotsquared.core.util.task.TaskTime;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import io.papermc.lib.PaperLib;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
@ -152,9 +155,11 @@ import org.bukkit.util.Vector;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@ -179,6 +184,7 @@ public class PlayerEventListener implements Listener {
Material.WRITTEN_BOOK
);
private static final Set<String> DYES;
static {
Set<String> mutableDyes = new HashSet<>(Set.of(
"WHITE_DYE",
@ -213,7 +219,7 @@ public class PlayerEventListener implements Listener {
// "temporary" fix for https://hub.spigotmc.org/jira/browse/SPIGOT-7813
// can (and should) be removed when 1.21 support is dropped
// List of all interactable 1.21 materials
INTERACTABLE_MATERIALS = Material.CHEST.isInteractable() ? null : Set.of(
INTERACTABLE_MATERIALS = Material.CHEST.isInteractable() ? null : Set.of(
"REDSTONE_ORE", "DEEPSLATE_REDSTONE_ORE", "CHISELED_BOOKSHELF", "DECORATED_POT", "CHEST", "CRAFTING_TABLE",
"FURNACE", "JUKEBOX", "OAK_FENCE", "SPRUCE_FENCE", "BIRCH_FENCE", "JUNGLE_FENCE", "ACACIA_FENCE", "CHERRY_FENCE",
"DARK_OAK_FENCE", "MANGROVE_FENCE", "BAMBOO_FENCE", "CRIMSON_FENCE", "WARPED_FENCE", "PUMPKIN",
@ -531,12 +537,14 @@ public class PlayerEventListener implements Listener {
// Delayed
// Async
TaskManager.runTaskLaterAsync(() -> {
if (!player.hasPlayedBefore() && player.isOnline()) {
player.saveData();
}
this.eventDispatcher.doJoinTask(pp);
}, TaskTime.seconds(1L));
TaskManager.runTaskLaterAsync(
() -> {
if (!player.hasPlayedBefore() && player.isOnline()) {
player.saveData();
}
this.eventDispatcher.doJoinTask(pp);
}, TaskTime.seconds(1L)
);
if (pp.hasPermission(Permission.PERMISSION_ADMIN_UPDATE_NOTIFICATION.toString()) && Settings.Enabled_Components.UPDATE_NOTIFICATIONS
&& PremiumVerification.isPremium() && UpdateUtility.hasUpdate) {
@ -597,7 +605,9 @@ public class PlayerEventListener implements Listener {
// to is identical to the plot's home location, and untrusted-visit is true
// i.e. untrusted-visit can override deny-teleport
// this is acceptable, because otherwise it wouldn't make sense to have both flags set
if (result || (plot.getFlag(UntrustedVisitFlag.class) && plot.getHomeSynchronous().equals(BukkitUtil.adaptComplete(to)))) {
if (result || (plot.getFlag(UntrustedVisitFlag.class) && plot
.getHomeSynchronous()
.equals(BukkitUtil.adaptComplete(to)))) {
// returns false if the player is not allowed to enter the plot (if they are denied, for example)
// don't let the move event cancel the entry after teleport, but rather catch and cancel early (#4647)
if (!plotListener.plotEntry(pp, plot)) {
@ -941,12 +951,15 @@ public class PlayerEventListener implements Listener {
builder.tag("plot_id", Tag.inserting(Component.text(id.toString())));
builder.tag("sender", Tag.inserting(Component.text(sender)));
if (plotPlayer.hasPermission("plots.chat.color")) {
builder.tag("msg", Tag.inserting(MiniMessage.miniMessage().deserialize(
message,
TagResolver.resolver(StandardTags.color(), StandardTags.gradient(),
StandardTags.rainbow(), StandardTags.decorations()
)
)));
builder.tag(
"msg", Tag.inserting(MiniMessage.miniMessage().deserialize(
message,
TagResolver.resolver(
StandardTags.color(), StandardTags.gradient(),
StandardTags.rainbow(), StandardTags.decorations()
)
))
);
} else {
builder.tag("msg", Tag.inserting(Component.text(message)));
}
@ -1255,7 +1268,9 @@ public class PlayerEventListener implements Listener {
eventType = PlayerBlockEventType.INTERACT_BLOCK;
blocktype1 = BukkitAdapter.asBlockType(block.getType());
if (INTERACTABLE_MATERIALS != null ? INTERACTABLE_MATERIALS.contains(blockType.name()) : blockType.isInteractable()) {
if (INTERACTABLE_MATERIALS != null
? INTERACTABLE_MATERIALS.contains(blockType.name())
: blockType.isInteractable()) {
if (!player.isSneaking()) {
break;
}
@ -1273,7 +1288,7 @@ public class PlayerEventListener implements Listener {
// in the following, lb needs to have the material of the item in hand i.e. type
switch (type.toString()) {
case "REDSTONE", "STRING", "PUMPKIN_SEEDS", "MELON_SEEDS", "COCOA_BEANS", "WHEAT_SEEDS", "BEETROOT_SEEDS",
"SWEET_BERRIES", "GLOW_BERRIES" -> {
"SWEET_BERRIES", "GLOW_BERRIES" -> {
return;
}
default -> {
@ -1380,6 +1395,16 @@ public class PlayerEventListener implements Listener {
}
BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
Plot plot = area.getPlot(location);
final List<BlockTypeWrapper> use =
Optional.ofNullable(plot).map(p -> p.getFlag(UseFlag.class)).orElse(area.isRoadFlags() ?
area.getFlag(UseFlag.class) : Collections.emptyList());
BlockType type = BukkitAdapter.asBlockType(block.getType());
for (final BlockTypeWrapper blockTypeWrapper : use) {
if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
.accepts(type)) {
return;
}
}
if (plot == null) {
if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
return;
@ -1451,6 +1476,16 @@ public class PlayerEventListener implements Listener {
Player player = event.getPlayer();
BukkitPlayer plotPlayer = BukkitUtil.adapt(player);
Plot plot = area.getPlot(location);
final List<BlockTypeWrapper> use =
Optional.ofNullable(plot).map(p -> p.getFlag(UseFlag.class)).orElse(area.isRoadFlags() ?
area.getFlag(UseFlag.class) : Collections.emptyList());
BlockType type = BukkitAdapter.asBlockType(blockClicked.getType());
for (final BlockTypeWrapper blockTypeWrapper : use) {
if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
.accepts(type)) {
return;
}
}
if (plot == null) {
if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
return;

View File

@ -24,6 +24,7 @@ import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
@ -98,6 +99,14 @@ public enum CommandCategory implements Caption {
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
}
@Override
public @NonNull Component toComponent(
@NonNull final LocaleHolder localeHolder,
final @NonNull TagResolver @NonNull ... tagResolvers
) {
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
}
/**
* Checks if a player has access to this command category
*

View File

@ -113,38 +113,34 @@ public class Help extends Command {
}
if (cat == null && page == 0) {
TextComponent.Builder builder = Component.text();
builder.append(MINI_MESSAGE.deserialize(TranslatableCaption.of("help.help_header").getComponent(player)));
builder.append(TranslatableCaption.of("help.help_header").toComponent(player));
for (CommandCategory c : CommandCategory.values()) {
if (!c.canAccess(player)) {
continue;
}
builder.append(Component.newline()).append(MINI_MESSAGE
.deserialize(
TranslatableCaption.of("help.help_info_item").getComponent(player),
TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text(c.name().toLowerCase())))
.tag("category_desc", Tag.inserting(c.toComponent(player)))
.build()
));
builder.append(Component.newline());
builder.append(TranslatableCaption.of("help.help_info_item").toComponent(
player, TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text(c.name().toLowerCase())))
.tag("category_desc", Tag.inserting(c.toComponent(player)))
.build()
));
}
builder.append(Component.newline()).append(MINI_MESSAGE
.deserialize(
TranslatableCaption.of("help.help_info_item").getComponent(player),
TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text("all")))
.tag(
"category_desc",
Tag.inserting(TranslatableCaption
.of("help.help_display_all_commands")
.toComponent(player))
)
.build()
));
builder.append(Component.newline()).append(MINI_MESSAGE.deserialize(TranslatableCaption
.of("help.help_footer")
.getComponent(player)));
builder.append(Component.newline());
builder.append(TranslatableCaption.of("help.help_info_item").toComponent(
player, TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text("all")))
.tag(
"category_desc", Tag.inserting(TranslatableCaption
.of("help.help_display_all_commands")
.toComponent(player))
)
.build()
));
builder.append(Component.newline());
builder.append(TranslatableCaption.of("help.help_footer").toComponent(player));
player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.asComponent())));
return true;
}

View File

@ -20,6 +20,7 @@ package com.plotsquared.core.configuration.caption;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
@ -44,6 +45,16 @@ public interface Caption {
*/
@NonNull Component toComponent(@NonNull LocaleHolder localeHolder);
/**
* Get the Adventure {@link ComponentLike} for this caption while applying custom {@link TagResolver}
* (apart from the default {@code core.prefix})
* @param localeHolder Local holder
* @param tagResolvers custom tag resolvers to replace placeholders / parameters
* @return {@link ComponentLike}
* @since TODO
*/
@NonNull Component toComponent(@NonNull LocaleHolder localeHolder, @NonNull TagResolver @NonNull... tagResolvers);
@NonNull String toString();
}

View File

@ -21,6 +21,7 @@ package com.plotsquared.core.configuration.caption;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
public final class StaticCaption implements Caption {
@ -51,6 +52,14 @@ public final class StaticCaption implements Caption {
return MiniMessage.miniMessage().deserialize(this.value);
}
@Override
public @NonNull Component toComponent(
@NonNull final LocaleHolder localeHolder,
final @NonNull TagResolver @NonNull ... tagResolvers
) {
return MiniMessage.miniMessage().deserialize(this.value, tagResolvers);
}
@Override
public @NonNull String toString() {
return "StaticCaption(" + value + ")";

View File

@ -27,6 +27,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Locale;
import java.util.regex.Pattern;
@ -96,13 +97,23 @@ public final class TranslatableCaption implements NamespacedCaption {
@Override
public @NonNull Component toComponent(@NonNull final LocaleHolder localeHolder) {
return this.toComponent(localeHolder, new TagResolver[0]);
}
@Override
public @NonNull Component toComponent(
@NonNull final LocaleHolder localeHolder,
final @NonNull TagResolver @NonNull ... tagResolvers
) {
if (getKey().equals("core.prefix")) {
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
}
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder), TagResolver.resolver(
TagResolver[] finalResolvers = Arrays.copyOf(tagResolvers, tagResolvers.length + 1);
finalResolvers[finalResolvers.length - 1] = TagResolver.resolver(
"prefix",
Tag.inserting(TranslatableCaption.of("core.prefix").toComponent(localeHolder))
));
);
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder), finalResolvers);
}
@Override

View File

@ -25,30 +25,24 @@ import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.util.StringMan;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.jetbrains.annotations.NotNull;
public class HelpObject implements ComponentLike {
static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
private final Component rendered;
public HelpObject(final Command command, final String label, final PlotPlayer<?> audience) {
rendered = MINI_MESSAGE.deserialize(
TranslatableCaption.of("help.help_item").getComponent(audience),
TagResolver.builder()
.tag("usage", Tag.inserting(Component.text(command.getUsage().replace("{label}", label))))
.tag("alias", Tag.inserting(Component.text(
command.getAliases().isEmpty() ? "" : StringMan.join(command.getAliases(), " | ")
)))
.tag("desc", Tag.inserting(command.getDescription().toComponent(audience)))
.tag("arguments", Tag.inserting(Component.text(buildArgumentList(command.getRequiredArguments()))))
.tag("label", Tag.inserting(Component.text(label)))
.build()
);
this.rendered = TranslatableCaption.of("help.help_item").toComponent(audience, TagResolver.builder()
.tag("usage", Tag.inserting(Component.text(command.getUsage().replace("{label}", label))))
.tag("alias", Tag.inserting(Component.text(
command.getAliases().isEmpty() ? "" : StringMan.join(command.getAliases(), " | ")
)))
.tag("desc", Tag.inserting(command.getDescription().toComponent(audience)))
.tag("arguments", Tag.inserting(Component.text(buildArgumentList(command.getRequiredArguments()))))
.tag("label", Tag.inserting(Component.text(label)))
.build());
}
private String buildArgumentList(final Argument<?>[] arguments) {

View File

@ -69,8 +69,8 @@ subprojects {
dependencies {
// Tests
testImplementation("org.junit.jupiter:junit-jupiter:5.12.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.12.2")
testImplementation("org.junit.jupiter:junit-jupiter:5.13.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.1")
}
plugins.withId("java") {

View File

@ -3,7 +3,7 @@
paper = "1.20.4-R0.1-SNAPSHOT"
guice = "7.0.0"
spotbugs = "4.9.3"
checkerqual = "3.49.3"
checkerqual = "3.49.4"
gson = "2.10"
guava = "31.1-jre"
snakeyaml = "2.0"
@ -15,8 +15,8 @@ log4j = "2.19.0"
worldedit = "7.2.20"
fawe = "2.13.0"
placeholderapi = "2.11.6"
luckperms = "5.4"
essentialsx = "2.21.0"
luckperms = "5.5"
essentialsx = "2.21.1"
mvdwapi = "3.1.1"
# Third party
@ -35,7 +35,7 @@ serverlib = "2.3.7"
# Gradle plugins
shadow = "8.3.6"
grgit = "4.1.1"
spotless = "7.0.3"
spotless = "7.0.4"
publish = "0.32.0"
runPaper = "2.3.1"

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME