feat(wip/prompt): bro discovered conversations

This commit is contained in:
ineanto 2025-05-16 20:35:36 +02:00
parent 63f2e7f699
commit 1646488316
Signed by: ineanto
GPG key ID: E511F9CAA2F9CE84
10 changed files with 156 additions and 385 deletions

View file

@ -10,7 +10,6 @@ import xyz.ineanto.nicko.config.Configuration;
import xyz.ineanto.nicko.config.ConfigurationManager; import xyz.ineanto.nicko.config.ConfigurationManager;
import xyz.ineanto.nicko.event.PlayerJoinListener; import xyz.ineanto.nicko.event.PlayerJoinListener;
import xyz.ineanto.nicko.event.PlayerQuitListener; import xyz.ineanto.nicko.event.PlayerQuitListener;
import xyz.ineanto.nicko.event.PromptCloseListener;
import xyz.ineanto.nicko.language.CustomLanguage; import xyz.ineanto.nicko.language.CustomLanguage;
import xyz.ineanto.nicko.language.Language; import xyz.ineanto.nicko.language.Language;
import xyz.ineanto.nicko.migration.ConfigurationMigrator; import xyz.ineanto.nicko.migration.ConfigurationMigrator;
@ -112,7 +111,6 @@ public class Nicko extends JavaPlugin {
getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this); getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this);
getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this); getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this);
getServer().getPluginManager().registerEvents(new PromptCloseListener(), this);
getLogger().info("Nicko has been enabled."); getLogger().info("Nicko has been enabled.");
} }

View file

@ -1,16 +0,0 @@
package xyz.ineanto.nicko.event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import xyz.ineanto.nicko.event.custom.PromptCloseEvent;
public class PromptCloseListener implements Listener {
@EventHandler
public void onPromptClose(PromptCloseEvent event) {
String playerName = event.getPlayer().getName();
String skin = event.getSkin().orElse("No skin selected");
String name = event.getName().orElse("No name selected");
System.out.println("Player " + playerName + " closed the prompt with skin: " + skin + " and name: " + name);
}
}

View file

@ -1,52 +0,0 @@
package xyz.ineanto.nicko.event.custom;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public class PromptCloseEvent extends Event implements Cancellable {
private static final HandlerList HANDLERS_LIST = new HandlerList();
private boolean isCancelled;
private final Player player;
private final String skin, name;
public PromptCloseEvent(Player player, String skin, String name) {
this.player = player;
this.skin = skin;
this.name = name;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public void setCancelled(boolean isCancelled) {
this.isCancelled = isCancelled;
}
public static HandlerList getHandlerList() { return HANDLERS_LIST; }
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS_LIST;
}
public Player getPlayer() {
return player;
}
public Optional<String> getSkin() {
return Optional.ofNullable(skin);
}
public Optional<String> getName() {
return Optional.ofNullable(name);
}
}

View file

@ -43,7 +43,7 @@ public class ChangeSkinItem {
if (clickType.isLeftClick() || clickType.isRightClick()) { if (clickType.isLeftClick() || clickType.isRightClick()) {
click.getEvent().getView().close(); click.getEvent().getView().close();
final PromptManager manager = new PromptManager(click.getPlayer()); final PromptManager manager = new PromptManager(click.getPlayer());
manager.displaySkinPromptThenUpdate(); manager.displaySkinPrompt();
} }
return true; return true;
}); });

View file

