Skip to content
chris edited this page Jun 6, 2024 · 5 revisions

Outdated information warning

This wiki hasn't been updated to Cumulus 1.1 and later.
For accurate information please visit the Cumulus information on the Geyser wiki.


Before we begin

Note that this is a general explanation of Forms and how Cumulus works.
Please head to the wiki of the platform implementing Cumulus for platform specific information.

Introduction to Forms

Bedrock has a cool exclusive feature called Forms.
While they're exclusive to Bedrock, using Cumulus, you'll able to use them.
Bedrock knows three types of Forms:

  • CustomForm
  • ModalForm
  • SimpleForm

We'll discuss them one by one starting with the easiest and ending with the least easy form type.
After that, you get an overview of every single component.
Then we'll talk about sending the form, receiving a response and doing advanced stuff.

ModalForm

While this is the easiest form type it's also the least customisable.
You have a title, description (content) and two buttons.

Example of a ModalForm

Code used in the image:

ModalForm.builder()
    .title("Title")
    .content("Content")
    .button1("Button 1")
    .button2("Button 2")

SimpleForm

While this one is less easy then ModalForm is, it also has more customizability.
It's still limited to title, content and buttons, but these buttons can also have images and do not have a minimum and maximum of two.

Example of a SimpleForm

Code used in the image:

SimpleForm.builder()
    .title("Title")
    .content("Content")
    .button("Button without an image")
    .button("Button with URL image", FormImage.Type.URL, "https://github.com/GeyserMC.png?size=200")
    .button("Button with path image", FormImage.Type.PATH, "textures/i/glyph_world_template.png")

CustomForm

While the CustomForm is the last one on our list (and thus the least easy one), it also has the greatest customizability.
This form exists of a title, content and a list of different components e.g. label, slider and input.
See Components for more information about every component you can use and in which form type.

Example of a CustomForm

Code used in the image:

CustomForm.builder()
    .title("Title")
    .dropdown("Text", "Option 1", "Option 2")
    .input("Input", "placeholder")
    .toggle("Toggle")
    .slider("Text", 0, 10, 1, 5)

Components

Forms have different component types, most of them are limited to the CustomForm and some are available to everything but CustomForm.

Button

Dropdown

Input

Label

Slider

Stepslider

Toggle

Sending a form

After you decided which form type you want to use and finished filling in the actual content, it's time to send the Form to the Bedrock player.
Since this is a global library, it's up to the projects who use Cumulus to decide this part.

Receiving a response from the client

It's nice and all that we can send forms to a client, but we also want to be able to get a response from a client and being able to handle them. We can do that using the responseHandler(BiConsumer<? extends Form, String>) method in the FormBuilder:

_Modal_Form.builder()
    .title("Feedback form")
    .content("We're asking for feedback, are you willing to enter some feedback to improve our server?")
    .button1("Yes") // id = 0
    .button2("No") // id = 1
    .responseHandler((form, responseData) -> {
        _Modal_FormResponse response = form.parseResponse(responseData);
        if (!response.isCorrect()) {
            // player closed the form or returned invalid info (see FormResponse)
            return;
        }
        
        // short version of getClickedButtonId == 0
        if (response.getResult()) {
            System.out.println("Yay, he wants to give us feedback");
            return;
        }
        System.out.println("No feedback for us :(");
    });

Or using the response handler setter of the Form (not a FormBuilder):

_Modal_Form form = ...;
form.setResponseHandler(responseData -> {
    _Modal_FormResponse response = form.parseResponse(responseData);
    if (!response.isCorrect()) {
        // player closed the form or returned invalid info (see FormResponse)
        return;
    }
        
    // short version of getClickedButtonId == 0
    if (response.getResult()) {
        System.out.println("Yay, he wants to give us feedback");
        return;
    }
    System.out.println("No feedback for us :(");
});

Advanced stuff

The FormBuilder also has support for translating the data used in the builder.
To add a translator, you can use the translator(BiFunction<String, String, String>) or the translator(BiFunction<String, String, String>, String) method:

_Modal_Form form = _Modal_Form.builder()
    .translator(this::translate, userLanguage)
    .title("Title")
    .content("Content")
    .button1("translate.button1")
    .button2("translate.button2")
    .build();

public String translate(String key, String locale) {
    // this method will be called for every string, in this case, 4 times:
    // Title, Content, translate.button1, translate.button2
    // your own translate logic here
    // return the value that replaces the key
}

Or you can have the translate method directly in the FormBuilder instead of a separate method:

_Modal_Form form = _Modal_Form.builder()
    .translator((key, unused) -> {
        // this method will be called for every string, in this case, 4 times:
        // Title, Content, translate.button1, translate.button2
        // since this isn't a separate method, you don't need the locale argument, so it's unused.
        // your own translate logic here
        // return the value that replaces the key
    })
    .title("Title")
    .content("Content")
    .button1("translate.button1")
    .button2("translate.button2")
    .build();