From e583abffdf6cf692a51a579fd6051321af5408db Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 26 Nov 2020 23:00:43 +0100 Subject: [PATCH] Split Forms into an Api and an implementation --- .../org/geysermc/common/PlatformType.java | 1 - .../org/geysermc/common/form/CustomForm.java | 151 +++---------- .../java/org/geysermc/common/form/Form.java | 156 ++++--------- .../org/geysermc/common/form/FormBuilder.java | 44 ++++ .../org/geysermc/common/form/FormImage.java | 62 ++++++ .../org/geysermc/common/form/FormType.java | 51 +++++ .../java/org/geysermc/common/form/Forms.java | 46 ++++ .../org/geysermc/common/form/ModalForm.java | 76 +------ .../org/geysermc/common/form/SimpleForm.java | 90 ++------ .../form/component/ButtonComponent.java | 27 +-- .../common/form/component/Component.java | 51 +---- .../common/form/component/ComponentType.java | 61 ++++++ .../form/component/DropdownComponent.java | 71 ++---- .../common/form/component/InputComponent.java | 30 +-- .../common/form/component/LabelComponent.java | 13 +- .../form/component/SliderComponent.java | 53 ++--- .../form/component/StepSliderComponent.java | 85 ++----- .../form/component/ToggleComponent.java | 22 +- .../common/form/impl/CustomFormImpl.java | 179 +++++++++++++++ .../geysermc/common/form/impl/FormImpl.java | 121 ++++++++++ .../common/form/impl/ModalFormImpl.java | 105 +++++++++ .../common/form/impl/SimpleFormImpl.java | 120 ++++++++++ .../impl/component/ButtonComponentImpl.java | 52 +++++ .../common/form/impl/component/Component.java | 45 ++++ .../impl/component/DropdownComponentImpl.java | 102 +++++++++ .../impl/component/InputComponentImpl.java | 56 +++++ .../impl/component/LabelComponentImpl.java | 41 ++++ .../impl/component/SliderComponentImpl.java | 76 +++++++ .../component/StepSliderComponentImpl.java | 118 ++++++++++ .../impl/component/ToggleComponentImpl.java | 52 +++++ .../impl/response/CustomFormResponseImpl.java | 207 ++++++++++++++++++ .../impl/response/ModalFormResponseImpl.java | 62 ++++++ .../impl/response/SimpleFormResponseImpl.java | 62 ++++++ .../form/{ => impl}/util/FormAdaptor.java | 51 +++-- .../util/FormImageImpl.java} | 28 +-- .../form/response/CustomFormResponse.java | 163 ++------------ .../form/response/ModalFormResponse.java | 34 +-- .../form/response/SimpleFormResponse.java | 27 +-- .../network/session/GeyserSession.java | 7 +- .../network/session/cache/FormCache.java | 20 +- .../BedrockNetworkStackLatencyTranslator.java | 31 ++- .../java/JavaPluginMessageTranslator.java | 6 +- .../connector/utils/SettingsUtils.java | 2 +- 43 files changed, 1965 insertions(+), 892 deletions(-) create mode 100644 common/src/main/java/org/geysermc/common/form/FormBuilder.java create mode 100644 common/src/main/java/org/geysermc/common/form/FormImage.java create mode 100644 common/src/main/java/org/geysermc/common/form/FormType.java create mode 100644 common/src/main/java/org/geysermc/common/form/Forms.java create mode 100644 common/src/main/java/org/geysermc/common/form/component/ComponentType.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/CustomFormImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/FormImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/ModalFormImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/SimpleFormImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/ButtonComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/Component.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/DropdownComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/InputComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/LabelComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/SliderComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/StepSliderComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/component/ToggleComponentImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/response/CustomFormResponseImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/response/ModalFormResponseImpl.java create mode 100644 common/src/main/java/org/geysermc/common/form/impl/response/SimpleFormResponseImpl.java rename common/src/main/java/org/geysermc/common/form/{ => impl}/util/FormAdaptor.java (71%) rename common/src/main/java/org/geysermc/common/form/{util/FormImage.java => impl/util/FormImageImpl.java} (72%) diff --git a/common/src/main/java/org/geysermc/common/PlatformType.java b/common/src/main/java/org/geysermc/common/PlatformType.java index 883490208..e3770f299 100644 --- a/common/src/main/java/org/geysermc/common/PlatformType.java +++ b/common/src/main/java/org/geysermc/common/PlatformType.java @@ -31,7 +31,6 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum PlatformType { - ANDROID("Android"), BUNGEECORD("BungeeCord"), FABRIC("Fabric"), diff --git a/common/src/main/java/org/geysermc/common/form/CustomForm.java b/common/src/main/java/org/geysermc/common/form/CustomForm.java index 5e8d93252..41097b60e 100644 --- a/common/src/main/java/org/geysermc/common/form/CustomForm.java +++ b/common/src/main/java/org/geysermc/common/form/CustomForm.java @@ -25,153 +25,62 @@ 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.component.Component; +import org.geysermc.common.form.component.DropdownComponent; +import org.geysermc.common.form.component.StepSliderComponent; +import org.geysermc.common.form.impl.CustomFormImpl; 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 interface CustomForm extends Form { + static CustomForm.Builder builder() { + return new CustomFormImpl.Builder(); } - public static Builder builder() { - return new Builder(); + static CustomForm of(String title, FormImage icon, List content) { + return CustomFormImpl.of(title, icon, content); } - public static CustomForm of(String title, FormImage icon, List content) { - return new CustomForm(title, icon, content); - } + interface Builder extends FormBuilder { + Builder icon(FormImage.Type type, String data); - public CustomFormResponse parseResponse(String data) { - if (isClosed(data)) { - return CustomFormResponse.closed(); - } - return CustomFormResponse.of(this, data); - } + Builder iconPath(String path); - public static final class Builder extends Form.Builder { - private final List components = new ArrayList<>(); - private FormImage icon; + Builder iconUrl(String url); - public Builder icon(FormImage.Type type, String data) { - icon = FormImage.of(type, data); - return this; - } + Builder component(Component component); - public Builder iconPath(String path) { - return icon(FormImage.Type.PATH, path); - } + Builder dropdown(DropdownComponent.Builder dropdownBuilder); - public Builder iconUrl(String url) { - return icon(FormImage.Type.URL, url); - } + Builder dropdown(String text, int defaultOption, String... options); - public Builder component(Component component) { - components.add(component); - return this; - } + Builder dropdown(String text, String... options); - public Builder dropdown(DropdownComponent.Builder dropdownBuilder) { - return component(dropdownBuilder.translateAndBuild(this::translate)); - } + Builder input(String text, String placeholder, String defaultText); - 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)); - } + Builder input(String text, String placeholder); - public Builder dropdown(String text, String... options) { - return dropdown(text, -1, options); - } + Builder input(String text); - public Builder input(String text, String placeholder, String defaultText) { - return component(InputComponent.of( - translate(text), translate(placeholder), translate(defaultText) - )); - } + Builder label(String text); - public Builder input(String text, String placeholder) { - return component(InputComponent.of(translate(text), translate(placeholder))); - } + Builder slider(String text, float min, float max, int step, float defaultValue); - public Builder input(String text) { - return component(InputComponent.of(translate(text))); - } + Builder slider(String text, float min, float max, int step); - public Builder label(String text) { - return component(LabelComponent.of(translate(text))); - } + Builder slider(String text, float min, float max, float defaultValue); - public Builder slider(String text, float min, float max, int step, float defaultValue) { - return component(SliderComponent.of(text, min, max, step, defaultValue)); - } + Builder slider(String text, float min, float max); - public Builder slider(String text, float min, float max, int step) { - return slider(text, min, max, step, -1); - } + Builder stepSlider(StepSliderComponent.Builder stepSliderBuilder); - public Builder slider(String text, float min, float max, float defaultValue) { - return slider(text, min, max, -1, defaultValue); - } + Builder stepSlider(String text, int defaultStep, String... steps); - public Builder slider(String text, float min, float max) { - return slider(text, min, max, -1, -1); - } + Builder stepSlider(String text, String... steps); - public Builder stepSlider(StepSliderComponent.Builder stepSliderBuilder) { - return component(stepSliderBuilder.translateAndBuild(this::translate)); - } + Builder toggle(String text, boolean defaultValue); - 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; - } + Builder toggle(String text); } } diff --git a/common/src/main/java/org/geysermc/common/form/Form.java b/common/src/main/java/org/geysermc/common/form/Form.java index 5924e6896..6fd1cbc2d 100644 --- a/common/src/main/java/org/geysermc/common/form/Form.java +++ b/common/src/main/java/org/geysermc/common/form/Form.java @@ -25,123 +25,59 @@ package org.geysermc.common.form; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -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 org.geysermc.common.form.util.FormAdaptor; -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 GsonBuilder() - .registerTypeAdapter(ModalForm.class, new FormAdaptor()) - .create(); +/** + * Base class of all Forms. While it can be used it doesn't contain every data you could get when + * using the specific class of the form type. + * + * @param class provided by the specific form type. It understands the response data and makes + * the data easily accessible + */ +public interface Form { + /** + * Returns the form type of this specific instance. The valid form types can be found {@link + * FormType in the FormType class} + */ + FormType getType(); - private final Type type; + /** + * Returns the data that will be sent by Geyser to the Bedrock client + */ + String getJsonData(); - @Getter(AccessLevel.NONE) - protected String hardcodedJsonData = null; + /** + * Returns the handler that will be invoked once the form got a response from the Bedrock + * client + */ + Consumer getResponseHandler(); - @Setter protected Consumer responseHandler; + /** + * Sets the handler that will be invoked once the form got a response from the Bedrock client. + * This handler contains the raw data sent by the Bedrock client. See {@link + * #parseResponse(String)} if you want to turn the given data into something that's easier to + * handle. + * + * @param responseHandler the response handler + */ + void setResponseHandler(Consumer responseHandler); - public Form(Type type) { - this.type = type; - } + /** + * Parses the method into something provided by the form implementation, which will make the + * data given by the Bedrock client easier to handle. + * + * @param response the raw data given by the Bedrock client + * @return the data in an easy-to-handle class + */ + T parseResponse(String response); - public static T fromJson(String json, Class formClass) { - return GSON.fromJson(json, formClass); - } - - 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; - } - } + /** + * Checks if the given data by the Bedrock client is saying that the client closed the form. + * + * @param response the raw data given by the Bedrock client + * @return true if the raw data implies that the Bedrock client closed the form + */ + boolean isClosed(String response); } diff --git a/common/src/main/java/org/geysermc/common/form/FormBuilder.java b/common/src/main/java/org/geysermc/common/form/FormBuilder.java new file mode 100644 index 000000000..f5a1ce76d --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/FormBuilder.java @@ -0,0 +1,44 @@ +/* + * 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 java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +public interface FormBuilder, F extends Form> { + T title(String title); + + T translator(BiFunction translator, String locale); + + T translator(BiFunction translator); + + T responseHandler(BiConsumer responseHandler); + + T responseHandler(Consumer responseHandler); + + F build(); +} diff --git a/common/src/main/java/org/geysermc/common/form/FormImage.java b/common/src/main/java/org/geysermc/common/form/FormImage.java new file mode 100644 index 000000000..f5672923f --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/FormImage.java @@ -0,0 +1,62 @@ +/* + * 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.SerializedName; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.impl.util.FormImageImpl; + +public interface FormImage { + static FormImage of(Type type, String data) { + return FormImageImpl.of(type, data); + } + + static FormImage of(String type, String data) { + return FormImageImpl.of(type, data); + } + + Type getType(); + + String getData(); + + @RequiredArgsConstructor + enum Type { + @SerializedName("path") PATH, + @SerializedName("url") URL; + + private static final Type[] VALUES = values(); + + public static Type getByName(String name) { + String upper = name.toUpperCase(); + for (Type value : VALUES) { + if (value.name().equals(upper)) { + return value; + } + } + return null; + } + } +} diff --git a/common/src/main/java/org/geysermc/common/form/FormType.java b/common/src/main/java/org/geysermc/common/form/FormType.java new file mode 100644 index 000000000..064dec4b3 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/FormType.java @@ -0,0 +1,51 @@ +/* + * 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.SerializedName; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.impl.CustomFormImpl; +import org.geysermc.common.form.impl.ModalFormImpl; +import org.geysermc.common.form.impl.SimpleFormImpl; + +@Getter +@RequiredArgsConstructor +public enum FormType { + @SerializedName("form") + SIMPLE_FORM(SimpleFormImpl.class), + @SerializedName("modal") + MODAL_FORM(ModalFormImpl.class), + @SerializedName("custom_form") + CUSTOM_FORM(CustomFormImpl.class); + + private static final FormType[] VALUES = values(); + private final Class typeClass; + + public static FormType getByOrdinal(int ordinal) { + return ordinal < VALUES.length ? VALUES[ordinal] : null; + } +} diff --git a/common/src/main/java/org/geysermc/common/form/Forms.java b/common/src/main/java/org/geysermc/common/form/Forms.java new file mode 100644 index 000000000..1a1c0a4cb --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/Forms.java @@ -0,0 +1,46 @@ +/* + * 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.GsonBuilder; +import org.geysermc.common.form.impl.FormImpl; +import org.geysermc.common.form.impl.util.FormAdaptor; + +public final class Forms { + private static final Gson GSON = + new GsonBuilder() + .registerTypeAdapter(FormImpl.class, new FormAdaptor()) + .create(); + + public static Gson getGson() { + return GSON; + } + + public static > T fromJson(String json, Class formClass) { + return GSON.fromJson(json, formClass); + } +} diff --git a/common/src/main/java/org/geysermc/common/form/ModalForm.java b/common/src/main/java/org/geysermc/common/form/ModalForm.java index ea58a466e..8e35bc8b0 100644 --- a/common/src/main/java/org/geysermc/common/form/ModalForm.java +++ b/common/src/main/java/org/geysermc/common/form/ModalForm.java @@ -25,79 +25,23 @@ package org.geysermc.common.form; -import com.google.gson.annotations.JsonAdapter; -import lombok.Getter; +import org.geysermc.common.form.impl.ModalFormImpl; 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 interface ModalForm extends Form { + static Builder builder() { + return new ModalFormImpl.Builder(); } - public static Builder builder() { - return new Builder(); + static ModalForm of(String title, String content, String button1, String button2) { + return ModalFormImpl.of(title, content, button1, button2); } - public static ModalForm of(String title, String content, String button1, String button2) { - return new ModalForm(title, content, button1, button2); - } + interface Builder extends FormBuilder { + Builder content(String content); - public ModalFormResponse parseResponse(String data) { - if (isClosed(data)) { - return ModalFormResponse.closed(); - } + Builder button1(String button1); - 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; - } + Builder button2(String button2); } } diff --git a/common/src/main/java/org/geysermc/common/form/SimpleForm.java b/common/src/main/java/org/geysermc/common/form/SimpleForm.java index 275fb8a86..dd11544b9 100644 --- a/common/src/main/java/org/geysermc/common/form/SimpleForm.java +++ b/common/src/main/java/org/geysermc/common/form/SimpleForm.java @@ -25,93 +25,37 @@ 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.impl.SimpleFormImpl; 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 interface SimpleForm extends Form { + static Builder builder() { + return new SimpleFormImpl.Builder(); } - public static Builder builder() { - return new Builder(); + static SimpleForm of(String title, String content, List buttons) { + return SimpleFormImpl.of(title, content, buttons); } - public static SimpleForm of(String title, String content, List buttons) { - return new SimpleForm(title, content, buttons); - } + String getTitle(); - public SimpleFormResponse parseResponse(String data) { - if (isClosed(data)) { - return SimpleFormResponse.closed(); - } + String getContent(); - int buttonId; - try { - buttonId = Integer.parseInt(data); - } catch (Exception exception) { - return SimpleFormResponse.invalid(); - } + List getButtons(); - if (buttonId >= buttons.size()) { - return SimpleFormResponse.invalid(); - } + interface Builder extends FormBuilder { + Builder content(String content); - return SimpleFormResponse.of(buttonId, buttons.get(buttonId)); - } + Builder button(String text, FormImage.Type type, String data); - public static final class Builder extends Form.Builder { - private final List buttons = new ArrayList<>(); - private String content = ""; + Builder button(String text, FormImage image); - 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; - } + Builder button(String text); } } diff --git a/common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java b/common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java index de241e3dd..3e8f5d438 100644 --- a/common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/ButtonComponent.java @@ -25,26 +25,23 @@ package org.geysermc.common.form.component; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.geysermc.common.form.util.FormImage; +import org.geysermc.common.form.FormImage; +import org.geysermc.common.form.impl.component.ButtonComponentImpl; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class ButtonComponent { - private final String text; - private final FormImage image; - - public static ButtonComponent of(String text, FormImage image) { - return new ButtonComponent(text, image); +public interface ButtonComponent { + static ButtonComponent of(String text, FormImage image) { + return ButtonComponentImpl.of(text, image); } - public static ButtonComponent of(String text, FormImage.Type type, String data) { - return of(text, FormImage.of(type, data)); + static ButtonComponent of(String text, FormImage.Type type, String data) { + return ButtonComponentImpl.of(text, type, data); } - public static ButtonComponent of(String text) { + static ButtonComponent of(String text) { return of(text, null); } + + String getText(); + + FormImage getImage(); } 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 index 8f44a2a0a..524d2a1a9 100644 --- a/common/src/main/java/org/geysermc/common/form/component/Component.java +++ b/common/src/main/java/org/geysermc/common/form/component/Component.java @@ -25,53 +25,8 @@ package org.geysermc.common.form.component; -import com.google.gson.annotations.SerializedName; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +public interface Component { + ComponentType getType(); -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; - } - } + String getText(); } diff --git a/common/src/main/java/org/geysermc/common/form/component/ComponentType.java b/common/src/main/java/org/geysermc/common/form/component/ComponentType.java new file mode 100644 index 000000000..898e10d07 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/component/ComponentType.java @@ -0,0 +1,61 @@ +/* + * 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; + +@Getter +@RequiredArgsConstructor +public enum ComponentType { + @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 ComponentType[] VALUES = values(); + + private final String name = name().toLowerCase(); + private final Class componentClass; + + public static ComponentType getByName(String name) { + for (ComponentType 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 index 6bf3f5e1a..a68b3b1e2 100644 --- a/common/src/main/java/org/geysermc/common/form/component/DropdownComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/DropdownComponent.java @@ -25,78 +25,39 @@ package org.geysermc.common.form.component; -import com.google.gson.annotations.SerializedName; -import lombok.Getter; +import org.geysermc.common.form.impl.component.DropdownComponentImpl; -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 interface DropdownComponent extends Component { + static DropdownComponent of(String text, List options, int defaultOption) { + return DropdownComponentImpl.of(text, options, 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); + static Builder builder() { + return new DropdownComponentImpl.Builder(); } - public static Builder builder() { - return new Builder(); - } - - public static Builder builder(String text) { + 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; + List getOptions(); - public Builder text(String text) { - this.text = text; - return this; - } + int getDefaultOption(); - public Builder option(String option, boolean isDefault) { - options.add(option); - if (isDefault) { - defaultOption = options.size() - 1; - } - return this; - } + interface Builder { + Builder text(String text); - public Builder option(String option) { - return option(option, false); - } + Builder option(String option, boolean isDefault); - public Builder defaultOption(int defaultOption) { - this.defaultOption = defaultOption; - return this; - } + Builder option(String option); - public DropdownComponent build() { - return of(text, options, defaultOption); - } + Builder defaultOption(int defaultOption); - public DropdownComponent translateAndBuild(Function translator) { - for (int i = 0; i < options.size(); i++) { - options.set(i, translator.apply(options.get(i))); - } + DropdownComponent build(); - return of(translator.apply(text), options, defaultOption); - } + DropdownComponent translateAndBuild(Function translator); } } diff --git a/common/src/main/java/org/geysermc/common/form/component/InputComponent.java b/common/src/main/java/org/geysermc/common/form/component/InputComponent.java index b5e07535f..4f054957c 100644 --- a/common/src/main/java/org/geysermc/common/form/component/InputComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/InputComponent.java @@ -25,30 +25,22 @@ package org.geysermc.common.form.component; -import com.google.gson.annotations.SerializedName; -import lombok.Getter; +import org.geysermc.common.form.impl.component.InputComponentImpl; -@Getter -public class InputComponent extends Component { - private final String placeholder; - @SerializedName("default") - private final String defaultText; - - private InputComponent(String text, String placeholder, String defaultText) { - super(Type.INPUT, text); - this.placeholder = placeholder; - this.defaultText = defaultText; +public interface InputComponent extends Component { + static InputComponent of(String text, String placeholder, String defaultText) { + return InputComponentImpl.of(text, placeholder, defaultText); } - public static InputComponent of(String text, String placeholder, String defaultText) { - return new InputComponent(text, placeholder, defaultText); + static InputComponent of(String text, String placeholder) { + return InputComponentImpl.of(text, placeholder, ""); } - public static InputComponent of(String text, String placeholder) { - return new InputComponent(text, placeholder, ""); + static InputComponent of(String text) { + return InputComponentImpl.of(text, "", ""); } - public static InputComponent of(String text) { - return new InputComponent(text, "", ""); - } + String getPlaceholder(); + + String getDefaultText(); } diff --git a/common/src/main/java/org/geysermc/common/form/component/LabelComponent.java b/common/src/main/java/org/geysermc/common/form/component/LabelComponent.java index 410f14a25..ed6f47029 100644 --- a/common/src/main/java/org/geysermc/common/form/component/LabelComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/LabelComponent.java @@ -25,15 +25,10 @@ package org.geysermc.common.form.component; -import lombok.Getter; +import org.geysermc.common.form.impl.component.LabelComponentImpl; -@Getter -public class LabelComponent extends Component { - private LabelComponent(String text) { - super(Type.LABEL, text); - } - - public static LabelComponent of(String text) { - return new LabelComponent(text); +public interface LabelComponent extends Component { + static LabelComponent of(String text) { + return LabelComponentImpl.of(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 index 0eb019e3a..9a0e3343e 100644 --- a/common/src/main/java/org/geysermc/common/form/component/SliderComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/SliderComponent.java @@ -25,49 +25,30 @@ package org.geysermc.common.form.component; -import com.google.gson.annotations.SerializedName; -import lombok.Getter; +import org.geysermc.common.form.impl.component.SliderComponentImpl; -@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 interface SliderComponent extends Component { + static SliderComponent of(String text, float min, float max, int step, float defaultValue) { + return SliderComponentImpl.of(text, min, max, step, 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); + static SliderComponent of(String text, float min, float max, int step) { + return SliderComponentImpl.of(text, min, max, step); } - public static SliderComponent of(String text, float min, float max, int step) { - return of(text, min, max, step, -1); + static SliderComponent of(String text, float min, float max, float defaultValue) { + return SliderComponentImpl.of(text, min, max, defaultValue); } - public static SliderComponent of(String text, float min, float max, float defaultValue) { - return of(text, min, max, -1, defaultValue); + static SliderComponent of(String text, float min, float max) { + return SliderComponentImpl.of(text, min, max); } - public static SliderComponent of(String text, float min, float max) { - return of(text, min, max, -1, -1); - } + float getMin(); + + float getMax(); + + int getStep(); + + float getDefaultValue(); } 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 index 4d6d8fcb2..cda4e7acb 100644 --- a/common/src/main/java/org/geysermc/common/form/component/StepSliderComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/StepSliderComponent.java @@ -25,92 +25,47 @@ package org.geysermc.common.form.component; -import com.google.gson.annotations.SerializedName; -import lombok.Getter; +import org.geysermc.common.form.impl.component.StepSliderComponentImpl; -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 interface StepSliderComponent extends Component { + static StepSliderComponent of(String text, List steps, int defaultStep) { + return StepSliderComponentImpl.of(text, steps, 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); + static StepSliderComponent of(String text, int defaultStep, String... steps) { + return StepSliderComponentImpl.of(text, defaultStep, steps); } - public static StepSliderComponent of(String text, int defaultStep, String... steps) { - return of(text, Arrays.asList(steps), defaultStep); + static StepSliderComponent of(String text, String... steps) { + return StepSliderComponentImpl.of(text, steps); } - public static StepSliderComponent of(String text, String... steps) { - return of(text, 0, steps); + static Builder builder() { + return new StepSliderComponentImpl.Builder(); } - public static Builder builder() { - return new Builder(); - } - - public static Builder builder(String text) { + 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; + List getSteps(); - public Builder text(String text) { - this.text = text; - return this; - } + int getDefaultStep(); - public Builder step(String step, boolean defaultStep) { - steps.add(step); - if (defaultStep) { - this.defaultStep = steps.size() - 1; - } - return this; - } + interface Builder { + Builder text(String text); - public Builder step(String step) { - return step(step, false); - } + Builder step(String step, boolean defaultStep); - public Builder defaultStep(int defaultStep) { - this.defaultStep = defaultStep; - return this; - } + Builder step(String step); - public StepSliderComponent build() { - return of(text, steps, defaultStep); - } + Builder defaultStep(int defaultStep); - public StepSliderComponent translateAndBuild(Function translator) { - for (int i = 0; i < steps.size(); i++) { - steps.set(i, translator.apply(steps.get(i))); - } + StepSliderComponent build(); - return of(translator.apply(text), steps, defaultStep); - } + StepSliderComponent translateAndBuild(Function translator); } } diff --git a/common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java b/common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java index 08341aaca..e505e995b 100644 --- a/common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java +++ b/common/src/main/java/org/geysermc/common/form/component/ToggleComponent.java @@ -25,24 +25,16 @@ package org.geysermc.common.form.component; -import com.google.gson.annotations.SerializedName; -import lombok.Getter; +import org.geysermc.common.form.impl.component.ToggleComponentImpl; -@Getter -public class ToggleComponent extends Component { - @SerializedName("default") - private final boolean defaultValue; - - private ToggleComponent(String text, boolean defaultValue) { - super(Type.TOGGLE, text); - this.defaultValue = defaultValue; +public interface ToggleComponent extends Component { + static ToggleComponent of(String text, boolean defaultValue) { + return ToggleComponentImpl.of(text, defaultValue); } - public static ToggleComponent of(String text, boolean defaultValue) { - return new ToggleComponent(text, defaultValue); + static ToggleComponent of(String text) { + return ToggleComponentImpl.of(text); } - public static ToggleComponent of(String text) { - return of(text, false); - } + boolean getDefaultValue(); } diff --git a/common/src/main/java/org/geysermc/common/form/impl/CustomFormImpl.java b/common/src/main/java/org/geysermc/common/form/impl/CustomFormImpl.java new file mode 100644 index 000000000..4d1f80713 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/CustomFormImpl.java @@ -0,0 +1,179 @@ +/* + * 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.impl; + +import com.google.gson.annotations.JsonAdapter; +import lombok.Getter; +import org.geysermc.common.form.CustomForm; +import org.geysermc.common.form.FormImage; +import org.geysermc.common.form.FormType; +import org.geysermc.common.form.component.*; +import org.geysermc.common.form.impl.response.CustomFormResponseImpl; +import org.geysermc.common.form.impl.util.FormAdaptor; +import org.geysermc.common.form.impl.util.FormImageImpl; +import org.geysermc.common.form.response.CustomFormResponse; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@JsonAdapter(FormAdaptor.class) +public final class CustomFormImpl extends FormImpl implements CustomForm { + private final String title; + private final FormImage icon; + private final List content; + + private CustomFormImpl(String title, FormImage icon, List content) { + super(FormType.CUSTOM_FORM); + + this.title = title; + this.icon = icon; + this.content = Collections.unmodifiableList(content); + } + + public static CustomFormImpl of(String title, FormImage icon, List content) { + return new CustomFormImpl(title, icon, content); + } + + public CustomFormResponseImpl parseResponse(String data) { + if (isClosed(data)) { + return CustomFormResponseImpl.closed(); + } + return CustomFormResponseImpl.of(this, data); + } + + public static final class Builder extends FormImpl.Builder + implements CustomForm.Builder { + + private final List components = new ArrayList<>(); + private FormImage icon; + + public Builder icon(FormImage.Type type, String data) { + icon = FormImageImpl.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 CustomFormImpl build() { + CustomFormImpl 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/impl/FormImpl.java b/common/src/main/java/org/geysermc/common/form/impl/FormImpl.java new file mode 100644 index 000000000..e9d648b8b --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/FormImpl.java @@ -0,0 +1,121 @@ +/* + * 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.impl; + +import com.google.gson.Gson; +import lombok.Getter; +import lombok.Setter; +import org.geysermc.common.form.Form; +import org.geysermc.common.form.FormBuilder; +import org.geysermc.common.form.FormType; +import org.geysermc.common.form.Forms; +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 FormImpl implements Form { + protected static final Gson GSON = Forms.getGson(); + + private final FormType type; + protected String hardcodedJsonData = null; + @Setter protected Consumer responseHandler; + + public FormImpl(FormType type) { + this.type = type; + } + + @Override + public String getJsonData() { + if (hardcodedJsonData != null) { + return hardcodedJsonData; + } + return GSON.toJson(this); + } + + @Override + public boolean isClosed(String response) { + return response == null || response.isEmpty() || response.equalsIgnoreCase("null"); + } + + public static abstract class Builder, F extends Form> + implements FormBuilder { + + protected String title = ""; + + protected BiFunction translationHandler = null; + protected BiConsumer biResponseHandler; + protected Consumer responseHandler; + protected String locale; + + @Override + public T title(String title) { + this.title = translate(title); + return self(); + } + + @Override + public T translator(BiFunction translator, String locale) { + this.translationHandler = translator; + this.locale = locale; + return title(title); + } + + @Override + public T translator(BiFunction translator) { + return translator(translator, locale); + } + + @Override + public T responseHandler(BiConsumer responseHandler) { + biResponseHandler = responseHandler; + return self(); + } + + @Override + public T responseHandler(Consumer responseHandler) { + this.responseHandler = responseHandler; + return self(); + } + + @Override + 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/impl/ModalFormImpl.java b/common/src/main/java/org/geysermc/common/form/impl/ModalFormImpl.java new file mode 100644 index 000000000..b047dc59d --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/ModalFormImpl.java @@ -0,0 +1,105 @@ +/* + * 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.impl; + +import com.google.gson.annotations.JsonAdapter; +import lombok.Getter; +import org.geysermc.common.form.FormType; +import org.geysermc.common.form.ModalForm; +import org.geysermc.common.form.impl.response.ModalFormResponseImpl; +import org.geysermc.common.form.impl.util.FormAdaptor; +import org.geysermc.common.form.response.ModalFormResponse; + +@Getter +@JsonAdapter(FormAdaptor.class) +public final class ModalFormImpl extends FormImpl implements ModalForm { + private final String title; + private final String content; + private final String button1; + private final String button2; + + private ModalFormImpl(String title, String content, String button1, String button2) { + super(FormType.MODAL_FORM); + + this.title = title; + this.content = content; + this.button1 = button1; + this.button2 = button2; + } + + public static ModalFormImpl of(String title, String content, String button1, String button2) { + return new ModalFormImpl(title, content, button1, button2); + } + + public ModalFormResponse parseResponse(String data) { + if (isClosed(data)) { + return ModalFormResponseImpl.closed(); + } + data = data.trim(); + + if ("true".equals(data)) { + return ModalFormResponseImpl.of(0, button1); + } else if ("false".equals(data)) { + return ModalFormResponseImpl.of(1, button2); + } + return ModalFormResponseImpl.invalid(); + } + + public static final class Builder extends FormImpl.Builder + implements ModalForm.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() { + ModalFormImpl 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/impl/SimpleFormImpl.java b/common/src/main/java/org/geysermc/common/form/impl/SimpleFormImpl.java new file mode 100644 index 000000000..d5633ec05 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/SimpleFormImpl.java @@ -0,0 +1,120 @@ +/* + * 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.impl; + +import com.google.gson.annotations.JsonAdapter; +import lombok.Getter; +import org.geysermc.common.form.FormImage; +import org.geysermc.common.form.FormType; +import org.geysermc.common.form.SimpleForm; +import org.geysermc.common.form.component.ButtonComponent; +import org.geysermc.common.form.impl.component.ButtonComponentImpl; +import org.geysermc.common.form.impl.response.SimpleFormResponseImpl; +import org.geysermc.common.form.impl.util.FormAdaptor; +import org.geysermc.common.form.response.SimpleFormResponse; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@JsonAdapter(FormAdaptor.class) +public final class SimpleFormImpl extends FormImpl implements SimpleForm { + private final String title; + private final String content; + private final List buttons; + + private SimpleFormImpl(String title, String content, List buttons) { + super(FormType.SIMPLE_FORM); + + this.title = title; + this.content = content; + this.buttons = Collections.unmodifiableList(buttons); + } + + public static SimpleFormImpl of(String title, String content, List buttons) { + return new SimpleFormImpl(title, content, buttons); + } + + public SimpleFormResponse parseResponse(String data) { + if (isClosed(data)) { + return SimpleFormResponseImpl.closed(); + } + data = data.trim(); + + int buttonId; + try { + buttonId = Integer.parseInt(data); + } catch (Exception exception) { + return SimpleFormResponseImpl.invalid(); + } + + if (buttonId >= buttons.size()) { + return SimpleFormResponseImpl.invalid(); + } + + return SimpleFormResponseImpl.of(buttonId, buttons.get(buttonId)); + } + + public static final class Builder extends FormImpl.Builder + implements SimpleForm.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(ButtonComponentImpl.of(translate(text), type, data)); + return this; + } + + public Builder button(String text, FormImage image) { + buttons.add(ButtonComponentImpl.of(translate(text), image)); + return this; + } + + public Builder button(String text) { + buttons.add(ButtonComponentImpl.of(translate(text))); + return this; + } + + @Override + public SimpleForm build() { + SimpleFormImpl 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/form/impl/component/ButtonComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/ButtonComponentImpl.java new file mode 100644 index 000000000..0efac1a6d --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/ButtonComponentImpl.java @@ -0,0 +1,52 @@ +/* + * 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.impl.component; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.FormImage; +import org.geysermc.common.form.component.ButtonComponent; +import org.geysermc.common.form.impl.util.FormImageImpl; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ButtonComponentImpl implements ButtonComponent { + private final String text; + private final FormImageImpl image; + + public static ButtonComponentImpl of(String text, FormImage image) { + return new ButtonComponentImpl(text, (FormImageImpl) image); + } + + public static ButtonComponentImpl of(String text, FormImage.Type type, String data) { + return of(text, FormImageImpl.of(type, data)); + } + + public static ButtonComponentImpl of(String text) { + return of(text, null); + } +} diff --git a/common/src/main/java/org/geysermc/common/form/impl/component/Component.java b/common/src/main/java/org/geysermc/common/form/impl/component/Component.java new file mode 100644 index 000000000..ff2f29f87 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/Component.java @@ -0,0 +1,45 @@ +/* + * 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.impl.component; + +import lombok.Getter; +import org.geysermc.common.form.component.ComponentType; + +import java.util.Objects; + +@Getter +public abstract class Component { + private final ComponentType type; + private final String text; + + Component(ComponentType type, String text) { + Objects.requireNonNull(type, "Type cannot be null"); + Objects.requireNonNull(text, "Text cannot be null"); + + this.type = type; + this.text = text; + } +} diff --git a/common/src/main/java/org/geysermc/common/form/impl/component/DropdownComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/DropdownComponentImpl.java new file mode 100644 index 000000000..ae175ab74 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/DropdownComponentImpl.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.impl.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.component.DropdownComponent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +@Getter +public final class DropdownComponentImpl extends Component implements DropdownComponent { + private final List options; + @SerializedName("default") + private final int defaultOption; + + private DropdownComponentImpl(String text, List options, int defaultOption) { + super(ComponentType.DROPDOWN, text); + this.options = Collections.unmodifiableList(options); + this.defaultOption = defaultOption; + } + + public static DropdownComponentImpl of(String text, List options, int defaultOption) { + if (defaultOption == -1 || defaultOption >= options.size()) { + defaultOption = 0; + } + return new DropdownComponentImpl(text, options, defaultOption); + } + + public static class Builder implements DropdownComponent.Builder { + private final List options = new ArrayList<>(); + private String text; + private int defaultOption = 0; + + @Override + public Builder text(String text) { + this.text = text; + return this; + } + + @Override + public Builder option(String option, boolean isDefault) { + options.add(option); + if (isDefault) { + defaultOption = options.size() - 1; + } + return this; + } + + @Override + public Builder option(String option) { + return option(option, false); + } + + @Override + public Builder defaultOption(int defaultOption) { + this.defaultOption = defaultOption; + return this; + } + + @Override + public DropdownComponentImpl build() { + return of(text, options, defaultOption); + } + + @Override + public DropdownComponentImpl 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/form/impl/component/InputComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/InputComponentImpl.java new file mode 100644 index 000000000..ba741304b --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/InputComponentImpl.java @@ -0,0 +1,56 @@ +/* + * 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.impl.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.component.InputComponent; + +@Getter +public final class InputComponentImpl extends Component implements InputComponent { + private final String placeholder; + @SerializedName("default") + private final String defaultText; + + private InputComponentImpl(String text, String placeholder, String defaultText) { + super(ComponentType.INPUT, text); + this.placeholder = placeholder; + this.defaultText = defaultText; + } + + public static InputComponentImpl of(String text, String placeholder, String defaultText) { + return new InputComponentImpl(text, placeholder, defaultText); + } + + public static InputComponentImpl of(String text, String placeholder) { + return new InputComponentImpl(text, placeholder, ""); + } + + public static InputComponentImpl of(String text) { + return new InputComponentImpl(text, "", ""); + } +} diff --git a/common/src/main/java/org/geysermc/common/form/impl/component/LabelComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/LabelComponentImpl.java new file mode 100644 index 000000000..4dacb8406 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/LabelComponentImpl.java @@ -0,0 +1,41 @@ +/* + * 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.impl.component; + +import lombok.Getter; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.component.LabelComponent; + +@Getter +public final class LabelComponentImpl extends Component implements LabelComponent { + private LabelComponentImpl(String text) { + super(ComponentType.LABEL, text); + } + + public static LabelComponentImpl of(String text) { + return new LabelComponentImpl(text); + } +} diff --git a/common/src/main/java/org/geysermc/common/form/impl/component/SliderComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/SliderComponentImpl.java new file mode 100644 index 000000000..29fc18dc8 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/SliderComponentImpl.java @@ -0,0 +1,76 @@ +/* + * 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.impl.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.component.SliderComponent; + +@Getter +public final class SliderComponentImpl extends Component implements SliderComponent { + private final float min; + private final float max; + private final int step; + @SerializedName("default") + private final float defaultValue; + + private SliderComponentImpl(String text, float min, float max, int step, float defaultValue) { + super(ComponentType.SLIDER, text); + this.min = min; + this.max = max; + this.step = step; + this.defaultValue = defaultValue; + } + + public static SliderComponentImpl 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 SliderComponentImpl(text, min, max, step, defaultValue); + } + + public static SliderComponentImpl of(String text, float min, float max, int step) { + return of(text, min, max, step, -1); + } + + public static SliderComponentImpl of(String text, float min, float max, float defaultValue) { + return of(text, min, max, -1, defaultValue); + } + + public static SliderComponentImpl 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/impl/component/StepSliderComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/StepSliderComponentImpl.java new file mode 100644 index 000000000..dc87c54a5 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/StepSliderComponentImpl.java @@ -0,0 +1,118 @@ +/* + * 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.impl.component; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.component.StepSliderComponent; + +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 StepSliderComponentImpl extends Component implements StepSliderComponent { + private final List steps; + @SerializedName("default") + private final int defaultStep; + + private StepSliderComponentImpl(String text, List steps, int defaultStep) { + super(ComponentType.STEP_SLIDER, text); + this.steps = Collections.unmodifiableList(steps); + this.defaultStep = defaultStep; + } + + public static StepSliderComponentImpl of(String text, List steps, int defaultStep) { + if (text == null) { + text = ""; + } + + if (defaultStep >= steps.size() || defaultStep == -1) { + defaultStep = 0; + } + + return new StepSliderComponentImpl(text, steps, defaultStep); + } + + public static StepSliderComponentImpl of(String text, int defaultStep, String... steps) { + return of(text, Arrays.asList(steps), defaultStep); + } + + public static StepSliderComponentImpl 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 implements StepSliderComponent.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 StepSliderComponentImpl build() { + return of(text, steps, defaultStep); + } + + public StepSliderComponentImpl 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/form/impl/component/ToggleComponentImpl.java b/common/src/main/java/org/geysermc/common/form/impl/component/ToggleComponentImpl.java new file mode 100644 index 000000000..0e29462b3 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/component/ToggleComponentImpl.java @@ -0,0 +1,52 @@ +/* + * 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.impl.component; + +import com.google.gson.annotations.SerializedName; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.component.ToggleComponent; + +public final class ToggleComponentImpl extends Component implements ToggleComponent { + @SerializedName("default") + private final boolean defaultValue; + + private ToggleComponentImpl(String text, boolean defaultValue) { + super(ComponentType.TOGGLE, text); + this.defaultValue = defaultValue; + } + + public static ToggleComponentImpl of(String text, boolean defaultValue) { + return new ToggleComponentImpl(text, defaultValue); + } + + public static ToggleComponentImpl of(String text) { + return of(text, false); + } + + public boolean getDefaultValue() { + return defaultValue; + } +} diff --git a/common/src/main/java/org/geysermc/common/form/impl/response/CustomFormResponseImpl.java b/common/src/main/java/org/geysermc/common/form/impl/response/CustomFormResponseImpl.java new file mode 100644 index 000000000..cc0ce64b7 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/response/CustomFormResponseImpl.java @@ -0,0 +1,207 @@ +/* + * 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.impl.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.component.Component; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.impl.CustomFormImpl; +import org.geysermc.common.form.response.CustomFormResponse; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +public final class CustomFormResponseImpl implements CustomFormResponse { + private static final Gson GSON = new Gson(); + private static final CustomFormResponseImpl CLOSED = + new CustomFormResponseImpl(true, false, null, null); + private static final CustomFormResponseImpl INVALID = + new CustomFormResponseImpl(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 CustomFormResponseImpl closed() { + return CLOSED; + } + + public static CustomFormResponseImpl invalid() { + return INVALID; + } + + public static CustomFormResponseImpl of(CustomFormImpl 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 CustomFormResponseImpl of(List componentTypes, + JsonArray responses) { + if (componentTypes.size() != responses.size()) { + return invalid(); + } + + return new CustomFormResponseImpl(false, false, responses, + Collections.unmodifiableList(componentTypes)); + } + + @Override + @SuppressWarnings("unchecked") + public T next(boolean includeLabels) { + if (!hasNext()) { + return null; + } + + while (++index < responses.size()) { + ComponentType type = componentTypes.get(index); + if (type == ComponentType.LABEL && !includeLabels) { + continue; + } + return (T) getDataFromType(type, index); + } + return null; // we don't have anything to check anymore + } + + @Override + public T next() { + return next(false); + } + + @Override + public void skip(int amount) { + index += amount; + } + + @Override + public void skip() { + skip(1); + } + + @Override + public void index(int index) { + this.index = index; + } + + @Override + public boolean hasNext() { + return responses.size() > index + 1; + } + + @Override + public JsonPrimitive get(int index) { + try { + return responses.get(index).getAsJsonPrimitive(); + } catch (IllegalStateException exception) { + wrongType(index, "a primitive"); + return null; + } + } + + @Override + public int getDropdown(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isNumber()) { + wrongType(index, "dropdown"); + } + return primitive.getAsInt(); + } + + @Override + public String getInput(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isString()) { + wrongType(index, "input"); + } + return primitive.getAsString(); + } + + @Override + public float getSlider(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isNumber()) { + wrongType(index, "slider"); + } + return primitive.getAsFloat(); + } + + @Override + public int getStepSlide(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isNumber()) { + wrongType(index, "step slider"); + } + return primitive.getAsInt(); + } + + @Override + public boolean getToggle(int index) { + JsonPrimitive primitive = get(index); + if (!primitive.isBoolean()) { + wrongType(index, "toggle"); + } + return primitive.getAsBoolean(); + } + + private Object getDataFromType(ComponentType 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/form/impl/response/ModalFormResponseImpl.java b/common/src/main/java/org/geysermc/common/form/impl/response/ModalFormResponseImpl.java new file mode 100644 index 000000000..6d20431cd --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/response/ModalFormResponseImpl.java @@ -0,0 +1,62 @@ +/* + * 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.impl.response; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.response.FormResponse; +import org.geysermc.common.form.response.ModalFormResponse; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ModalFormResponseImpl implements ModalFormResponse { + private static final ModalFormResponseImpl CLOSED = + new ModalFormResponseImpl(true, false, -1, null); + private static final ModalFormResponseImpl INVALID = + new ModalFormResponseImpl(false, true, -1, null); + private final boolean closed; + private final boolean invalid; + + private final int clickedButtonId; + private final String clickedButtonText; + + public static ModalFormResponseImpl closed() { + return CLOSED; + } + + public static ModalFormResponseImpl invalid() { + return INVALID; + } + + public static ModalFormResponseImpl of(int clickedButtonId, String clickedButtonText) { + return new ModalFormResponseImpl(false, false, clickedButtonId, clickedButtonText); + } + + public boolean getResult() { + return clickedButtonId == 0; + } +} diff --git a/common/src/main/java/org/geysermc/common/form/impl/response/SimpleFormResponseImpl.java b/common/src/main/java/org/geysermc/common/form/impl/response/SimpleFormResponseImpl.java new file mode 100644 index 000000000..24bc39366 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/form/impl/response/SimpleFormResponseImpl.java @@ -0,0 +1,62 @@ +/* + * 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.impl.response; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.component.ButtonComponent; +import org.geysermc.common.form.response.SimpleFormResponse; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class SimpleFormResponseImpl implements SimpleFormResponse { + private static final SimpleFormResponseImpl CLOSED = + new SimpleFormResponseImpl(true, false, -1, null); + private static final SimpleFormResponseImpl INVALID = + new SimpleFormResponseImpl(false, true, -1, null); + private final boolean closed; + private final boolean invalid; + + private final int clickedButtonId; + private final ButtonComponent clickedButton; + + public static SimpleFormResponseImpl closed() { + return CLOSED; + } + + public static SimpleFormResponseImpl invalid() { + return INVALID; + } + + public static SimpleFormResponseImpl of(int clickedButtonId, ButtonComponent clickedButton) { + return new SimpleFormResponseImpl(false, false, clickedButtonId, clickedButton); + } + + public String getClickedButtonText() { + return clickedButton.getText(); + } +} diff --git a/common/src/main/java/org/geysermc/common/form/util/FormAdaptor.java b/common/src/main/java/org/geysermc/common/form/impl/util/FormAdaptor.java similarity index 71% rename from common/src/main/java/org/geysermc/common/form/util/FormAdaptor.java rename to common/src/main/java/org/geysermc/common/form/impl/util/FormAdaptor.java index 8c30e7e8f..11d7eefe4 100644 --- a/common/src/main/java/org/geysermc/common/form/util/FormAdaptor.java +++ b/common/src/main/java/org/geysermc/common/form/impl/util/FormAdaptor.java @@ -23,28 +23,31 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.form.util; +package org.geysermc.common.form.impl.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.FormImage; import org.geysermc.common.form.component.ButtonComponent; import org.geysermc.common.form.component.Component; +import org.geysermc.common.form.component.ComponentType; +import org.geysermc.common.form.impl.CustomFormImpl; +import org.geysermc.common.form.impl.FormImpl; +import org.geysermc.common.form.impl.ModalFormImpl; +import org.geysermc.common.form.impl.SimpleFormImpl; +import org.geysermc.common.form.impl.component.ButtonComponentImpl; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; -public class FormAdaptor implements JsonDeserializer
, JsonSerializer { +public final class FormAdaptor implements JsonDeserializer>, JsonSerializer> { private static final Type LIST_BUTTON_TYPE = - new TypeToken>() {}.getType(); + new TypeToken>() {}.getType(); @Override - public Form deserialize(JsonElement jsonElement, Type typeOfT, - JsonDeserializationContext context) + public FormImpl deserialize(JsonElement jsonElement, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { if (!jsonElement.isJsonObject()) { @@ -52,50 +55,50 @@ public class FormAdaptor implements JsonDeserializer, JsonSerializer } JsonObject json = jsonElement.getAsJsonObject(); - if (typeOfT == SimpleForm.class) { + if (typeOfT == SimpleFormImpl.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); + return SimpleFormImpl.of(title, content, buttons); } - if (typeOfT == ModalForm.class) { + if (typeOfT == ModalFormImpl.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); + return ModalFormImpl.of(title, content, button1, button2); } - if (typeOfT == CustomForm.class) { + if (typeOfT == CustomFormImpl.class) { String title = json.get("title").getAsString(); - FormImage icon = context.deserialize(json.get("icon"), FormImage.class); + FormImage icon = context.deserialize(json.get("icon"), FormImageImpl.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); + ComponentType type = ComponentType.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 CustomFormImpl.of(title, icon, content); } return null; } @Override - public JsonElement serialize(Form src, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize(FormImpl src, Type typeOfSrc, JsonSerializationContext context) { JsonObject result = new JsonObject(); result.add("type", context.serialize(src.getType())); - if (typeOfSrc == SimpleForm.class) { - SimpleForm form = (SimpleForm) src; + if (typeOfSrc == SimpleFormImpl.class) { + SimpleFormImpl form = (SimpleFormImpl) src; result.addProperty("title", form.getTitle()); result.addProperty("content", form.getContent()); @@ -103,8 +106,8 @@ public class FormAdaptor implements JsonDeserializer, JsonSerializer return result; } - if (typeOfSrc == ModalForm.class) { - ModalForm form = (ModalForm) src; + if (typeOfSrc == ModalFormImpl.class) { + ModalFormImpl form = (ModalFormImpl) src; result.addProperty("title", form.getTitle()); result.addProperty("content", form.getContent()); @@ -113,8 +116,8 @@ public class FormAdaptor implements JsonDeserializer, JsonSerializer return result; } - if (typeOfSrc == CustomForm.class) { - CustomForm form = (CustomForm) src; + if (typeOfSrc == CustomFormImpl.class) { + CustomFormImpl form = (CustomFormImpl) src; result.addProperty("title", form.getTitle()); result.add("icon", context.serialize(form.getIcon())); diff --git a/common/src/main/java/org/geysermc/common/form/util/FormImage.java b/common/src/main/java/org/geysermc/common/form/impl/util/FormImageImpl.java similarity index 72% rename from common/src/main/java/org/geysermc/common/form/util/FormImage.java rename to common/src/main/java/org/geysermc/common/form/impl/util/FormImageImpl.java index 876e662d0..4b8248e94 100644 --- a/common/src/main/java/org/geysermc/common/form/util/FormImage.java +++ b/common/src/main/java/org/geysermc/common/form/impl/util/FormImageImpl.java @@ -23,36 +23,24 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.form.util; +package org.geysermc.common.form.impl.util; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.geysermc.common.form.FormImage; @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public class FormImage { - private final String type; +public final class FormImageImpl implements FormImage { + private final Type type; private final String data; - public static FormImage of(String type, String data) { - return new FormImage(type, data); + public static FormImageImpl of(Type type, String data) { + return new FormImageImpl(type, data); } - public static FormImage of(Type type, String data) { - return of(type.getName(), data); - } - - @RequiredArgsConstructor - public enum Type { - PATH("path"), - URL("url"); - - @Getter private final String name; - - @Override - public String toString() { - return name; - } + public static FormImageImpl of(String type, String data) { + return of(Type.getByName(type), data); } } 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 index 8ef50c7fb..d4d0fd99e 100644 --- a/common/src/main/java/org/geysermc/common/form/response/CustomFormResponse.java +++ b/common/src/main/java/org/geysermc/common/form/response/CustomFormResponse.java @@ -25,169 +25,38 @@ 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 org.geysermc.common.form.component.ComponentType; -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; +public interface CustomFormResponse extends FormResponse { + JsonArray getResponses(); - private final JsonArray responses; - private final List componentTypes; + List getComponentTypes(); - private int index = -1; + T next(boolean includeLabels); - public static CustomFormResponse closed() { - return CLOSED; - } + T next(); - public static CustomFormResponse invalid() { - return INVALID; - } + void skip(int amount); - 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); - } + void skip(); - public static CustomFormResponse of(List componentTypes, - JsonArray responses) { - if (componentTypes.size() != responses.size()) { - return invalid(); - } + void index(int index); - return new CustomFormResponse(false, false, responses, - Collections.unmodifiableList(componentTypes)); - } + boolean hasNext(); - @SuppressWarnings("unchecked") - public T next(boolean includeLabels) { - if (!hasNext()) { - return null; - } + JsonPrimitive get(int index); - 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 - } + int getDropdown(int index); - public T next() { - return next(false); - } + String getInput(int index); - public void skip(int amount) { - index += amount; - } + float getSlider(int index); - public void skip() { - skip(1); - } + int getStepSlide(int index); - 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())); - } + boolean getToggle(int index); } diff --git a/common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java b/common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java index b0e4de883..6e594e0ee 100644 --- a/common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java +++ b/common/src/main/java/org/geysermc/common/form/response/ModalFormResponse.java @@ -25,36 +25,10 @@ package org.geysermc.common.form.response; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +public interface ModalFormResponse extends FormResponse { + int getClickedButtonId(); -@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; + String getClickedButtonText(); - private final int clickedButtonId; - private final String clickedButtonText; - - public static ModalFormResponse closed() { - return CLOSED; - } - - public static ModalFormResponse invalid() { - return INVALID; - } - - public static ModalFormResponse of(int clickedButtonId, String clickedButtonText) { - return new ModalFormResponse(false, false, clickedButtonId, clickedButtonText); - } - - public boolean getResult() { - return clickedButtonId == 0; - } + boolean getResult(); } diff --git a/common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java b/common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java index 6ee819f37..528a79ed1 100644 --- a/common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java +++ b/common/src/main/java/org/geysermc/common/form/response/SimpleFormResponse.java @@ -25,31 +25,10 @@ package org.geysermc.common.form.response; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; import org.geysermc.common.form.component.ButtonComponent; -@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 interface SimpleFormResponse extends FormResponse { + int getClickedButtonId(); - private final int clickedButtonId; - private final ButtonComponent clickedButton; - - public static SimpleFormResponse closed() { - return CLOSED; - } - - public static SimpleFormResponse invalid() { - return INVALID; - } - - public static SimpleFormResponse of(int clickedButtonId, ButtonComponent clickedButton) { - return new SimpleFormResponse(false, false, clickedButtonId, clickedButton); - } + ButtonComponent getClickedButton(); } 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 d3bb2436f..5f0a844b0 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 @@ -58,13 +58,13 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; import org.geysermc.common.form.Form; +import org.geysermc.common.form.FormBuilder; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.inventory.PlayerInventory; -import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; @@ -72,6 +72,7 @@ import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.translators.BiomeTranslator; import org.geysermc.connector.network.translators.EntityIdentifierRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.utils.*; @@ -606,11 +607,11 @@ public class GeyserSession implements CommandSender { return this.upstream.getAddress(); } - public void sendForm(Form form) { + public void sendForm(Form form) { formCache.showForm(form); } - public void sendForm(Form.Builder formBuilder) { + public void sendForm(FormBuilder formBuilder) { formCache.showForm(formBuilder.build()); } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java index 20ade2869..1f112e9ce 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java @@ -27,40 +27,50 @@ package org.geysermc.connector.network.session.cache; import com.nukkitx.protocol.bedrock.packet.ModalFormRequestPacket; import com.nukkitx.protocol.bedrock.packet.ModalFormResponsePacket; +import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.RequiredArgsConstructor; import org.geysermc.common.form.Form; import org.geysermc.connector.network.session.GeyserSession; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @RequiredArgsConstructor public class FormCache { private final AtomicInteger formId = new AtomicInteger(0); - private final Int2ObjectMap forms = new Int2ObjectOpenHashMap<>(); + private final Int2ObjectMap> forms = new Int2ObjectOpenHashMap<>(); private final GeyserSession session; - public int addForm(Form form) { + public int addForm(Form form) { int windowId = formId.getAndIncrement(); forms.put(windowId, form); return windowId; } - public int showForm(Form form) { + public int showForm(Form form) { int windowId = addForm(form); ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); formRequestPacket.setFormId(windowId); formRequestPacket.setFormData(form.getJsonData()); - session.sendUpstreamPacket(formRequestPacket); + + // Hack to fix the url image loading bug + NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket(); + latencyPacket.setFromServer(true); + latencyPacket.setTimestamp(-System.currentTimeMillis()); + session.getConnector().getGeneralThreadPool().schedule(() -> + session.sendUpstreamPacket(latencyPacket), + 500, TimeUnit.MILLISECONDS); + return windowId; } public void handleResponse(ModalFormResponsePacket response) { - Form form = forms.get(response.getFormId()); + Form form = forms.get(response.getFormId()); if (form == null) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java index d480b526f..c4ddfb4c2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java @@ -27,9 +27,16 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientKeepAlivePacket; import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; +import org.geysermc.connector.entity.attribute.Attribute; +import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; +import org.geysermc.connector.utils.AttributeUtils; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; /** * Used to send the keep alive packet back to the server @@ -39,8 +46,26 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator 0) { + // The client sends a timestamp back but it's rounded and therefore unreliable when we need the exact number + ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(session.getLastKeepAliveTimestamp()); + session.sendDownstreamPacket(keepAlivePacket); + return; + } + + // Hack to fix the url image loading bug + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); + + Attribute attribute = session.getPlayerEntity().getAttributes().get(AttributeType.EXPERIENCE_LEVEL); + if (attribute != null) { + attributesPacket.setAttributes(Collections.singletonList(AttributeUtils.getBedrockAttribute(attribute))); + } else { + attributesPacket.setAttributes(Collections.singletonList(AttributeUtils.getBedrockAttribute(AttributeType.EXPERIENCE_LEVEL.getAttribute(0)))); + } + + session.getConnector().getGeneralThreadPool().schedule( + () -> session.sendUpstreamPacket(attributesPacket), + 500, TimeUnit.MILLISECONDS); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java index 2a791d5a2..57d64ac75 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java @@ -29,6 +29,8 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessag import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPluginMessagePacket; import com.google.common.base.Charsets; import org.geysermc.common.form.Form; +import org.geysermc.common.form.FormType; +import org.geysermc.common.form.Forms; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -53,7 +55,7 @@ public class JavaPluginMessageTranslator extends PacketTranslator form = Forms.fromJson(dataString, type.getTypeClass()); form.setResponseHandler(response -> { byte[] raw = response.getBytes(StandardCharsets.UTF_8); byte[] finalData = new byte[raw.length + 2]; 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 68ca24831..d3d1c703b 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -89,7 +89,7 @@ public class SettingsUtils { } builder.responseHandler((form, responseData) -> { - CustomFormResponse response = form.parseResponseAs(responseData); + CustomFormResponse response = form.parseResponse(responseData); if (response.isClosed() || response.isInvalid()) { return; }