@ -1,102 +0,0 @@
package xyz.ineanto.nicko.gui.prompt;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.event.custom.PromptCloseEvent;
import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.language.PlayerLanguage;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class ChatPrompt implements Prompt {
private final Player player;
private final PlayerLanguage playerLanguage;
private String name = null;
private String skin = null;
private final List<UUID> inPrompt = new ArrayList<>();
public ChatPrompt(Player player, PlayerLanguage playerLanguage) {
this.player = player;
this.playerLanguage = playerLanguage;
}
@Override
public Optional<String[]> displayNameThenSkinPrompt() {
promptInChat(false, false);
if (skin == null || name == null) {
return Optional.empty();
}
return Optional.of(new String[]{name, skin});
}
@Override
public Optional<String> displaySkinPrompt() {
promptInChat(true, true);
return Optional.empty();
}
@Override
public Optional<String> displayNamePrompt() {
promptInChat(false, true);
return Optional.empty();
}
private void promptInChat(boolean isSkin, boolean last) {
// what the HELL did I wrote (Ineanto)
// TODO (Ineanto, 11/05/2025): Doesn't work, the event is registered multiple times and thus is breaking the logic.
if (!inPrompt.contains(player.getUniqueId())) {
Bukkit.broadcast(Component.text("WAS NOT IN"));
inPrompt.add(player.getUniqueId());
Bukkit.getScheduler().runTaskLaterAsynchronously(Nicko.getInstance(), () -> inPrompt.remove(player.getUniqueId()), 20 * 15L);
}
if (isSkin) {
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Appearance.Set.CHAT_PROMPT, true, "skin"));
} else {
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Appearance.Set.CHAT_PROMPT, true, "name"));
}
Nicko.getInstance().getServer().getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onPlayerChat(AsyncChatEvent event) {
if (!inPrompt.contains(event.getPlayer().getUniqueId())) {
Bukkit.broadcast(Component.text("IS NOT IN (chat)"));
return;
}
event.setCancelled(true);
final String plain = PlainTextComponentSerializer.plainText().serialize(event.originalMessage());
if (isSkin) {
skin = plain;
} else {
name = plain;
}
if (last) {
Bukkit.broadcast(Component.text("WAS LAST"));
inPrompt.remove(event.getPlayer().getUniqueId());
Bukkit.getScheduler().runTask(Nicko.getInstance(), () -> Bukkit
.getPluginManager()
.callEvent(new PromptCloseEvent(event.getPlayer(), skin, name))
);
} else {
Bukkit.broadcast(Component.text("PROMPTING FOR SKIN NOW"));
promptInChat(true, true);
}
}
}, Nicko.getInstance());
}
}

View file

@ -1,11 +1,63 @@
package xyz.ineanto.nicko.gui.prompt; package xyz.ineanto.nicko.gui.prompt;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.appearance.ActionResult;
import xyz.ineanto.nicko.appearance.AppearanceManager;
import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.language.PlayerLanguage;
import xyz.ineanto.nicko.profile.NickoProfile;
import xyz.ineanto.nicko.storage.PlayerDataStore;
import javax.annotation.Nullable;
import java.util.Optional; import java.util.Optional;
public interface Prompt { public abstract class Prompt {
Optional<String[]> displayNameThenSkinPrompt(); private final Player player;
private final AppearanceManager appearanceManager;
private final NickoProfile profile;
private final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
Optional<String> displaySkinPrompt(); protected final PlayerLanguage playerLanguage;
Optional<String> displayNamePrompt(); public Prompt(Player player) {
this.player = player;
this.appearanceManager = new AppearanceManager(player);
this.playerLanguage = new PlayerLanguage(player);
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
this.profile = optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
}
public abstract void displayNameThenSkinPrompt();
public abstract void displaySkinPrompt();
public abstract void displayNamePrompt();
public void update(@Nullable String name, @Nullable String skin, boolean skinChange) {
if (name != null && !name.isBlank()) {
profile.setName(name);
}
if (skin != null && !skin.isBlank()) {
profile.setSkin(skin);
}
dataStore.updateCache(player.getUniqueId(), profile);
final ActionResult actionResult = appearanceManager.update(skinChange);
if (!actionResult.isError()) {
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Set.OK));
player.playSound(player.getLocation(), Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1, 1f);
} else {
player.sendMessage(
playerLanguage.translateWithOops(
LanguageKey.Event.Appearance.Set.ERROR,
playerLanguage.translate(actionResult.getErrorKey(), false)
));
player.playSound(player.getLocation(), Sound.BLOCK_ANVIL_PLACE, 1f, 1f);
}
}
} }

