Skip to content

Updating from 1.0 to 1.1 (and 2.0)

Tim203 edited this page Jun 5, 2022 · 3 revisions

There have been quite a few changes to the Cumulus API to make it more understandable and easier to use.

In 1.1 we are introducing new methods that are officially part of the 2.0 update, but to not force plugin developers to update their plugin once 2.0 comes out (which would make the plugin incompatible with either newer versions of Geyser/Floodgate or older versions) we decided to have a transition period where both the new methods as the old methods work.
Because of that we also started to deprecate some methods and classes. Most of them have replacements but some of them will be removed permanently.

We intend to maintain support for everything that have been deprecated in 1.1 until we release 2.0 (which will likely be with the release of Minecraft 1.20)

Cumulus 1.1

There is one behavioural change compared to 1.0.

Optional components

In 1.0 optional components were a simple if check.

Let's take a modified example from Geyser:

SimpleForm.builder()
    .optionalButton("geyser.auth.login.form.notice.btn_login.mojang", isPasswordAuthEnabled) // is the id 0 or absent??
    .button("geyser.auth.login.form.notice.btn_login.microsoft") // is the id 0 or 1??
    .button("geyser.auth.login.form.notice.btn_disconnect") // is the id 1 or 2??
    .validResponseHandler(response -> {
        if (isPasswordAuthEnabled && response.clickedButtonId() == 0) {
            session.setMicrosoftAccount(false);
            buildAndShowLoginDetailsWindow(session);
            return;
        }

        if (isPasswordAuthEnabled && response.clickedButtonId() == 1 ||
                !isPasswordAuthEnabled && response.clickedButtonId() == 0) {

            session.setMicrosoftAccount(true);
            buildAndShowMicrosoftAuthenticationWindow(session);
            return;
        }

        session.disconnect(...)
    })

Note: The code example is using the new response handling system which is discussed here


This was how an optional component would work:

.optionalButton(data,shouldAdd) -> if (shouldAdd) button(data)


This led to awkward response handling where you basically had to check if it was added manually, as you can see in the code example above.

The new logic guarantees that the optional component will claim an id, but it still depends on the shouldAdd to be shown to the client or not.
Meaning that the code example above can be simplified to this:

SimpleForm.builder()
    .optionalButton("geyser.auth.login.form.notice.btn_login.mojang", isPasswordAuthEnabled) // always 0
    .button("geyser.auth.login.form.notice.btn_login.microsoft") // always 1
    .button("geyser.auth.login.form.notice.btn_disconnect") // always 2
    .validResponseHandler(response -> {
        if (response.clickedButtonId() == 0) {
            session.setMicrosoftAccount(false);
            buildAndShowLoginDetailsWindow(session);
            return;
        }

        if (response.clickedButtonId() == 1) {
            session.setMicrosoftAccount(true);
            buildAndShowMicrosoftAuthenticationWindow(session);
            return;
        }
		
        session.disconnect(...)
    })

Cumulus 2.0

While most of the changes are already introduced in 1.1, they'll be the only method starting in 2.0

Response handling changes

One of the weirdest parts of Cumulus 1.0 was how responses had to be parsed manually. Here follows a modified example from a form used in Geyser:

CustomForm.builder()
    .title("geyser.auth.login.form.details.title")
    .label("geyser.auth.login.form.details.desc")
    .input("geyser.auth.login.form.details.email", "[email protected]", "")
    .input("geyser.auth.login.form.details.pass", "123456", "")
    .responseHandler(responseData -> {
        CustomFormResponse response = form.parseResponse(responseData);
        if (!response.isCorrect()) {
            buildAndShowLoginDetailsWindow(session);
            return;
        }

        session.authenticate(response.next(), response.next());
    }));

2.0 introduces various new methods to make it easier to handle responses.

It introduces validResultHandler(BiConsumer<Form, ValidFormResponseResult> | Consumer<ValidFormResponseResult>), invalidResultHandler and closedResultHandler (and closedOrInvalidResultHandler).

It also introduces resultHandler(BiConsumer<Form, FormResponseResult> | Consumer<FormResult>, ResultType...), which allows you to handle multiple responses in one handler.

In 2.0, the given code example could be changed to something like this:

CustomForm.builder()
    .title("geyser.auth.login.form.details.title")
    .label("geyser.auth.login.form.details.desc")
    .input("geyser.auth.login.form.details.email", "[email protected]", "")
    .input("geyser.auth.login.form.details.pass", "123456", "")
    .closedOrInvalidResultHandler(() -> buildAndShowLoginDetailsWindow(session))
    .validResultHandler(response -> session.authenticate(response.next(), response.next())));
Clone this wiki locally