From af4ab9409e2142e33a6c9f7f0c59f99fce82e077 Mon Sep 17 00:00:00 2001 From: 3arthqu4ke <56741599+3arthqu4ke@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:34:49 +0100 Subject: [PATCH] [0.3.2] RadioSettings, Fixed Hmc command --- gradle.properties | 2 +- .../DescriptionArgumentTypeImpl.java | 22 ++++++ .../arguments/NameableArgumentTypeImpl.java | 6 ++ .../pingbypass/api/config/impl/Parsers.java | 28 ++++++++ .../setting/impl/AbstractSettingBuilder.java | 2 - .../api/setting/impl/types/RadioSetting.java | 48 +++++++++++++ .../impl/types/RadioSettingBuilder.java | 72 +++++++++++++++++++ .../impl/types/RegistersSettingTypes.java | 13 +++- .../setting/settings/RadioSettingTest.java | 53 ++++++++++++++ .../server/headlessmc/HmcPbCommand.java | 15 ++-- 10 files changed, 253 insertions(+), 8 deletions(-) create mode 100644 pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/DescriptionArgumentTypeImpl.java create mode 100644 pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSetting.java create mode 100644 pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSettingBuilder.java create mode 100644 pb-api/src/test/java/me/earth/pingbypass/api/setting/settings/RadioSettingTest.java diff --git a/gradle.properties b/gradle.properties index f887c61..615fa8b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx2G -version = 0.3.1 +version = 0.3.2 minecraft_version = 1.20.4 mapping_version = 1 neoforge_version = 1-beta diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/DescriptionArgumentTypeImpl.java b/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/DescriptionArgumentTypeImpl.java new file mode 100644 index 0000000..f5ac2d5 --- /dev/null +++ b/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/DescriptionArgumentTypeImpl.java @@ -0,0 +1,22 @@ +package me.earth.pingbypass.api.command.impl.arguments; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import me.earth.pingbypass.api.traits.HasDescription; +import me.earth.pingbypass.api.traits.Nameable; +import me.earth.pingbypass.api.traits.Streamable; + +import java.util.concurrent.CompletableFuture; + +public class DescriptionArgumentTypeImpl extends NameableArgumentTypeImpl implements DescriptionArgumentType { + public DescriptionArgumentTypeImpl(Streamable nameables, String type) { + super(nameables, type); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return DescriptionArgumentType.super.listSuggestions(context, builder); + } + +} diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/NameableArgumentTypeImpl.java b/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/NameableArgumentTypeImpl.java index 433b463..8ca0570 100644 --- a/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/NameableArgumentTypeImpl.java +++ b/pb-api/src/main/java/me/earth/pingbypass/api/command/impl/arguments/NameableArgumentTypeImpl.java @@ -9,6 +9,7 @@ @Getter public class NameableArgumentTypeImpl implements NameableArgumentType { + private static final NameableArgumentTypeImpl EMPTY = new NameableArgumentTypeImpl<>(Streamable.empty(), "empty"); private final Streamable nameables; private final String type; @@ -28,4 +29,9 @@ public static ExtendedRequiredArgumentBuilder(of(name, args), name); } + @SuppressWarnings("unchecked") + public static NameableArgumentType empty() { + return (NameableArgumentType) EMPTY; + } + } diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/config/impl/Parsers.java b/pb-api/src/main/java/me/earth/pingbypass/api/config/impl/Parsers.java index 108e7ca..fbec108 100644 --- a/pb-api/src/main/java/me/earth/pingbypass/api/config/impl/Parsers.java +++ b/pb-api/src/main/java/me/earth/pingbypass/api/config/impl/Parsers.java @@ -5,11 +5,14 @@ import com.google.gson.JsonPrimitive; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.experimental.Delegate; +import me.earth.pingbypass.api.config.ConfigParseException; import me.earth.pingbypass.api.config.JsonDeserializationFunction; import me.earth.pingbypass.api.config.JsonParser; import me.earth.pingbypass.api.config.JsonSerializationFunction; import me.earth.pingbypass.api.input.Bind; +import me.earth.pingbypass.api.registry.Registry; import me.earth.pingbypass.api.setting.impl.types.container.Container; import me.earth.pingbypass.api.traits.Nameable; import me.earth.pingbypass.api.traits.NameableImpl; @@ -19,6 +22,8 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class Parsers implements JsonParser { + private static final JsonParser DUMMY = new Parsers<>(e -> { throw new RuntimeException("dummy"); }, v -> { throw new RuntimeException("dummy"); }); + public static final JsonParser> CONTAINER = new Parsers<>(e -> Container.fromJson(e, Parsers.NAME), Container::toJson); public static final JsonParser BOOL = new Parsers<>(JsonElement::getAsBoolean, JsonPrimitive::new); public static final JsonParser STRING = new Parsers<>(JsonElement::getAsString, JsonPrimitive::new); @@ -54,4 +59,27 @@ public static > JsonParser collection(JsonParser JsonParser registry(Registry registry) { + return new JsonParser<>() { + @Override + @SneakyThrows + public T deserialize(JsonElement element) { + return registry + .getByName(element.getAsString()) + .orElseThrow(() -> new ConfigParseException("Failed to find " + element.getAsString() + " on registry " + registry)); + } + + @Override + @SneakyThrows + public JsonElement serialize(T element) { + return new JsonPrimitive(element.getName()); + } + }; + } + + @SuppressWarnings("unchecked") + public static JsonParser dummy() { + return (JsonParser) DUMMY; + } + } diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/AbstractSettingBuilder.java b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/AbstractSettingBuilder.java index 76608bb..4b920e4 100644 --- a/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/AbstractSettingBuilder.java +++ b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/AbstractSettingBuilder.java @@ -32,7 +32,6 @@ public abstract class AbstractSettingBuilder, B extends private String description; private T defaultValue; private String name; - private T value; protected abstract S create(); @@ -83,7 +82,6 @@ public B withDescription(String description) { public B withValue(T value) { this.defaultValue = value; - this.value = value; return getSelf(); } diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSetting.java b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSetting.java new file mode 100644 index 0000000..bae6751 --- /dev/null +++ b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSetting.java @@ -0,0 +1,48 @@ +package me.earth.pingbypass.api.setting.impl.types; + +import com.mojang.brigadier.arguments.ArgumentType; +import lombok.Getter; +import me.earth.pingbypass.api.config.ConfigType; +import me.earth.pingbypass.api.config.JsonParser; +import me.earth.pingbypass.api.registry.Registry; +import me.earth.pingbypass.api.registry.impl.OrderedRegistryImpl; +import me.earth.pingbypass.api.setting.Complexity; +import me.earth.pingbypass.api.setting.impl.SettingImpl; +import me.earth.pingbypass.api.traits.CanBeVisible; +import me.earth.pingbypass.api.traits.Nameable; +import net.minecraft.network.chat.Component; + +import java.util.LinkedHashSet; +import java.util.function.Function; + +/** + * Similar to an {@link EnumSetting}, but allows values of any type, not only Enums and thus also supports adding values later. + * + * @param the type of object handled by this setting. + */ +public class RadioSetting extends SettingImpl { + @Getter + private final Registry values; + private final Class type; + + public RadioSetting(Function componentFactory, CanBeVisible visibility, Complexity complexity, + ConfigType configType, String description, T defaultValue, String name, Class type, LinkedHashSet values, + Function, ArgumentType> argumentType, Function, JsonParser> parser) { + this(new OrderedRegistryImpl<>(), componentFactory, visibility, complexity, configType, description, defaultValue, name, type, values, argumentType, parser); + } + + private RadioSetting(Registry registry, Function componentFactory, CanBeVisible visibility, Complexity complexity, + ConfigType configType, String description, T defaultValue, String name, Class type, LinkedHashSet values, + Function, ArgumentType> argumentType, Function, JsonParser> parser) { + super(componentFactory, visibility, argumentType.apply(registry), complexity, parser.apply(registry), configType, description, defaultValue, name); + this.type = type; + this.values = registry; + values.forEach(this.values::register); + } + + @Override + public Class getType() { + return type; + } + +} diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSettingBuilder.java b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSettingBuilder.java new file mode 100644 index 0000000..ec11f7a --- /dev/null +++ b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RadioSettingBuilder.java @@ -0,0 +1,72 @@ +package me.earth.pingbypass.api.setting.impl.types; + +import com.mojang.brigadier.arguments.ArgumentType; +import me.earth.pingbypass.api.command.impl.arguments.DescriptionArgumentTypeImpl; +import me.earth.pingbypass.api.command.impl.arguments.NameableArgumentTypeImpl; +import me.earth.pingbypass.api.config.JsonParser; +import me.earth.pingbypass.api.config.impl.Parsers; +import me.earth.pingbypass.api.registry.Registry; +import me.earth.pingbypass.api.setting.impl.AbstractSettingBuilder; +import me.earth.pingbypass.api.traits.HasDescription; +import me.earth.pingbypass.api.traits.Nameable; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.function.Function; + +public final class RadioSettingBuilder extends AbstractSettingBuilder, RadioSettingBuilder> { + private final Class type; + + private Function, ArgumentType> argumentTypeFactory; + private Function, JsonParser> parserFactory; + private LinkedHashSet values = new LinkedHashSet<>(); + + @SuppressWarnings({"rawtypes", "unchecked"}) + public RadioSettingBuilder(Class type) { + this.type = type; + withArgumentTypeFactory(registry -> HasDescription.class.isAssignableFrom(type) + ? (ArgumentType) new DescriptionArgumentTypeImpl(registry, "value") + : new NameableArgumentTypeImpl<>(registry, "value")) + .withParserFactory(Parsers::registry) + .withArgumentType(NameableArgumentTypeImpl.empty()) // just to make .verify happy + .withParser(Parsers.dummy()); // just to make .verify happy + } + + public RadioSettingBuilder withArgumentTypeFactory(Function, ArgumentType> argumentTypeFactory) { + this.argumentTypeFactory = argumentTypeFactory; + return getSelf(); + } + + public RadioSettingBuilder withParserFactory(Function, JsonParser> parserFactory) { + this.parserFactory = parserFactory; + return getSelf(); + } + + @SafeVarargs + public final RadioSettingBuilder withValues(T... values) { + this.values = new LinkedHashSet<>(); + this.values.addAll(Arrays.asList(values)); + if (getDefaultValue() == null) { + if (values.length > 0) { + super.withValue(values[0]); + } + } else { + this.values.add(getDefaultValue()); + } + + return getSelf(); + } + + @Override + protected RadioSetting create() { + return new RadioSetting<>(getComponentFactory(), getVisibility(), getComplexity(), getConfigType(), getDescription(), + getDefaultValue(), getName(), type, values, argumentTypeFactory, parserFactory); + } + + @Override + public RadioSettingBuilder withValue(T value) { + values.add(value); + return super.withValue(value); + } + +} diff --git a/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RegistersSettingTypes.java b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RegistersSettingTypes.java index 6e38665..ddddcb8 100644 --- a/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RegistersSettingTypes.java +++ b/pb-api/src/main/java/me/earth/pingbypass/api/setting/impl/types/RegistersSettingTypes.java @@ -2,6 +2,7 @@ import me.earth.pingbypass.api.setting.Setting; import me.earth.pingbypass.api.setting.SettingRegistry; +import me.earth.pingbypass.api.traits.Nameable; public interface RegistersSettingTypes extends SettingRegistry { default BoolBuilder boolBuilder(String name, boolean value) { @@ -49,7 +50,17 @@ default > Setting constant(String name, T value, String des } default > EnumBuilder enumBuilder(String name, T value) { - return new EnumBuilder(value).withName(name); + return new EnumBuilder<>(value).withName(name); + } + + @SuppressWarnings("unchecked") + default RadioSetting radio(String name, Class type, String description, T... values) { + return radioBuilder(name, type, description, values).registerOnBuild(this).build(); + } + + @SuppressWarnings("unchecked") + default RadioSettingBuilder radioBuilder(String name, Class type, String description, T... values) { + return new RadioSettingBuilder<>(type).withName(name).withDescription(description).withValues(values); } } diff --git a/pb-api/src/test/java/me/earth/pingbypass/api/setting/settings/RadioSettingTest.java b/pb-api/src/test/java/me/earth/pingbypass/api/setting/settings/RadioSettingTest.java new file mode 100644 index 0000000..9bce4a8 --- /dev/null +++ b/pb-api/src/test/java/me/earth/pingbypass/api/setting/settings/RadioSettingTest.java @@ -0,0 +1,53 @@ +package me.earth.pingbypass.api.setting.settings; + +import com.google.gson.JsonPrimitive; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.earth.pingbypass.api.config.ConfigParseException; +import me.earth.pingbypass.api.setting.impl.SettingRegistryImpl; +import me.earth.pingbypass.api.setting.impl.types.RegistersSettingTypes; +import me.earth.pingbypass.api.traits.Nameable; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class RadioSettingTest { + @Test + public void testCreate() throws CommandSyntaxException { + var settingRegistry = new SettingRegistryRegisteringSettings(); + var builder = settingRegistry.radioBuilder("Test", NameableValue.class, "TestDescription", NameableValue.VALUE_1, NameableValue.VALUE_3); + var radioSetting = builder.build(); + assertEquals("Test", radioSetting.getName()); + assertEquals(NameableValue.class, radioSetting.getType()); + assertEquals("TestDescription", radioSetting.getDescription()); + assertEquals(NameableValue.VALUE_1, radioSetting.getDefaultValue()); + assertEquals(NameableValue.VALUE_1, radioSetting.getValue()); + assertTrue(radioSetting.getValues().contains("1")); + assertFalse(radioSetting.getValues().contains("2")); + assertTrue(radioSetting.getValues().contains("3")); + radioSetting.fromJson(new JsonPrimitive("3")); + assertEquals(NameableValue.VALUE_3, radioSetting.getValue()); + assertThrows(ConfigParseException.class, () -> radioSetting.fromJson(new JsonPrimitive("2"))); + radioSetting.getValues().register(NameableValue.VALUE_2); + radioSetting.fromJson(new JsonPrimitive("2")); + assertEquals(NameableValue.VALUE_2, radioSetting.getValue()); + assertEquals(NameableValue.VALUE_2, radioSetting.getArgumentType().parse(new StringReader("2"))); + } + + @Getter + @RequiredArgsConstructor + private enum NameableValue implements Nameable { + VALUE_1("1"), + VALUE_2("2"), + VALUE_3("3"); + + private final String name; + } + + private static final class SettingRegistryRegisteringSettings extends SettingRegistryImpl implements RegistersSettingTypes { + + } + +} diff --git a/pb-server/src/main/java/me/earth/pingbypass/server/headlessmc/HmcPbCommand.java b/pb-server/src/main/java/me/earth/pingbypass/server/headlessmc/HmcPbCommand.java index 45bf428..c037a3f 100644 --- a/pb-server/src/main/java/me/earth/pingbypass/server/headlessmc/HmcPbCommand.java +++ b/pb-server/src/main/java/me/earth/pingbypass/server/headlessmc/HmcPbCommand.java @@ -7,10 +7,10 @@ import me.earth.pingbypass.api.command.CommandManager; import me.earth.pingbypass.api.command.DelegatingCommandSource; import me.earth.pingbypass.server.PingBypassServer; +import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; -import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.Component; -// TODO: I do not think this works properly! // TODO: proper API for adding stuff to HMC! public class HmcPbCommand extends AbstractCommand { public HmcPbCommand(HeadlessMc ctx) { @@ -21,11 +21,18 @@ public HmcPbCommand(HeadlessMc ctx) { public void execute(String... args) { // this is kinda eh PingBypassApi.instances().filter(pb -> pb instanceof PingBypassServer).findFirst().ifPresent(server -> { + if (args.length <= 1) { + Minecraft.getInstance().gui.getChat().addMessage(Component.literal("Please specify a PingBypass command!").withStyle(ChatFormatting.RED)); + return; + } + // takeout the "pingbypass" at the start of the args + String[] pbArgs = new String[args.length - 1]; + System.arraycopy(args, 1, pbArgs, 0, args.length - 1); CommandManager cm = server.getCommandManager(); try { - cm.execute(String.join(" ", args), new DelegatingCommandSource(Minecraft.getInstance(), server)); + cm.execute(String.join(" ", pbArgs), new DelegatingCommandSource(Minecraft.getInstance(), server)); } catch (CommandSyntaxException e) { - Minecraft.getInstance().gui.getChat().addMessage(ComponentUtils.fromMessage(e.getRawMessage())); + Minecraft.getInstance().gui.getChat().addMessage(Component.literal(e.getMessage()).withStyle(ChatFormatting.RED)); } }); }