View file

@ -1,79 +1,24 @@
package xyz.ineanto.nicko.gui.prompt; package xyz.ineanto.nicko.gui.prompt;
import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import xyz.ineanto.nicko.Nicko; import xyz.ineanto.nicko.gui.prompt.conversation.ConversationPrompt;
import xyz.ineanto.nicko.appearance.ActionResult;
import xyz.ineanto.nicko.appearance.AppearanceManager;
import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.language.PlayerLanguage;
import xyz.ineanto.nicko.profile.NickoProfile;
import xyz.ineanto.nicko.storage.PlayerDataStore;
import java.util.Optional;
public class PromptManager { public class PromptManager {
private final AppearanceManager appearanceManager;
private final NickoProfile profile;
private final Player player;
private final Prompt prompt; private final Prompt prompt;
private final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
private final PlayerLanguage playerLanguage;
public PromptManager(Player player) { public PromptManager(Player player) {
this.player = player; this.prompt = new ConversationPrompt(player);
this.playerLanguage = new PlayerLanguage(player);
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
this.profile = optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
this.appearanceManager = new AppearanceManager(player);
this.prompt = new ChatPrompt(player, playerLanguage);
} }
public void displayNameThenSkinPrompt() { public void displayNameThenSkinPrompt() {
final Optional<String[]> optionalValues = prompt.displayNameThenSkinPrompt(); prompt.displayNameThenSkinPrompt();
if (optionalValues.isPresent()) {
final String[] values = optionalValues.get();
profile.setName(values[0]);
profile.setSkin(values[1]);
dataStore.updateCache(player.getUniqueId(), profile);
update(true);
}
} }
public void displaySkinPromptThenUpdate() { public void displaySkinPrompt() {
final Optional<String> name = prompt.displaySkinPrompt(); prompt.displaySkinPrompt();
if (name.isPresent()) {
profile.setName(name.get());
dataStore.updateCache(player.getUniqueId(), profile);
update(false);
}
} }
public void displayNamePromptThenUpdate() { public void displayNamePromptThenUpdate() {
final Optional<String> name = prompt.displayNamePrompt(); prompt.displayNamePrompt();
if (name.isPresent()) {
profile.setName(name.get());
dataStore.updateCache(player.getUniqueId(), profile);
update(false);
}
}
private void update(boolean skinChange) {
final ActionResult actionResult = appearanceManager.update(skinChange);
if (!actionResult.isError()) {
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Set.OK));
player.playSound(player.getLocation(), Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1, 1f);
} else {
player.sendMessage(
playerLanguage.translateWithOops(
LanguageKey.Event.Appearance.Set.ERROR,
playerLanguage.translate(actionResult.getErrorKey(), false)
));
player.playSound(player.getLocation(), Sound.BLOCK_ANVIL_PLACE, 1f, 1f);
}
} }
} }

View file

