1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-28 19:29:14 +00:00

Implement dialog_list type, small refactors with button dialogs

This commit is contained in:
Eclipse
2025-06-05 14:29:10 +00:00
parent 77b704bf23
commit 2e699f4cee
9 changed files with 123 additions and 21 deletions

View File

@@ -128,6 +128,8 @@ public final class GeyserHolderSet<T> {
/**
* Reads a HolderSet from a NBT object. Does not support reading HolderSets that can hold inline values.
*
* <p>Uses {@link JavaRegistryKey#keyToNetworkId(GeyserSession, Key)} to resolve registry keys to network IDs.</p>
*
* @param session the Geyser session.
* @param registry the registry the HolderSet contains IDs from.
* @param holderSet the HolderSet as a NBT object.
@@ -136,6 +138,13 @@ public final class GeyserHolderSet<T> {
return readHolderSet(registry, holderSet, key -> registry.keyToNetworkId(session, key));
}
/**
* Reads a HolderSet from a NBT object. Does not support reading HolderSets that can hold inline values.
*
* @param registry the registry the HolderSet contains IDs from.
* @param holderSet the HolderSet as a NBT object.
* @param idMapper a function that maps a key in this registry to its respective network ID.
*/
public static <T> GeyserHolderSet<T> readHolderSet(JavaRegistryKey<T> registry, @Nullable Object holderSet, ToIntFunction<Key> idMapper) {
return readHolderSet(registry, holderSet, idMapper, null);
}

View File

@@ -30,13 +30,29 @@ import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MinecraftKey;
import java.util.List;
import java.util.Optional;
public class ConfirmationDialog extends DialogWithButtons {
public static final Key TYPE = MinecraftKey.key("confirmation");
private final DialogButton yes;
private final DialogButton no;
public ConfirmationDialog(GeyserSession session, NbtMap map, IdGetter idGetter) {
super(session, map, parseOptionalList(DialogButton.read(session, map.get("yes"), idGetter), DialogButton.read(session, map.get("no"), idGetter)), Optional.empty());
super(session, map, Optional.empty());
yes = DialogButton.read(session, map.get("yes"), idGetter).orElseThrow();
no = DialogButton.read(session, map.get("no"), idGetter).orElseThrow();
}
@Override
protected List<DialogButton> buttons(DialogHolder holder) {
return List.of(yes, no);
}
@Override
protected Optional<DialogButton> onCancel() {
return Optional.of(no);
}
}

View File

@@ -59,8 +59,10 @@ public abstract class Dialog {
private static final Key PLAIN_MESSAGE_BODY = MinecraftKey.key("plain_message");
@Getter
private final String title;
private final String externalTitle;
@Getter
private final Optional<String> externalTitle;
@Getter
private final boolean canCloseWithEscape;
@Getter
@@ -72,7 +74,7 @@ public abstract class Dialog {
protected Dialog(GeyserSession session, NbtMap map) {
title = MessageTranslator.convertFromNullableNbtTag(session, map.get("title"));
externalTitle = MessageTranslator.convertFromNullableNbtTag(session, map.get("title"));
externalTitle = Optional.ofNullable(MessageTranslator.convertFromNullableNbtTag(session, map.get("external_title")));
canCloseWithEscape = map.getBoolean("can_close_with_escape", true);
afterAction = AfterAction.fromString(map.getString("after_action"));
@@ -167,6 +169,8 @@ public abstract class Dialog {
Key type = MinecraftKey.key(map.getString("type"));
if (type.equals(NoticeDialog.TYPE)) {
return new NoticeDialog(session, map, idGetter);
} else if (type.equals(DialogListDialog.TYPE)) {
return new DialogListDialog(session, map, idGetter);
} else if (type.equals(ConfirmationDialog.TYPE)) {
return new ConfirmationDialog(session, map, idGetter);
} else if (type.equals(MultiActionDialog.TYPE)) {

View File

@@ -42,7 +42,7 @@ public record DialogButton(String label, Optional<DialogAction> action) {
}
List<DialogButton> buttons = new ArrayList<>();
for (NbtMap map : tag) {
buttons.add(read(session, map, idGetter).orElseThrow()); // Should never throw
buttons.add(read(session, map, idGetter).orElseThrow()); // Should never throw because we know map is a NbtMap
}
return buttons;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.session.dialog;
import net.kyori.adventure.key.Key;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.tags.GeyserHolderSet;
import org.geysermc.geyser.session.dialog.action.DialogAction;
import org.geysermc.geyser.util.MinecraftKey;
import java.util.List;
import java.util.Optional;
public class DialogListDialog extends DialogWithButtons {
public static final Key TYPE = MinecraftKey.key("dialog_list");
private final GeyserHolderSet<Dialog> dialogs;
public DialogListDialog(GeyserSession session, NbtMap map, IdGetter idGetter) {
super(session, map, readDefaultExitAction(session, map, idGetter));
dialogs = GeyserHolderSet.readHolderSet(JavaRegistries.DIALOG, map.get("dialogs"), idGetter, dialog -> Dialog.readDialogFromNbt(session, dialog, idGetter));
}
@Override
protected List<DialogButton> buttons(DialogHolder holder) {
return dialogs.resolve(holder.session()).stream()
.map(dialog -> new DialogButton(dialog.externalTitle().orElseGet(dialog::title),
Optional.of(new DialogAction.ShowDialog(dialog))))
.toList();
}
}

View File

@@ -32,23 +32,24 @@ import org.geysermc.cumulus.form.SimpleForm;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.dialog.input.ParsedInputs;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public abstract class DialogWithButtons extends Dialog {
protected final List<DialogButton> buttons;
protected final Optional<DialogButton> exitAction;
protected DialogWithButtons(GeyserSession session, NbtMap map, List<DialogButton> buttons, Optional<DialogButton> exitAction) {
protected DialogWithButtons(GeyserSession session, NbtMap map, Optional<DialogButton> exitAction) {
super(session, map);
this.buttons = buttons;
this.exitAction = exitAction;
}
protected abstract List<DialogButton> buttons(DialogHolder holder);
@Override
protected void addCustomComponents(DialogHolder holder, CustomForm.Builder builder) {
List<DialogButton> buttons = buttons(holder);
DropdownComponent.Builder dropdown = DropdownComponent.builder();
dropdown.text("Please select an option:");
for (DialogButton button : buttons) {
@@ -69,6 +70,7 @@ public abstract class DialogWithButtons extends Dialog {
@Override
protected void addCustomComponents(DialogHolder holder, SimpleForm.Builder builder) {
List<DialogButton> buttons = buttons(holder);
for (DialogButton button : buttons) {
builder.button(button.label());
}
@@ -88,12 +90,7 @@ public abstract class DialogWithButtons extends Dialog {
return exitAction;
}
@SafeVarargs
protected static List<DialogButton> parseOptionalList(Optional<DialogButton>... buttons) {
List<DialogButton> checked = new ArrayList<>();
for (Optional<DialogButton> button : buttons) {
checked.add(button.orElseThrow());
}
return checked;
protected static Optional<DialogButton> readDefaultExitAction(GeyserSession session, NbtMap map, IdGetter idGetter) {
return DialogButton.read(session, map.get("exit_action"), idGetter);
}
}

View File

@@ -31,11 +31,21 @@ import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MinecraftKey;
import java.util.List;
public class MultiActionDialog extends DialogWithButtons {
public static final Key TYPE = MinecraftKey.key("multi_action");
private final List<DialogButton> buttons;
protected MultiActionDialog(GeyserSession session, NbtMap map, IdGetter idGetter) {
super(session, map, DialogButton.readList(session, map.getList("actions", NbtType.COMPOUND), idGetter), DialogButton.read(session, map.get("exit_action"), idGetter));
super(session, map, readDefaultExitAction(session, map, idGetter));
buttons = DialogButton.readList(session, map.getList("actions", NbtType.COMPOUND), idGetter);
}
@Override
protected List<DialogButton> buttons(DialogHolder holder) {
return buttons;
}
}

View File

@@ -34,6 +34,11 @@ import java.util.Optional;
public class ServerLinksDialog extends DialogWithButtons {
protected ServerLinksDialog(GeyserSession session, NbtMap map, List<DialogButton> buttons) {
super(session, map, buttons, Optional.empty());
super(session, map, Optional.empty());
}
@Override
protected List<DialogButton> buttons(DialogHolder holder) {
return List.of();
}
}

View File

@@ -108,22 +108,26 @@ public interface DialogAction {
}
}
record ShowDialog(Holder<NbtMap> dialog) implements DialogAction {
record ShowDialog(Optional<Dialog> dialog, Holder<NbtMap> holder) implements DialogAction {
public static final Key TYPE = MinecraftKey.key("show_dialog");
public ShowDialog(Dialog dialog) {
this(Optional.of(dialog), null);
}
private static ShowDialog read(Object dialog, Dialog.IdGetter idGetter) {
if (dialog instanceof NbtMap map) {
return new ShowDialog(Holder.ofCustom(map));
return new ShowDialog(Optional.empty(), Holder.ofCustom(map));
} else if (dialog instanceof String string) {
return new ShowDialog(Holder.ofId(idGetter.applyAsInt(MinecraftKey.key(string))));
return new ShowDialog(Optional.empty(), Holder.ofId(idGetter.applyAsInt(MinecraftKey.key(string))));
}
throw new IllegalArgumentException("Expected dialog in show_dialog action to be a NBT map or a resource location");
}
@Override
public void run(GeyserSession session, ParsedInputs inputs) {
session.getDialogManager().openDialog(dialog);
dialog.ifPresentOrElse(normal -> session.getDialogManager().openDialog(normal), () -> session.getDialogManager().openDialog(holder));
}
}