diff --git a/src/main/java/com/minelittlepony/unicopia/command/Commands.java b/src/main/java/com/minelittlepony/unicopia/command/Commands.java index 146ac079..b2aef39f 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/Commands.java +++ b/src/main/java/com/minelittlepony/unicopia/command/Commands.java @@ -3,19 +3,19 @@ package com.minelittlepony.unicopia.command; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.command.argument.ArgumentTypes; -import net.minecraft.command.argument.serialize.ConstantArgumentSerializer; import net.minecraft.server.MinecraftServer; public class Commands { + @SuppressWarnings({ "deprecation", "unchecked", "rawtypes" }) public static void bootstrap() { - ArgumentTypes.register("unicopia:race", RaceArgument.class, new ConstantArgumentSerializer<>(RaceArgument::new)); + ArgumentTypes.register("unicopia:enumeration", EnumArgumentType.class, new EnumArgumentType.Serializer()); CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { SpeciesCommand.register(dispatcher); RacelistCommand.register(dispatcher); GravityCommand.register(dispatcher); DisguiseCommand.register(dispatcher); + TraitCommand.register(dispatcher); }); - @SuppressWarnings("deprecation") Object game = FabricLoader.getInstance().getGameInstance(); if (game instanceof MinecraftServer) { ((MinecraftServer)game).setFlightEnabled(true); diff --git a/src/main/java/com/minelittlepony/unicopia/command/EnumArgumentType.java b/src/main/java/com/minelittlepony/unicopia/command/EnumArgumentType.java new file mode 100644 index 00000000..2230ef9c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/command/EnumArgumentType.java @@ -0,0 +1,126 @@ +package com.minelittlepony.unicopia.command; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import com.google.common.base.Strings; +import com.google.gson.JsonObject; +import com.minelittlepony.unicopia.Race; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; + +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import net.minecraft.command.argument.serialize.ArgumentSerializer; +import net.minecraft.network.PacketByteBuf; + +class EnumArgumentType> implements ArgumentType, Serializable { + private static final long serialVersionUID = 3731493854867412243L; + + private static final EnumArgumentType RACE = of(Race.class, Race::isUsable, Race.EARTH); + + public static EnumArgumentType race() { + return RACE; + } + + public static > EnumArgumentType of(Class type, Predicate filter, T def) { + return new EnumArgumentType<>(type, filter, def); + } + + public static > EnumArgumentType of(Class type, T def) { + return new EnumArgumentType<>(type, s -> true, def); + } + + public static > EnumArgumentType of(Class type) { + return new EnumArgumentType<>(type, s -> true, null); + } + + private final T def; + private final T[] values; + private final List suggestions; + + private EnumArgumentType(Class type, Predicate filter, T def) { + this.def = def; + values = type.getEnumConstants(); + suggestions = Arrays.stream(values).filter(filter).map(T::name).toList(); + } + + private EnumArgumentType(List suggestions, T[] values, T def) { + this.suggestions = suggestions; + this.values = values; + this.def = def; + } + + @Override + public T parse(StringReader reader) throws CommandSyntaxException { + return fromName(reader.readUnquotedString()); + } + + @Override + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + suggestions.stream() + .filter(i -> i.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) + .forEach(builder::suggest); + + return builder.buildFuture(); + } + + @SuppressWarnings("unlikely-arg-type") + private T fromName(String s) { + if (!Strings.isNullOrEmpty(s)) { + for (T i : values) { + if (i.equals(s) || i.name().equalsIgnoreCase(s)) { + return i; + } + } + } + + try { + int ordinal = Integer.parseInt(s); + if (ordinal >= 0 && ordinal < values.length) { + return values[ordinal]; + } + } catch (NumberFormatException e) { } + + return def; + } + + @Override + public Collection getExamples() { + return suggestions; + } + + public static class Serializer> implements ArgumentSerializer> { + @Override + public void toPacket(EnumArgumentType type, PacketByteBuf buf) { + try (ObjectOutputStream stream = new ObjectOutputStream(new ByteBufOutputStream(buf))) { + stream.writeObject(type); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + @Override + public EnumArgumentType fromPacket(PacketByteBuf buf) { + try (ObjectInputStream stream = new ObjectInputStream(new ByteBufInputStream(buf))) { + return (EnumArgumentType)stream.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public void toJson(EnumArgumentType type, JsonObject json) { } + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/command/RaceArgument.java b/src/main/java/com/minelittlepony/unicopia/command/RaceArgument.java deleted file mode 100644 index 98d2eb76..00000000 --- a/src/main/java/com/minelittlepony/unicopia/command/RaceArgument.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.minelittlepony.unicopia.command; - -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -import com.minelittlepony.unicopia.Race; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; - -class RaceArgument implements ArgumentType { - static final Collection EXAMPLES = Arrays.stream(Race.values()) - .filter(Race::isUsable) - .map(Race::name) - .collect(Collectors.toList()); - - @Override - public Race parse(StringReader reader) throws CommandSyntaxException { - return Race.fromName(reader.readUnquotedString(), Race.EARTH); - } - - @Override - public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { - Arrays.stream(Race.values()) - .filter(Race::isUsable) - .map(i -> i.name().toLowerCase()) - .filter(i -> i.startsWith(builder.getRemaining().toLowerCase())) - .forEach(i -> builder.suggest(i.toLowerCase())); - - return builder.buildFuture(); - } - - @Override - public Collection getExamples() { - return EXAMPLES; - } -} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java b/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java index 593c02e4..9c68614d 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java @@ -19,8 +19,10 @@ class RacelistCommand { static void register(CommandDispatcher dispatcher) { LiteralArgumentBuilder builder = CommandManager.literal("racelist").requires(s -> s.hasPermissionLevel(4)); + EnumArgumentType raceArgument = EnumArgumentType.of(Race.class, Race::isUsable, Race.EARTH); + builder.then(CommandManager.literal("allow") - .then(CommandManager.argument("race", new RaceArgument()) + .then(CommandManager.argument("race", raceArgument) .executes(context -> toggle(context.getSource(), context.getSource().getPlayer(), context.getArgument("race", Race.class), "allowed", race -> { boolean result = Unicopia.getConfig().speciesWhiteList.get().add(race); @@ -30,7 +32,7 @@ class RacelistCommand { })) )); builder.then(CommandManager.literal("disallow") - .then(CommandManager.argument("race", new RaceArgument()) + .then(CommandManager.argument("race", raceArgument) .executes(context -> toggle(context.getSource(), context.getSource().getPlayer(), context.getArgument("race", Race.class), "disallowed", race -> { boolean result = Unicopia.getConfig().speciesWhiteList.get().remove(race); diff --git a/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java b/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java index 35483595..7abb242f 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java @@ -20,6 +20,8 @@ class SpeciesCommand { static void register(CommandDispatcher dispatcher) { LiteralArgumentBuilder builder = CommandManager.literal("race"); + EnumArgumentType raceArgument = EnumArgumentType.of(Race.class, Race::isUsable, Race.EARTH); + builder.then(CommandManager.literal("get") .executes(context -> get(context.getSource(), context.getSource().getPlayer(), true)) .then(CommandManager.argument("target", EntityArgumentType.player()) @@ -27,14 +29,14 @@ class SpeciesCommand { )); builder.then(CommandManager.literal("set") - .then(CommandManager.argument("race", new RaceArgument()) + .then(CommandManager.argument("race", raceArgument) .executes(context -> set(context.getSource(), context.getSource().getPlayer(), context.getArgument("race", Race.class), true)) .then(CommandManager.argument("target", EntityArgumentType.player()) .executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), context.getArgument("race", Race.class), false)) ))); builder.then(CommandManager.literal("describe") - .then(CommandManager.argument("race", new RaceArgument()) + .then(CommandManager.argument("race", raceArgument) .executes(context -> describe(context.getSource().getPlayer(), context.getArgument("race", Race.class)) ))); diff --git a/src/main/java/com/minelittlepony/unicopia/command/TraitCommand.java b/src/main/java/com/minelittlepony/unicopia/command/TraitCommand.java new file mode 100644 index 00000000..1bc81dcb --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/command/TraitCommand.java @@ -0,0 +1,95 @@ +package com.minelittlepony.unicopia.command; + +import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; +import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Hand; + +class TraitCommand { + static void register(CommandDispatcher dispatcher) { + LiteralArgumentBuilder builder = CommandManager + .literal("trait") + .requires(s -> s.hasPermissionLevel(4)); + + builder.then(CommandManager.literal("add") + .then(CommandManager.argument("trait", EnumArgumentType.of(Trait.class)) + .then(CommandManager.argument("value", FloatArgumentType.floatArg()).executes(source -> add( + source.getSource(), + source.getSource().getPlayer(), + source.getArgument("trait", Trait.class), + FloatArgumentType.getFloat(source, "value") + ))) + )); + builder.then(CommandManager.literal("remove") + .then(CommandManager.argument("trait", EnumArgumentType.of(Trait.class)).executes(source -> remove( + source.getSource(), + source.getSource().getPlayer(), + source.getArgument("trait", Trait.class) + )) + )); + + dispatcher.register(builder); + } + + static int add(ServerCommandSource source, PlayerEntity player, Trait trait, float amount) throws CommandSyntaxException { + if (trait == null) { + source.sendError(new LiteralText("Invalid trait")); + return 0; + } + + ItemStack stack = player.getMainHandStack(); + if (stack.isEmpty()) { + source.sendError(new LiteralText("That trait cannot be added to the current item")); + return 0; + } + + player.setStackInHand(Hand.MAIN_HAND, SpellTraits.union(SpellTraits.of(stack), new SpellTraits.Builder().with(trait, amount).build()).applyTo(stack)); + + return 0; + } + + static int remove(ServerCommandSource source, PlayerEntity player, Trait trait) throws CommandSyntaxException { + if (trait == null) { + source.sendError(new LiteralText("Invalid trait")); + return 0; + } + + ItemStack stack = player.getMainHandStack(); + + SpellTraits existing = SpellTraits.of(stack); + if (existing.get(trait) == 0) { + return 0; + } + + player.setStackInHand(Hand.MAIN_HAND, existing.map((t, v) -> t == trait ? 0 : v).applyTo(stack)); + + return 0; + } + + static int get(ServerCommandSource source, PlayerEntity player, Trait trait, float amount) throws CommandSyntaxException { + String translationKey = "commands.gravity.get"; + + Pony iplayer = Pony.of(player); + + float gravity = iplayer.getPhysics().getGravityModifier(); + + if (source.getPlayer() == player) { + player.sendMessage(new TranslatableText(translationKey, gravity), false); + } else { + source.sendFeedback(new TranslatableText(translationKey + ".other", player.getName(), gravity), true); + } + + return 0; + } +}