Skip to content
Merged

5.2 #3291

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d1660d7
Add bypass permission for join location
benwoo1110 Jul 7, 2025
d92fe17
Merge branch 'main' into feat/joinlocation-bypass
benwoo1110 Jul 15, 2025
35b1297
Merge pull request #3285 from Multiverse/feat/joinlocation-bypass
benwoo1110 Jul 15, 2025
7c6b1d0
Add causeBy chaining for Attempt
benwoo1110 Jul 18, 2025
d3cc950
Merge pull request #3296 from Multiverse/feat/attempt-causeby
benwoo1110 Jul 18, 2025
cc60b9c
Implement StringMatcher api
benwoo1110 Jul 18, 2025
3e9425c
Merge pull request #3297 from Multiverse/feat/matcher
benwoo1110 Jul 18, 2025
021d86e
Add an environment check when loading a world that already exist in b…
benwoo1110 Jul 19, 2025
878c4be
Add option to skip folder check on import
benwoo1110 Jul 19, 2025
9137ef0
Merge pull request #3300 from Multiverse/feat/load-env-check
benwoo1110 Jul 19, 2025
6d39372
Merge pull request #3299 from Multiverse/feat/skip-folder-check
benwoo1110 Jul 19, 2025
eea2af1
Improve support for importing worlds already loaded on the server
benwoo1110 Jul 19, 2025
04f0765
Merge pull request #3301 from Multiverse/feat/import-bukkit
benwoo1110 Jul 20, 2025
fa5aa41
Add options to remove a world without unloading or saving the bukkit …
benwoo1110 Jul 20, 2025
6122935
Mark initAllWorlds as internal api
benwoo1110 Jul 20, 2025
654da03
Remove unnecessary casting
benwoo1110 Jul 20, 2025
5118864
Use our ChatTextFormatter to remove colour from string
benwoo1110 Jul 20, 2025
5177f29
Merge pull request #3304 from Multiverse/refactor/wm-cleanup
benwoo1110 Jul 20, 2025
8bc3a59
Merge pull request #3303 from Multiverse/feat/remove-options
benwoo1110 Jul 20, 2025
4553967
Fix ChatTextFormatter handling null strings
benwoo1110 Jul 21, 2025
02081b4
Merge pull request #3305 from Multiverse/fix/text-format-null
benwoo1110 Jul 26, 2025
a50bd80
Deprecate other removeWorld methods in favour of using RemoveWorldOpt…
benwoo1110 Jul 26, 2025
907a6dd
Fix spawn command using deprecated teleport method
benwoo1110 Jul 26, 2025
09314e8
Use newer save method with chunk flushing when possible
benwoo1110 Jul 26, 2025
57796d2
Bump dependencies
benwoo1110 Jul 26, 2025
cbb3a7a
Merge pull request #3310 from Multiverse/fix/spawn-command
benwoo1110 Jul 27, 2025
2da6ece
Merge pull request #3312 from Multiverse/chore/bump-deps
benwoo1110 Jul 27, 2025
87dd416
Merge pull request #3313 from Multiverse/fix/save-flush
benwoo1110 Jul 27, 2025
f9d783b
Merge pull request #3311 from Multiverse/refactor/remove-deprecate
benwoo1110 Jul 27, 2025
eb8ef32
Fix debug-permission comment spacing
benwoo1110 Jul 27, 2025
e89b4f1
Merge pull request #3314 from Multiverse/fix/debug-permissions
benwoo1110 Jul 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repositories {
configure(apiDependencies) {
serverApiVersion = '1.19.4-R0.1-SNAPSHOT'
mockBukkitServerApiVersion = '1.21'
mockBukkitVersion = '4.31.1'
mockBukkitVersion = '4.72.2'
}

dependencies {
Expand All @@ -48,9 +48,9 @@ dependencies {
}

// Utils
shadowed 'io.vavr:vavr:0.10.4'
shadowed 'org.glassfish.hk2:hk2-locator:3.0.3'
shadowed('org.glassfish.hk2:hk2-inhabitant-generator:3.0.3') {
shadowed 'io.vavr:vavr:0.10.7'
shadowed 'org.glassfish.hk2:hk2-locator:3.1.1'
shadowed('org.glassfish.hk2:hk2-inhabitant-generator:3.1.1') {
exclude group: 'org.apache.maven', module: 'maven-core'
}
shadowed('com.dumptruckman.minecraft:Logging:1.1.1') {
Expand All @@ -60,8 +60,8 @@ dependencies {
shadowed('org.bstats:bstats-bukkit:3.1.0') {
exclude group: 'org.bukkit', module: 'bukkit'
}
shadowed 'net.minidev:json-smart:2.4.9'
shadowed 'org.jetbrains:annotations:22.0.0'
shadowed 'net.minidev:json-smart:2.5.2'
shadowed 'org.jetbrains:annotations:26.0.2'
shadowed 'io.papermc:paperlib:1.0.8'

// Tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ void onImportCommand(
.biome(parsedFlags.flagValue(flags.biome, ""))
.environment(environment)
.generator(parsedFlags.flagValue(flags.generator, String.class))
.useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn)))
.useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn))
.doFolderCheck(!parsedFlags.hasFlag(flags.skipFolderCheck)))
.onSuccess(newWorld -> {
Logging.fine("World import success: " + newWorld);
issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS, Replace.WORLD.with(newWorld.getName()));
Expand Down Expand Up @@ -105,6 +106,10 @@ private Flags(@NotNull CommandFlagsManager flagsManager, @NotNull GeneratorProvi
private final CommandValueFlag<String> biome = flag(CommandValueFlag.builder("--biome", String.class)
.addAlias("-b")
.build());

private final CommandFlag skipFolderCheck = flag(CommandFlag.builder("--skip-folder-check")
.addAlias("-f")
.build());
}

@Service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
Expand All @@ -23,19 +25,20 @@
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
import org.mvplugins.multiverse.core.world.options.RemoveWorldOptions;

@Service
class RemoveCommand extends CoreCommand {

private final WorldManager worldManager;
private final PlayerWorldTeleporter playerWorldTeleporter;
private final RemovePlayerFlags flags;
private final Flags flags;

@Inject
RemoveCommand(
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull RemovePlayerFlags flags
@NotNull Flags flags
) {
this.worldManager = worldManager;
this.playerWorldTeleporter = playerWorldTeleporter;
Expand All @@ -44,7 +47,7 @@ class RemoveCommand extends CoreCommand {

@Subcommand("remove")
@CommandPermission("multiverse.core.remove")
@CommandCompletion("@mvworlds:scope=both @flags:groupName=" + RemovePlayerFlags.NAME)
@CommandCompletion("@mvworlds:scope=both @flags:groupName=" + Flags.NAME)
@Syntax("<world>")
@Description("{@@mv-core.remove.description}")
void onRemoveCommand(
Expand All @@ -64,12 +67,14 @@ void onRemoveCommand(
? worldManager.getLoadedWorld(world).map(playerWorldTeleporter::removeFromWorld).getOrElse(AsyncAttemptsAggregate::emptySuccess)
: AsyncAttemptsAggregate.emptySuccess();

future.onSuccess(() -> doWorldRemoving(issuer, world))
future.onSuccess(() -> doWorldRemoving(issuer, world, parsedFlags))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
}

private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world) {
worldManager.removeWorld(world)
private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world, ParsedCommandFlags parsedFlags) {
worldManager.removeWorld(RemoveWorldOptions.world(world)
.saveBukkitWorld(!parsedFlags.hasFlag(flags.noSave))
.unloadBukkitWorld(!parsedFlags.hasFlag(flags.noUnloadBukkitWorld)))
.onSuccess(removedWorldName -> {
Logging.fine("World remove success: " + removedWorldName);
issuer.sendInfo(MVCorei18n.REMOVE_SUCCESS, Replace.WORLD.with(removedWorldName));
Expand All @@ -79,13 +84,32 @@ private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world) {
});
}

@Service
private static final class Flags extends RemovePlayerFlags {

private static final String NAME = "mvremove";

@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}

private final CommandFlag noUnloadBukkitWorld = flag(CommandFlag.builder("--no-unload-bukkit-world")
.addAlias("-b")
.build());

private final CommandFlag noSave = flag(CommandFlag.builder("--no-save")
.addAlias("-n")
.build());
}

@Service
private static final class LegacyAlias extends RemoveCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
RemovePlayerFlags flags
@NotNull Flags flags
) {
super(worldManager, playerWorldTeleporter, flags);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private void handleSingleTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld
safetyTeleporter.to(mvWorld.getSpawnLocation())
.by(issuer)
.checkSafety(checkSafety)
.teleport(entity)
.teleportSingle(entity)
.onSuccess(() -> issuer.sendInfo(MVCorei18n.SPAWN_SUCCESS,
Replace.PLAYER.with(entity.equals(issuer.getPlayer())
? Message.of(MVCorei18n.GENERIC_YOU)
Expand All @@ -119,7 +119,7 @@ private void handleSingleTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld
? Message.of(MVCorei18n.GENERIC_YOU)
: Message.of(entity.getName())),
Replace.WORLD.with(mvWorld.getName()),
Replace.REASON.with(failure.getFailureMessage())));
Replace.REASON.with(failure.getFirst().getFailureMessage())));
}