@ -1,120 +0,0 @@
package xyz.ineanto.nicko.gui.prompt;
import de.rapha149.signgui.SignGUI;
import de.rapha149.signgui.SignGUIAction;
import de.rapha149.signgui.exception.SignGUIVersionException;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.language.PlayerLanguage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class SignPrompt implements Prompt {
private final CompletableFuture<Void> future = new CompletableFuture<>();
private final Player player;
private final PlayerLanguage playerLanguage;
private final ArrayList<String> lines = new ArrayList<>(
List.of(
"VVVVVVVVVVVVVVV",
"",
"",
"ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ"
)
);
private String name = null;
private String skin = null;
public SignPrompt(Player player, PlayerLanguage playerLanguage) {
this.player = player;
this.playerLanguage = playerLanguage;
}
@Override
public Optional<String[]> displayNameThenSkinPrompt() {
displayNamePrompt();
displaySkinPrompt();
if (skin == null || name == null) {
return Optional.empty();
}
return Optional.of(new String[]{name, skin});
}
@Override
public Optional<String> displaySkinPrompt() {
this.lines.set(1, playerLanguage.translate(LanguageKey.GUI.NEW_SKIN, false));
displaySign(true);
synchronized (future) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
if (skin == null) {
return Optional.empty();
}
return Optional.of(skin);
}
@Override
public Optional<String> displayNamePrompt() {
this.lines.set(1, playerLanguage.translate(LanguageKey.GUI.NEW_NAME, false));
displaySign(false);
synchronized (future) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
if (name == null) {
return Optional.empty();
}
return Optional.of(skin);
}
private void displaySign(boolean isSkin) {
try {
final SignGUI gui = SignGUI.builder()
.setLines(lines.toArray(new String[0]))
.setType(Material.OAK_SIGN)
.callHandlerSynchronously(Nicko.getInstance())
.setHandler((_, result) -> {
final String internalLine1 = result.getLineWithoutColor(1);
if (internalLine1.isEmpty()) {
return List.of(SignGUIAction.displayNewLines(lines.toArray(new String[0])));
}
if (isSkin) {
skin = internalLine1;
} else {
name = internalLine1;
}
future.complete(null);
return Collections.emptyList();
})
.build();
gui.open(player);
} catch (SignGUIVersionException _) { }
}
}

View file

