diff --git a/COMPILING.md b/COMPILING.md
index d43218dbb..cd34b7de7 100644
--- a/COMPILING.md
+++ b/COMPILING.md
@@ -1,11 +1,11 @@
Compiling
=========
-You can compile WorldGuard as long as you have some version of Java greater than or equal to 16 installed. Gradle will download JDK 16 specifically if needed,
+You can compile WorldGuard as long as you have some version of Java greater than or equal to 17 installed. Gradle will download JDK 17 specifically if needed,
but it needs some version of Java to bootstrap from.
-Note that if you have JRE 16 installed, Gradle will currently attempt to use that to compile, which will not work. It is easiest to uninstall JRE 16 and
-replace it with JDK 16.
+Note that if you have JRE 17 installed, Gradle will currently attempt to use that to compile, which will not work. It is easiest to uninstall JRE 16 and
+replace it with JDK 17.
The build process uses Gradle, which you do *not* need to download. WorldGuard is a multi-module project with three modules:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5a77c98e3..ef1038d8c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,7 @@ ask that you make note of the following guidelines.
* **Follow the [Oracle coding conventions](https://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html).**
We can't stress this enough; if your code has notable issues, it may delay
the process significantly.
-* **Target Java 16 for source and compilation.**
+* **Target Java 17 for source and compilation.**
* **Use only spaces for indentation.** Our indents are 4-spaces long, and tabs
are unacceptable.
* **Wrap code to a 89 column limit.** We do this to make side by side diffs
diff --git a/README.md b/README.md
index 9fca3721f..0f15eee92 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ A Bukkit server implementation (such as [Paper](https://papermc.io)) and the [Wo
Compiling
---------
-The project is written for Java 16 and our build process makes use of
+The project is written for Java 17 and our build process makes use of
[Gradle](http://gradle.org).
Dependencies are automatically handled by Gradle.
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 2c086d6c0..9d3289bd4 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -7,4 +7,5 @@ object Versions {
const val SQUIRRELID = "0.3.0"
const val GUAVA = "31.0.1-jre"
const val FINDBUGS = "3.0.2"
+ const val ADVENTURE = "4.10.1"
}
diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml
index 7da27610d..df1f49e18 100644
--- a/config/checkstyle/import-control.xml
+++ b/config/checkstyle/import-control.xml
@@ -17,6 +17,7 @@
+
diff --git a/worldguard-bukkit/build.gradle.kts b/worldguard-bukkit/build.gradle.kts
index 3773dd7eb..413ab75b3 100644
--- a/worldguard-bukkit/build.gradle.kts
+++ b/worldguard-bukkit/build.gradle.kts
@@ -28,8 +28,8 @@ configurations {
dependencies {
"api"(project(":worldguard-core"))
- "compileOnly"("io.papermc.paper:paper-api:1.17.1-R0.1-SNAPSHOT")
- "runtimeOnly"("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT") {
+ "compileOnly"("io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT")
+ "runtimeOnly"("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT") {
exclude("junit", "junit")
}
"api"("com.sk89q.worldedit:worldedit-bukkit:${Versions.WORLDEDIT}") { isTransitive = false }
diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java
index 5ed933c5c..2c56ecbf8 100644
--- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java
+++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java
@@ -25,12 +25,15 @@
import com.sk89q.worldedit.world.weather.WeatherTypes;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
-import com.sk89q.worldguard.util.MessagingUtil;
import io.papermc.lib.PaperLib;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.title.Title;
import org.bukkit.BanList.Type;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
+import java.util.concurrent.CompletableFuture;
+
public class BukkitPlayer extends com.sk89q.worldedit.bukkit.BukkitPlayer implements LocalPlayer {
protected final WorldGuardPlugin plugin;
@@ -173,12 +176,13 @@ public void setCompassTarget(Location location) {
}
@Override
- public void sendTitle(String title, String subtitle) {
- if (WorldGuard.getInstance().getPlatform().getGlobalStateManager().get(getWorld()).forceDefaultTitleTimes) {
- getPlayer().sendTitle(title, subtitle, 10, 70, 20);
- } else {
- getPlayer().sendTitle(title, subtitle, -1, -1, -1);
- }
+ public void sendMessage(Component message) {
+ getPlayer().sendMessage(message);
+ }
+
+ @Override
+ public void showTitle(Title title) {
+ getPlayer().showTitle(title);
}
@Override
@@ -187,19 +191,8 @@ public void resetFallDistance() {
}
@Override
- public void teleport(Location location, String successMessage, String failMessage) {
- PaperLib.teleportAsync(getPlayer(), BukkitAdapter.adapt(location))
- .thenApply(success -> {
- if (success) {
- // The success message can be cleared via flag
- if (!successMessage.isEmpty()) {
- MessagingUtil.sendStringToChat(this, successMessage);
- }
- } else {
- printError(failMessage);
- }
- return success;
- });
+ public CompletableFuture teleport(Location location) {
+ return PaperLib.teleportAsync(getPlayer(), BukkitAdapter.adapt(location));
}
@Override
diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitStringMatcher.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitStringMatcher.java
index 86445fdba..189674b47 100644
--- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitStringMatcher.java
+++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitStringMatcher.java
@@ -29,12 +29,16 @@
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.internal.platform.StringMatcher;
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
+import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
public class BukkitStringMatcher implements StringMatcher {
@@ -226,21 +230,13 @@ public World getWorldByName(String worldName) {
}
@Override
- public String replaceMacros(Actor sender, String message) {
- Collection extends Player> online = Bukkit.getServer().getOnlinePlayers();
-
- message = message.replace("%name%", sender.getName());
- message = message.replace("%id%", sender.getUniqueId().toString());
- message = message.replace("%online%", String.valueOf(online.size()));
-
- if (sender instanceof LocalPlayer) {
- LocalPlayer player = (LocalPlayer) sender;
- World world = (World) player.getExtent();
-
- message = message.replace("%world%", world.getName());
- message = message.replace("%health%", String.valueOf(player.getHealth()));
- }
-
- return message;
+ public TagResolver replacements(LocalPlayer sender) {
+ return TagResolver.builder()
+ .resolver(Placeholder.unparsed("name", sender.getName()))
+ .resolver(Placeholder.unparsed("id", sender.getUniqueId().toString()))
+ .resolver(Placeholder.unparsed("online", String.valueOf(Bukkit.getServer().getOnlinePlayers().size())))
+ .resolver(Placeholder.unparsed("world", sender.getWorld().getName()))
+ .resolver(Placeholder.unparsed("health", String.valueOf(sender.getHealth())))
+ .build();
}
}
diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java
index 50e4ba0a6..0efedb295 100644
--- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java
+++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java
@@ -62,6 +62,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java
index 785ad2eee..a2f06b207 100644
--- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java
+++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java
@@ -73,6 +73,7 @@
import com.sk89q.worldguard.protection.managers.storage.sql.SQLDriver;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.util.logging.RecordMessagePrefixer;
+import io.papermc.lib.PaperLib;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.DrilldownPie;
import org.bstats.charts.SimplePie;
@@ -140,6 +141,13 @@ public void onEnable() {
ClassSourceValidator verifier = new ClassSourceValidator(this);
verifier.reportMismatches(ImmutableList.of(WorldGuard.class, ProtectedRegion.class, Flag.class));
+ if (!PaperLib.isPaper()) {
+ // TODO: Tidy up this message
+ getLogger().severe("Unable to start WorldGuard. This version does only support Paper.");
+ getLogger().severe("Update your server or downgrade WorldGuard.");
+ throw new RuntimeException("No capable platform found.");
+ }
+
configureLogger();
getDataFolder().mkdirs(); // Need to create the plugins/WorldGuard folder
diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java
index ed7db2df0..be08e63a1 100644
--- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java
+++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java
@@ -39,13 +39,13 @@
import com.sk89q.worldguard.bukkit.util.Events;
import com.sk89q.worldguard.bukkit.util.InteropUtils;
import com.sk89q.worldguard.bukkit.util.Materials;
-import com.sk89q.worldguard.commands.CommandUtils;
import com.sk89q.worldguard.config.WorldConfiguration;
import com.sk89q.worldguard.protection.association.RegionAssociable;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.StateFlag.State;
import com.sk89q.worldguard.protection.regions.RegionQuery;
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -67,6 +67,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* Handle events that need to be processed by region protection.
@@ -101,8 +102,7 @@ private void tellErrorMessage(DelegateEvent event, Cause cause, Location locatio
Object rootCause = cause.getRootCause();
- if (rootCause instanceof Player) {
- Player player = (Player) rootCause;
+ if (rootCause instanceof Player player) {
long now = System.currentTimeMillis();
Long lastTime = WGMetadata.getIfPresent(player, DENY_MESSAGE_KEY, Long.class);
@@ -118,9 +118,7 @@ private void tellErrorMessage(DelegateEvent event, Cause cause, Location locatio
static void formatAndSendDenyMessage(String what, LocalPlayer localPlayer, String message) {
if (message == null || message.isEmpty()) return;
- message = WorldGuard.getInstance().getPlatform().getMatcher().replaceMacros(localPlayer, message);
- message = CommandUtils.replaceColorMacros(message);
- localPlayer.printRaw(message.replace("%what%", what));
+ localPlayer.sendMessage(WorldGuard.getInstance().getMiniMessage().deserialize(message, Placeholder.unparsed("what", what)));
}
/**
diff --git a/worldguard-core/build.gradle.kts b/worldguard-core/build.gradle.kts
index 33be66e5c..3703aeff2 100644
--- a/worldguard-core/build.gradle.kts
+++ b/worldguard-core/build.gradle.kts
@@ -11,6 +11,10 @@ dependencies {
"implementation"("org.yaml:snakeyaml:1.30")
"implementation"("com.google.guava:guava:${Versions.GUAVA}")
+ "api"("net.kyori:adventure-api:${Versions.ADVENTURE}")
+ "api"("net.kyori:adventure-text-minimessage:${Versions.ADVENTURE}")
+ "api"("net.kyori:adventure-text-serializer-legacy:${Versions.ADVENTURE}")
+
"compileOnly"("com.google.code.findbugs:jsr305:${Versions.FINDBUGS}")
"testImplementation"("org.hamcrest:hamcrest-library:1.2.1")
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/LocalPlayer.java b/worldguard-core/src/main/java/com/sk89q/worldguard/LocalPlayer.java
index 7395bebf6..ddf396695 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/LocalPlayer.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/LocalPlayer.java
@@ -19,15 +19,18 @@
package com.sk89q.worldguard;
-import com.google.common.annotations.Beta;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.weather.WeatherType;
import com.sk89q.worldguard.domains.Association;
import com.sk89q.worldguard.protection.association.RegionAssociable;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.title.Title;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
public interface LocalPlayer extends Player, RegionAssociable {
@@ -200,13 +203,18 @@ default Association getAssociation(List regions) {
void setCompassTarget(Location location);
/**
- * This should preferably take Components but there's no way to do that yet
+ * Send the message to the player
*
- * @param title the title to display
- * @param subtitle the subtitle to display
+ * @param message the message as component
*/
- @Beta
- void sendTitle(String title, String subtitle);
+ void sendMessage(Component message);
+
+ /**
+ * Send the message to the player
+ *
+ * @param title the title
+ */
+ void showTitle(Title title);
/**
* Clears fall distance.
@@ -214,10 +222,9 @@ default Association getAssociation(List regions) {
void resetFallDistance();
/**
- * Teleport the player, potentially async, displaying the message on a success.
- * @param location location to teleport to
- * @param successMessage message to display on success
- * @param failMessage message to display on failure
+ * Teleport the player, potentially async.
+ * @param location location to teleport to
+ * @return a future object whether the teleport was successful
*/
- void teleport(Location location, String successMessage, String failMessage);
+ CompletableFuture teleport(Location location);
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java b/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java
index 637eee7bd..2c989e7f3 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java
@@ -38,6 +38,7 @@
import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry;
import com.sk89q.worldguard.util.WorldGuardExceptionConverter;
import com.sk89q.worldguard.util.concurrent.EvenMoreExecutors;
+import net.kyori.adventure.text.minimessage.MiniMessage;
import java.io.File;
import java.io.IOException;
@@ -61,6 +62,8 @@ public final class WorldGuard {
private ListeningExecutorService executorService;
private WorldGuardExceptionConverter exceptionConverter = new WorldGuardExceptionConverter();
+ private MiniMessage miniMessage = MiniMessage.miniMessage();
+
static {
Flags.registerAll();
}
@@ -235,4 +238,7 @@ public static String getVersion() {
return version;
}
+ public MiniMessage getMiniMessage() {
+ return miniMessage;
+ }
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java
index 7ed7eac5b..57c7dd464 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java
@@ -65,6 +65,7 @@
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
+import com.sk89q.worldguard.protection.managers.migration.ComponentFlagMigration;
import com.sk89q.worldguard.protection.managers.migration.DriverMigration;
import com.sk89q.worldguard.protection.managers.migration.MigrationException;
import com.sk89q.worldguard.protection.managers.migration.UUIDMigration;
@@ -81,6 +82,8 @@
import com.sk89q.worldguard.session.Session;
import com.sk89q.worldguard.util.Enums;
import com.sk89q.worldguard.util.logging.LoggerToChatHandler;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import java.util.ArrayList;
import java.util.Collections;
@@ -1150,6 +1153,60 @@ public void migrateHeights(CommandContext args, Actor sender) throws CommandExce
}
}
+ /**
+ * Migrate components from region flags
+ *
+ * @param args the arguments
+ * @param sender the sender
+ * @throws CommandException any error
+ */
+ @Command(aliases = {"migratecomponents"},
+ usage = "[world]", max = 1,
+ flags = "yw:",
+ desc = "Migrate regions from old legacy format to minimessage format")
+ public void migrateComponents(CommandContext args, Actor sender) throws CommandException {
+ // Check permissions
+ if (!getPermissionModel(sender).mayMigrateRegionHeights()) {
+ throw new CommandPermissionsException();
+ }
+
+ if (!args.hasFlag('y')) {
+ throw new CommandException("This command is potentially dangerous.\n" +
+ "Please ensure you have made a backup of your data, and then re-enter the command with -y tacked on at the end to proceed.");
+ }
+
+ World world = null;
+ try {
+ world = checkWorld(args, sender, 'w');
+ } catch (CommandException ignored) {
+ }
+
+ LoggerToChatHandler handler = null;
+ Logger minecraftLogger = null;
+
+ if (sender instanceof LocalPlayer) {
+ handler = new LoggerToChatHandler(sender);
+ handler.setLevel(Level.ALL);
+ minecraftLogger = Logger.getLogger("com.sk89q.worldguard");
+ minecraftLogger.addHandler(handler);
+ }
+
+ try {
+ RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
+ RegionDriver driver = container.getDriver();
+ ComponentFlagMigration migration = new ComponentFlagMigration(driver, WorldGuard.getInstance().getFlagRegistry(), world);
+ container.migrate(migration);
+ sender.print("Migration complete!");
+ } catch (MigrationException e) {
+ log.log(Level.WARNING, "Failed to migrate", e);
+ throw new CommandException("Error encountered while migrating: " + e.getMessage());
+ } finally {
+ if (minecraftLogger != null) {
+ minecraftLogger.removeHandler(handler);
+ }
+ }
+ }
+
/**
* Teleport to a region
@@ -1218,9 +1275,14 @@ public void teleport(CommandContext args, Actor sender) throws CommandException
message = Flags.TELE_MESSAGE.getDefault();
}
- player.teleport(teleportLocation,
- message.replace("%id%", existing.getId()),
- "Unable to teleport to region '" + existing.getId() + "'.");
+ final String successMessage = message;
+ player.teleport(teleportLocation).thenAccept(success -> {
+ if (success && successMessage != null) {
+ player.sendMessage(worldGuard.getMiniMessage().deserialize(successMessage, Placeholder.unparsed("id", existing.getId())));
+ } else {
+ player.sendMessage(net.kyori.adventure.text.Component.text("Unable to teleport to region '" + existing.getId() + "'.", NamedTextColor.RED));
+ }
+ });
}
@Command(aliases = {"toggle-bypass", "bypass"},
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/internal/platform/StringMatcher.java b/worldguard-core/src/main/java/com/sk89q/worldguard/internal/platform/StringMatcher.java
index 28234896b..5ac5bf471 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/internal/platform/StringMatcher.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/internal/platform/StringMatcher.java
@@ -25,10 +25,12 @@
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.LocalPlayer;
+import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
public interface StringMatcher {
@@ -154,21 +156,20 @@ default Iterable matchPlayers(LocalPlayer player) {
@Nullable
World getWorldByName(String worldName);
+
/**
- * Replace macros in the text.
+ * Get the replacements for the current sender context
*
* The macros replaced are as follows:
- * %name%: The name of {@code sender}.
- * %id%: The unique name of the sender.
- * %online%: The number of players currently online on the server
- * If {@code sender} is a Player:
- * %world%: The name of the world {@code sender} is located in
- * %health%: The health of {@code sender}.
+ * name: The name of {@code sender}.
+ * id: The unique name of the sender.
+ * online: The number of players currently online on the server
+ * world: The name of the world {@code sender} is located in
+ * health: The health of {@code sender}.
*
* @param sender The sender to check
- * @param message The message to replace macros in
- * @return The message with macros replaced
+ * @return The set of tag resolvers for the sender
*/
- String replaceMacros(Actor sender, String message);
+ TagResolver replacements(LocalPlayer sender);
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flags.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flags.java
index 218637b53..b3a96d406 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flags.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flags.java
@@ -19,11 +19,6 @@
package com.sk89q.worldguard.protection.flags;
-import com.google.common.collect.Sets;
-import com.sk89q.worldedit.util.formatting.text.TextComponent;
-import com.sk89q.worldedit.util.formatting.text.format.TextColor;
-import com.sk89q.worldedit.util.formatting.text.format.TextDecoration;
-import com.sk89q.worldedit.util.formatting.text.serializer.legacy.LegacyComponentSerializer;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.world.weather.WeatherType;
@@ -140,14 +135,8 @@ public final class Flags {
public static final LocationFlag TELE_LOC = register(new LocationFlag("teleport"));
public static final LocationFlag SPAWN_LOC = register(new LocationFlag("spawn", RegionGroup.MEMBERS));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
public static final StringFlag TELE_MESSAGE = register(new StringFlag("teleport-message",
- LegacyComponentSerializer.INSTANCE.serialize(TextComponent.of("").append(TextComponent.of(
- "Teleported you to the region '%id%'.", TextColor.LIGHT_PURPLE)))));
+ "Teleported you to the region ''."));
// idk?
public static final StateFlag INVINCIBILITY = register(new StateFlag("invincible", false));
@@ -164,29 +153,9 @@ public final class Flags {
public static final StateFlag ENDERPEARL = register(new StateFlag("enderpearl", true));
public static final StateFlag CHORUS_TELEPORT = register(new StateFlag("chorus-fruit-teleport", true));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
public static final StringFlag GREET_MESSAGE = register(new StringFlag("greeting"));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
public static final StringFlag FAREWELL_MESSAGE = register(new StringFlag("farewell"));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
public static final StringFlag GREET_TITLE = register(new StringFlag("greeting-title"));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
public static final StringFlag FAREWELL_TITLE = register(new StringFlag("farewell-title"));
public static final BooleanFlag NOTIFY_ENTER = register(new BooleanFlag("notify-enter"));
@@ -210,33 +179,14 @@ public final class Flags {
public static final IntegerFlag MAX_FOOD = register(new IntegerFlag("feed-max-hunger"), f -> f.setSuggestedValues(VITALS_MAXS));
// deny messages
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
public static final StringFlag DENY_MESSAGE = register(new StringFlag("deny-message",
- LegacyComponentSerializer.INSTANCE.serialize(TextComponent.of("").append(TextComponent.of("Hey!",
- TextColor.RED, Sets.newHashSet(TextDecoration.BOLD)))
- .append(TextComponent.of(" Sorry, but you can't %what% here.", TextColor.GRAY)))));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
+ "Hey! Sorry, but you can't here."));
+
public static final StringFlag ENTRY_DENY_MESSAGE = register(new StringFlag("entry-deny-message",
- LegacyComponentSerializer.INSTANCE.serialize(TextComponent.of("").append(TextComponent.of("Hey!",
- TextColor.RED, Sets.newHashSet(TextDecoration.BOLD)))
- .append(TextComponent.of(" You are not permitted to enter this area.", TextColor.GRAY)))));
- /**
- * @deprecated The type of this flag will change from a StringFlag to a ComponentFlag to support JSON text
- * in a future release. If you depend on the type of this flag, take proper precaution for future breakage.
- */
- @Deprecated
+ "Hey! You are not permitted to enter this area."));
+
public static final StringFlag EXIT_DENY_MESSAGE = register(new StringFlag("exit-deny-message",
- LegacyComponentSerializer.INSTANCE.serialize(TextComponent.of("").append(TextComponent.of("Hey!",
- TextColor.RED, Sets.newHashSet(TextDecoration.BOLD)))
- .append(TextComponent.of(" You are not permitted to leave this area.", TextColor.GRAY)))));
+ "Hey! You are not permitted to leave this area."));
private Flags() {
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java
index 080e2571a..c9b55f729 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java
@@ -19,8 +19,6 @@
package com.sk89q.worldguard.protection.flags;
-import com.sk89q.worldguard.commands.CommandUtils;
-
import javax.annotation.Nullable;
/**
@@ -58,10 +56,7 @@ public String getDefault() {
@Override
public String parseInput(FlagContext context) throws InvalidFlagFormat {
- String lines = context.getUserInput().replaceAll("(?
+ * Copyright (C) WorldGuard team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldguard.protection.managers.migration;
+
+import com.sk89q.worldedit.world.World;
+import com.sk89q.worldguard.WorldGuard;
+import com.sk89q.worldguard.protection.flags.Flags;
+import com.sk89q.worldguard.protection.flags.StringFlag;
+import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
+import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
+import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
+import com.sk89q.worldguard.protection.managers.storage.StorageException;
+import com.sk89q.worldguard.protection.regions.ProtectedRegion;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+
+import javax.annotation.Nullable;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ComponentFlagMigration extends AbstractMigration {
+ private static final Logger log = Logger.getLogger(ComponentFlagMigration.class.getCanonicalName());
+
+ private static final Set REPLACE_FLAGS = Set.of(
+ Flags.TELE_MESSAGE,
+ Flags.GREET_MESSAGE,
+ Flags.FAREWELL_MESSAGE,
+ Flags.GREET_TITLE,
+ Flags.FAREWELL_TITLE,
+ Flags.DENY_MESSAGE,
+ Flags.ENTRY_DENY_MESSAGE,
+ Flags.EXIT_DENY_MESSAGE
+ );
+
+ private static final Map REPLACE_TEXTS = Map.of(
+ "%what%", "",
+ "%name%", "",
+ "%id%", "",
+ "%online%", "",
+ "%world%", "",
+ "%health%", ""
+ );
+
+ private final FlagRegistry flagRegistry;
+ private final World world;
+ private int changed = 0;
+
+ public ComponentFlagMigration(RegionDriver driver, FlagRegistry flagRegistry, @Nullable World world) {
+ super(driver);
+ this.flagRegistry = flagRegistry;
+ this.world = world;
+ }
+
+ @Override
+ protected void migrate(RegionDatabase store) throws MigrationException {
+ if (world != null && !store.getName().equals(world.getName())) return;
+
+ log.log(Level.INFO, "Migrating regions in '" + store.getName() + "' to new component formats...");
+
+ Set regions;
+ try {
+ regions = store.loadAll(flagRegistry);
+ } catch (StorageException e) {
+ throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e);
+ }
+ for (ProtectedRegion region : regions) {
+ for (StringFlag replaceFlag : REPLACE_FLAGS) {
+ String val = region.getFlag(replaceFlag);
+ if (val != null) {
+ String migration = migrateToMinimessage(val);
+ if (!val.equals(migration)) {
+ region.setFlag(replaceFlag, migration);
+ changed++;
+ }
+ }
+ }
+ }
+ try {
+ store.saveAll(regions);
+ } catch (StorageException e) {
+ throw new MigrationException("Failed to save region data after migration of the world '" + store.getName() + "'", e);
+ }
+ }
+
+ private String migrateToMinimessage(String string) {
+ // first strip all legacy color formats from the string
+ if (string.indexOf('ยง') != -1) {
+ TextComponent deserialize = LegacyComponentSerializer.legacySection().deserialize(string);
+ string = WorldGuard.getInstance().getMiniMessage().serialize(deserialize);
+ }
+
+ // then replace all legacy placeholder with tags
+ for (Map.Entry replacement : REPLACE_TEXTS.entrySet()) {
+ string = string.replace(replacement.getKey(), replacement.getValue());
+ }
+ return string;
+ }
+
+ @Override
+ protected void postMigration() {
+ log.log(Level.INFO, "A total of " + changed + " flags were updated to a new component.");
+ }
+}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/EntryFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/EntryFlag.java
index 7cedea589..19a3fb0f7 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/EntryFlag.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/EntryFlag.java
@@ -22,12 +22,12 @@
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.LocalPlayer;
-import com.sk89q.worldguard.commands.CommandUtils;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.session.MoveType;
import com.sk89q.worldguard.session.Session;
+import com.sk89q.worldguard.util.MessagingUtil;
import java.util.Set;
@@ -57,7 +57,7 @@ public boolean onCrossBoundary(LocalPlayer player, Location from, Location to, A
long now = System.currentTimeMillis();
if ((now - lastMessage) > MESSAGE_THRESHOLD && message != null && !message.isEmpty()) {
- player.printRaw(CommandUtils.replaceColorMacros(message));
+ MessagingUtil.sendStringToChat(player, message);
lastMessage = now;
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/ExitFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/ExitFlag.java
index dcae5390a..a8879bd87 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/ExitFlag.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/ExitFlag.java
@@ -22,13 +22,13 @@
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.LocalPlayer;
-import com.sk89q.worldguard.commands.CommandUtils;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.StateFlag.State;
import com.sk89q.worldguard.session.MoveType;
import com.sk89q.worldguard.session.Session;
+import com.sk89q.worldguard.util.MessagingUtil;
public class ExitFlag extends FlagValueChangeHandler {
@@ -60,7 +60,7 @@ private void sendMessage(LocalPlayer player) {
long now = System.currentTimeMillis();
if ((now - lastMessage) > MESSAGE_THRESHOLD && storedMessage != null && !storedMessage.isEmpty()) {
- player.printRaw(CommandUtils.replaceColorMacros(storedMessage));
+ MessagingUtil.sendStringToChat(player, storedMessage);
lastMessage = now;
}
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/FarewellFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/FarewellFlag.java
index e47c48b41..87a177cc2 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/FarewellFlag.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/FarewellFlag.java
@@ -66,7 +66,7 @@ public boolean onCrossBoundary(LocalPlayer player, Location from, Location to, A
Set entered, Set exited, MoveType moveType) {
lastMessageStack = collectAndSend(player, toSet, Flags.FAREWELL_MESSAGE, lastMessageStack, MessagingUtil::sendStringToChat);
- lastTitleStack = collectAndSend(player, toSet, Flags.FAREWELL_TITLE, lastTitleStack, MessagingUtil::sendStringToTitle);
+ lastTitleStack = collectAndSend(player, toSet, Flags.FAREWELL_TITLE, lastTitleStack, MessagingUtil::formatTitleFromString);
return true;
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/GreetingFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/GreetingFlag.java
index fd8b946b2..c9cd6e609 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/GreetingFlag.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/session/handler/GreetingFlag.java
@@ -60,7 +60,7 @@ private Set getMessages(LocalPlayer player, ApplicableRegionSet set, Fla
public boolean onCrossBoundary(LocalPlayer player, Location from, Location to, ApplicableRegionSet toSet,
Set entered, Set exited, MoveType moveType) {
lastMessageStack = sendAndCollect(player, toSet, Flags.GREET_MESSAGE, lastMessageStack, MessagingUtil::sendStringToChat);
- lastTitleStack = sendAndCollect(player, toSet, Flags.GREET_TITLE, lastTitleStack, MessagingUtil::sendStringToTitle);
+ lastTitleStack = sendAndCollect(player, toSet, Flags.GREET_TITLE, lastTitleStack, MessagingUtil::formatTitleFromString);
return true;
}
diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/util/MessagingUtil.java b/worldguard-core/src/main/java/com/sk89q/worldguard/util/MessagingUtil.java
index 4c2b5ce28..d486d36c3 100644
--- a/worldguard-core/src/main/java/com/sk89q/worldguard/util/MessagingUtil.java
+++ b/worldguard-core/src/main/java/com/sk89q/worldguard/util/MessagingUtil.java
@@ -21,32 +21,29 @@
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
-import com.sk89q.worldguard.commands.CommandUtils;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
+import net.kyori.adventure.title.Title;
public final class MessagingUtil {
-
private MessagingUtil() {
}
public static void sendStringToChat(LocalPlayer player, String message) {
- String effective = CommandUtils.replaceColorMacros(message);
- effective = WorldGuard.getInstance().getPlatform().getMatcher().replaceMacros(player, effective);
- for (String mess : effective.replaceAll("\\\\n", "\n").split("\\n")) {
- player.printRaw(mess);
- }
+ player.sendMessage(WorldGuard.getInstance().getMiniMessage().deserialize(message,
+ WorldGuard.getInstance().getPlatform().getMatcher().replacements(player)));
}
- public static void sendStringToTitle(LocalPlayer player, String message) {
+ public static void formatTitleFromString(LocalPlayer player, String message) {
String[] parts = message.replaceAll("\\\\n", "\n").split("\\n", 2);
- String title = CommandUtils.replaceColorMacros(parts[0]);
- title = WorldGuard.getInstance().getPlatform().getMatcher().replaceMacros(player, title);
- if (parts.length > 1) {
- String subtitle = CommandUtils.replaceColorMacros(parts[1]);
- subtitle = WorldGuard.getInstance().getPlatform().getMatcher().replaceMacros(player, subtitle);
- player.sendTitle(title, subtitle);
- } else {
- player.sendTitle(title, null);
- }
+ TagResolver resolvers = WorldGuard.getInstance().getPlatform().getMatcher().replacements(player);
+
+ Component title = WorldGuard.getInstance().getMiniMessage().deserialize(parts[0], resolvers);
+ Component subtitle = parts.length > 1 ? WorldGuard.getInstance().getMiniMessage().deserialize(parts[1], resolvers) : Component.empty();
+
+ Title t = WorldGuard.getInstance().getPlatform().getGlobalStateManager().get(player.getWorld()).forceDefaultTitleTimes ?
+ Title.title(title, subtitle, Title.DEFAULT_TIMES) : Title.title(title, subtitle);
+ player.showTitle(t);
}
}
diff --git a/worldguard-core/src/test/java/com/sk89q/worldguard/TestPlayer.java b/worldguard-core/src/test/java/com/sk89q/worldguard/TestPlayer.java
index e5bb902a1..d42323d0f 100644
--- a/worldguard-core/src/test/java/com/sk89q/worldguard/TestPlayer.java
+++ b/worldguard-core/src/test/java/com/sk89q/worldguard/TestPlayer.java
@@ -30,11 +30,14 @@
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.weather.WeatherType;
+import net.kyori.adventure.title.Title;
import java.util.HashSet;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
@@ -174,20 +177,25 @@ public void setCompassTarget(Location location) {
}
@Override
- public void sendTitle(String title, String subtitle) {
+ public void showTitle(Title title) {
}
@Override
- public void resetFallDistance() {
+ public void sendMessage(net.kyori.adventure.text.Component message) {
}
@Override
- public void teleport(Location location, String successMessage, String failMessage) {
+ public void resetFallDistance() {
}
+ @Override
+ public CompletableFuture teleport(Location location) {
+ return CompletableFuture.completedFuture(false);
+ }
+
@Override
public void printRaw(String msg) {
System.out.println("-> TestPlayer{" + this.name + "}: " + msg);