Compare commits

..

13 Commits

Author SHA1 Message Date
3ec7e992a3 Update junit-framework monorepo (#4689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 15:01:17 +00:00
9acaa9c554 Update dependency gradle to v8.14.3 (#4688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 15:00:50 +00:00
e132c01331 Update dependency com.gradleup.shadow to v8.3.8 (#4686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 16:45:35 +00:00
24e4e51884 Update dependency org.checkerframework:checker-qual to v3.49.5 (#4684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 19:35:10 +00:00
84ec090df1 fix: Add entity cap check of armor stands & end crystals into player interact listener (#4654)
When cancelled in the creature spawn event and placed by a player, the player will lose the armor stand / end crystal item from their inventory. This PR aims to fix this by moving the verification whether the entity should be cancelled up to the spawn egg right click, this way the item is not touched.
2025-06-26 17:35:40 +01:00
6c6ea1c1b4 Update dependency org.junit.jupiter:junit-jupiter to v5.13.2 (#4679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 19:54:47 +00:00
97989face1 Update dependency org.junit.platform:junit-platform-launcher to v1.13.2 (#4680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 13:51:13 +00:00
f471c02330 Update dependency com.gradleup.shadow to v8.3.7 (#4678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 03:03:11 +00:00
94322d5982 Update dependency com.vanniktech.maven.publish to v0.33.0 (#4676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-22 17:57:31 +00:00
6cbb894249 Fix/plot info syntax message (#4671)
* Adding plot-info category messages

* Use plot-info category list from language config
2025-06-15 12:42:06 +02: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
14 changed files with 97 additions and 386 deletions

View File

@ -22,20 +22,12 @@ import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.plotsquared.bukkit.permissions.BukkitPermissionHandler; import com.plotsquared.bukkit.permissions.BukkitPermissionHandler;
import com.plotsquared.bukkit.permissions.BukkitRangedPermissionResolver;
import com.plotsquared.bukkit.permissions.LuckPermsRangedPermissionResolver;
import com.plotsquared.bukkit.permissions.VaultPermissionHandler; import com.plotsquared.bukkit.permissions.VaultPermissionHandler;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.permissions.PermissionHandler; import com.plotsquared.core.permissions.PermissionHandler;
import com.plotsquared.core.permissions.RangedPermissionResolver;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
public class PermissionModule extends AbstractModule { public class PermissionModule extends AbstractModule {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + PermissionModule.class.getSimpleName());
@Provides @Provides
@Singleton @Singleton
PermissionHandler providePermissionHandler() { PermissionHandler providePermissionHandler() {
@ -48,18 +40,4 @@ public class PermissionModule extends AbstractModule {
return new BukkitPermissionHandler(); return new BukkitPermissionHandler();
} }
@Provides
@Singleton
RangedPermissionResolver provideRangedPermissionResolver() {
if (Settings.Permissions.USE_LUCKPERMS_RANGE_RESOLVER) {
if (Bukkit.getPluginManager().isPluginEnabled("LuckPerms")) {
LOGGER.info("Using experimental LuckPerms ranged permission resolver");
return new LuckPermsRangedPermissionResolver();
}
LOGGER.warn("Enabled LuckPerms ranged permission resolver, but LuckPerms is not installed. " +
"Falling back to default Bukkit ranged permission resolver");
}
return new BukkitRangedPermissionResolver();
}
} }

View File

@ -1298,6 +1298,17 @@ public class PlayerEventListener implements Listener {
//Allow all players to eat while also allowing the block place event to be fired //Allow all players to eat while also allowing the block place event to be fired
return; return;
} }
// Process creature spawning of armor stands & end crystals here if spawned by the player in order to be able to
// reset the player's hand item if spawning needs to be cancelled.
if (type == Material.ARMOR_STAND || type == Material.END_CRYSTAL) {
Plot plot = location.getOwnedPlotAbs();
if (BukkitEntityUtil.checkEntity(type == Material.ARMOR_STAND ? EntityType.ARMOR_STAND : EntityType.ENDER_CRYSTAL,
plot)) {
event.setCancelled(true);
break;
}
}
// Continue with normal place event checks
if (type == Material.ARMOR_STAND) { if (type == Material.ARMOR_STAND) {
location = BukkitUtil.adapt(block.getRelative(event.getBlockFace()).getLocation()); location = BukkitUtil.adapt(block.getRelative(event.getBlockFace()).getLocation());
eventType = PlayerBlockEventType.PLACE_MISC; eventType = PlayerBlockEventType.PLACE_MISC;

View File

@ -1,107 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* 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.permissions;
import com.plotsquared.bukkit.player.BukkitPlayer;
import com.plotsquared.core.permissions.RangedPermissionResolver;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.util.MathMan;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Set;
import java.util.stream.IntStream;
public class BukkitRangedPermissionResolver implements RangedPermissionResolver {
private static boolean CHECK_EFFECTIVE = true;
@Override
public @NonNegative int getPermissionRange(
final @NonNull PlotPlayer<?> generic,
final @NonNull String stub,
final @Nullable String worldContext,
@NonNegative final int range
) {
if (!(generic instanceof BukkitPlayer player)) {
throw new IllegalArgumentException("PlotPlayer is not a BukkitPlayer");
}
if (hasWildcardRange(player, stub, worldContext)) {
return INFINITE_RANGE_VALUE;
}
int max = 0;
if (CHECK_EFFECTIVE) {
boolean hasAny = false;
String stubPlus = stub + ".";
final Set<PermissionAttachmentInfo> effective = player.getPlatformPlayer().getEffectivePermissions();
if (!effective.isEmpty()) {
for (PermissionAttachmentInfo attach : effective) {
// Ignore all "false" permissions
if (!attach.getValue()) {
continue;
}
String permStr = attach.getPermission();
if (permStr.startsWith(stubPlus)) {
hasAny = true;
String end = permStr.substring(stubPlus.length());
if (MathMan.isInteger(end)) {
int val = Integer.parseInt(end);
if (val > range) {
return val;
}
if (val > max) {
max = val;
}
}
}
}
if (hasAny) {
return max;
}
// Workaround
for (PermissionAttachmentInfo attach : effective) {
String permStr = attach.getPermission();
if (permStr.startsWith("plots.") && !permStr.equals("plots.use")) {
return max;
}
}
CHECK_EFFECTIVE = false;
}
}
for (int i = range; i > 0; i--) {
if (player.hasPermission(worldContext, stub + "." + i)) {
return i;
}
}
return max;
}
@Override
public @NonNull IntStream streamFullPermissionRange(
final @NonNull PlotPlayer<?> player,
final @NonNull String stub,
final @Nullable String worldContext,
@NonNegative final int range
) {
return IntStream.of(getPermissionRange(player, stub, worldContext, range));
}
}

View File

@ -1,95 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* 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.permissions;
import com.plotsquared.core.permissions.RangedPermissionResolver;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.util.MathMan;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.model.user.User;
import net.luckperms.api.node.NodeType;
import net.luckperms.api.node.types.PermissionNode;
import net.luckperms.api.query.QueryOptions;
import org.bukkit.Bukkit;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.stream.IntStream;
public class LuckPermsRangedPermissionResolver implements RangedPermissionResolver {
private final LuckPerms luckPerms;
public LuckPermsRangedPermissionResolver() {
this.luckPerms = Objects.requireNonNull(
Bukkit.getServicesManager().getRegistration(LuckPerms.class),
"LuckPerms is not available"
).getProvider();
}
@Override
public @NonNegative int getPermissionRange(
final @NonNull PlotPlayer<?> player,
final @NonNull String stub,
final @Nullable String worldContext,
final @NonNegative int range
) {
// no need to use LuckPerms for basic checks
if (this.hasWildcardRange(player, stub, worldContext)) {
return INFINITE_RANGE_VALUE;
}
return this.streamFullPermissionRange(player, stub, worldContext, range)
.sorted()
.reduce((first, second) -> second)
.orElse(0);
}
@NonNull
public IntStream streamFullPermissionRange(
final @NonNull PlotPlayer<?> player,
final @NonNull String stub,
final @Nullable String worldContext,
@NonNegative final int range
) {
final User user = this.luckPerms.getUserManager().getUser(player.getUUID());
if (user == null) {
throw new IllegalStateException("Luckperms User is null - is the Player online? (UUID: %s)".formatted(player.getUUID()));
}
final QueryOptions queryOptions = worldContext == null ?
QueryOptions.nonContextual() :
QueryOptions.contextual(ImmutableContextSet.of("world", worldContext));
return user.resolveInheritedNodes(queryOptions).stream()
// only support normal permission nodes (regex permission nodes would be a pita to support)
.filter(NodeType.PERMISSION::matches)
.map(node -> ((PermissionNode) node).getPermission())
// check that the node actually has additional data after the stub
.filter(permission -> permission.startsWith(stub + "."))
// extract the raw data after the stub
.map(permission -> permission.substring(stub.length() + 1))
// check if data is integer and parse
.filter(MathMan::isInteger)
.mapToInt(Integer::parseInt)
// only use values that are positive
.filter(value -> value > -1);
}
}

View File

@ -24,12 +24,14 @@ import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.events.TeleportCause;
import com.plotsquared.core.location.Location; import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.permissions.PermissionHandler; import com.plotsquared.core.permissions.PermissionHandler;
import com.plotsquared.core.player.ConsolePlayer; import com.plotsquared.core.player.ConsolePlayer;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.PlotWeather; import com.plotsquared.core.plot.PlotWeather;
import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.EventDispatcher; import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.MathMan;
import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.WorldUtil;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
@ -45,11 +47,13 @@ import org.bukkit.entity.Player;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.EventException; import org.bukkit.event.EventException;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.RegisteredListener; import org.bukkit.plugin.RegisteredListener;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static com.sk89q.worldedit.world.gamemode.GameModes.ADVENTURE; import static com.sk89q.worldedit.world.gamemode.GameModes.ADVENTURE;
@ -146,13 +150,77 @@ public class BukkitPlayer extends PlotPlayer<Player> {
} }
} }
@SuppressWarnings("StringSplitter")
@Override @Override
@NonNegative @NonNegative
public int hasPermissionRange( public int hasPermissionRange(
final @NonNull String stub, final @NonNull String stub,
@NonNegative final int range @NonNegative final int range
) { ) {
return PlotSquared.platform().rangedPermissionResolver().getPermissionRange(this, stub, null, range); if (hasPermission(Permission.PERMISSION_ADMIN.toString())) {
return Integer.MAX_VALUE;
}
final String[] nodes = stub.split("\\.");
final StringBuilder n = new StringBuilder();
// Wildcard check from less specific permission to more specific permission
for (int i = 0; i < (nodes.length - 1); i++) {
n.append(nodes[i]).append(".");
if (!stub.equals(n + Permission.PERMISSION_STAR.toString())) {
if (hasPermission(n + Permission.PERMISSION_STAR.toString())) {
return Integer.MAX_VALUE;
}
}
}
// Wildcard check for the full permission
if (hasPermission(stub + ".*")) {
return Integer.MAX_VALUE;
}
// Permission value cache for iterative check
int max = 0;
if (CHECK_EFFECTIVE) {
boolean hasAny = false;
String stubPlus = stub + ".";
final Set<PermissionAttachmentInfo> effective = player.getEffectivePermissions();
if (!effective.isEmpty()) {
for (PermissionAttachmentInfo attach : effective) {
// Ignore all "false" permissions
if (!attach.getValue()) {
continue;
}
String permStr = attach.getPermission();
if (permStr.startsWith(stubPlus)) {
hasAny = true;
String end = permStr.substring(stubPlus.length());
if (MathMan.isInteger(end)) {
int val = Integer.parseInt(end);
if (val > range) {
return val;
}
if (val > max) {
max = val;
}
}
}
}
if (hasAny) {
return max;
}
// Workaround
for (PermissionAttachmentInfo attach : effective) {
String permStr = attach.getPermission();
if (permStr.startsWith("plots.") && !permStr.equals("plots.use")) {
return max;
}
}
CHECK_EFFECTIVE = false;
}
}
for (int i = range; i > 0; i--) {
if (hasPermission(stub + "." + i)) {
return i;
}
}
return max;
} }
@Override @Override

View File

@ -354,13 +354,17 @@ public class BukkitEntityUtil {
} }
public static boolean checkEntity(Entity entity, Plot plot) { public static boolean checkEntity(Entity entity, Plot plot) {
return checkEntity(entity.getType(), plot);
}
public static boolean checkEntity(EntityType type, Plot plot) {
if (plot == null || !plot.hasOwner() || plot.getFlags().isEmpty() && plot.getArea() if (plot == null || !plot.hasOwner() || plot.getFlags().isEmpty() && plot.getArea()
.getFlagContainer().getFlagMap().isEmpty()) { .getFlagContainer().getFlagMap().isEmpty()) {
return false; return false;
} }
final com.sk89q.worldedit.world.entity.EntityType entityType = final com.sk89q.worldedit.world.entity.EntityType entityType =
BukkitAdapter.adapt(entity.getType()); BukkitAdapter.adapt(type);
if (EntityCategories.PLAYER.contains(entityType)) { if (EntityCategories.PLAYER.contains(entityType)) {
return false; return false;

View File

@ -31,7 +31,6 @@ import com.plotsquared.core.generator.IndependentPlotGenerator;
import com.plotsquared.core.inject.annotations.DefaultGenerator; import com.plotsquared.core.inject.annotations.DefaultGenerator;
import com.plotsquared.core.location.World; import com.plotsquared.core.location.World;
import com.plotsquared.core.permissions.PermissionHandler; import com.plotsquared.core.permissions.PermissionHandler;
import com.plotsquared.core.permissions.RangedPermissionResolver;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.expiration.ExpireManager; import com.plotsquared.core.plot.expiration.ExpireManager;
import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.plot.world.PlotAreaManager;
@ -350,10 +349,6 @@ public interface PlotPlatform<P> extends LocaleHolder {
return injector().getInstance(PermissionHandler.class); return injector().getInstance(PermissionHandler.class);
} }
default @NonNull RangedPermissionResolver rangedPermissionResolver() {
return injector().getInstance(RangedPermissionResolver.class);
}
/** /**
* Get the {@link ServicePipeline} implementation * Get the {@link ServicePipeline} implementation
* *

View File

@ -20,7 +20,6 @@ package com.plotsquared.core.command;
import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.Caption; import com.plotsquared.core.configuration.caption.Caption;
import com.plotsquared.core.configuration.caption.StaticCaption;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.database.DBFunc; import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
@ -131,13 +130,9 @@ public class Info extends SubCommand {
info = getCaption(arg); info = getCaption(arg);
if (info == null) { if (info == null) {
if (Settings.Ratings.USE_LIKES) { if (Settings.Ratings.USE_LIKES) {
player.sendMessage(StaticCaption.of( player.sendMessage(TranslatableCaption.of("info.plot_info_categories.use_likes"));
"&6Categories&7: &amembers&7, &aalias&7, &abiome&7, &aseen&7, &adenied&7, &aflags&7, &aid&7, &asize&7, &atrusted&7, "
+ "&aowner&7, " + " &alikes"));
} else { } else {
player.sendMessage(StaticCaption.of( player.sendMessage(TranslatableCaption.of("info.plot_info_categories.use_rating"));
"&6Categories&7: &amembers&7, &aalias&7, &abiome&7, &aseen&7, &adenied&7, &aflags&7, &aid&7, &asize&7, &atrusted&7, "
+ "&aowner&7, " + " &arating"));
} }
return false; return false;
} }

View File

@ -525,8 +525,8 @@ public class Settings extends Config {
public static final class Limit { public static final class Limit {
@Comment("Should the limit be global (over multiple worlds)") @Comment("Should the limit be global (over multiple worlds)")
public static boolean GLOBAL = false; public static boolean GLOBAL =
false;
@Comment({"The max range of integer permissions to check for, e.g. 'plots.plot.127' or 'plots.set.flag.mob-cap.127'", @Comment({"The max range of integer permissions to check for, e.g. 'plots.plot.127' or 'plots.set.flag.mob-cap.127'",
"The value covers the permission range to check, you need to assign the permission to players/groups still", "The value covers the permission range to check, you need to assign the permission to players/groups still",
"Modifying the value does NOT change the amount of plots players can claim"}) "Modifying the value does NOT change the amount of plots players can claim"})
@ -737,7 +737,6 @@ public class Settings extends Config {
@Comment("If \"instabreak\" should consider the used tool.") @Comment("If \"instabreak\" should consider the used tool.")
public static boolean INSTABREAK_CONSIDER_TOOL = false; public static boolean INSTABREAK_CONSIDER_TOOL = false;
} }
@Comment({"Enable or disable parts of the plugin", @Comment({"Enable or disable parts of the plugin",
@ -831,17 +830,4 @@ public class Settings extends Config {
} }
@Comment({"Permission-Resolver specific settings"})
public static final class Permissions {
@Comment({
"use the new LuckPerms resolver for ranged permissions (e.g., plots.plot.<number>).",
"in contrary to the default resolver, this resolver can handle inheritance of permissions.",
"e.g. if the player has multiple matching permissions, for example, due to multiple group memberships " +
"(plots.plot.5 & plots.plot.15), this resolver would use 15 as the limit as opposed to the previous 5."
})
public static boolean USE_LUCKPERMS_RANGE_RESOLVER = false;
}
} }

View File

@ -1,126 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* 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.permissions;
import com.plotsquared.core.player.PlotPlayer;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.stream.IntStream;
/**
* Represents a resolver for ranged permissions. Return values depend on the actual implementation (see Bukkit module).
* <br>
* Even though this interface is not linked to platform implementations by design, implementation-specific details are added to
* the Javadocs.
*
* @since TODO
*/
public interface RangedPermissionResolver {
int INFINITE_RANGE_VALUE = Integer.MAX_VALUE;
/**
* Gets the applicable range value of a player for a specific permission stub
* ({@code plots.plot} would check for {@code plots.plot.<numeric>}).
* <br>
* The standard bukkit implementation would return the lowest numeric value, while the LuckPerms specific resolver would
* try returning the highest possible value.
*
* @param player the permission holder
* @param stub the permission stub to check against
* @param worldContext the optional world context of the action requiring the range
* @param range the maximum permission range to check against (for the default bukkit resolver)
* @return the applicable range value of the player for the given permission stub
* @since TODO
*/
@NonNegative
int getPermissionRange(
final @NonNull PlotPlayer<?> player,
final @NonNull String stub,
final @Nullable String worldContext,
final @NonNegative int range
);
/**
* Gets a stream of all applicable permission range values for the given stub. The stream is unordered by default. If a
* specific order is needed, use the stateful {@link IntStream#sorted()} operation.
* <br>
* The standard bukkit implementation will only return a stream containing a single value equal to
* {@link #getPermissionRange(PlotPlayer, String, String, int)}. For LuckPerms, all applicable node values will be in the
* stream.
*
* @param player the permission holder
* @param stub the permission stub to check against
* @param worldContext the optional world context of the action requiring the range
* @param range the maximum permission range to check against (for the default bukkit resolver)
* @return a stream of all applicable permission range values for the given stub
* @since TODO
*/
@NonNull
IntStream streamFullPermissionRange(
final @NonNull PlotPlayer<?> player,
final @NonNull String stub,
final @Nullable String worldContext,
final @NonNegative int range
);
/**
* Checks if the given player has a wildcard range for the given permission stub.
* <br>
* For example, if checking for the stub {@code plots.plot}, this method would check for:
* <ul>
* <li>{@code *}</li>
* <li>{@code plots.admin}</li>
* <li>{@code plots.plot.*}</li>
* <li>{@code plots.*}</li>
* </ul>
*
* @param player the permission holder
* @param stub the permission stub to check against
* @param worldContext the optional world context of the action requiring the range
* @return {@code true} if the player has a wildcard range for the given permission stub, else {@code false}
* @since TODO
*/
default boolean hasWildcardRange(
final @NonNull PlotPlayer<?> player,
final @NonNull String stub,
final @Nullable String worldContext
) {
if (player.hasPermission(Permission.PERMISSION_STAR) ||
player.hasPermission(Permission.PERMISSION_ADMIN) ||
player.hasPermission(worldContext, stub + ".*")) {
return true;
}
String node = stub;
while (true) {
int lastIndex = node.lastIndexOf('.');
if (lastIndex == -1) {
break;
}
node = node.substring(0, lastIndex);
if (player.hasPermission(worldContext, node + ".*")) {
return true;
}
}
return false;
}
}

View File

@ -353,6 +353,8 @@
"info.infinite": "<gray>Infinite</gray>", "info.infinite": "<gray>Infinite</gray>",
"info.plot_unowned": "<prefix><red>The current plot must have an owner to perform this action.</red>", "info.plot_unowned": "<prefix><red>The current plot must have an owner to perform this action.</red>",
"info.plot_info_unclaimed": "<prefix><gray>Plot <gold><plot></gold> is not yet claimed.</gray>", "info.plot_info_unclaimed": "<prefix><gray>Plot <gold><plot></gold> is not yet claimed.</gray>",
"info.plot_info_categories.use_rating": "<prefix><gold>Categories: </gold> <gray>members, alias, biome, seen, denied, flags, id, size, trusted, owner, rating</gray>",
"info.plot_info_categories.use_likes": "<prefix><gold>Categories: </gold> <gray>members, alias, biome, seen, denied, flags, id, size, trusted, owner, likes</gray>",
"info.plot_info_header": "<dark_gray><strikethrough>--------- <reset><gold>INFO </gold><dark_gray><strikethrough>---------</dark_gray><reset>", "info.plot_info_header": "<dark_gray><strikethrough>--------- <reset><gold>INFO </gold><dark_gray><strikethrough>---------</dark_gray><reset>",
"info.plot_info_hidden": "<prefix><red>You cannot view the information about this plot.</red>", "info.plot_info_hidden": "<prefix><red>You cannot view the information about this plot.</red>",
"info.plot_info_format": "<header>\n<gold>ID: <gray><id></gray>\nCreation: <gray><creationdate></gray>\nArea: <gray><area></gray>\nAlias: <gray><alias></gray>\nOwner: <gray><owner></gray>\nBiome: <gray><biome></gray>\nCan Build: <gray><build></gray>\nRating: <gray><rating></gray>\nSeen: <gray><seen></gray>\nTrusted: <gray><trusted></gray>\nMembers: <gray><members></gray>\nDenied: <gray><denied></gray>\nFlags: <gray><flags></gray>\nDescription: <gray><desc></gray></gold>\n<footer>", "info.plot_info_format": "<header>\n<gold>ID: <gray><id></gray>\nCreation: <gray><creationdate></gray>\nArea: <gray><area></gray>\nAlias: <gray><alias></gray>\nOwner: <gray><owner></gray>\nBiome: <gray><biome></gray>\nCan Build: <gray><build></gray>\nRating: <gray><rating></gray>\nSeen: <gray><seen></gray>\nTrusted: <gray><trusted></gray>\nMembers: <gray><members></gray>\nDenied: <gray><denied></gray>\nFlags: <gray><flags></gray>\nDescription: <gray><desc></gray></gold>\n<footer>",

View File

@ -69,8 +69,8 @@ subprojects {
dependencies { dependencies {
// Tests // Tests
testImplementation("org.junit.jupiter:junit-jupiter:5.13.0") testImplementation("org.junit.jupiter:junit-jupiter:5.13.3")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.0") testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.3")
} }
plugins.withId("java") { plugins.withId("java") {

View File

@ -3,7 +3,7 @@
paper = "1.20.4-R0.1-SNAPSHOT" paper = "1.20.4-R0.1-SNAPSHOT"
guice = "7.0.0" guice = "7.0.0"
spotbugs = "4.9.3" spotbugs = "4.9.3"
checkerqual = "3.49.3" checkerqual = "3.49.5"
gson = "2.10" gson = "2.10"
guava = "31.1-jre" guava = "31.1-jre"
snakeyaml = "2.0" snakeyaml = "2.0"
@ -33,10 +33,10 @@ vault = "1.7.1"
serverlib = "2.3.7" serverlib = "2.3.7"
# Gradle plugins # Gradle plugins
shadow = "8.3.6" shadow = "8.3.8"
grgit = "4.1.1" grgit = "4.1.1"
spotless = "7.0.4" spotless = "7.0.4"
publish = "0.32.0" publish = "0.33.0"
runPaper = "2.3.1" runPaper = "2.3.1"
[libraries] [libraries]

View File

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