private void handleMultiTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld mvWorld,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ private <N extends Node> N node(N node) {
.build());

final ConfigNode<Boolean> debugPermissions = node(ConfigNode.builder("misc.debug-permissions", Boolean.class)
.comment("")
.comment("Sets whether console will log every permission check done by all multiverse plugins.")
.comment("This will only work if the above 'global-debug' is set to 1 or more.")
.defaultValue(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ public boolean checkMatch(String value) {
if (!hasValidRegex()) {
return false;
}
String text = ChatTextFormatter.removeColor(String.valueOf(value)).toLowerCase();
String text = ChatTextFormatter.removeColor(String.valueOf(value));
if (text == null) {
return false;
}
try {
return regexPattern.matcher(text).find();
return regexPattern.matcher(text.toLowerCase()).find();
} catch (PatternSyntaxException ignored) {
Logging.warning("Error parsing regex '%s' for input '%s'", regexString, text);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ void asyncPlayerChat(AsyncPlayerChatEvent event) {
String prefixChatFormat = config.getPrefixChatFormat();
prefixChatFormat = prefixChatFormat.replace("%world%", worldName).replace("%chat%", chat);
prefixChatFormat = ChatTextFormatter.colorize(prefixChatFormat);

event.setFormat(prefixChatFormat);
if (prefixChatFormat != null) {
event.setFormat(prefixChatFormat);
}
}

private String getWorldName(Player player) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.event.MVRespawnEvent;
import org.mvplugins.multiverse.core.locale.PluginLocales;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.TeleportQueue;
import org.mvplugins.multiverse.core.utils.result.ResultChain;
Expand Down Expand Up @@ -70,6 +71,7 @@ final class MVPlayerListener implements CoreListener {
private final DestinationsProvider destinationsProvider;
private final EnforcementHandler enforcementHandler;
private final DimensionFinder dimensionFinder;
private final CorePermissionsChecker corePermissionsChecker;

private final Map<String, String> playerWorld = new ConcurrentHashMap<>();

Expand All @@ -86,7 +88,8 @@ final class MVPlayerListener implements CoreListener {
Provider<MVCommandManager> commandManagerProvider,
DestinationsProvider destinationsProvider,
EnforcementHandler enforcementHandler,
DimensionFinder dimensionFinder) {
DimensionFinder dimensionFinder,
CorePermissionsChecker corePermissionsChecker) {
this.plugin = plugin;
this.config = config;
this.worldManagerProvider = worldManagerProvider;
Expand All @@ -99,6 +102,7 @@ final class MVPlayerListener implements CoreListener {
this.destinationsProvider = destinationsProvider;
this.enforcementHandler = enforcementHandler;
this.dimensionFinder = dimensionFinder;
this.corePermissionsChecker = corePermissionsChecker;
}

private WorldManager getWorldManager() {
Expand Down Expand Up @@ -244,6 +248,10 @@ private void handleJoinLocation(PlayerSpawnLocationEvent event) {
Logging.warning("Joindestination is enabled but no destination has been specified in config!");
return;
}
if (corePermissionsChecker.hasJoinLocationBypassPermission(event.getPlayer())) {
Logging.finer("Player %s has bypass permission for JoinDestination", event.getPlayer().getName());
return;
}
Logging.finer("JoinDestination is " + config.getJoinDestination());
destinationsProvider.parseDestination(config.getJoinDestination())
.map(destination -> destination.getLocation(event.getPlayer())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,13 @@ public enum MVCorei18n implements MessageKeyProvider {
IMPORTWORLD_WORLDEXISTUNLOADED,
IMPORTWORLD_WORLDEXISTLOADED,
IMPORTWORLD_WORLDFOLDERINVALID,
IMPORTWORLD_BUKKITENVIRONMENTMISMATCH,

LOADWORLD_WORLDALREADYLOADING,
LOADWORLD_WORLDNONEXISTENT,
LOADWORLD_WORLDEXISTFOLDER,
LOADWORLD_WORLDEXISTLOADED,
LOADWORLD_BUKKITENVIRONMENTMISMATCH,

REMOVEWORLD_WORLDNONEXISTENT,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
Expand All @@ -15,6 +16,11 @@

@Service
public final class CorePermissions {
/**
* Permission to bypass the join location.
*/
static final String JOINLOCATION_BYPASS = "mv.bypass.joinlocation";

/**
* Permission to access a world.
*/
Expand Down Expand Up @@ -52,6 +58,12 @@ public final class CorePermissions {
this.pluginManager = pluginManager;
}

@PostConstruct
void registerBasePermissions() {
pluginManager.addPermission(new Permission(JOINLOCATION_BYPASS, PermissionDefault.FALSE));
Logging.fine("Successfully registered base permissions");
}

public Try<Void> addWorldPermissions(@NotNull MultiverseWorld world) {
return Try.run(() -> {
pluginManager.addPermission(new Permission(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.inject.Inject;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
Expand Down Expand Up @@ -50,6 +51,19 @@ public final class CorePermissionsChecker {
this.worldManager = worldManager;
}

/**
* Checks if the sender has permission to bypass the join location restriction.
*
* @param sender The command sender.
* @return True if the sender has bypass permission, false otherwise.
*
* @since 5.2
*/
@ApiStatus.AvailableSince("5.2")
public boolean hasJoinLocationBypassPermission(@NotNull CommandSender sender) {
return hasPermission(sender, CorePermissions.JOINLOCATION_BYPASS);
}

/**
* Checks if the sender has permission to access the specified world.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.mvplugins.multiverse.core.utils.matcher;

import org.jetbrains.annotations.ApiStatus;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
* ExactStringMatcher is a StringMatcher that matches strings against a set of exact values.
* It can be initialized with a single string, a collection of strings, or can have exact matches added later.
*
* @since 5.2
*/
@ApiStatus.AvailableSince("5.2")
public class ExactStringMatcher implements StringMatcher {
private final Set<String> exactMatches;

/**
* Creates a new ExactStringMatcher with no initial matches. Use {@link #addExactMatch(String)} to add matches later.
*
* @since 5.2
*/
@ApiStatus.AvailableSince("5.2")
public ExactStringMatcher() {
this.exactMatches = new HashSet<>();
}

/**
* Creates a new ExactStringMatcher with a single exact match.
*
* @param exactMatch the exact string to match against
*
* @since 5.2
*/
@ApiStatus.AvailableSince("5.2")
public ExactStringMatcher(String exactMatch) {
this.exactMatches = new HashSet<>();
this.exactMatches.add(exactMatch);
}

/**
* Creates a new ExactStringMatcher with multiple exact matches.
*
* @param exactMatches the collection of exact strings to match against
*
* @since 5.2
*/
@ApiStatus.AvailableSince("5.2")
public ExactStringMatcher(Collection<String> exactMatches) {
this.exactMatches = new HashSet<>(exactMatches);
}

/**
* Adds an exact match string to this matcher.
*
* @param value the exact string to add to the matcher
*
* @since 5.2
*/
@ApiStatus.AvailableSince("5.2")
public void addExactMatch(String value) {
this.exactMatches.add(value);
}

/**
* {@inheritDoc}
*/
@Override
public boolean matches(String value) {
return exactMatches.contains(value);
}
}
Loading
Loading