From b708c176004a0dcff11a5487f0b24cc69016184c Mon Sep 17 00:00:00 2001 From: Eclipse Date: Wed, 4 Jun 2025 17:41:14 +0000 Subject: [PATCH] Implement "waiting for response" screen --- .../geyser/session/GeyserSession.java | 2 + .../geyser/session/dialog/Dialog.java | 3 + .../geyser/session/dialog/DialogHolder.java | 64 ++++++++++++++++--- .../geyser/session/dialog/DialogManager.java | 6 ++ .../geyser/session/dialog/NoticeDialog.java | 5 +- 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 041dc68e0..a703544f9 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1240,6 +1240,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // but this will work once we implement matching Java custom tick cycles sendDownstreamGamePacket(ServerboundClientTickEndPacket.INSTANCE); } + + dialogManager.tick(); } catch (Throwable throwable) { throwable.printStackTrace(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java index 00011c523..93d30f641 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java @@ -42,6 +42,7 @@ import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.dialog.input.DialogInput; import org.geysermc.geyser.session.dialog.input.ParsedInputs; +import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.game.Holder; @@ -111,6 +112,7 @@ public abstract class Dialog { protected FormBuilder, ? extends Form, ? extends FormResponse> createForm(GeyserSession session, Optional restored, DialogHolder holder) { if (inputs.isEmpty()) { SimpleForm.Builder builder = SimpleForm.builder() + .translator(MinecraftLocale::getLocaleString, session.locale()) .title(title); builder.content(String.join("\n\n", labels)); @@ -119,6 +121,7 @@ public abstract class Dialog { return builder; } else { CustomForm.Builder builder = CustomForm.builder() + .translator(MinecraftLocale::getLocaleString, session.locale()) .title(title); restored.ifPresentOrElse(last -> last.restore(builder), () -> inputs.forEach(input -> input.addComponent(builder))); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java index b24cbfd98..a044f4277 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java @@ -26,14 +26,18 @@ package org.geysermc.geyser.session.dialog; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.geyser.session.dialog.action.DialogAction; import org.geysermc.geyser.session.dialog.input.ParsedInputs; +import org.geysermc.geyser.text.MinecraftLocale; import java.util.Optional; public class DialogHolder { private final DialogManager manager; private final Dialog dialog; + private long responseWaitTime = 0; + private boolean sendBackButton = false; private ParsedInputs lastInputs; public DialogHolder(DialogManager manager, Dialog dialog) { @@ -67,7 +71,23 @@ public class DialogHolder { manager.close(); } } - case WAIT_FOR_RESPONSE -> {} // TODO + case WAIT_FOR_RESPONSE -> { + // If no new dialog was opened, open a form telling the user we're waiting on a response from the server + // This dialog is replaced with a similar form with a "back" button after 5 seconds, matching Java behaviour + if (stillValid) { + responseWaitTime = System.currentTimeMillis(); + sendBackButton = false; + waitForResponse(); + } + } + } + } + + public void tick() { + // Replace wait form with one with a back button if no replacement dialog was given + if (responseWaitTime > 0 && !sendBackButton && System.currentTimeMillis() - responseWaitTime > 5000) { + sendBackButton = true; + manager.session().closeForm(); // Automatically reopens with a back button } } @@ -81,16 +101,44 @@ public class DialogHolder { } } else if (manager.open() == this) { // Check if this is still the currently open dialog // If player should not have been able to close the dialog, reopen it with the last inputs - - // lastInputs might be null here since it's possible none were sent yet, and bedrock doesn't send them when just closing the form - if (lastInputs == null) { - dialog.sendForm(manager.session(), this); - } else { - dialog.restoreForm(manager.session(), lastInputs, this); - } + reopenDialog(); } } + private void reopenDialog() { + responseWaitTime = 0; + + // lastInputs might be null here since it's possible none were sent yet + // Bedrock doesn't send them when just closing the form + if (lastInputs == null) { + dialog.sendForm(manager.session(), this); + } else { + dialog.restoreForm(manager.session(), lastInputs, this); + } + } + + private void waitForResponse() { + String content = "Geyser is waiting for the server to respond with a new dialog."; + if (sendBackButton) { + content += " The server is taking a while to respond. You can press the button below to go back to the game."; + } else { + content += " If no new dialog is shown within 5 seconds, a button will appear to go back to the game."; + } + + manager.session().sendDialogForm(SimpleForm.builder() + .translator(MinecraftLocale::getLocaleString, manager.session().locale()) + .title("gui.waitingForResponse.title") + .content(content) + .optionalButton("Back", sendBackButton) + .closedOrInvalidResultHandler(() -> { + if (manager.open() == this) { // If still waiting on a new dialog + waitForResponse(); + } + }) + .validResultHandler(response -> manager.close()) // Back button was pressed, meaning no new dialog was sent + .build()); + } + // Returns true if this dialog is still regarded open by the DialogManager // When returning false, that means a new dialog has been opened, possibly by the action, which takes this dialog's place private boolean runAction(Optional button, ParsedInputs inputs) { diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogManager.java b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogManager.java index b0e50c190..c7d70bac5 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogManager.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogManager.java @@ -58,6 +58,12 @@ public class DialogManager { dialog.sendForm(session, open); } + public void tick() { + if (open != null) { + open.tick(); + } + } + /** * Closes the currently open dialog, if any. The dialog's closing action will not be executed. */ diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java index 10fc1942c..54c226b80 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java @@ -53,11 +53,12 @@ public class NoticeDialog extends Dialog { @Override protected void addCustomComponents(GeyserSession session, CustomForm.Builder builder, DialogHolder holder) { - builder.validResultHandler(response -> holder.runButton(onCancel(), parseInput(response))); + builder.validResultHandler(response -> holder.runButton(button, parseInput(response))); } @Override protected void addCustomComponents(GeyserSession session, SimpleForm.Builder builder, DialogHolder holder) { - builder.validResultHandler(response -> holder.runButton(onCancel(), ParsedInputs.EMPTY)); + builder.button(button.map(DialogButton::label).orElse("gui.ok")) + .validResultHandler(response -> holder.runButton(button, ParsedInputs.EMPTY)); } }