Add dialog-based confirmation for island teleportation.

- Introduced player dialog system for confirmation prompts.
- Enhanced teleportation logic to include cancel and confirm options.
- Refactored inventory checks and confirmation flow for better usability.
- Added utility for command-style text components.
This commit is contained in:
Nick Guy 2025-08-11 18:23:41 +01:00
parent 6332a26ce0
commit 3edf908b89
4 changed files with 119 additions and 29 deletions

View file

@ -4,6 +4,7 @@ import com.ncguy.usefulskyblock.pdc.AnonymousDataContainerRef;
import com.ncguy.usefulskyblock.pdc.IDataContainerRef;
import com.ncguy.usefulskyblock.pdc.VectorPersistentDataType;
import io.papermc.paper.datacomponent.DataComponentType;
import net.kyori.adventure.key.Key;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.block.Block;
@ -21,6 +22,9 @@ public class Reference {
return new NamespacedKey("usefulskyblock", key);
}
public static Key DIALOG_AGREE = key("dialog/agree");
public static Key DIALOG_DISAGREE = key("dialog/disagree");
public static IDataContainerRef<BlockVector, World> SKYBLOCK_TEAM_ROOT = new AnonymousDataContainerRef<>(key("world.skyblock.team"), VectorPersistentDataType.Instance);
public static IDataContainerRef<Integer, World> SKYBLOCK_TEAM_COUNT = SKYBLOCK_TEAM_ROOT.withSuffix(".count").withType(PersistentDataType.INTEGER);
public static IDataContainerRef<Boolean, World> WORLD_INIT = new AnonymousDataContainerRef<>(key("world.init"), PersistentDataType.BOOLEAN);

View file