@ -1,4 +1,4 @@
package xyz.ineanto.nicko.gui.prompt; package xyz.ineanto.nicko.gui.prompt.anvil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.wesjd.anvilgui.AnvilGUI; import net.wesjd.anvilgui.AnvilGUI;
@ -7,32 +7,30 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import xyz.ineanto.nicko.Nicko; import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.gui.prompt.Prompt;
import xyz.ineanto.nicko.language.LanguageKey; import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.language.PlayerLanguage;
import xyz.ineanto.nicko.mojang.MojangUtils; import xyz.ineanto.nicko.mojang.MojangUtils;
import java.util.Collections; import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* This is currently unused, I'm waiting for AnvilGUI * This is currently unused, I'm waiting for AnvilGUI
* to be updated to compile with Paper mappings. * to be updated to compile with Paper mappings.
*/ */
public class AnvilPrompt implements Prompt { // TODO (Ineanto, 16/05/2025): Do some validation on the inputs
public class AnvilPrompt extends Prompt {
private final Player player; private final Player player;
private final PlayerLanguage playerLanguage;
private final AtomicReference<Optional<String>> name = new AtomicReference<>(); private String name;
private final AtomicReference<Optional<String>> skin = new AtomicReference<>(); private String skin;
public AnvilPrompt(Player player, PlayerLanguage playerLanguage) { public AnvilPrompt(Player player) {
super(player);
this.player = player; this.player = player;
this.playerLanguage = playerLanguage;
} }
@Override @Override
public Optional<String[]> displayNameThenSkinPrompt() { public void displayNameThenSkinPrompt() {
new AnvilGUI.Builder() new AnvilGUI.Builder()
.plugin(Nicko.getInstance()) .plugin(Nicko.getInstance())
.itemLeft(getLeftItem(false)) .itemLeft(getLeftItem(false))
@ -43,25 +41,17 @@ public class AnvilPrompt implements Prompt {
return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!"));
} else { } else {
// Praying that it works. This is untested code! // Praying that it works. This is untested code!
name.set(Optional.of(snapshot.getText())); name = snapshot.getText();
displaySkinPrompt();
final Optional<String> skinFromAnvil = displaySkinPrompt();
skin.set(skinFromAnvil);
} }
} }
return Collections.emptyList(); return Collections.emptyList();
}) })
.text("New name...").open(player); .text("New name...").open(player);
if (name.get().isEmpty() || skin.get().isEmpty()) {
return Optional.empty();
}
return Optional.of(new String[]{name.get().orElse(player.getName()), skin.get().orElse(player.getName())});
} }
@Override @Override
public Optional<String> displaySkinPrompt() { public void displaySkinPrompt() {
new AnvilGUI.Builder() new AnvilGUI.Builder()
.plugin(Nicko.getInstance()) .plugin(Nicko.getInstance())
.itemLeft(getLeftItem(true)) .itemLeft(getLeftItem(true))
@ -71,18 +61,18 @@ public class AnvilPrompt implements Prompt {
if (MojangUtils.isUsernameInvalid(snapshot.getText())) { if (MojangUtils.isUsernameInvalid(snapshot.getText())) {
return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!"));
} else { } else {
skin.set(Optional.of(snapshot.getText())); skin = snapshot.getText();
update(name == null ? null : name, skin, true);
return Collections.singletonList(AnvilGUI.ResponseAction.close()); return Collections.singletonList(AnvilGUI.ResponseAction.close());
} }
} }
return Collections.emptyList(); return Collections.emptyList();
}) })
.text("New skin...").open(player); .text("New skin...").open(player);
return skin.get();
} }
@Override @Override
public Optional<String> displayNamePrompt() { public void displayNamePrompt() {
new AnvilGUI.Builder() new AnvilGUI.Builder()
.plugin(Nicko.getInstance()) .plugin(Nicko.getInstance())
.itemLeft(getLeftItem(false)) .itemLeft(getLeftItem(false))
@ -92,14 +82,13 @@ public class AnvilPrompt implements Prompt {
if (MojangUtils.isUsernameInvalid(snapshot.getText())) { if (MojangUtils.isUsernameInvalid(snapshot.getText())) {
return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!"));
} else { } else {
name.set(Optional.of(snapshot.getText())); update(snapshot.getText(), null, false);
return Collections.singletonList(AnvilGUI.ResponseAction.close()); return Collections.singletonList(AnvilGUI.ResponseAction.close());
} }
} }
return Collections.emptyList(); return Collections.emptyList();
}) })
.text("New name...").open(player); .text("New name...").open(player);
return name.get();
} }
private ItemStack getLeftItem(boolean skin) { private ItemStack getLeftItem(boolean skin) {

View file

@ -0,0 +1,77 @@
package xyz.ineanto.nicko.gui.prompt.conversation;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.ConversationFactory;
import org.bukkit.conversations.StringPrompt;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.gui.prompt.Prompt;
import xyz.ineanto.nicko.language.PlayerLanguage;
import java.util.Map;
import java.util.Objects;
public class ConversationPrompt extends Prompt {
private final ConversationFactory conversationFactory = new ConversationFactory(Nicko.getInstance());
private final String identifier;
private final Player player;
private String name;
private String skin;
public ConversationPrompt(Player player) {
super(player);
this.player = player;
this.identifier = "nicko-conversation-" + player.getUniqueId();
}
@Override
public void displayNameThenSkinPrompt() {
}
@Override
public void displaySkinPrompt() {
conversationFactory
.thatExcludesNonPlayersWithMessage("Player only")
.withTimeout(30)
.withFirstPrompt(new NickoConversation(player, playerLanguage))
.withInitialSessionData(Map.of(identifier, true, identifier + "-skin", true))
.withLocalEcho(false)
.buildConversation(player)
.begin();
}
@Override
public void displayNamePrompt() {
}
private class NickoConversation extends StringPrompt {
private final Player player;
private final PlayerLanguage playerLanguage;
public NickoConversation(Player player, PlayerLanguage playerLanguage) {
this.player = player;
this.playerLanguage = playerLanguage;
}
@Override
public @NotNull String getPromptText(@NotNull ConversationContext context) {
return "Enter your skin";
}
@Override
public @Nullable org.bukkit.conversations.Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) {
if (Objects.equals(context.getSessionData(identifier + "-skin"), true)) {
skin = input;
update(null, skin, true);
return END_OF_CONVERSATION;
}
return END_OF_CONVERSATION;
}
}
}