mirror of
				https://github.com/IntellectualSites/PlotSquared.git
				synced 2025-11-03 18:53:43 +01:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			fix/cartog
			...
			feat/range
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					5a100ef988 | ||
| 
						 | 
					711fba0b2a | ||
| 
						 | 
					671a27fa6f | ||
| 
						 | 
					03de685dc4 | ||
| 
						 | 
					f616885206 | ||
| 
						 | 
					7da4eb1ab5 | ||
| 
						 | 
					629646ab06 | ||
| 
						 | 
					74a1a1f954 | 
@@ -22,12 +22,20 @@ 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() {
 | 
				
			||||||
@@ -40,4 +48,18 @@ 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();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent;
 | 
				
			|||||||
import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
 | 
					import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.plotsquared.bukkit.util.BukkitUtil;
 | 
					import com.plotsquared.bukkit.util.BukkitUtil;
 | 
				
			||||||
 | 
					import com.plotsquared.core.PlotSquared;
 | 
				
			||||||
import com.plotsquared.core.command.Command;
 | 
					import com.plotsquared.core.command.Command;
 | 
				
			||||||
import com.plotsquared.core.command.MainCommand;
 | 
					import com.plotsquared.core.command.MainCommand;
 | 
				
			||||||
import com.plotsquared.core.configuration.Settings;
 | 
					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.player.PlotPlayer;
 | 
				
			||||||
import com.plotsquared.core.plot.Plot;
 | 
					import com.plotsquared.core.plot.Plot;
 | 
				
			||||||
import com.plotsquared.core.plot.PlotArea;
 | 
					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.FlagContainer;
 | 
				
			||||||
import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag;
 | 
					import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag;
 | 
				
			||||||
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
 | 
					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.plot.world.PlotAreaManager;
 | 
				
			||||||
import com.plotsquared.core.util.PlotFlagUtil;
 | 
					import com.plotsquared.core.util.PlotFlagUtil;
 | 
				
			||||||
import io.papermc.paper.event.entity.EntityMoveEvent;
 | 
					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.Component;
 | 
				
			||||||
import net.kyori.adventure.text.minimessage.tag.Tag;
 | 
					import net.kyori.adventure.text.minimessage.tag.Tag;
 | 
				
			||||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
 | 
					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(
 | 
					    private boolean getBooleanFlagValue(
 | 
				
			||||||
            @NonNull FlagContainer container,
 | 
					            @NonNull FlagContainer container,
 | 
				
			||||||
            @NonNull Class<? extends BooleanFlag<?>> flagClass,
 | 
					            @NonNull Class<? extends BooleanFlag<?>> flagClass,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -24,14 +24,12 @@ 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;
 | 
				
			||||||
@@ -47,13 +45,11 @@ 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;
 | 
				
			||||||
@@ -150,77 +146,13 @@ 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
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        if (hasPermission(Permission.PERMISSION_ADMIN.toString())) {
 | 
					        return PlotSquared.platform().rangedPermissionResolver().getPermissionRange(this, stub, null, range);
 | 
				
			||||||
            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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ 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;
 | 
				
			||||||
@@ -349,6 +350,10 @@ 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
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ import com.plotsquared.core.configuration.caption.TranslatableCaption;
 | 
				
			|||||||
import com.plotsquared.core.player.PlotPlayer;
 | 
					import com.plotsquared.core.player.PlotPlayer;
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
 | 
					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.checkerframework.checker.nullness.qual.NonNull;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,6 +99,14 @@ public enum CommandCategory implements Caption {
 | 
				
			|||||||
        return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
 | 
					        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
 | 
					     * Checks if a player has access to this command category
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,38 +113,34 @@ public class Help extends Command {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            if (cat == null && page == 0) {
 | 
					            if (cat == null && page == 0) {
 | 
				
			||||||
                TextComponent.Builder builder = Component.text();
 | 
					                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()) {
 | 
					                for (CommandCategory c : CommandCategory.values()) {
 | 
				
			||||||
                    if (!c.canAccess(player)) {
 | 
					                    if (!c.canAccess(player)) {
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    builder.append(Component.newline()).append(MINI_MESSAGE
 | 
					                    builder.append(Component.newline());
 | 
				
			||||||
                            .deserialize(
 | 
					                    builder.append(TranslatableCaption.of("help.help_info_item").toComponent(
 | 
				
			||||||
                                    TranslatableCaption.of("help.help_info_item").getComponent(player),
 | 
					                            player, TagResolver.builder()
 | 
				
			||||||
                                    TagResolver.builder()
 | 
					                                    .tag("command", Tag.inserting(Component.text("/plot help")))
 | 
				
			||||||
                                            .tag("command", Tag.inserting(Component.text("/plot help")))
 | 
					                                    .tag("category", Tag.inserting(Component.text(c.name().toLowerCase())))
 | 
				
			||||||
                                            .tag("category", Tag.inserting(Component.text(c.name().toLowerCase())))
 | 
					                                    .tag("category_desc", Tag.inserting(c.toComponent(player)))
 | 
				
			||||||
                                            .tag("category_desc", Tag.inserting(c.toComponent(player)))
 | 
					                                    .build()
 | 
				
			||||||
                                            .build()
 | 
					                    ));
 | 
				
			||||||
                            ));
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                builder.append(Component.newline()).append(MINI_MESSAGE
 | 
					                builder.append(Component.newline());
 | 
				
			||||||
                        .deserialize(
 | 
					                builder.append(TranslatableCaption.of("help.help_info_item").toComponent(
 | 
				
			||||||
                                TranslatableCaption.of("help.help_info_item").getComponent(player),
 | 
					                        player, TagResolver.builder()
 | 
				
			||||||
                                TagResolver.builder()
 | 
					                                .tag("command", Tag.inserting(Component.text("/plot help")))
 | 
				
			||||||
                                        .tag("command", Tag.inserting(Component.text("/plot help")))
 | 
					                                .tag("category", Tag.inserting(Component.text("all")))
 | 
				
			||||||
                                        .tag("category", Tag.inserting(Component.text("all")))
 | 
					                                .tag(
 | 
				
			||||||
                                        .tag(
 | 
					                                        "category_desc", Tag.inserting(TranslatableCaption
 | 
				
			||||||
                                                "category_desc",
 | 
					                                                .of("help.help_display_all_commands")
 | 
				
			||||||
                                                Tag.inserting(TranslatableCaption
 | 
					                                                .toComponent(player))
 | 
				
			||||||
                                                        .of("help.help_display_all_commands")
 | 
					                                )
 | 
				
			||||||
                                                        .toComponent(player))
 | 
					                                .build()
 | 
				
			||||||
                                        )
 | 
					                ));
 | 
				
			||||||
                                        .build()
 | 
					                builder.append(Component.newline());
 | 
				
			||||||
                        ));
 | 
					                builder.append(TranslatableCaption.of("help.help_footer").toComponent(player));
 | 
				
			||||||
                builder.append(Component.newline()).append(MINI_MESSAGE.deserialize(TranslatableCaption
 | 
					 | 
				
			||||||
                        .of("help.help_footer")
 | 
					 | 
				
			||||||
                        .getComponent(player)));
 | 
					 | 
				
			||||||
                player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.asComponent())));
 | 
					                player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.asComponent())));
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 =
 | 
					        public static boolean GLOBAL = false;
 | 
				
			||||||
                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,6 +737,7 @@ 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",
 | 
				
			||||||
@@ -830,4 +831,17 @@ 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ package com.plotsquared.core.configuration.caption;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import net.kyori.adventure.text.ComponentLike;
 | 
					import net.kyori.adventure.text.ComponentLike;
 | 
				
			||||||
 | 
					import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
 | 
				
			||||||
import org.checkerframework.checker.nullness.qual.NonNull;
 | 
					import org.checkerframework.checker.nullness.qual.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -44,6 +45,16 @@ public interface Caption {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @NonNull Component toComponent(@NonNull LocaleHolder localeHolder);
 | 
					    @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();
 | 
					    @NonNull String toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ package com.plotsquared.core.configuration.caption;
 | 
				
			|||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
 | 
					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.checkerframework.checker.nullness.qual.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public final class StaticCaption implements Caption {
 | 
					public final class StaticCaption implements Caption {
 | 
				
			||||||
@@ -51,6 +52,14 @@ public final class StaticCaption implements Caption {
 | 
				
			|||||||
        return MiniMessage.miniMessage().deserialize(this.value);
 | 
					        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
 | 
					    @Override
 | 
				
			||||||
    public @NonNull String toString() {
 | 
					    public @NonNull String toString() {
 | 
				
			||||||
        return "StaticCaption(" + value + ")";
 | 
					        return "StaticCaption(" + value + ")";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
 | 
				
			|||||||
import org.checkerframework.checker.nullness.qual.NonNull;
 | 
					import org.checkerframework.checker.nullness.qual.NonNull;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
import java.util.regex.Pattern;
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,13 +97,23 @@ public final class TranslatableCaption implements NamespacedCaption {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public @NonNull Component toComponent(@NonNull final LocaleHolder localeHolder) {
 | 
					    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")) {
 | 
					        if (getKey().equals("core.prefix")) {
 | 
				
			||||||
            return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
 | 
					            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",
 | 
					                "prefix",
 | 
				
			||||||
                Tag.inserting(TranslatableCaption.of("core.prefix").toComponent(localeHolder))
 | 
					                Tag.inserting(TranslatableCaption.of("core.prefix").toComponent(localeHolder))
 | 
				
			||||||
        ));
 | 
					        );
 | 
				
			||||||
 | 
					        return MiniMessage.miniMessage().deserialize(getComponent(localeHolder), finalResolvers);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,30 +25,24 @@ import com.plotsquared.core.player.PlotPlayer;
 | 
				
			|||||||
import com.plotsquared.core.util.StringMan;
 | 
					import com.plotsquared.core.util.StringMan;
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import net.kyori.adventure.text.ComponentLike;
 | 
					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.Tag;
 | 
				
			||||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
 | 
					import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class HelpObject implements ComponentLike {
 | 
					public class HelpObject implements ComponentLike {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Component rendered;
 | 
					    private final Component rendered;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public HelpObject(final Command command, final String label, final PlotPlayer<?> audience) {
 | 
					    public HelpObject(final Command command, final String label, final PlotPlayer<?> audience) {
 | 
				
			||||||
        rendered = MINI_MESSAGE.deserialize(
 | 
					        this.rendered = TranslatableCaption.of("help.help_item").toComponent(audience, TagResolver.builder()
 | 
				
			||||||
                TranslatableCaption.of("help.help_item").getComponent(audience),
 | 
					                .tag("usage", Tag.inserting(Component.text(command.getUsage().replace("{label}", label))))
 | 
				
			||||||
                TagResolver.builder()
 | 
					                .tag("alias", Tag.inserting(Component.text(
 | 
				
			||||||
                        .tag("usage", Tag.inserting(Component.text(command.getUsage().replace("{label}", label))))
 | 
					                        command.getAliases().isEmpty() ? "" : StringMan.join(command.getAliases(), " | ")
 | 
				
			||||||
                        .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("desc", Tag.inserting(command.getDescription().toComponent(audience)))
 | 
					                .tag("label", Tag.inserting(Component.text(label)))
 | 
				
			||||||
                        .tag("arguments", Tag.inserting(Component.text(buildArgumentList(command.getRequiredArguments()))))
 | 
					                .build());
 | 
				
			||||||
                        .tag("label", Tag.inserting(Component.text(label)))
 | 
					 | 
				
			||||||
                        .build()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String buildArgumentList(final Argument<?>[] arguments) {
 | 
					    private String buildArgumentList(final Argument<?>[] arguments) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,8 +69,8 @@ subprojects {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    dependencies {
 | 
					    dependencies {
 | 
				
			||||||
        // Tests
 | 
					        // Tests
 | 
				
			||||||
        testImplementation("org.junit.jupiter:junit-jupiter:5.12.2")
 | 
					        testImplementation("org.junit.jupiter:junit-jupiter:5.13.0")
 | 
				
			||||||
        testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.12.2")
 | 
					        testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.0")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    plugins.withId("java") {
 | 
					    plugins.withId("java") {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,8 +15,8 @@ log4j = "2.19.0"
 | 
				
			|||||||
worldedit = "7.2.20"
 | 
					worldedit = "7.2.20"
 | 
				
			||||||
fawe = "2.13.0"
 | 
					fawe = "2.13.0"
 | 
				
			||||||
placeholderapi = "2.11.6"
 | 
					placeholderapi = "2.11.6"
 | 
				
			||||||
luckperms = "5.4"
 | 
					luckperms = "5.5"
 | 
				
			||||||
essentialsx = "2.21.0"
 | 
					essentialsx = "2.21.1"
 | 
				
			||||||
mvdwapi = "3.1.1"
 | 
					mvdwapi = "3.1.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Third party
 | 
					# Third party
 | 
				
			||||||
@@ -35,7 +35,7 @@ serverlib = "2.3.7"
 | 
				
			|||||||
# Gradle plugins
 | 
					# Gradle plugins
 | 
				
			||||||
shadow = "8.3.6"
 | 
					shadow = "8.3.6"
 | 
				
			||||||
grgit = "4.1.1"
 | 
					grgit = "4.1.1"
 | 
				
			||||||
spotless = "7.0.3"
 | 
					spotless = "7.0.4"
 | 
				
			||||||
publish = "0.32.0"
 | 
					publish = "0.32.0"
 | 
				
			||||||
runPaper = "2.3.1"
 | 
					runPaper = "2.3.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -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-bin.zip
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
 | 
				
			||||||
networkTimeout=10000
 | 
					networkTimeout=10000
 | 
				
			||||||
validateDistributionUrl=true
 | 
					validateDistributionUrl=true
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user