@ -6,7 +6,22 @@ import com.ncguy.usefulskyblock.recipe.BiomeRod;
import com.ncguy.usefulskyblock.recipe.IRecipeProvider;
import com.ncguy.usefulskyblock.recipe.SmeltingCraftingHandler;
import com.ncguy.usefulskyblock.world.PortalHandler;
import io.papermc.paper.connection.PlayerCommonConnection;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.event.player.PlayerCustomClickEvent;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.inventory.Recipe;
import org.bukkit.plugin.PluginManager;
@ -15,11 +30,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public final class UsefulSkyblock extends JavaPlugin implements Listener {
private static final Logger log = LoggerFactory.getLogger(UsefulSkyblock.class);
public UsefulSkyblock() {
dialogMaps = new java.util.concurrent.ConcurrentHashMap<>();
}
@Override
public void onEnable() {
saveDefaultConfig();
@ -69,4 +91,43 @@ public final class UsefulSkyblock extends JavaPlugin implements Listener {
// Plugin shutdown logic
}
private final Map<PlayerCommonConnection, CompletableFuture<Boolean>> dialogMaps;
@EventHandler
void onHandleDialog(PlayerCustomClickEvent event) {
Key key = event.getIdentifier();
CompletableFuture<Boolean> f = dialogMaps.get(event.getCommonConnection());
Boolean res = null;
if(key.equals(Reference.DIALOG_AGREE))
res = true;
else if(key.equals(Reference.DIALOG_DISAGREE))
res = false;
if(res != null && f != null) {
f.complete(res);
dialogMaps.remove(event.getCommonConnection());
}
}
public CompletableFuture<Boolean> confirm(Player player, Component base, Consumer<DialogBase.Builder> builder) {
var confirmBtn = ActionButton.builder(Component.text("Confirm", NamedTextColor.GREEN)).tooltip(Component.text("Click to confirm")).action(DialogAction.customClick(Reference.DIALOG_AGREE, null)).build();
var cancelBtn = ActionButton.builder(Component.text("Cancel", NamedTextColor.RED)).tooltip(Component.text("Click to cancel")).action(DialogAction.customClick(Reference.DIALOG_DISAGREE, null)).build();
Dialog dialog = Dialog.create(innerBuilder -> {
DialogBase.Builder baseBuilder = DialogBase.builder(base);
baseBuilder.canCloseWithEscape(false);
builder.accept(baseBuilder);
innerBuilder.empty()
.base(baseBuilder.build())
.type(DialogType.confirmation(confirmBtn, cancelBtn));
});
player.showDialog(dialog);
CompletableFuture<Boolean> f = new CompletableFuture<>();
dialogMaps.put(player.getConnection(), f);
return f;
}
}

View file

@ -8,15 +8,14 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.ncguy.usefulskyblock.Advancements;
import com.ncguy.usefulskyblock.IslandNetworkGenerator;
import com.ncguy.usefulskyblock.Reference;
import com.ncguy.usefulskyblock.StructureRef;
import com.ncguy.usefulskyblock.*;
import com.ncguy.usefulskyblock.data.BiomedStructure;
import com.ncguy.usefulskyblock.utils.Components;
import com.ncguy.usefulskyblock.utils.TextUtils;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
@ -30,6 +29,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.scoreboard.Team;
@ -37,11 +37,9 @@ import org.bukkit.util.BlockVector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import static com.ncguy.usefulskyblock.Reference.key;
@ -226,18 +224,42 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
var islandHomeLoc = Reference.ISLAND_HOME_LOC.assign(player);
World overworld = getServer().getWorld(new NamespacedKey("minecraft", "overworld"));
if (islandHomeLoc.has()) {
if (!isInventoryEmpty(confirmationGiven, executor, player))
return -1;
Runnable doTeleport = () -> {
executor.sendMessage("Teleported to island home..");
BlockVector blockVector = islandHomeLoc.get();
Location loc = new Location(overworld, blockVector.getX(), blockVector.getY(), blockVector.getZ());
if (!player.teleport(loc, PlayerTeleportEvent.TeleportCause.COMMAND)) {
player.sendMessage("Failed to teleport you to your island home, please notify an administrator");
return -1;
return;
}
player.getInventory().clear();
if(player.getGameMode() != GameMode.SURVIVAL)
player.setGameMode(GameMode.SURVIVAL);
};
Runnable tryTeleport = () -> {
if (!isInventoryEmpty(confirmationGiven, executor, player)) {
JavaPlugin.getPlugin(UsefulSkyblock.class)
.confirm(player, Component.text("Teleporting to an island will clear your inventory"), b -> {
b.body(List.of(
DialogBody.plainMessage(Component.text("Know that this action is irreversible")),
DialogBody.plainMessage(Component.newline()),
DialogBody.plainMessage(Component.text("To skip this notice in future, use the command variant ").append(Components.Command("/skyblock confirm")).append(Component.text(".")))
));
}).thenAccept(res -> {
if (res) {
doTeleport.run();
} else {
player.sendMessage("Teleport cancelled");
}
});
return;
}
doTeleport.run();
if (islandHomeLoc.has()) {
tryTeleport.run();
return 0;
}
@ -255,19 +277,8 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
String sanitizedTeamName = playerTeamName.replaceAll("[^a-z0-9_.]", "_");
var teamSpawn = Reference.SKYBLOCK_TEAM_ROOT.withSuffix("." + sanitizedTeamName).assign(overworld);
if (teamSpawn.has()) {
if (!isInventoryEmpty(confirmationGiven, executor, player))
return -1;
islandHomeLoc.set(teamSpawn.get());
executor.sendMessage("Teleported to island home..");
BlockVector blockVector = islandHomeLoc.get();
Location loc = new Location(overworld, blockVector.getX(), blockVector.getY(), blockVector.getZ());
if (!player.teleport(loc, PlayerTeleportEvent.TeleportCause.COMMAND)) {
player.sendMessage("Failed to teleport you to your island home, please notify an administrator");
return -1;
}
player.getInventory().clear();
tryTeleport.run();
return 0;
}
@ -284,10 +295,11 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
boolean hasItemInOffhand = inventory.getItemInOffHand().getType() != Material.AIR;
boolean hasItemInInventory = Arrays.stream(inventory.getContents()).anyMatch(Objects::nonNull);
if(hasArmour || hasItemInOffhand || hasItemInInventory) {
if(!confirmationGiven) {
if (hasArmour || hasItemInOffhand || hasItemInInventory) {
if (!confirmationGiven) {
executor.sendMessage(Component.text("You have items in your inventory, teleporting you to your island will remove them."));
executor.sendMessage(Component.text("If you wish to keep your items, please confirm via the command ").append(Component.text("/skyblock confirm", Style.style(TextColor.color(0x00, 0xff, 0xff)))));
executor.sendMessage(Component.text("If you wish to keep your items, please confirm via the command ")
.append(Component.text("/skyblock confirm", Style.style(TextColor.color(0x00, 0xff, 0xff)))));
return false;
}
}

View file

@ -0,0 +1,13 @@
package com.ncguy.usefulskyblock.utils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
public class Components {
public static Component Command(String command) {
return Component.text(command, Style.style(TextColor.color(0x00, 0xff, 0xff)));
}
}