diff --git a/common/pom.xml b/common/pom.xml index 85dde12c6..30c8afc06 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -15,7 +15,7 @@ com.google.code.gson gson - 2.8.2 + 2.8.5 compile diff --git a/common/src/main/java/org/geysermc/common/form/CustomForm.java b/common/src/main/java/org/geysermc/common/form/CustomForm.java new file mode 100644 index 000000000..5e8d93252 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/CustomForm.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2019-2020 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.common.form; + +import com.google.gson.annotations.JsonAdapter; +import lombok.Getter; +import org.geysermc.common.form.component.*; +import org.geysermc.common.form.response.CustomFormResponse; +import org.geysermc.common.form.util.FormAdaptor; +import org.geysermc.common.form.util.FormImage; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@JsonAdapter(FormAdaptor.class) +public final class CustomForm extends Form { + private final String title; + private final FormImage icon; + private final List content; + + private CustomForm(String title, FormImage icon, List content) { + super(Form.Type.CUSTOM_FORM); + + this.title = title; + this.icon = icon; + this.content = Collections.unmodifiableList(content); + } + + public static Builder builder() { + return new Builder(); + } + + public static CustomForm of(String title, FormImage icon, List content) { + return new CustomForm(title, icon, content); + } + + public CustomFormResponse parseResponse(String data) { + if (isClosed(data)) { + return CustomFormResponse.closed(); + } + return CustomFormResponse.of(this, data); + } + + public static final class Builder extends Form.Builder { + private final List components = new ArrayList<>(); + private FormImage icon; + + public Builder icon(FormImage.Type type, String data) { + icon = FormImage.of(type, data); + return this; + } + + public Builder iconPath(String path) { + return icon(FormImage.Type.PATH, path); + } + + public Builder iconUrl(String url) { + return icon(FormImage.Type.URL, url); + } + + public Builder component(Component component) { + components.add(component); + return this; + } + + public Builder dropdown(DropdownComponent.Builder dropdownBuilder) { + return component(dropdownBuilder.translateAndBuild(this::translate)); + } + + public Builder dropdown(String text, int defaultOption, String... options) { + List optionsList = new ArrayList<>(); + for (String option : options) { + optionsList.add(translate(option)); + } + return component(DropdownComponent.of(translate(text), optionsList, defaultOption)); + } + + public Builder dropdown(String text, String... options) { + return dropdown(text, -1, options); + } + + public Builder input(String text, String placeholder, String defaultText) { + return component(InputComponent.of( + translate(text), translate(placeholder), translate(defaultText) + )); + } + + public Builder input(String text, String placeholder) { + return component(InputComponent.of(translate(text), translate(placeholder))); + } + + public Builder input(String text) { + return component(InputComponent.of(translate(text))); + } + + public Builder label(String text) { + return component(LabelComponent.of(translate(text))); + } + + public Builder slider(String text, float min, float max, int step, float defaultValue) { + return component(SliderComponent.of(text, min, max, step, defaultValue)); + } + + public Builder slider(String text, float min, float max, int step) { + return slider(text, min, max, step, -1); + } + + public Builder slider(String text, float min, float max, float defaultValue) { + return slider(text, min, max, -1, defaultValue); + } + + public Builder slider(String text, float min, float max) { + return slider(text, min, max, -1, -1); + } + + public Builder stepSlider(StepSliderComponent.Builder stepSliderBuilder) { + return component(stepSliderBuilder.translateAndBuild(this::translate)); + } + + public Builder stepSlider(String text, int defaultStep, String... steps) { + List stepsList = new ArrayList<>(); + for (String option : steps) { + stepsList.add(translate(option)); + } + return component(StepSliderComponent.of(translate(text), stepsList, defaultStep)); + } + + public Builder stepSlider(String text, String... steps) { + return stepSlider(text, -1, steps); + } + + public Builder toggle(String text, boolean defaultValue) { + return component(ToggleComponent.of(translate(text), defaultValue)); + } + + public Builder toggle(String text) { + return component(ToggleComponent.of(translate(text))); + } + + @Override + public CustomForm build() { + CustomForm form = of(title, icon, components); + if (biResponseHandler != null) { + form.setResponseHandler(response -> biResponseHandler.accept(form, response)); + return form; + } + + form.setResponseHandler(responseHandler); + return form; + } + } +} diff --git a/common/src/main/java/org/geysermc/common/form/Form.java b/common/src/main/java/org/geysermc/common/form/Form.java new file mode 100644 index 000000000..5b3437572 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/Form.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019-2020 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.common.form; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.geysermc.common.form.response.FormResponse; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +@Getter +public abstract class Form { + protected static final Gson GSON = new Gson(); + private final Type type; + + @Getter(AccessLevel.NONE) + protected String hardcodedJsonData = null; + + @Setter protected Consumer responseHandler; + + public Form(Type type) { + this.type = type; + } + + public String getJsonData() { + if (hardcodedJsonData != null) { + return hardcodedJsonData; + } + return GSON.toJson(this); + } + + public abstract FormResponse parseResponse(String response); + + @SuppressWarnings("unchecked") + public T parseResponseAs(String response) { + return (T) parseResponse(response); + } + + public boolean isClosed(String response) { + return response == null || response.isEmpty() || response.equalsIgnoreCase("null"); + } + + @Getter + @RequiredArgsConstructor + public enum Type { + @SerializedName("form") + SIMPLE_FORM(SimpleForm.class), + @SerializedName("modal") + MODAL_FORM(ModalForm.class), + @SerializedName("custom_form") + CUSTOM_FORM(CustomForm.class); + + private static final Type[] VALUES = values(); + private final Class typeClass; + + public static Type getByOrdinal(int ordinal) { + return ordinal < VALUES.length ? VALUES[ordinal] : null; + } + } + + public static abstract class Builder, F extends Form> { + protected String title = ""; + + protected BiFunction translationHandler = null; + protected BiConsumer biResponseHandler; + protected Consumer responseHandler; + protected String locale; + + public T title(String title) { + this.title = translate(title); + return self(); + } + + public T translator(BiFunction translator, String locale) { + this.translationHandler = translator; + this.locale = locale; + return title(title); + } + + public T translator(BiFunction translator) { + return translator(translator, locale); + } + + public T responseHandler(BiConsumer responseHandler) { + biResponseHandler = responseHandler; + return self(); + } + + public T responseHandler(Consumer responseHandler) { + this.responseHandler = responseHandler; + return self(); + } + + public abstract F build(); + + protected String translate(String text) { + if (translationHandler != null && text != null && !text.isEmpty()) { + return translationHandler.apply(text, locale); + } + return text; + } + + @SuppressWarnings("unchecked") + protected T self() { + return (T) this; + } + } +} diff --git a/common/src/main/java/org/geysermc/common/form/ModalForm.java b/common/src/main/java/org/geysermc/common/form/ModalForm.java new file mode 100644 index 000000000..10309f9c2 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/ModalForm.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019-2020 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.common.form; + +import com.google.gson.annotations.JsonAdapter; +import lombok.Getter; +import org.geysermc.common.form.response.ModalFormResponse; +import org.geysermc.common.form.util.FormAdaptor; + +@Getter +@JsonAdapter(FormAdaptor.class) +public class ModalForm extends Form { + private final String title; + private final String content; + private final String button1; + private final String button2; + + private ModalForm(String title, String content, String button1, String button2) { + super(Type.MODAL_FORM); + + this.title = title; + this.content = content; + this.button1 = button1; + this.button2 = button2; + } + + public static Builder builder() { + return new Builder(); + } + + public static ModalForm of(String title, String content, String button1, String button2) { + return new ModalForm(title, content, button1, button2); + } + + public ModalFormResponse parseResponse(String data) { + if (isClosed(data)) { + return ModalFormResponse.closed(); + } + + if ("true".equals(data)) { + return ModalFormResponse.of(0, button1); + } else if ("false".equals(data)) { + return ModalFormResponse.of(1, button2); + } + return ModalFormResponse.invalid(); + } + + public static final class Builder extends Form.Builder { + private String content; + private String button1; + private String button2; + + public Builder content(String content) { + this.content = translate(content); + return this; + } + + public Builder button1(String button1) { + this.button1 = translate(button1); + return this; + } + + public Builder button2(String button2) { + this.button2 = translate(button2); + return this; + } + + @Override + public ModalForm build() { + ModalForm form = of(title, content, button1, button2); + if (biResponseHandler != null) { + form.setResponseHandler(response -> biResponseHandler.accept(form, response)); + return form; + } + + form.setResponseHandler(responseHandler); + return form; + } + } +} diff --git a/common/src/main/java/org/geysermc/common/form/SimpleForm.java b/common/src/main/java/org/geysermc/common/form/SimpleForm.java new file mode 100644 index 000000000..1a1ce616b --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/SimpleForm.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2019-2020 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.common.form; + +import com.google.gson.annotations.JsonAdapter; +import lombok.Getter; +import org.geysermc.common.form.component.ButtonComponent; +import org.geysermc.common.form.response.SimpleFormResponse; +import org.geysermc.common.form.util.FormAdaptor; +import org.geysermc.common.form.util.FormImage; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@JsonAdapter(FormAdaptor.class) +public final class SimpleForm extends Form { + private final String title; + private final String content; + private final List buttons; + + private SimpleForm(String title, String content, List buttons) { + super(Type.SIMPLE_FORM); + + this.title = title; + this.content = content; + this.buttons = Collections.unmodifiableList(buttons); + } + + public static Builder builder() { + return new Builder(); + } + + public static SimpleForm of(String title, String content, List buttons) { + return new SimpleForm(title, content, buttons); + } + + public SimpleFormResponse parseResponse(String data) { + if (isClosed(data)) { + return SimpleFormResponse.closed(); + } + + int buttonId; + try { + buttonId = Integer.parseInt(data); + } catch (Exception exception) { + return SimpleFormResponse.invalid(); + } + + if (buttonId >= buttons.size()) { + return SimpleFormResponse.invalid(); + } + + return SimpleFormResponse.of(buttonId, buttons.get(buttonId)); + } + + public static final class Builder extends Form.Builder { + private final List buttons = new ArrayList<>(); + private String content; + + public Builder content(String content) { + this.content = translate(content); + return this; + } + + public Builder button(String text, FormImage.Type type, String data) { + buttons.add(ButtonComponent.of(translate(text), type, data)); + return this; + } + + public Builder button(String text, FormImage image) { + buttons.add(ButtonComponent.of(translate(text), image)); + return this; + } + + public Builder button(String text) { + buttons.add(ButtonComponent.of(translate(text))); + return this; + } + + @Override + public SimpleForm build() { + SimpleForm form = of(title, content, buttons); + if (biResponseHandler != null) { + form.setResponseHandler(response -> biResponseHandler.accept(form, response)); + return form; + } + + form.setResponseHandler(responseHandler); + return form; + } + } +} diff --git a/common/src/main/java/org/geysermc/common/window/button/FormButton.java b/common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java similarity index 65% rename from common/src/main/java/org/geysermc/common/window/button/FormButton.java rename to common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java index 6daa2feae..de241e3dd 100644 --- a/common/src/main/java/org/geysermc/common/window/button/FormButton.java +++ b/common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java @@ -23,35 +23,28 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.button; +package org.geysermc.common.form.component; +import lombok.AccessLevel; import lombok.Getter; -import lombok.Setter; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.util.FormImage; -public class FormButton { +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ButtonComponent { + private final String text; + private final FormImage image; - @Getter - @Setter - private String text; - - @Getter - private FormImage image; - - public FormButton(String text) { - this.text = text; + public static ButtonComponent of(String text, FormImage image) { + return new ButtonComponent(text, image); } - public FormButton(String text, FormImage image) { - this.text = text; - - if (image.getData() != null && !image.getData().isEmpty()) { - this.image = image; - } + public static ButtonComponent of(String text, FormImage.Type type, String data) { + return of(text, FormImage.of(type, data)); } - public void setImage(FormImage image) { - if (image.getData() != null && !image.getData().isEmpty()) { - this.image = image; - } + public static ButtonComponent of(String text) { + return of(text, null); } } diff --git a/common/src/main/java/org/geysermc/common/form/component/Component.java b/common/src/main/java/org/geysermc/common/form/component/Component.java new file mode 100644 index 000000000..8f44a2a0a --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/component/Component.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019-2020 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.common.form.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Objects; + +@Getter +public abstract class Component { + private final Type type; + private final String text; + + Component(Type type, String text) { + Objects.requireNonNull(type, "Type cannot be null"); + Objects.requireNonNull(text, "Text cannot be null"); + + this.type = type; + this.text = text; + } + + @Getter + @RequiredArgsConstructor + public enum Type { + @SerializedName("dropdown") + DROPDOWN(DropdownComponent.class), + @SerializedName("input") + INPUT(InputComponent.class), + @SerializedName("label") + LABEL(LabelComponent.class), + @SerializedName("slider") + SLIDER(SliderComponent.class), + @SerializedName("step_slider") + STEP_SLIDER(StepSliderComponent.class), + @SerializedName("toggle") + TOGGLE(ToggleComponent.class); + + private static final Type[] VALUES = values(); + + private final String name = name().toLowerCase(); + private final Class componentClass; + + public static Type getByName(String name) { + for (Type type : VALUES) { + if (type.name.equals(name)) { + return type; + } + } + return null; + } + } +} diff --git a/common/src/main/java/org/geysermc/common/form/component/DropdownComponent.java b/common/src/main/java/org/geysermc/common/form/component/DropdownComponent.java new file mode 100644 index 000000000..6bf3f5e1a --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/component/DropdownComponent.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019-2020 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.common.form.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +@Getter +public class DropdownComponent extends Component { + private final List options; + @SerializedName("default") + private final int defaultOption; + + private DropdownComponent(String text, List options, int defaultOption) { + super(Type.DROPDOWN, text); + this.options = Collections.unmodifiableList(options); + this.defaultOption = defaultOption; + } + + public static DropdownComponent of(String text, List options, int defaultOption) { + if (defaultOption == -1 || defaultOption >= options.size()) { + defaultOption = 0; + } + return new DropdownComponent(text, options, defaultOption); + } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(String text) { + return builder().text(text); + } + + public static class Builder { + private final List options = new ArrayList<>(); + private String text; + private int defaultOption = 0; + + public Builder text(String text) { + this.text = text; + return this; + } + + public Builder option(String option, boolean isDefault) { + options.add(option); + if (isDefault) { + defaultOption = options.size() - 1; + } + return this; + } + + public Builder option(String option) { + return option(option, false); + } + + public Builder defaultOption(int defaultOption) { + this.defaultOption = defaultOption; + return this; + } + + public DropdownComponent build() { + return of(text, options, defaultOption); + } + + public DropdownComponent translateAndBuild(Function translator) { + for (int i = 0; i < options.size(); i++) { + options.set(i, translator.apply(options.get(i))); + } + + return of(translator.apply(text), options, defaultOption); + } + } +} diff --git a/common/src/main/java/org/geysermc/common/window/component/InputComponent.java b/common/src/main/java/org/geysermc/common/form/component/InputComponent.java similarity index 62% rename from common/src/main/java/org/geysermc/common/window/component/InputComponent.java rename to common/src/main/java/org/geysermc/common/form/component/InputComponent.java index fad6a0fed..b5e07535f 100644 --- a/common/src/main/java/org/geysermc/common/window/component/InputComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/InputComponent.java @@ -23,30 +23,32 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.common.form.component; +import com.google.gson.annotations.SerializedName; import lombok.Getter; -import lombok.Setter; -public class InputComponent extends FormComponent { +@Getter +public class InputComponent extends Component { + private final String placeholder; + @SerializedName("default") + private final String defaultText; - @Getter - @Setter - private String text; - - @Getter - @Setter - private String placeholder; - - @Getter - @Setter - private String defaultText; - - public InputComponent(String text, String placeholder, String defaultText) { - super("input"); - - this.text = text; + private InputComponent(String text, String placeholder, String defaultText) { + super(Type.INPUT, text); this.placeholder = placeholder; this.defaultText = defaultText; } + + public static InputComponent of(String text, String placeholder, String defaultText) { + return new InputComponent(text, placeholder, defaultText); + } + + public static InputComponent of(String text, String placeholder) { + return new InputComponent(text, placeholder, ""); + } + + public static InputComponent of(String text) { + return new InputComponent(text, "", ""); + } } diff --git a/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java b/common/src/main/java/org/geysermc/common/form/component/LabelComponent.java similarity index 81% rename from common/src/main/java/org/geysermc/common/window/component/LabelComponent.java rename to common/src/main/java/org/geysermc/common/form/component/LabelComponent.java index a76b313fa..410f14a25 100644 --- a/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/LabelComponent.java @@ -23,20 +23,17 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.common.form.component; import lombok.Getter; -import lombok.Setter; -public class LabelComponent extends FormComponent { +@Getter +public class LabelComponent extends Component { + private LabelComponent(String text) { + super(Type.LABEL, text); + } - @Getter - @Setter - private String text; - - public LabelComponent(String text) { - super("label"); - - this.text = text; + public static LabelComponent of(String text) { + return new LabelComponent(text); } } diff --git a/common/src/main/java/org/geysermc/common/form/component/SliderComponent.java b/common/src/main/java/org/geysermc/common/form/component/SliderComponent.java new file mode 100644 index 000000000..0eb019e3a --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/component/SliderComponent.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019-2020 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.common.form.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; + +@Getter +public final class SliderComponent extends Component { + private final float min; + private final float max; + private final int step; + @SerializedName("default") + private final float defaultValue; + + private SliderComponent(String text, float min, float max, int step, float defaultValue) { + super(Type.SLIDER, text); + this.min = min; + this.max = max; + this.step = step; + this.defaultValue = defaultValue; + } + + public static SliderComponent of(String text, float min, float max, int step, float defaultValue) { + min = Math.max(min, 0f); + max = Math.max(max, min); + + if (step < 1) { + step = 1; + } + + if (defaultValue == -1f) { + defaultValue = (int) Math.floor(min + max / 2D); + } + + return new SliderComponent(text, min, max, step, defaultValue); + } + + public static SliderComponent of(String text, float min, float max, int step) { + return of(text, min, max, step, -1); + } + + public static SliderComponent of(String text, float min, float max, float defaultValue) { + return of(text, min, max, -1, defaultValue); + } + + public static SliderComponent of(String text, float min, float max) { + return of(text, min, max, -1, -1); + } +} diff --git a/common/src/main/java/org/geysermc/common/form/component/StepSliderComponent.java b/common/src/main/java/org/geysermc/common/form/component/StepSliderComponent.java new file mode 100644 index 000000000..4d6d8fcb2 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/component/StepSliderComponent.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019-2020 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.common.form.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +@Getter +public final class StepSliderComponent extends Component { + private final List steps; + @SerializedName("default") + private final int defaultStep; + + private StepSliderComponent(String text, List steps, int defaultStep) { + super(Type.STEP_SLIDER, text); + this.steps = Collections.unmodifiableList(steps); + this.defaultStep = defaultStep; + } + + public static StepSliderComponent of(String text, List steps, int defaultStep) { + if (text == null) { + text = ""; + } + + if (defaultStep >= steps.size() || defaultStep == -1) { + defaultStep = 0; + } + + return new StepSliderComponent(text, steps, defaultStep); + } + + public static StepSliderComponent of(String text, int defaultStep, String... steps) { + return of(text, Arrays.asList(steps), defaultStep); + } + + public static StepSliderComponent of(String text, String... steps) { + return of(text, 0, steps); + } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(String text) { + return builder().text(text); + } + + public static final class Builder { + private final List steps = new ArrayList<>(); + private String text; + private int defaultStep; + + public Builder text(String text) { + this.text = text; + return this; + } + + public Builder step(String step, boolean defaultStep) { + steps.add(step); + if (defaultStep) { + this.defaultStep = steps.size() - 1; + } + return this; + } + + public Builder step(String step) { + return step(step, false); + } + + public Builder defaultStep(int defaultStep) { + this.defaultStep = defaultStep; + return this; + } + + public StepSliderComponent build() { + return of(text, steps, defaultStep); + } + + public StepSliderComponent translateAndBuild(Function translator) { + for (int i = 0; i < steps.size(); i++) { + steps.set(i, translator.apply(steps.get(i))); + } + + return of(translator.apply(text), steps, defaultStep); + } + } +} diff --git a/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java b/common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java similarity index 70% rename from common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java rename to common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java index f972d5906..08341aaca 100644 --- a/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java @@ -23,29 +23,26 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.common.form.component; +import com.google.gson.annotations.SerializedName; import lombok.Getter; -import lombok.Setter; -public class ToggleComponent extends FormComponent { +@Getter +public class ToggleComponent extends Component { + @SerializedName("default") + private final boolean defaultValue; - @Getter - @Setter - private String text; - - @Getter - @Setter - private boolean defaultValue; - - public ToggleComponent(String text) { - this(text, false); - } - - public ToggleComponent(String text, boolean defaultValue) { - super("toggle"); - - this.text = text; + private ToggleComponent(String text, boolean defaultValue) { + super(Type.TOGGLE, text); this.defaultValue = defaultValue; } + + public static ToggleComponent of(String text, boolean defaultValue) { + return new ToggleComponent(text, defaultValue); + } + + public static ToggleComponent of(String text) { + return of(text, false); + } } diff --git a/common/src/main/java/org/geysermc/common/form/response/CustomFormResponse.java b/common/src/main/java/org/geysermc/common/form/response/CustomFormResponse.java new file mode 100644 index 000000000..8ef50c7fb --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/response/CustomFormResponse.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2019-2020 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.common.form.response; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonPrimitive; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.geysermc.common.form.CustomForm; +import org.geysermc.common.form.component.Component; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +public class CustomFormResponse implements FormResponse { + private static final Gson GSON = new Gson(); + private static final CustomFormResponse CLOSED = + new CustomFormResponse(true, false, null, null); + private static final CustomFormResponse INVALID = + new CustomFormResponse(false, true, null, null); + private final boolean closed; + private final boolean invalid; + + private final JsonArray responses; + private final List componentTypes; + + private int index = -1; + + public static CustomFormResponse closed() { + return CLOSED; + } + + public static CustomFormResponse invalid() { + return INVALID; + } + + public static CustomFormResponse of(CustomForm form, String responseData) { + JsonArray responses = GSON.fromJson(responseData, JsonArray.class); + List types = new ArrayList<>(); + for (Component component : form.getContent()) { + types.add(component.getType()); + } + return of(types, responses); + } + + public static CustomFormResponse of(List componentTypes, + JsonArray responses) { + if (componentTypes.size() != responses.size()) { + return invalid(); + } + + return new CustomFormResponse(false, false, responses, + Collections.unmodifiableList(componentTypes)); + } + + @SuppressWarnings("unchecked") + public T next(boolean includeLabels) { + if (!hasNext()) { + return null; + } + + while (++index < responses.size()) { + Component.Type type = componentTypes.get(index); + if (type == Component.Type.LABEL && !includeLabels) { + continue; + } + return (T) getDataFromType(type, index); + } + return null; // we don't have anything to check anymore + } + + public T next() { + return next(false); + } + + public void skip(int amount) { + index += amount; + } + + public void skip() { + skip(1); + } + + public void index(int index) { + this.index = index; + } + + public boolean hasNext() { + return responses.size() > index + 1; + } + + public JsonPrimitive get(int index) { + try { + return responses.get(index).getAsJsonPrimitive(); + } catch (IllegalStateException exception) { + wrongType(index, "a primitive"); + return null; + } + } + + public int getDropdown(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isNumber()) { + wrongType(index, "dropdown"); + } + return primitive.getAsInt(); + } + + public String getInput(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isString()) { + wrongType(index, "input"); + } + return primitive.getAsString(); + } + + public float getSlider(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isNumber()) { + wrongType(index, "slider"); + } + return primitive.getAsFloat(); + } + + public int getStepSlide(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isNumber()) { + wrongType(index, "step slider"); + } + return primitive.getAsInt(); + } + + public boolean getToggle(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isBoolean()) { + wrongType(index, "toggle"); + } + return primitive.getAsBoolean(); + } + + private Object getDataFromType(Component.Type type, int index) { + switch (type) { + case DROPDOWN: + return getDropdown(index); + case INPUT: + return getInput(index); + case SLIDER: + return getSlider(index); + case STEP_SLIDER: + return getStepSlide(index); + case TOGGLE: + return getToggle(index); + default: + return null; // label e.g. is always null + } + } + + private void wrongType(int index, String expected) { + throw new IllegalStateException(String.format( + "Expected %s on %s, got %s", + expected, index, responses.get(index).toString())); + } +} diff --git a/common/src/main/java/org/geysermc/common/window/response/FormResponse.java b/common/src/main/java/org/geysermc/common/form/response/FormResponse.java similarity index 87% rename from common/src/main/java/org/geysermc/common/window/response/FormResponse.java rename to common/src/main/java/org/geysermc/common/form/response/FormResponse.java index 2be646837..7c62236f6 100644 --- a/common/src/main/java/org/geysermc/common/window/response/FormResponse.java +++ b/common/src/main/java/org/geysermc/common/form/response/FormResponse.java @@ -23,7 +23,13 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.response; +package org.geysermc.common.form.response; public interface FormResponse { + boolean isClosed(); + boolean isInvalid(); + + default boolean isCorrect() { + return !isClosed() && !isInvalid(); + } } diff --git a/common/src/main/java/org/geysermc/common/window/FormWindow.java b/common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java similarity index 55% rename from common/src/main/java/org/geysermc/common/window/FormWindow.java rename to common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java index c3cc4258b..b0e4de883 100644 --- a/common/src/main/java/org/geysermc/common/window/FormWindow.java +++ b/common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java @@ -23,37 +23,38 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window; +package org.geysermc.common.form.response; -import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AccessLevel; import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.response.FormResponse; +import lombok.RequiredArgsConstructor; -public abstract class FormWindow { +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ModalFormResponse implements FormResponse { + private static final ModalFormResponse CLOSED = + new ModalFormResponse(true, false, -1, null); + private static final ModalFormResponse INVALID = + new ModalFormResponse(false, true, -1, null); + private final boolean closed; + private final boolean invalid; - @Getter - private final String type; + private final int clickedButtonId; + private final String clickedButtonText; - @Getter - protected FormResponse response; - - @Getter - @Setter - protected boolean closed; - - public FormWindow(String type) { - this.type = type; + public static ModalFormResponse closed() { + return CLOSED; } - // Lombok won't work here, so we need to make our own method - public void setResponse(FormResponse response) { - this.response = response; + public static ModalFormResponse invalid() { + return INVALID; } - @JsonIgnore - public abstract String getJSONData(); - - public abstract void setResponse(String response); + public static ModalFormResponse of(int clickedButtonId, String clickedButtonText) { + return new ModalFormResponse(false, false, clickedButtonId, clickedButtonText); + } + public boolean getResult() { + return clickedButtonId == 0; + } } diff --git a/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java b/common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java similarity index 56% rename from common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java rename to common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java index 8f128d1a4..6ee819f37 100644 --- a/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java +++ b/common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java @@ -23,47 +23,33 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.common.form.response; +import lombok.AccessLevel; import lombok.Getter; -import lombok.Setter; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.component.ButtonComponent; -import java.util.ArrayList; -import java.util.List; +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class SimpleFormResponse implements FormResponse { + private static final SimpleFormResponse CLOSED = new SimpleFormResponse(true, false, -1, null); + private static final SimpleFormResponse INVALID = new SimpleFormResponse(false, true, -1, null); + private final boolean closed; + private final boolean invalid; -public class StepSliderComponent extends FormComponent { + private final int clickedButtonId; + private final ButtonComponent clickedButton; - @Getter - @Setter - private String text; - - @Getter - private List steps; - - @Getter - @Setter - private int defaultStepIndex; - - public StepSliderComponent(String text) { - this(text, new ArrayList()); + public static SimpleFormResponse closed() { + return CLOSED; } - public StepSliderComponent(String text, List steps) { - this(text, steps, 0); + public static SimpleFormResponse invalid() { + return INVALID; } - public StepSliderComponent(String text, List steps, int defaultStepIndex) { - super("step_slider"); - - this.text = text; - this.steps = steps; - this.defaultStepIndex = defaultStepIndex; - } - - public void addStep(String step, boolean isDefault) { - steps.add(step); - - if (isDefault) - defaultStepIndex = steps.size() - 1; + public static SimpleFormResponse of(int clickedButtonId, ButtonComponent clickedButton) { + return new SimpleFormResponse(false, false, clickedButtonId, clickedButton); } } diff --git a/common/src/main/java/org/geysermc/common/form/util/FormAdaptor.java b/common/src/main/java/org/geysermc/common/form/util/FormAdaptor.java new file mode 100644 index 000000000..8c30e7e8f --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/util/FormAdaptor.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019-2020 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.common.form.util; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import org.geysermc.common.form.CustomForm; +import org.geysermc.common.form.Form; +import org.geysermc.common.form.ModalForm; +import org.geysermc.common.form.SimpleForm; +import org.geysermc.common.form.component.ButtonComponent; +import org.geysermc.common.form.component.Component; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public class FormAdaptor implements JsonDeserializer
, JsonSerializer { + private static final Type LIST_BUTTON_TYPE = + new TypeToken>() {}.getType(); + + @Override + public Form deserialize(JsonElement jsonElement, Type typeOfT, + JsonDeserializationContext context) + throws JsonParseException { + + if (!jsonElement.isJsonObject()) { + throw new JsonParseException("Form has to be a JsonObject"); + } + JsonObject json = jsonElement.getAsJsonObject(); + + if (typeOfT == SimpleForm.class) { + String title = json.get("title").getAsString(); + String content = json.get("content").getAsString(); + List buttons = context + .deserialize(json.get("buttons"), LIST_BUTTON_TYPE); + return SimpleForm.of(title, content, buttons); + } + + if (typeOfT == ModalForm.class) { + String title = json.get("title").getAsString(); + String content = json.get("content").getAsString(); + String button1 = json.get("button1").getAsString(); + String button2 = json.get("button2").getAsString(); + return ModalForm.of(title, content, button1, button2); + } + + if (typeOfT == CustomForm.class) { + String title = json.get("title").getAsString(); + FormImage icon = context.deserialize(json.get("icon"), FormImage.class); + List content = new ArrayList<>(); + + JsonArray contentArray = json.getAsJsonArray("content"); + for (JsonElement contentElement : contentArray) { + String typeName = contentElement.getAsJsonObject().get("type").getAsString(); + + Component.Type type = Component.Type.getByName(typeName); + if (type == null) { + throw new JsonParseException("Failed to find Component type " + typeName); + } + + content.add(context.deserialize(contentElement, type.getComponentClass())); + } + return CustomForm.of(title, icon, content); + } + return null; + } + + @Override + public JsonElement serialize(Form src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject result = new JsonObject(); + result.add("type", context.serialize(src.getType())); + + if (typeOfSrc == SimpleForm.class) { + SimpleForm form = (SimpleForm) src; + + result.addProperty("title", form.getTitle()); + result.addProperty("content", form.getContent()); + result.add("buttons", context.serialize(form.getButtons(), LIST_BUTTON_TYPE)); + return result; + } + + if (typeOfSrc == ModalForm.class) { + ModalForm form = (ModalForm) src; + + result.addProperty("title", form.getTitle()); + result.addProperty("content", form.getContent()); + result.addProperty("button1", form.getButton1()); + result.addProperty("button2", form.getButton2()); + return result; + } + + if (typeOfSrc == CustomForm.class) { + CustomForm form = (CustomForm) src; + + result.addProperty("title", form.getTitle()); + result.add("icon", context.serialize(form.getIcon())); + result.add("content", context.serialize(form.getContent())); + return result; + } + return null; + } +} diff --git a/common/src/main/java/org/geysermc/common/window/button/FormImage.java b/common/src/main/java/org/geysermc/common/form/util/FormImage.java similarity index 72% rename from common/src/main/java/org/geysermc/common/window/button/FormImage.java rename to common/src/main/java/org/geysermc/common/form/util/FormImage.java index 72579f7ac..876e662d0 100644 --- a/common/src/main/java/org/geysermc/common/window/button/FormImage.java +++ b/common/src/main/java/org/geysermc/common/form/util/FormImage.java @@ -23,36 +23,32 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.button; +package org.geysermc.common.form.util; +import lombok.AccessLevel; import lombok.Getter; -import lombok.Setter; +import lombok.RequiredArgsConstructor; +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class FormImage { + private final String type; + private final String data; - @Getter - @Setter - private String type; - - @Getter - @Setter - private String data; - - public FormImage(FormImageType type, String data) { - this.type = type.getName(); - this.data = data; + public static FormImage of(String type, String data) { + return new FormImage(type, data); } - public enum FormImageType { + public static FormImage of(Type type, String data) { + return of(type.getName(), data); + } + + @RequiredArgsConstructor + public enum Type { PATH("path"), URL("url"); - @Getter - private String name; - - FormImageType(String name) { - this.name = name; - } + @Getter private final String name; @Override public String toString() { diff --git a/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java b/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java deleted file mode 100644 index 004b00a96..000000000 --- a/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window; - -import lombok.Getter; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.component.FormComponent; -import org.geysermc.common.window.response.CustomFormResponse; - -public class CustomFormBuilder { - - @Getter - private CustomFormWindow form; - - public CustomFormBuilder(String title) { - form = new CustomFormWindow(title); - } - - public CustomFormBuilder setTitle(String title) { - form.setTitle(title); - return this; - } - - public CustomFormBuilder setIcon(FormImage icon) { - form.setIcon(icon); - return this; - } - - public CustomFormBuilder setResponse(String data) { - form.setResponse(data); - return this; - } - - public CustomFormBuilder setResponse(CustomFormResponse response) { - form.setResponse(response); - return this; - } - - public CustomFormBuilder addComponent(FormComponent component) { - form.addComponent(component); - return this; - } - - public CustomFormWindow build() { - return form; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java b/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java deleted file mode 100644 index efc71ae8d..000000000 --- a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.component.*; -import org.geysermc.common.window.response.CustomFormResponse; -import org.geysermc.common.window.response.FormResponseData; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class CustomFormWindow extends FormWindow { - - @Getter - @Setter - private String title; - - @Getter - @Setter - private FormImage icon; - - @Getter - private List content; - - public CustomFormWindow(String title) { - this(title, new ArrayList<>()); - } - - public CustomFormWindow(String title, List content) { - this(title, content, (FormImage) null); - } - - public CustomFormWindow(String title, List content, String icon) { - this(title, content, new FormImage(FormImage.FormImageType.URL, icon)); - } - - public CustomFormWindow(String title, List content, FormImage icon) { - super("custom_form"); - - this.title = title; - this.content = content; - this.icon = icon; - } - - public void addComponent(FormComponent component) { - content.add(component); - } - - public String getJSONData() { - String toModify = ""; - try { - toModify = new ObjectMapper().writeValueAsString(this); - } catch (JsonProcessingException e) { } - - //We need to replace this due to Java not supporting declaring class field 'default' - return toModify.replace("defaultOptionIndex", "default") - .replace("defaultText", "default") - .replace("defaultValue", "default") - .replace("defaultStepIndex", "default"); - } - - public void setResponse(String data) { - if (data == null || data.equalsIgnoreCase("null") || data.isEmpty()) { - closed = true; - return; - } - - int i = 0; - Map dropdownResponses = new HashMap(); - Map inputResponses = new HashMap(); - Map sliderResponses = new HashMap(); - Map stepSliderResponses = new HashMap(); - Map toggleResponses = new HashMap(); - Map responses = new HashMap(); - Map labelResponses = new HashMap(); - - List componentResponses = new ArrayList<>(); - try { - componentResponses = new ObjectMapper().readValue(data, new TypeReference>(){}); - } catch (IOException e) { } - - for (String response : componentResponses) { - if (i >= content.size()) { - break; - } - - FormComponent component = content.get(i); - if (component == null) - return; - - if (component instanceof LabelComponent) { - LabelComponent labelComponent = (LabelComponent) component; - labelResponses.put(i, labelComponent.getText()); - } - - if (component instanceof DropdownComponent) { - DropdownComponent dropdownComponent = (DropdownComponent) component; - String option = dropdownComponent.getOptions().get(Integer.parseInt(response)); - - dropdownResponses.put(i, new FormResponseData(Integer.parseInt(response), option)); - responses.put(i, option); - } - - if (component instanceof InputComponent) { - inputResponses.put(i, response); - responses.put(i, response); - } - - if (component instanceof SliderComponent) { - float value = Float.parseFloat(response); - sliderResponses.put(i, value); - responses.put(i, value); - } - - if (component instanceof StepSliderComponent) { - StepSliderComponent stepSliderComponent = (StepSliderComponent) component; - String step = stepSliderComponent.getSteps().get(Integer.parseInt(response)); - stepSliderResponses.put(i, new FormResponseData(Integer.parseInt(response), step)); - responses.put(i, step); - } - - if (component instanceof ToggleComponent) { - boolean answer = Boolean.parseBoolean(response); - toggleResponses.put(i, answer); - responses.put(i, answer); - } - i++; - } - - this.response = new CustomFormResponse(responses, dropdownResponses, inputResponses, - sliderResponses, stepSliderResponses, toggleResponses, labelResponses); - } -} diff --git a/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java b/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java deleted file mode 100644 index bfeafa1b0..000000000 --- a/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.response.ModalFormResponse; - -public class ModalFormWindow extends FormWindow { - - @Getter - @Setter - private String title; - - @Getter - @Setter - private String content; - - @Getter - @Setter - private String button1; - - @Getter - @Setter - private String button2; - - public ModalFormWindow(String title, String content, String button1, String button2) { - super("modal"); - - this.title = title; - this.content = content; - this.button1 = button1; - this.button2 = button2; - } - - @Override - public String getJSONData() { - try { - return new ObjectMapper().writeValueAsString(this); - } catch (JsonProcessingException e) { - return ""; - } - } - - public void setResponse(String data) { - if (data == null || data.equalsIgnoreCase("null")) { - closed = true; - return; - } - - if (Boolean.parseBoolean(data)) { - response = new ModalFormResponse(0, button1); - } else { - response = new ModalFormResponse(1, button2); - } - } -} diff --git a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java b/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java deleted file mode 100644 index 7c1acc26f..000000000 --- a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.response.SimpleFormResponse; - -import java.util.ArrayList; -import java.util.List; - - -public class SimpleFormWindow extends FormWindow { - - @Getter - @Setter - private String title; - - @Getter - @Setter - private String content; - - @Getter - @Setter - private List buttons; - - public SimpleFormWindow(String title, String content) { - this(title, content, new ArrayList()); - } - - public SimpleFormWindow(String title, String content, List buttons) { - super("form"); - - this.title = title; - this.content = content; - this.buttons = buttons; - } - - @Override - public String getJSONData() { - try { - return new ObjectMapper().writeValueAsString(this); - } catch (JsonProcessingException e) { - return ""; - } - } - - public void setResponse(String data) { - if (data == null || data.equalsIgnoreCase("null")) { - closed = true; - return; - } - - int buttonID; - try { - buttonID = Integer.parseInt(data); - } catch (Exception ex) { - return; - } - - if (buttonID >= buttons.size()) { - response = new SimpleFormResponse(buttonID, null); - return; - } - - response = new SimpleFormResponse(buttonID, buttons.get(buttonID)); - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java b/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java deleted file mode 100644 index 4dac6b043..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.component; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -public class DropdownComponent extends FormComponent { - - @Getter - @Setter - private String text; - - @Getter - @Setter - private List options; - - @Getter - @Setter - private int defaultOptionIndex; - - public DropdownComponent() { - super("dropdown"); - } - - public void addOption(String option, boolean isDefault) { - options.add(option); - if (isDefault) - defaultOptionIndex = options.size() - 1; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/FormComponent.java b/common/src/main/java/org/geysermc/common/window/component/FormComponent.java deleted file mode 100644 index 5a56ae0cc..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/FormComponent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.component; - -import lombok.Getter; - -public abstract class FormComponent { - - @Getter - private final String type; - - public FormComponent(String type) { - this.type = type; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java b/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java deleted file mode 100644 index a7a78362e..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.component; - -import lombok.Getter; -import lombok.Setter; - -public class SliderComponent extends FormComponent { - - @Getter - @Setter - private String text; - - @Getter - @Setter - private float min; - - @Getter - @Setter - private float max; - - @Getter - @Setter - private int step; - - @Getter - @Setter - private float defaultValue; - - public SliderComponent(String text, float min, float max, int step, float defaultValue) { - super("slider"); - - this.text = text; - this.min = Math.max(min, 0f); - this.max = max > this.min ? max : this.min; - if (step != -1f && step > 0) - this.step = step; - - if (defaultValue != -1f) - this.defaultValue = defaultValue; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java deleted file mode 100644 index 6cdd70978..000000000 --- a/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.geysermc.common.window.response.FormResponse; -import org.geysermc.common.window.response.FormResponseData; - -import java.util.Map; - -@Getter -@AllArgsConstructor -public class CustomFormResponse implements FormResponse { - - private Map responses; - private Map dropdownResponses; - private Map inputResponses; - private Map sliderResponses; - private Map stepSliderResponses; - private Map toggleResponses; - private Map labelResponses; -} diff --git a/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java b/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java deleted file mode 100644 index fd40be0fb..000000000 --- a/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public class FormResponseData { - - private int elementID; - private String elementContent; -} diff --git a/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java deleted file mode 100644 index e1a14039d..000000000 --- a/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.geysermc.common.window.response.FormResponse; - -@Getter -@AllArgsConstructor -public class ModalFormResponse implements FormResponse { - - private int clickedButtonId; - private String clickedButtonText; -} diff --git a/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java deleted file mode 100644 index e80d58e78..000000000 --- a/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.common.window.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.response.FormResponse; - -@Getter -@AllArgsConstructor -public class SimpleFormResponse implements FormResponse { - - private int clickedButtonId; - private FormButton clickedButton; -} diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 7e97d4298..9f6a88e18 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -39,7 +39,6 @@ import org.geysermc.connector.utils.LoginEncryptionUtils; import org.geysermc.connector.utils.MathUtils; import org.geysermc.connector.utils.ResourcePack; import org.geysermc.connector.utils.ResourcePackManifest; -import org.geysermc.connector.utils.SettingsUtils; import java.io.FileInputStream; import java.io.InputStream; @@ -139,11 +138,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ModalFormResponsePacket packet) { - if (packet.getFormId() == SettingsUtils.SETTINGS_FORM_ID) { - return SettingsUtils.handleSettingsForm(session, packet.getFormData()); - } - - return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData()); + session.getFormCache().handleResponse(packet); + return true; } private boolean couldLoginUserByName(String bedrockUsername) { @@ -170,7 +166,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) { // TODO it is safer to key authentication on something that won't change (UUID, not username) if (!couldLoginUserByName(session.getAuthData().getName())) { - LoginEncryptionUtils.showLoginWindow(session); + LoginEncryptionUtils.buildAndShowLoginWindow(session); } // else we were able to log the user in } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 42c2f2437..ec8c7f0ee 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -55,8 +55,7 @@ import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import lombok.Getter; import lombok.Setter; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.FormWindow; +import org.geysermc.common.form.Form; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; @@ -101,7 +100,7 @@ public class GeyserSession implements CommandSender { private EntityCache entityCache; private InventoryCache inventoryCache; private WorldCache worldCache; - private WindowCache windowCache; + private FormCache formCache; @Setter private TeleportCache teleportCache; @@ -192,9 +191,6 @@ public class GeyserSession implements CommandSender { private boolean reducedDebugInfo = false; - @Setter - private CustomFormWindow settingsForm; - /** * The op permission level set by the server */ @@ -239,7 +235,7 @@ public class GeyserSession implements CommandSender { /** * Stores the last text inputted into a sign. - * + *

* Bedrock sends packets every time you update the sign, Java only wants the final packet. * Until we determine that the user has finished editing, we save the sign's current status. */ @@ -256,7 +252,7 @@ public class GeyserSession implements CommandSender { this.entityCache = new EntityCache(this); this.inventoryCache = new InventoryCache(this); this.worldCache = new WorldCache(this); - this.windowCache = new WindowCache(this); + this.formCache = new FormCache(this); this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); this.inventory = new PlayerInventory(); @@ -497,7 +493,7 @@ public class GeyserSession implements CommandSender { this.entityCache = null; this.worldCache = null; this.inventoryCache = null; - this.windowCache = null; + this.formCache = null; closed = true; } @@ -533,10 +529,6 @@ public class GeyserSession implements CommandSender { return false; } - public void sendForm(FormWindow window, int id) { - windowCache.showWindow(window, id); - } - public void setRenderDistance(int renderDistance) { renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle if (renderDistance > 32) renderDistance = 32; // <3 u ViaVersion but I don't like crashing clients x) @@ -551,8 +543,12 @@ public class GeyserSession implements CommandSender { return this.upstream.getAddress(); } - public void sendForm(FormWindow window) { - windowCache.showWindow(window); + public void sendForm(Form form) { + formCache.showForm(form); + } + + public void sendForm(Form.Builder formBuilder) { + formCache.showForm(formBuilder.build()); } private void startGame() { @@ -678,7 +674,7 @@ public class GeyserSession implements CommandSender { * Send a gamerule value to the client * * @param gameRule The gamerule to send - * @param value The value of the gamerule + * @param value The value of the gamerule */ public void sendGameRule(String gameRule, Object value) { GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java similarity index 56% rename from connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java rename to connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java index 15b9a7705..20ade2869 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java @@ -26,56 +26,54 @@ package org.geysermc.connector.network.session.cache; import com.nukkitx.protocol.bedrock.packet.ModalFormRequestPacket; - +import com.nukkitx.protocol.bedrock.packet.ModalFormResponsePacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - -import lombok.Getter; - -import org.geysermc.common.window.FormWindow; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.Form; import org.geysermc.connector.network.session.GeyserSession; -public class WindowCache { +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; - private GeyserSession session; +@RequiredArgsConstructor +public class FormCache { + private final AtomicInteger formId = new AtomicInteger(0); + private final Int2ObjectMap forms = new Int2ObjectOpenHashMap<>(); + private final GeyserSession session; - @Getter - private Int2ObjectMap windows = new Int2ObjectOpenHashMap<>(); - - public WindowCache(GeyserSession session) { - this.session = session; + public int addForm(Form form) { + int windowId = formId.getAndIncrement(); + forms.put(windowId, form); + return windowId; } - public void addWindow(FormWindow window) { - windows.put(windows.size() + 1, window); + public int showForm(Form form) { + int windowId = addForm(form); + + ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); + formRequestPacket.setFormId(windowId); + formRequestPacket.setFormData(form.getJsonData()); + + session.sendUpstreamPacket(formRequestPacket); + return windowId; } - public void addWindow(FormWindow window, int id) { - windows.put(id, window); - } - - public void showWindow(FormWindow window) { - showWindow(window, windows.size() + 1); - } - - public void showWindow(int id) { - if (!windows.containsKey(id)) + public void handleResponse(ModalFormResponsePacket response) { + Form form = forms.get(response.getFormId()); + if (form == null) { return; + } - ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); - formRequestPacket.setFormId(id); - formRequestPacket.setFormData(windows.get(id).getJSONData()); + Consumer responseConsumer = form.getResponseHandler(); + if (responseConsumer != null) { + responseConsumer.accept(response.getFormData().trim()); + } - session.sendUpstreamPacket(formRequestPacket); + removeWindow(response.getFormId()); } - public void showWindow(FormWindow window, int id) { - ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); - formRequestPacket.setFormId(id); - formRequestPacket.setFormData(window.getJSONData()); - - session.sendUpstreamPacket(formRequestPacket); - - addWindow(window, id); + public boolean removeWindow(int id) { + return forms.remove(id) != null; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java index a8591cd7f..01bf07b9c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.bedrock; import com.nukkitx.protocol.bedrock.packet.ServerSettingsRequestPacket; import com.nukkitx.protocol.bedrock.packet.ServerSettingsResponsePacket; +import org.geysermc.common.form.CustomForm; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -37,11 +38,12 @@ public class BedrockServerSettingsRequestTranslator extends PacketTranslator { - - private static byte[] brandData; + private static final byte[] brandData; static { byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8); @@ -48,21 +54,11 @@ public class JavaPluginMessageTranslator extends PacketTranslator>>= 7; if (value != 0) { temp |= 0b10000000; @@ -85,4 +81,48 @@ public class JavaPluginMessageTranslator extends PacketTranslator { + byte[] raw = response.getBytes(StandardCharsets.UTF_8); + byte[] finalData = new byte[raw.length + 2]; + + finalData[0] = data[1]; + finalData[1] = data[2]; + System.arraycopy(raw, 0, finalData, 2, raw.length); + + session.sendDownstreamPacket(new ClientPluginMessagePacket(channel, finalData)); + }); + session.sendForm(form); + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 62d70f612..8109ef219 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -34,20 +34,16 @@ import com.nukkitx.network.util.Preconditions; import com.nukkitx.protocol.bedrock.packet.LoginPacket; import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket; import com.nukkitx.protocol.bedrock.util.EncryptionUtils; -import org.geysermc.common.window.CustomFormBuilder; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.FormWindow; -import org.geysermc.common.window.SimpleFormWindow; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.component.InputComponent; -import org.geysermc.common.window.component.LabelComponent; -import org.geysermc.common.window.response.CustomFormResponse; -import org.geysermc.common.window.response.SimpleFormResponse; +import org.geysermc.common.form.CustomForm; +import org.geysermc.common.form.ModalForm; +import org.geysermc.common.form.SimpleForm; +import org.geysermc.common.form.response.CustomFormResponse; +import org.geysermc.common.form.response.ModalFormResponse; +import org.geysermc.common.form.response.SimpleFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; -import org.geysermc.connector.network.session.cache.WindowCache; import javax.crypto.SecretKey; import java.io.IOException; @@ -72,7 +68,7 @@ public class LoginEncryptionUtils { } if (lastKey != null) { - if (!EncryptionUtils.verifyJwt(jwt, lastKey)) return false; + if (!EncryptionUtils.verifyJwt(jwt, lastKey)) return false; } JsonNode payloadNode = JSON_MAPPER.readTree(jwt.getPayload().toString()); @@ -159,69 +155,49 @@ public class LoginEncryptionUtils { session.sendUpstreamPacketImmediately(packet); } - private static int AUTH_FORM_ID = 1336; - private static int AUTH_DETAILS_FORM_ID = 1337; - - public static void showLoginWindow(GeyserSession session) { + public static void buildAndShowLoginWindow(GeyserSession session) { String userLanguage = session.getClientData().getLanguageCode(); - SimpleFormWindow window = new SimpleFormWindow(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.title", userLanguage), LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.desc", userLanguage)); - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_login", userLanguage))); - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_disconnect", userLanguage))); - session.sendForm(window, AUTH_FORM_ID); - } + session.sendForm( + SimpleForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, userLanguage) + .title("geyser.auth.login.form.notice.title") + .content("geyser.auth.login.form.notice.desc") + .button("geyser.auth.login.form.notice.btn_login") // id = 0 + .button("geyser.auth.login.form.notice.btn_disconnect") + .responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData.trim()); + if (!response.isCorrect()) { + buildAndShowLoginWindow(session); + return; + } - public static void showLoginDetailsWindow(GeyserSession session) { - String userLanguage = session.getClientData().getLanguageCode(); - CustomFormWindow window = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.title", userLanguage)) - .addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.desc", userLanguage))) - .addComponent(new InputComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.email", userLanguage), "account@geysermc.org", "")) - .addComponent(new InputComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.pass", userLanguage), "123456", "")) - .build(); + if (response.getClickedButtonId() == 0) { + buildAndShowLoginDetailsWindow(session); + return; + } - session.sendForm(window, AUTH_DETAILS_FORM_ID); - } - - public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, int formId, String formData) { - WindowCache windowCache = session.getWindowCache(); - if (!windowCache.getWindows().containsKey(formId)) - return false; - - if(formId == AUTH_FORM_ID || formId == AUTH_DETAILS_FORM_ID) { - FormWindow window = windowCache.getWindows().remove(formId); - window.setResponse(formData.trim()); - - if (!session.isLoggedIn()) { - if (formId == AUTH_DETAILS_FORM_ID && window instanceof CustomFormWindow) { - CustomFormWindow customFormWindow = (CustomFormWindow) window; - - CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); - if (response != null) { - String email = response.getInputResponses().get(1); - String password = response.getInputResponses().get(2); - - session.authenticate(email, password); - } else { - showLoginDetailsWindow(session); - } - - // Clear windows so authentication data isn't accidentally cached - windowCache.getWindows().clear(); - } else if (formId == AUTH_FORM_ID && window instanceof SimpleFormWindow) { - SimpleFormResponse response = (SimpleFormResponse) window.getResponse(); - if (response != null) { - if (response.getClickedButtonId() == 0) { - showLoginDetailsWindow(session); - } else if(response.getClickedButtonId() == 1) { session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getClientData().getLanguageCode())); - } - } else { - showLoginWindow(session); - } - } - } - } - return true; + })); } + public static void buildAndShowLoginDetailsWindow(GeyserSession session) { + String userLanguage = session.getClientData().getLanguageCode(); + session.sendForm( + CustomForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, userLanguage) + .title("geyser.auth.login.form.details.title") + .label("geyser.auth.login.form.details.desc") + .input("geyser.auth.login.form.details.email", "account@geysermc.org", "") + .input("geyser.auth.login.form.details.pass", "123456", "") + .responseHandler((form, responseData) -> { + CustomFormResponse response = form.parseResponse(responseData.trim()); + if (!response.isCorrect()) { + buildAndShowLoginDetailsWindow(session); + return; + } + + session.authenticate(response.next(), response.next()); + })); + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java index 89e9fe67b..a2f6aeba4 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -27,62 +27,53 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; -import org.geysermc.common.window.CustomFormBuilder; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.component.DropdownComponent; -import org.geysermc.common.window.component.InputComponent; -import org.geysermc.common.window.component.LabelComponent; -import org.geysermc.common.window.component.ToggleComponent; -import org.geysermc.common.window.response.CustomFormResponse; +import org.geysermc.common.form.CustomForm; +import org.geysermc.common.form.component.DropdownComponent; +import org.geysermc.common.form.response.CustomFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; - -import java.util.ArrayList; +import org.geysermc.connector.network.translators.world.WorldManager; public class SettingsUtils { - // Used in UpstreamPacketHandler.java - public static final int SETTINGS_FORM_ID = 1338; - /** * Build a settings form for the given session and store it for later * * @param session The session to build the form for */ - public static void buildForm(GeyserSession session) { + public static CustomForm buildForm(GeyserSession session) { // Cache the language for cleaner access String language = session.getClientData().getLanguageCode(); - CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language)); - builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png")); - - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); - builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language, session.getWorldCache().isShowCoordinates()))); + CustomForm.Builder builder = CustomForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, language) + .title("geyser.settings.title.main") + .iconPath("textures/ui/settings_glyph_color_2x.png") + .label("geyser.settings.title.client") + .toggle("geyser.settings.option.coordinates"); if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.server", language))); + builder.label("geyser.settings.title.server"); - DropdownComponent gamemodeDropdown = new DropdownComponent(); - gamemodeDropdown.setText("%createWorldScreen.gameMode.personal"); - gamemodeDropdown.setOptions(new ArrayList<>()); + DropdownComponent.Builder gamemodeDropdown = DropdownComponent.builder("%createWorldScreen.gameMode.personal"); for (GameMode gamemode : GameMode.values()) { - gamemodeDropdown.addOption(LocaleUtils.getLocaleString("selectWorld.gameMode." + gamemode.name().toLowerCase(), language), session.getGameMode() == gamemode); + gamemodeDropdown.option("selectWorld.gameMode." + gamemode.name().toLowerCase(), session.getGameMode() == gamemode); } - builder.addComponent(gamemodeDropdown); + builder.dropdown(gamemodeDropdown); - DropdownComponent difficultyDropdown = new DropdownComponent(); - difficultyDropdown.setText("%options.difficulty"); - difficultyDropdown.setOptions(new ArrayList<>()); + DropdownComponent.Builder difficultyDropdown = DropdownComponent.builder("%options.difficulty"); for (Difficulty difficulty : Difficulty.values()) { - difficultyDropdown.addOption("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty); + difficultyDropdown.option("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty); } - builder.addComponent(difficultyDropdown); + builder.dropdown(difficultyDropdown); } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.game_rules", language))); + builder.label("geyser.settings.title.game_rules") + .translator(LocaleUtils::getLocaleString); // we need translate gamerules next + + WorldManager worldManager = GeyserConnector.getInstance().getWorldManager(); for (GameRule gamerule : GameRule.values()) { if (gamerule.equals(GameRule.UNKNOWN)) { continue; @@ -90,74 +81,54 @@ public class SettingsUtils { // Add the relevant form item based on the gamerule type if (Boolean.class.equals(gamerule.getType())) { - builder.addComponent(new ToggleComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), GeyserConnector.getInstance().getWorldManager().getGameRuleBool(session, gamerule))); + builder.toggle("gamerule." + gamerule.getJavaID(), worldManager.getGameRuleBool(session, gamerule)); } else if (Integer.class.equals(gamerule.getType())) { - builder.addComponent(new InputComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), "", String.valueOf(GeyserConnector.getInstance().getWorldManager().getGameRuleInt(session, gamerule)))); + builder.input("gamerule." + gamerule.getJavaID(), "", String.valueOf(worldManager.getGameRuleInt(session, gamerule))); } } } - session.setSettingsForm(builder.build()); - } - - /** - * Handle the settings form response - * - * @param session The session that sent the response - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public static boolean handleSettingsForm(GeyserSession session, String response) { - CustomFormWindow settingsForm = session.getSettingsForm(); - settingsForm.setResponse(response); - - CustomFormResponse settingsResponse = (CustomFormResponse) settingsForm.getResponse(); - int offset = 0; - - offset++; // Client settings title - - session.getWorldCache().setShowCoordinates(settingsResponse.getToggleResponses().get(offset)); - offset++; - - if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { - offset++; // Server settings title - - GameMode gameMode = GameMode.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()]; - if (gameMode != null && gameMode != session.getGameMode()) { - session.getConnector().getWorldManager().setPlayerGameMode(session, gameMode); + builder.responseHandler((form, responseData) -> { + CustomFormResponse response = form.parseResponseAs(responseData); + if (response.isClosed() || response.isInvalid()) { + return; } - offset++; - Difficulty difficulty = Difficulty.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()]; - if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) { - session.getConnector().getWorldManager().setDifficulty(session, difficulty); - } - offset++; - } + session.getWorldCache().setShowCoordinates(response.next()); - if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { - offset++; // Game rule title - - for (GameRule gamerule : GameRule.values()) { - if (gamerule.equals(GameRule.UNKNOWN)) { - continue; + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { + GameMode gameMode = GameMode.values()[(int) response.next()]; + if (gameMode != null && gameMode != session.getGameMode()) { + session.getConnector().getWorldManager().setPlayerGameMode(session, gameMode); } - if (Boolean.class.equals(gamerule.getType())) { - Boolean value = settingsResponse.getToggleResponses().get(offset).booleanValue(); - if (value != session.getConnector().getWorldManager().getGameRuleBool(session, gamerule)) { - session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + Difficulty difficulty = Difficulty.values()[(int) response.next()]; + if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) { + session.getConnector().getWorldManager().setDifficulty(session, difficulty); + } + } + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { + for (GameRule gamerule : GameRule.values()) { + if (gamerule.equals(GameRule.UNKNOWN)) { + continue; } - } else if (Integer.class.equals(gamerule.getType())) { - int value = Integer.parseInt(settingsResponse.getInputResponses().get(offset)); - if (value != session.getConnector().getWorldManager().getGameRuleInt(session, gamerule)) { - session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + + if (Boolean.class.equals(gamerule.getType())) { + boolean value = response.next(); + if (value != session.getConnector().getWorldManager().getGameRuleBool(session, gamerule)) { + session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + } + } else if (Integer.class.equals(gamerule.getType())) { + int value = Integer.parseInt(response.next()); + if (value != session.getConnector().getWorldManager().getGameRuleInt(session, gamerule)) { + session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + } } } - offset++; } - } + }); - return true; + return builder.build(); } }