diff --git a/src/main/java/io/github/skippyall/minions/gui/MinionsGui.java b/src/main/java/io/github/skippyall/minions/gui/MinionsGui.java new file mode 100644 index 0000000..ebec233 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/MinionsGui.java @@ -0,0 +1,54 @@ +package io.github.skippyall.minions.gui; + +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; + +public abstract class MinionsGui { + protected final @Nullable MinionsGui parent; + protected final ServerPlayerEntity viewer; + private @Nullable MinionsGui child = null; + private boolean open = true; + + public MinionsGui(MinionsGui parent) { + this.viewer = parent.viewer; + this.parent = parent; + parent.child = this; + open(); + } + + public MinionsGui(ServerPlayerEntity viewer) { + this.viewer = viewer; + this.parent = null; + open(); + } + + protected void open() {} + + protected void reopen() { + open(); + } + + public void onBackingClosed() { + if(child != null && child.open) { + return; + } + + close(); + } + + public void close() { + if(open) { + if(child != null) { + child.close(); + } + if(parent != null) { + parent.child = null; + parent.reopen(); + } + onClose(); + open = false; + } + } + + protected void onClose() {} +} diff --git a/src/main/java/io/github/skippyall/minions/gui/PaginatedList.java b/src/main/java/io/github/skippyall/minions/gui/PaginatedList.java new file mode 100644 index 0000000..2dde6ef --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/PaginatedList.java @@ -0,0 +1,59 @@ +package io.github.skippyall.minions.gui; + +import eu.pb4.sgui.api.elements.GuiElementBuilder; +import eu.pb4.sgui.api.gui.SlotGuiInterface; +import io.github.skippyall.minions.gui.minion.MinionGui; +import net.minecraft.item.Items; +import net.minecraft.text.Text; +import net.minecraft.util.collection.IndexedIterable; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +public class PaginatedList { + public static void createList(SlotGuiInterface gui, int size, Function display, Runnable onBack) { + AtomicInteger page = new AtomicInteger(0); + addItems(page, gui, size, display); + + gui.setSlot(27, MinionGui.backButton(onBack)); + } + + public static void createList(SlotGuiInterface gui, List list, Function display, Runnable onBack) { + createList(gui, list.size(), i -> display.apply(list.get(i)), onBack); + } + + public static void createList(SlotGuiInterface gui, IndexedIterable list, Function display, Runnable onBack) { + createList(gui, list.size(), i -> display.apply(list.get(i)), onBack); + } + + private static void addItems(AtomicInteger page, SlotGuiInterface gui, int size, Function display) { + for(int i = 27 * page.get(); i < Math.min(27 * (page.get() + 1), size); i++) { + gui.addSlot(display.apply(i)); + } + + if(page.get() > 0) { + gui.setSlot(30, new GuiElementBuilder(Items.SPECTRAL_ARROW) + .setItemName(Text.translatable("book.page_button.previous")) + .setCallback(() -> { + page.decrementAndGet(); + addItems(page, gui, size, display); + }) + ); + } else { + gui.clearSlot(30); + } + + if(27 * (page.get() + 1) < size) { + gui.setSlot(32, new GuiElementBuilder(Items.ARROW) + .setItemName(Text.translatable("book.page_button.next")) + .setCallback(() -> { + page.incrementAndGet(); + addItems(page, gui, size, display); + }) + ); + } else { + gui.clearSlot(32); + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/gui/input/Result.java b/src/main/java/io/github/skippyall/minions/gui/input/Result.java index 24cd9ec..8dd4e9b 100644 --- a/src/main/java/io/github/skippyall/minions/gui/input/Result.java +++ b/src/main/java/io/github/skippyall/minions/gui/input/Result.java @@ -25,11 +25,11 @@ public interface Result { boolean isSuccess(); - @NotNull T getOrDefault(@NotNull T defaultValue); + T getOrDefault(T defaultValue); - @NotNull T getOrThrow(); + T getOrThrow(); - @NotNull E getErrorOrThrow(); + E getErrorOrThrow(); @NotNull Optional getOptional(); @@ -37,24 +37,24 @@ public interface Result { void ifError(@NotNull Consumer> handler); - record Success(@NotNull T result) implements Result { + record Success(T result) implements Result { @Override public boolean isSuccess() { return true; } @Override - public @NotNull T getOrDefault(@NotNull T defaultValue) { + public T getOrDefault(T defaultValue) { return result; } @Override - public @NotNull T getOrThrow() { + public T getOrThrow() { return result; } @Override - public @NotNull E getErrorOrThrow() { + public E getErrorOrThrow() { throw new RuntimeException("Result was not an Error"); } @@ -74,24 +74,25 @@ public interface Result { } } - record Error(@NotNull E message) implements Result { + record Error(E message) implements Result { @Override public boolean isSuccess() { return false; } @Override - public @NotNull T getOrDefault(@NotNull T defaultValue) { + public T getOrDefault( + T defaultValue) { return defaultValue; } @Override - public @NotNull T getOrThrow() { + public T getOrThrow() { throw new RuntimeException("Result was an error: " + message); } @Override - public @NotNull E getErrorOrThrow() { + public E getErrorOrThrow() { return message; } diff --git a/src/main/java/io/github/skippyall/minions/gui/instruction/ArgumentGui.java b/src/main/java/io/github/skippyall/minions/gui/instruction/ArgumentGui.java new file mode 100644 index 0000000..7a03fb5 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/instruction/ArgumentGui.java @@ -0,0 +1,81 @@ +package io.github.skippyall.minions.gui.instruction; + +import eu.pb4.sgui.api.elements.GuiElementBuilder; +import eu.pb4.sgui.api.gui.SimpleGui; +import io.github.skippyall.minions.gui.GuiDisplay; +import io.github.skippyall.minions.minion.MinionRuntime; +import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; +import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; +import io.github.skippyall.minions.program.supplier.Parameter; +import io.github.skippyall.minions.program.supplier.ValueSupplier; +import io.github.skippyall.minions.program.supplier.ValueSupplierType; +import io.github.skippyall.minions.registration.MinionRegistries; +import io.github.skippyall.minions.util.TranslationUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; + +public class ArgumentGui { + public static > void configureArgumentMenu(String instructionName, ConfiguredInstruction instruction, Parameter parameter, MinionFakePlayer minion, ServerPlayerEntity player) { + if (!InstructionGui.checkInstructionExists(instructionName, instruction, minion, player)) { + return; + } + + @Nullable ValueSupplier argument = instruction.getArguments().getArgument(parameter); + + if(argument == null) { + configureTypeAndValue(instructionName, instruction, parameter, minion, player); + return; + } + + configureArgumentHelper(instructionName, instruction, parameter, argument, minion, player); + } + + private static void configureArgumentHelper(String instructionName, ConfiguredInstruction instruction, Parameter parameter, ValueSupplier argument, MinionFakePlayer minion, ServerPlayerEntity player) { + SimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion, instruction); + + ItemStack displayStack = GuiDisplay.getDisplayStack(MinionRegistries.VALUE_SUPPLIER_TYPES, argument.getType(), player.getRegistryManager()); + + gui.setSlot(3, new GuiElementBuilder(displayStack) + .setName(Text.translatable("minions.gui.instruction.argument.configure.type", Text.translatable(TranslationUtil.getTranslationKey(argument.getType(), MinionRegistries.VALUE_SUPPLIER_TYPES, "minions.gui.instruction.argument.configure.type.unset")))) + .setCallback(() -> configureTypeAndValue(instructionName, instruction, parameter, minion, player)) + ); + gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID) + .setName(Text.literal("Configure")) + .setCallback(() -> argument.getType().openConfiguration(player, argument.getValueType(), argument) + .thenAccept(newArgument -> { + instruction.getArguments().setArgument(parameter, newArgument); + configureArgumentMenu(instructionName, instruction, parameter, minion, player); + }) + ) + ); + gui.open(); + } + + public static CompletableFuture> selectArgumentType(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction) { + CompletableFuture> future = new CompletableFuture<>(); + SimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion, instruction); + for (ValueSupplierType type : MinionRegistries.VALUE_SUPPLIER_TYPES) { + gui.addSlot(new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_SUPPLIER_TYPES, type, player.getRegistryManager())) + .setCallback(() -> future.complete(type)) + ); + } + gui.open(); + return future; + } + + public static void configureTypeAndValue(String name, ConfiguredInstruction instruction, Parameter parameter, MinionFakePlayer minion, ServerPlayerEntity player) { + selectArgumentType(player, minion, instruction) + .thenApply(type -> type.openConfiguration(player, parameter.type(), null) + .thenAccept(v -> { + instruction.getArguments().setArgument(parameter, v); + configureArgumentMenu(name, instruction, parameter, minion, player); + }) + ); + } +} diff --git a/src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java b/src/main/java/io/github/skippyall/minions/gui/instruction/ConfigureInstructionGui.java similarity index 96% rename from src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java rename to src/main/java/io/github/skippyall/minions/gui/instruction/ConfigureInstructionGui.java index 79aef56..5d1d78a 100644 --- a/src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/instruction/ConfigureInstructionGui.java @@ -1,4 +1,4 @@ -package io.github.skippyall.minions.gui; +package io.github.skippyall.minions.gui.instruction; import eu.pb4.sgui.api.elements.GuiElementBuilder; import io.github.skippyall.minions.gui.input.ChoiceInput; @@ -108,7 +108,7 @@ public class ConfigureInstructionGui extends InstructionBoundSimpleGui { int slot = 12; for(Parameter parameter : instruction.getInstruction().getParameters().reversed()) { setSlot(slot, InstructionGui.createParameterElement(parameter, instruction.getArguments().getArgument(parameter), player.getRegistryManager()) - .setCallback(() -> InstructionGui.configureArgumentMenu(name, instruction, parameter, minion, player)) + .setCallback(() -> ArgumentGui.configureArgumentMenu(name, instruction, parameter, minion, player)) ); slot--; } diff --git a/src/main/java/io/github/skippyall/minions/gui/instruction/ConverterGui.java b/src/main/java/io/github/skippyall/minions/gui/instruction/ConverterGui.java new file mode 100644 index 0000000..d90e63b --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/instruction/ConverterGui.java @@ -0,0 +1,71 @@ +package io.github.skippyall.minions.gui.instruction; + +import eu.pb4.sgui.api.elements.GuiElementBuilder; +import io.github.skippyall.minions.gui.GuiDisplay; +import io.github.skippyall.minions.gui.PaginatedList; +import io.github.skippyall.minions.minion.MinionRuntime; +import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; +import io.github.skippyall.minions.program.conversion.ValueConverter; +import io.github.skippyall.minions.program.conversion.ValueConverterType; +import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; +import io.github.skippyall.minions.program.supplier.Parameter; +import io.github.skippyall.minions.program.supplier.ValueSupplierList; +import io.github.skippyall.minions.program.value.ValueType; +import io.github.skippyall.minions.registration.MinionRegistries; +import net.minecraft.item.Items; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; + +public class ConverterGui { + public static void configureConvertersMenu(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction, Parameter parameter, Runnable back) { + InstructionBoundSimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X4, player, minion, instruction); + + ValueSupplierList.ValueSupplierEntry entry = instruction.getArguments().getEntry(parameter); + PaginatedList.createList( + gui, + entry.getConverters(), + converter -> createConverterElement(converter, player.getRegistryManager()) + .setCallback(() -> configureConverter(player, minion, instruction, parameter, entry, converter)), + back + ); + gui.open(); + } + + public static GuiElementBuilder createConverterElement(ValueConverter converter, DynamicRegistryManager manager) { + GuiElementBuilder builder = new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_CONVERTER_TYPES, converter.getType(), manager)); + builder.addLoreLine(converter.getDisplayText()); + return builder; + } + + public static void configureConverter(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction, Parameter parameter, ValueSupplierList.ValueSupplierEntry entry, @Nullable ValueConverter converter) { + InstructionBoundSimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion, instruction); + gui.setSlot(3, new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_CONVERTER_TYPES, converter.getType(), player.getRegistryManager())) + .setCallback(() -> { + InstructionBoundSimpleGui chooseTypeGui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X4, player, minion, instruction); + PaginatedList.createList( + chooseTypeGui, + MinionRegistries.VALUE_CONVERTER_TYPES, + type -> new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_CONVERTER_TYPES, type, player.getRegistryManager())) + .setCallback(() -> { + entry.set + }), + () -> configureConverter(player, minion, instruction, parameter, entry, converter) + ); + })); + + gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID)); + + gui.open(); + } + + public static void selectConverterMenu(ValueType from, ValueType to, ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction) { + InstructionBoundSimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion, instruction); + for(ValueConverterType valueConverterType : MinionRegistries.VALUE_CONVERTER_TYPES) { + gui.addSlot(new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_CONVERTER_TYPES, valueConverterType, player.getRegistryManager())) + .setCallback(() -> valueConverterType.configure(player, from, to, null).thenAccept(c -> ))); + } + gui.open(); + } +} diff --git a/src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java b/src/main/java/io/github/skippyall/minions/gui/instruction/InstructionBoundSimpleGui.java similarity index 90% rename from src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java rename to src/main/java/io/github/skippyall/minions/gui/instruction/InstructionBoundSimpleGui.java index fd7bfe0..40fe37a 100644 --- a/src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/instruction/InstructionBoundSimpleGui.java @@ -1,5 +1,6 @@ -package io.github.skippyall.minions.gui; +package io.github.skippyall.minions.gui.instruction; +import io.github.skippyall.minions.gui.MinionBoundSimpleGui; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; diff --git a/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java b/src/main/java/io/github/skippyall/minions/gui/instruction/InstructionGui.java similarity index 71% rename from src/main/java/io/github/skippyall/minions/gui/InstructionGui.java rename to src/main/java/io/github/skippyall/minions/gui/instruction/InstructionGui.java index c7d0ead..37426a7 100644 --- a/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/instruction/InstructionGui.java @@ -1,7 +1,9 @@ -package io.github.skippyall.minions.gui; +package io.github.skippyall.minions.gui.instruction; import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.SimpleGui; +import io.github.skippyall.minions.gui.GuiDisplay; +import io.github.skippyall.minions.gui.MinionBoundSimpleGui; import io.github.skippyall.minions.registration.MinionComponentTypes; import io.github.skippyall.minions.registration.MinionRegistries; import io.github.skippyall.minions.gui.input.Result; @@ -10,7 +12,6 @@ import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.module.MinionModule; import io.github.skippyall.minions.program.supplier.ValueSupplier; -import io.github.skippyall.minions.program.supplier.ValueSupplierType; import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; import io.github.skippyall.minions.program.instruction.InstructionType; import io.github.skippyall.minions.program.supplier.Parameter; @@ -99,62 +100,6 @@ public class InstructionGui { return stillExists; } - - public static > void configureArgumentMenu(String instructionName, ConfiguredInstruction instruction, Parameter parameter, MinionFakePlayer minion, ServerPlayerEntity player) { - if (!checkInstructionExists(instructionName, instruction, minion, player)) { - return; - } - - @Nullable ValueSupplier argument = instruction.getArguments().getArgument(parameter); - - if(argument == null) { - configureTypeAndValue(instructionName, instruction, parameter, minion, player); - return; - } - - configureArgumentHelper(instructionName, instruction, parameter, argument, minion, player); - } - - private static void configureArgumentHelper(String instructionName, ConfiguredInstruction instruction, Parameter parameter, ValueSupplier argument, MinionFakePlayer minion, ServerPlayerEntity player) { - SimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion, instruction); - - ItemStack displayStack = GuiDisplay.getDisplayStack(MinionRegistries.VALUE_SUPPLIER_TYPES, argument.getType(), player.getRegistryManager()); - - gui.setSlot(3, new GuiElementBuilder(displayStack) - .setName(Text.translatable("minions.gui.instruction.argument.configure.type", Text.translatable(TranslationUtil.getTranslationKey(argument.getType(), MinionRegistries.VALUE_SUPPLIER_TYPES, "minions.gui.instruction.argument.configure.type.unset")))) - .setCallback(() -> configureTypeAndValue(instructionName, instruction, parameter, minion, player)) - ); - gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID) - .setName(Text.literal("Configure")) - .setCallback(() -> argument.getType().openConfiguration(player, argument.getValueType(), argument) - .thenAccept(newArgument -> instruction.getArguments().setArgument(parameter, newArgument)) - ) - ); - gui.open(); - } - - public static CompletableFuture> selectArgumentType(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction) { - CompletableFuture> future = new CompletableFuture<>(); - SimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion, instruction); - for (ValueSupplierType type : MinionRegistries.VALUE_SUPPLIER_TYPES) { - gui.addSlot(new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_SUPPLIER_TYPES, type, player.getRegistryManager())) - .setCallback(() -> future.complete(type)) - ); - } - gui.open(); - return future; - } - - public static void configureTypeAndValue(String name, ConfiguredInstruction instruction, Parameter parameter, MinionFakePlayer minion, ServerPlayerEntity player) { - selectArgumentType(player, minion, instruction) - .thenApply(type -> type.openConfiguration(player, parameter.type(), null) - .thenAccept(newArgument -> { - instruction.getArguments().setArgument(parameter, newArgument); - configureArgumentMenu(name, instruction, parameter, minion, player); - }) - ); - } - public static CompletableFuture> selectInstructionModuleMenu(MinionFakePlayer minion, ServerPlayerEntity player) { if (minion.getModuleInventory().getModules().isEmpty()) { player.sendMessage(Text.translatable("minions.gui.instruction.no_modules")); diff --git a/src/main/java/io/github/skippyall/minions/gui/minion/MinionGui.java b/src/main/java/io/github/skippyall/minions/gui/minion/MinionGui.java new file mode 100644 index 0000000..ba0f210 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/minion/MinionGui.java @@ -0,0 +1,93 @@ +package io.github.skippyall.minions.gui.minion; + +import eu.pb4.sgui.api.elements.GuiElementBuilder; +import eu.pb4.sgui.api.gui.SimpleGui; +import io.github.skippyall.minions.gui.MinionsGui; +import io.github.skippyall.minions.gui.instruction.InstructionGui; +import io.github.skippyall.minions.minion.MinionListener; +import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; +import io.github.skippyall.minions.module.ModuleInventory; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.screen.slot.ArmorSlot; +import net.minecraft.screen.slot.Slot; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; + +public class MinionGui extends MinionsGui implements MinionListener { + private final MinionFakePlayer minion; + private SimpleGui gui; + + public MinionGui(ServerPlayerEntity viewer, MinionFakePlayer minion) { + super(viewer); + this.minion = minion; + minion.addMinionListener(this); + } + + public MinionFakePlayer getMinion() { + return minion; + } + + @Override + protected void open() { + gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, viewer, false) { + @Override + public void onClose() { + onBackingClosed(); + } + }; + + gui.setTitle(minion.getName()); + + gui.setSlot(1, new GuiElementBuilder() + .setItem(Items.COMMAND_BLOCK) + .setName(Text.translatable("minions.gui.main.instructions")) + .setCallback(() -> { + InstructionGui.openInstructionMainMenu(minion, viewer); + }) + ); + gui.setSlot(3, new GuiElementBuilder() + .setItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) + .setName(Text.translatable("minions.gui.main.modules")) + .setCallback(() -> { + ModuleInventory.openModuleInventory(viewer, minion); + }) + ); + gui.setSlot(5, new GuiElementBuilder() + .setItem(Items.CHEST) + .setName(Text.translatable("minions.gui.main.inventory")) + .setCallback(() -> new MinionInventoryGui(this)) + ); + gui.setSlot(7, new GuiElementBuilder() + .setItem(Items.BARRIER) + .setName(Text.translatable("minions.gui.main.pickup")) + .setCallback(() -> minion.kill(minion.getWorld())) + ); + gui.open(); + } + + @Override + protected void reopen() { + gui.open(); + } + + @Override + protected void onClose() { + gui.close(); + minion.removeMinionListener(this); + } + + @Override + public void onMinionRemove(MinionFakePlayer minion) { + close(); + } + + public static GuiElementBuilder backButton(Runnable onBack) { + return new GuiElementBuilder(Items.COMPASS) + .setItemName(Text.translatable("gui.back")) + .setCallback(onBack); + } +} diff --git a/src/main/java/io/github/skippyall/minions/gui/MinionGui.java b/src/main/java/io/github/skippyall/minions/gui/minion/MinionInventoryGui.java similarity index 51% rename from src/main/java/io/github/skippyall/minions/gui/MinionGui.java rename to src/main/java/io/github/skippyall/minions/gui/minion/MinionInventoryGui.java index 9694a74..4efc147 100644 --- a/src/main/java/io/github/skippyall/minions/gui/MinionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/minion/MinionInventoryGui.java @@ -1,9 +1,8 @@ -package io.github.skippyall.minions.gui; +package io.github.skippyall.minions.gui.minion; -import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.SimpleGui; +import io.github.skippyall.minions.gui.MinionsGui; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; -import io.github.skippyall.minions.module.ModuleInventory; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; @@ -11,51 +10,28 @@ import net.minecraft.item.Items; import net.minecraft.screen.ScreenHandlerType; import net.minecraft.screen.slot.ArmorSlot; import net.minecraft.screen.slot.Slot; -import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; -public class MinionGui { - public static void openInventory(ServerPlayerEntity player, MinionFakePlayer minion) { - openServerSideInventory(player, minion); +public class MinionInventoryGui extends MinionsGui { + protected final MinionGui parent; + private final MinionFakePlayer minion; + + private SimpleGui gui; + + public MinionInventoryGui(MinionGui parent) { + super(parent); + this.parent = parent; + this.minion = parent.getMinion(); } - public static void openServerSideInventory(ServerPlayerEntity player, MinionFakePlayer minion) { - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); - gui.setTitle(minion.getName()); - - gui.setSlot(1, new GuiElementBuilder() - .setItem(Items.COMMAND_BLOCK) - .setName(Text.translatable("minions.gui.main.instructions")) - .setCallback((i, clickType, slotActionType) -> { - InstructionGui.openInstructionMainMenu(minion, player); - }) - ); - gui.setSlot(3, new GuiElementBuilder() - .setItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) - .setName(Text.translatable("minions.gui.main.modules")) - .setCallback(() -> { - ModuleInventory.openModuleInventory(player, minion); - }) - ); - gui.setSlot(5, new GuiElementBuilder() - .setItem(Items.CHEST) - .setName(Text.translatable("minions.gui.main.inventory")) - .setCallback(() -> { - openMinionInventory(player, minion); - }) - ); - gui.setSlot(7, new GuiElementBuilder() - .setItem(Items.BARRIER) - .setName(Text.translatable("minions.gui.main.pickup")) - .setCallback(() -> { - minion.kill(minion.getWorld()); - }) - ); - gui.open(); - } - - public static void openMinionInventory(ServerPlayerEntity player, MinionFakePlayer minion) { - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X6, player, false); + @Override + protected void open() { + gui = new SimpleGui(ScreenHandlerType.GENERIC_9X6, viewer, false) { + @Override + public void onClose() { + onBackingClosed(); + } + }; gui.setTitle(Text.translatable("minions.gui.inventory.title")); for(int i = 0; i < 18; i++) { @@ -83,4 +59,9 @@ public class MinionGui { } gui.open(); } + + @Override + protected void onClose() { + gui.close(); + } } diff --git a/src/main/java/io/github/skippyall/minions/minion/fakeplayer/MinionFakePlayer.java b/src/main/java/io/github/skippyall/minions/minion/fakeplayer/MinionFakePlayer.java index 214b384..46f7154 100644 --- a/src/main/java/io/github/skippyall/minions/minion/fakeplayer/MinionFakePlayer.java +++ b/src/main/java/io/github/skippyall/minions/minion/fakeplayer/MinionFakePlayer.java @@ -7,7 +7,7 @@ import io.github.skippyall.minions.registration.MinionConfigOptions; import io.github.skippyall.minions.registration.MinionItems; import io.github.skippyall.minions.minion.MinionListener; import io.github.skippyall.minions.minion.MinionData; -import io.github.skippyall.minions.gui.MinionGui; +import io.github.skippyall.minions.gui.minion.MinionGui; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.MinionItem; import io.github.skippyall.minions.minion.MinionPersistentState; @@ -24,7 +24,6 @@ import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.HungerManager; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.AbstractBoatEntity; -import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.item.ItemStack; import net.minecraft.network.DisconnectionInfo; import net.minecraft.network.NetworkSide; @@ -156,7 +155,7 @@ public class MinionFakePlayer extends ServerPlayerEntity { @Override public ActionResult interact(PlayerEntity player, Hand hand) { if(player instanceof ServerPlayerEntity spe) { - MinionGui.openInventory(spe, this); + new MinionGui(spe, this); } return ActionResult.CONSUME; } diff --git a/src/main/java/io/github/skippyall/minions/minion/program/supplier/AnalogInputSupplier.java b/src/main/java/io/github/skippyall/minions/minion/program/supplier/AnalogInputSupplier.java index 0f7888a..038606e 100644 --- a/src/main/java/io/github/skippyall/minions/minion/program/supplier/AnalogInputSupplier.java +++ b/src/main/java/io/github/skippyall/minions/minion/program/supplier/AnalogInputSupplier.java @@ -62,7 +62,7 @@ public class AnalogInputSupplier implements ValueSupplier { @Override public Text getDisplayText() { - return Text.translatable("value_supplier_type.minions.analog_input.display", analogInputPos.toString(), analogInputWorld.getValue()); + return Text.translatable("value_supplier_type.minions.analog_input.display", analogInputPos.toString(), analogInputWorld.getValue().toString()); } public static class AnalogInputSupplierType extends ValueSupplierType { @@ -75,12 +75,8 @@ public class AnalogInputSupplier implements ValueSupplier { } @Override - public CompletableFuture> openConfiguration(ServerPlayerEntity player, ValueType valueType, @Nullable ValueSupplier previous) { - if(valueType != ValueTypes.LONG) { - return CompletableFuture.failedFuture(new IllegalArgumentException("Value type " + valueType + " not allowed")); - } - - CompletableFuture> future = new CompletableFuture<>(); + public CompletableFuture> openConfiguration(ServerPlayerEntity player, ValueType valueType, @Nullable ValueSupplier previous) { + CompletableFuture> future = new CompletableFuture<>(); SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); gui.setTitle(Text.translatable("value_supplier_type.minions.analog_input")); @@ -89,11 +85,12 @@ public class AnalogInputSupplier implements ValueSupplier { .setCallback(() -> { ItemStack cursor = player.currentScreenHandler.getCursorStack(); if(cursor.isOf(MinionItems.REFERENCE_ITEM) && cursor.get(MinionComponentTypes.REFERENCE) instanceof BlockPosClipboard pos) { - future.complete(new AnalogInputSupplier(pos.world(), pos.pos()).cast(valueType)); + future.complete(new AnalogInputSupplier(pos.world(), pos.pos())); } }) .setItemName(Text.translatable("value_supplier_type.minions.analog_input.config.click_with_reference")) ); + gui.open(); return future; } } diff --git a/src/main/java/io/github/skippyall/minions/program/value/Cast.java b/src/main/java/io/github/skippyall/minions/program/conversion/Cast.java similarity index 93% rename from src/main/java/io/github/skippyall/minions/program/value/Cast.java rename to src/main/java/io/github/skippyall/minions/program/conversion/Cast.java index 55f7638..85694ad 100644 --- a/src/main/java/io/github/skippyall/minions/program/value/Cast.java +++ b/src/main/java/io/github/skippyall/minions/program/conversion/Cast.java @@ -1,4 +1,6 @@ -package io.github.skippyall.minions.program.value; +package io.github.skippyall.minions.program.conversion; + +import io.github.skippyall.minions.program.value.ValueType; public class Cast { private final ValueType from; diff --git a/src/main/java/io/github/skippyall/minions/program/conversion/CastConverter.java b/src/main/java/io/github/skippyall/minions/program/conversion/CastConverter.java new file mode 100644 index 0000000..722570b --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/conversion/CastConverter.java @@ -0,0 +1,64 @@ +package io.github.skippyall.minions.program.conversion; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.github.skippyall.minions.program.value.ValueType; +import io.github.skippyall.minions.registration.MinionRegistries; +import io.github.skippyall.minions.registration.ValueConverters; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; + +public class CastConverter implements ValueConverter { + private static final MapCodec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + MinionRegistries.VALUE_TYPES.getCodec().fieldOf("from").forGetter(CastConverter::getFrom), + MinionRegistries.VALUE_TYPES.getCodec().fieldOf("to").forGetter(CastConverter::getTo) + ).apply(instance, CastConverter::new) + ); + + private final ValueType from; + private final ValueType to; + + public CastConverter(ValueType from, ValueType to) { + this.from = from; + this.to = to; + } + + @Override + public T convert(F fromValue) { + return Casts.getCast(from, to).cast(fromValue); + } + + @Override + public ValueType getFrom() { + return from; + } + + @Override + public ValueType getTo() { + return to; + } + + @Override + public ValueConverterType getType() { + return ValueConverters.CAST_CONVERTER; + } + + public static class Type implements ValueConverterType> { + @Override + public MapCodec> getCodec() { + return CastConverter.CODEC; + } + + @Override + public boolean isSupportedConversion(ValueType from, ValueType to) { + return Casts.getCast(from, to) != null; + } + + @Override + public CompletableFuture> configure(ServerPlayerEntity player, ValueType from, ValueType to, @Nullable CastConverter old) { + return CompletableFuture.completedFuture(new CastConverter<>(from, to)); + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/value/Casts.java b/src/main/java/io/github/skippyall/minions/program/conversion/Casts.java similarity index 92% rename from src/main/java/io/github/skippyall/minions/program/value/Casts.java rename to src/main/java/io/github/skippyall/minions/program/conversion/Casts.java index 1cd0724..0fdf426 100644 --- a/src/main/java/io/github/skippyall/minions/program/value/Casts.java +++ b/src/main/java/io/github/skippyall/minions/program/conversion/Casts.java @@ -1,5 +1,6 @@ -package io.github.skippyall.minions.program.value; +package io.github.skippyall.minions.program.conversion; +import io.github.skippyall.minions.program.value.ValueType; import io.github.skippyall.minions.registration.ValueTypes; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/io/github/skippyall/minions/program/conversion/EqualityConverter.java b/src/main/java/io/github/skippyall/minions/program/conversion/EqualityConverter.java new file mode 100644 index 0000000..619ab00 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/conversion/EqualityConverter.java @@ -0,0 +1,73 @@ +package io.github.skippyall.minions.program.conversion; + +import com.mojang.serialization.MapCodec; +import io.github.skippyall.minions.program.value.ValueType; +import io.github.skippyall.minions.registration.MinionRegistries; +import io.github.skippyall.minions.registration.ValueTypes; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; + +public class EqualityConverter implements ValueConverter { + public static final MapCodec> CODEC = MinionRegistries.VALUE_TYPES.getCodec().dispatchMap( + EqualityConverter::getFrom, + EqualityConverter::getCodec + ); + + private ValueType fromType; + private F compareValue; + + public EqualityConverter(ValueType fromType, F compareValue) { + this.fromType = fromType; + this.compareValue = compareValue; + } + + @Override + public Boolean convert(F from) { + return compareValue.equals(from); + } + + @Override + public ValueType getFrom() { + return fromType; + } + + @Override + public ValueType getTo() { + return ValueTypes.BOOLEAN; + } + + @Override + public ValueConverterType getType() { + return null; + } + + private static MapCodec> getCodec(ValueType fromType) { + return fromType.codec().fieldOf("compareValue") + .xmap(compareValue -> new EqualityConverter<>(fromType, compareValue), converter -> converter.compareValue); + } + + public static class EqualityConverterType implements ValueConverterType> { + @Override + public MapCodec> getCodec() { + return CODEC; + } + + @Override + public boolean isSupportedConversion(ValueType from, ValueType to) { + return to == ValueTypes.BOOLEAN; + } + + @Override + public CompletableFuture> configure(ServerPlayerEntity player, ValueType from, ValueType to, @Nullable EqualityConverter old) { + if(to == ValueTypes.BOOLEAN) { + //noinspection unchecked + return from.openValueDialog(player, old != null && old.fromType == from ? (F) old.compareValue : null) + .thenApply(compareValue -> new EqualityConverter<>(from, compareValue)); + } else { + return CompletableFuture.failedFuture(new IllegalArgumentException("EqualityConverter does not support converting to " + to)); + } + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/conversion/ValueConverter.java b/src/main/java/io/github/skippyall/minions/program/conversion/ValueConverter.java new file mode 100644 index 0000000..cf72e77 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/conversion/ValueConverter.java @@ -0,0 +1,29 @@ +package io.github.skippyall.minions.program.conversion; + +import com.mojang.serialization.Codec; +import io.github.skippyall.minions.program.value.ValueType; +import io.github.skippyall.minions.registration.MinionRegistries; +import net.minecraft.text.Text; + +public interface ValueConverter { + Codec> CODEC = MinionRegistries.VALUE_CONVERTER_TYPES.getCodec().dispatch(ValueConverter::getType, ValueConverterType::getCodec); + + T convert(F from); + + ValueType getFrom(); + + ValueType getTo(); + + ValueConverterType getType(); + + Text getDisplayText(); + + default ValueConverter cast(ValueType from, ValueType to) { + if(from == getFrom() && to == getTo()) { + //noinspection unchecked + return (ValueConverter) this; + } else { + return null; + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/conversion/ValueConverterType.java b/src/main/java/io/github/skippyall/minions/program/conversion/ValueConverterType.java new file mode 100644 index 0000000..e5ac035 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/conversion/ValueConverterType.java @@ -0,0 +1,16 @@ +package io.github.skippyall.minions.program.conversion; + +import com.mojang.serialization.MapCodec; +import io.github.skippyall.minions.program.value.ValueType; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; + +public interface ValueConverterType> { + MapCodec getCodec(); + + boolean isSupportedConversion(ValueType from, ValueType to); + + CompletableFuture configure(ServerPlayerEntity player, ValueType from, ValueType to, @Nullable C old); +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstruction.java b/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstruction.java index dd33b30..999d4ef 100644 --- a/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstruction.java +++ b/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstruction.java @@ -11,6 +11,10 @@ import net.minecraft.storage.ReadView; import net.minecraft.storage.WriteView; import org.jetbrains.annotations.Nullable; +/** + * Holds an instruction, its configuration and is responsible for executing the instruction + * @param The runtime holding this object + */ public class ConfiguredInstruction> { private final InstructionType instruction; private final ValueSupplierList arguments; @@ -48,7 +52,7 @@ public class ConfiguredInstruction> { } public boolean canRun() { - return instruction != null && arguments != null && arguments.hasArgumentForAll(instruction.getParameters()); + return instruction != null && arguments != null && arguments.checkRun(instruction).isSuccess(); } public boolean isRunning() { diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/InstructionType.java b/src/main/java/io/github/skippyall/minions/program/instruction/InstructionType.java index 7c17a88..1852f20 100644 --- a/src/main/java/io/github/skippyall/minions/program/instruction/InstructionType.java +++ b/src/main/java/io/github/skippyall/minions/program/instruction/InstructionType.java @@ -9,6 +9,11 @@ import java.util.Collection; import java.util.List; import java.util.function.Supplier; +/** + * Defines the semantics of an instruction and creates {@link InstructionExecution} + * InstructionTypes for minions can be registered to {@link io.github.skippyall.minions.registration.MinionRegistries#INSTRUCTION_TYPES} + * @param The runtime that this instruction can be executed in + */ public class InstructionType> { private final List> parameters; private final List> returnParameters; diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/Parameter.java b/src/main/java/io/github/skippyall/minions/program/supplier/Parameter.java index f57ee4b..f2d111b 100644 --- a/src/main/java/io/github/skippyall/minions/program/supplier/Parameter.java +++ b/src/main/java/io/github/skippyall/minions/program/supplier/Parameter.java @@ -1,6 +1,7 @@ package io.github.skippyall.minions.program.supplier; import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.github.skippyall.minions.program.value.SimpleValueType; import io.github.skippyall.minions.registration.MinionRegistries; @@ -8,11 +9,12 @@ import io.github.skippyall.minions.program.value.ValueType; import org.jetbrains.annotations.Nullable; public record Parameter(String name, ValueType type) { - public static final Codec> CODEC = RecordCodecBuilder.create(instance -> + public static final MapCodec> MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( Codec.STRING.fieldOf("name").forGetter(Parameter::name), MinionRegistries.VALUE_TYPES.getCodec().fieldOf("type").forGetter(Parameter::type) ).apply(instance, Parameter::new)); + public static final Codec> CODEC = MAP_CODEC.codec(); public @Nullable Parameter cast(ValueType type) { if(this.type == type) { diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierList.java b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierList.java index 522881e..a74e479 100644 --- a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierList.java +++ b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierList.java @@ -1,69 +1,100 @@ package io.github.skippyall.minions.program.supplier; import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.github.skippyall.minions.gui.input.Result; import io.github.skippyall.minions.program.InstructionRuntime; -import io.github.skippyall.minions.program.value.Cast; -import io.github.skippyall.minions.program.value.Casts; +import io.github.skippyall.minions.program.conversion.Cast; +import io.github.skippyall.minions.program.conversion.Casts; +import io.github.skippyall.minions.program.conversion.ValueConverter; +import io.github.skippyall.minions.program.instruction.InstructionType; +import io.github.skippyall.minions.program.value.ValueType; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.Consumer; public class ValueSupplierList> { - private final Map> arguments; + private final Map, ValueSupplierEntry> arguments = new HashMap<>(); private final List>> changeListeners = new ArrayList<>(); public ValueSupplierList() { - arguments = new HashMap<>(); + } - public ValueSupplierList(Map> arguments) { - this.arguments = new HashMap<>(arguments); + private ValueSupplierList(List> arguments) { + for(ValueSupplierEntry argument : arguments) { + this.arguments.put(argument.parameter, argument); + } } - public T getValue(Parameter parameter, R runtime) { - //noinspection unchecked - ValueSupplier valueSupplier = (ValueSupplier) getArgument(parameter); - if(valueSupplier == null) { - return null; - } + private List> toCodecList() { + return List.copyOf(arguments.values()); + } - if(valueSupplier.getValueType() == parameter.type()) { - return valueSupplier.cast(parameter.type()).resolve(runtime); - } else { - Cast cast = Casts.getCast(valueSupplier.getValueType(), parameter.type()); - if(cast != null) { - return cast.cast(valueSupplier.resolve(runtime)); - } - } - return null; + public T getValue(Parameter parameter, R runtime) { + ValueSupplierEntry entry = getEntry(parameter); + return parameter.type().checkedCast(entry.getValue(runtime)); } public ValueSupplier getArgument(Parameter parameter) { - return arguments.get(parameter.name()); + if(arguments.containsKey(parameter)) { + return arguments.get(parameter).supplier; + } else { + return null; + } } - public void setArgument(Parameter parameter, ValueSupplier valueSupplier) { - arguments.put(parameter.name(), valueSupplier); + public

ValueSupplierEntry getEntry(Parameter

parameter) { + //noinspection unchecked + return (ValueSupplierEntry) arguments.get(parameter); + } + + public void setArgument(Parameter parameter, ValueSupplier valueSupplier) { + arguments.put(parameter, new ValueSupplierEntry<>(parameter, valueSupplier, List.of())); + onChange(parameter); + } + + public void setArgument(Parameter parameter, ValueSupplier valueSupplier, List> converter) { + arguments.put(parameter, new ValueSupplierEntry<>(parameter, valueSupplier, converter)); onChange(parameter); } public boolean hasArgumentFor(Parameter parameter) { - return arguments.containsKey(parameter.name()); + return arguments.containsKey(parameter); } - public boolean hasArgumentForAll(Collection> checkParameters) { + public Result<@Nullable Void, Text> checkHasArguments(Collection> checkParameters) { for(Parameter parameter : checkParameters) { if(!hasArgumentFor(parameter)) { - return false; + return new Result.Error<>(Text.translatable("minions.gui.instruction.check.argument_not_set", parameter.name())); } } - return true; + return new Result.Success<>(null); } + public Result<@Nullable Void, Text> checkRun(InstructionType instructionType) { + Result<@Nullable Void, Text> checkResult = checkHasArguments(instructionType.getParameters()); + if(!checkResult.isSuccess()) { + return checkResult; + } + + for(ValueSupplierEntry entry : arguments.values()) { + checkResult = entry.check(); + if(!checkResult.isSuccess()) { + return checkResult; + } + } + return new Result.Success<>(null); + } private void onChange(Parameter parameter) { for (Consumer> listener : changeListeners) { @@ -80,7 +111,98 @@ public class ValueSupplierList> { } public static > Codec> getCodec(Codec> argumentCodec) { - return Codec.unboundedMap(Codec.STRING, argumentCodec) - .xmap(ValueSupplierList::new, list -> list.arguments); + return ValueSupplierEntry.getCodec(argumentCodec) + .codec() + .listOf() + .xmap(ValueSupplierList::new, ValueSupplierList::toCodecList); + } + + public static class ValueSupplierEntry> { + private Parameter

parameter; + private ValueSupplier supplier; + private List> converters; + + public ValueSupplierEntry(Parameter

parameter, ValueSupplier supplier, Collection> converters) { + this.parameter = parameter; + this.supplier = supplier; + this.converters = new LinkedList<>(converters); + } + + public List> getConverters() { + return converters; + } + + public Parameter

getParameter() { + return parameter; + } + + public ValueSupplier getSupplier() { + return supplier; + } + + public void setParameter(Parameter

parameter) { + this.parameter = parameter; + } + + public void setSupplier(ValueSupplier supplier) { + this.supplier = supplier; + } + + public void setConverters(List> converters) { + this.converters = converters; + } + + public @Nullable P getValue(R runtime) { + S value = supplier.resolve(runtime); + Iterator> iterator = converters.iterator(); + Object convertedValue = convert(supplier.getValueType(), value, iterator.next(), iterator); + + return finalCast(convertedValue); + } + + private @Nullable P finalCast(Object value) { + //noinspection unchecked + ValueType lastConvertedType = (ValueType) converters.getLast().getTo(); + F convertedValue = lastConvertedType.checkedCast(value); + if(convertedValue == null) { + return null; + } + + Cast cast = Casts.getCast(lastConvertedType, parameter.type()); + if(cast != null) { + return cast.cast(convertedValue); + } + return null; + } + + private @Nullable Object convert(ValueType fromType, F from, ValueConverter converter, Iterator> iterator) { + Cast cast = Casts.getCast(fromType, converter.getFrom()); + if(cast == null) { + return null; + } + I inter = cast.cast(from); + if(inter == null) { + return null; + } + T to = converter.convert(inter); + + if(iterator.hasNext() && to != null) { + return convert(converter.getTo(), to, iterator.next(), iterator); + } else { + return to; + } + } + + public Result<@Nullable Void, Text> check() { + return new Result.Success<>(null); + } + + public static > MapCodec> getCodec(Codec> argumentCodec) { + return RecordCodecBuilder.mapCodec(instance -> instance.group( + Parameter.CODEC.fieldOf("parameter").forGetter(e -> e.parameter), + argumentCodec.fieldOf("argument").forGetter(e -> e.supplier), + ValueConverter.CODEC.listOf().optionalFieldOf("converter", List.of()).forGetter(e -> e.converters) + ).apply(instance, ValueSupplierEntry::new)); + } } } diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierType.java b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierType.java index eb50215..0d79c3f 100644 --- a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierType.java +++ b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplierType.java @@ -11,5 +11,5 @@ import java.util.concurrent.CompletableFuture; public abstract class ValueSupplierType> { public abstract Codec> getCodec(ValueType type); - public abstract CompletableFuture> openConfiguration(ServerPlayerEntity player, ValueType valueType, @Nullable ValueSupplier previous); + public abstract CompletableFuture> openConfiguration(ServerPlayerEntity player, ValueType valueType, @Nullable ValueSupplier previous); } diff --git a/src/main/java/io/github/skippyall/minions/program/value/LongValueType.java b/src/main/java/io/github/skippyall/minions/program/value/LongValueType.java deleted file mode 100644 index 2b937a5..0000000 --- a/src/main/java/io/github/skippyall/minions/program/value/LongValueType.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.skippyall.minions.program.value; - -import com.mojang.serialization.Codec; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; - -import java.util.concurrent.CompletableFuture; - -public class LongValueType implements ValueType { - @Override - public CompletableFuture openValueDialog(ServerPlayerEntity player, Long previousValue) { - return null; - } - - @Override - public Text getDisplayText(Long value) { - return null; - } - - @Override - public Codec codec() { - return null; - } - - @Override - public Long defaultValue() { - return 0L; - } -} diff --git a/src/main/java/io/github/skippyall/minions/program/value/SimpleValueType.java b/src/main/java/io/github/skippyall/minions/program/value/SimpleValueType.java index efce1dd..62ff567 100644 --- a/src/main/java/io/github/skippyall/minions/program/value/SimpleValueType.java +++ b/src/main/java/io/github/skippyall/minions/program/value/SimpleValueType.java @@ -9,7 +9,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Function; -public record SimpleValueType(Codec codec, T defaultValue, BiFunction> valueDialogOpener, Function textDisplay) implements ValueType { +public record SimpleValueType(Codec codec, T defaultValue, Function checkedCast, BiFunction> valueDialogOpener, Function textDisplay) implements ValueType { @Override public CompletableFuture openValueDialog(ServerPlayerEntity player, T previousValue) { return valueDialogOpener.apply(player, previousValue); @@ -19,4 +19,9 @@ public record SimpleValueType(Codec codec, T defaultValue, BiFunction { Codec codec(); T defaultValue(); + + @Nullable T checkedCast(Object value); + + default boolean isOf(Object value) { + return checkedCast(value) != null; + } } diff --git a/src/main/java/io/github/skippyall/minions/program/value/conversion/ValueConverter.java b/src/main/java/io/github/skippyall/minions/program/value/conversion/ValueConverter.java deleted file mode 100644 index ca6fce2..0000000 --- a/src/main/java/io/github/skippyall/minions/program/value/conversion/ValueConverter.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.skippyall.minions.program.value.conversion; - -import io.github.skippyall.minions.program.value.ValueType; - -public interface ValueConverter { - T convert(F from); - - ValueType getFrom(); - - ValueType getTo(); - - ValueConverterType getType(); -} diff --git a/src/main/java/io/github/skippyall/minions/program/value/conversion/ValueConverterType.java b/src/main/java/io/github/skippyall/minions/program/value/conversion/ValueConverterType.java deleted file mode 100644 index 52db014..0000000 --- a/src/main/java/io/github/skippyall/minions/program/value/conversion/ValueConverterType.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.skippyall.minions.program.value.conversion; - -import com.mojang.serialization.Codec; -import io.github.skippyall.minions.program.value.ValueType; -import net.minecraft.server.network.ServerPlayerEntity; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -public interface ValueConverterType> { - Codec getCodec(ValueType from, ValueType to); - - boolean isSupportedFromType(ValueType type); - - boolean isSupportedToType(ValueType type); - - CompletableFuture configure(ServerPlayerEntity player, ValueType from, ValueType to, @Nullable C old); -} diff --git a/src/main/java/io/github/skippyall/minions/registration/MinionComponentTypes.java b/src/main/java/io/github/skippyall/minions/registration/MinionComponentTypes.java index 3d00933..7eee3d8 100644 --- a/src/main/java/io/github/skippyall/minions/registration/MinionComponentTypes.java +++ b/src/main/java/io/github/skippyall/minions/registration/MinionComponentTypes.java @@ -16,7 +16,7 @@ import java.util.UUID; public class MinionComponentTypes { public static final ComponentType MINION_DATA = register("minion_data", ComponentType.builder().codec(Uuids.CODEC).build()); public static final ComponentType MODULE = register("minion_module", ComponentType.builder().codec(MinionModule.CODEC).build()); - public static final ComponentType REFERENCE = ComponentType.builder().codec(Clipboard.CODEC).build(); + public static final ComponentType REFERENCE = register("reference", ComponentType.builder().codec(Clipboard.CODEC).build()); private static > T register(String name, T type) { Registry.register(Registries.DATA_COMPONENT_TYPE, Identifier.of(Minions.MOD_ID, name), type); diff --git a/src/main/java/io/github/skippyall/minions/registration/MinionRegistration.java b/src/main/java/io/github/skippyall/minions/registration/MinionRegistration.java index 780748e..d05bce9 100644 --- a/src/main/java/io/github/skippyall/minions/registration/MinionRegistration.java +++ b/src/main/java/io/github/skippyall/minions/registration/MinionRegistration.java @@ -15,6 +15,7 @@ public class MinionRegistration { MinionListeners.register(); SkinProviders.register(); SpecialAbilities.register(); + ValueConverters.register(); ValueSuppliers.register(); ValueTypes.register(); diff --git a/src/main/java/io/github/skippyall/minions/registration/MinionRegistries.java b/src/main/java/io/github/skippyall/minions/registration/MinionRegistries.java index 00ff630..3468bac 100644 --- a/src/main/java/io/github/skippyall/minions/registration/MinionRegistries.java +++ b/src/main/java/io/github/skippyall/minions/registration/MinionRegistries.java @@ -17,6 +17,7 @@ import io.github.skippyall.minions.program.instruction.InstructionType; import io.github.skippyall.minions.program.consumer.ValueConsumerType; import io.github.skippyall.minions.program.value.ValueType; import io.github.skippyall.minions.clipboard.Clipboard; +import io.github.skippyall.minions.program.conversion.ValueConverterType; import net.fabricmc.fabric.api.event.registry.DynamicRegistries; import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; import net.fabricmc.fabric.api.event.registry.RegistryAttribute; @@ -29,6 +30,8 @@ public class MinionRegistries { public static final Registry> VALUE_SUPPLIER_TYPES = registry("value_supplier_type"); public static final Registry> VALUE_CONSUMER_TYPES = registry("value_consumer_type"); public static final Registry> INSTRUCTION_TYPES = registry("instruction_type"); + public static final Registry> VALUE_CONVERTER_TYPES = registry("value_converter_type"); + public static final Registry SKIN_PROVIDERS = registry("skin_provider"); public static final Registry> GUI_DISPLAY_TYPE = registry("gui_display_type"); public static final Registry> INSTRUCTION_LISTENER_CODECS = registry("instruction_listener_codec"); diff --git a/src/main/java/io/github/skippyall/minions/registration/ValueConverters.java b/src/main/java/io/github/skippyall/minions/registration/ValueConverters.java new file mode 100644 index 0000000..900f247 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/registration/ValueConverters.java @@ -0,0 +1,19 @@ +package io.github.skippyall.minions.registration; + +import io.github.skippyall.minions.Minions; +import io.github.skippyall.minions.program.conversion.CastConverter; +import io.github.skippyall.minions.program.conversion.EqualityConverter; +import io.github.skippyall.minions.program.conversion.ValueConverterType; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +public class ValueConverters { + public static final EqualityConverter.EqualityConverterType EQUALITY_CONVERTER = register("equality_converter", new EqualityConverter.EqualityConverterType()); + public static final CastConverter.Type CAST_CONVERTER = register("cast_converter", new CastConverter.Type()); + + private static > T register(String name, T type) { + return Registry.register(MinionRegistries.VALUE_CONVERTER_TYPES, Identifier.of(Minions.MOD_ID, name), type); + } + + public static void register() {} +} diff --git a/src/main/java/io/github/skippyall/minions/registration/ValueTypes.java b/src/main/java/io/github/skippyall/minions/registration/ValueTypes.java index c8344c5..164890a 100644 --- a/src/main/java/io/github/skippyall/minions/registration/ValueTypes.java +++ b/src/main/java/io/github/skippyall/minions/registration/ValueTypes.java @@ -8,85 +8,88 @@ import io.github.skippyall.minions.gui.input.ChoiceInput; import io.github.skippyall.minions.gui.input.TextInput; import io.github.skippyall.minions.minion.program.instruction.move.TurnDirection; import net.minecraft.registry.Registry; -import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import net.minecraft.util.Identifier; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; -import java.util.function.Function; - public class ValueTypes { - public static ValueType LONG = registerSimple( + public static ValueType LONG = register( "long", - Codec.LONG, - 0L, - (player, oldValue) -> TextInput.inputLong( - player, - Text.literal("Integer"), - String.valueOf(oldValue) - ), - value -> Text.literal(value.toString()) + new SimpleValueType<>( + Codec.LONG, + 0L, + o -> o instanceof Long l ? l : null, + (player, oldValue) -> TextInput.inputLong( + player, + Text.literal("Integer"), + String.valueOf(oldValue) + ), + value -> Text.literal(value.toString()) + ) ); - public static ValueType DOUBLE = registerSimple( + public static ValueType DOUBLE = register( "double", - Codec.DOUBLE, - 0D, - (player, oldValue) -> TextInput.inputDouble( - player, - Text.literal("Number"), - String.valueOf(oldValue) - ), - value -> Text.literal(value.toString()) + new SimpleValueType<>( + Codec.DOUBLE, + 0D, + o -> o instanceof Double d ? d : null, + (player, oldValue) -> TextInput.inputDouble( + player, + Text.literal("Number"), + String.valueOf(oldValue) + ), + value -> Text.literal(value.toString()) + ) ); - public static ValueType BOOLEAN = registerSimple( + public static ValueType BOOLEAN = register( "boolean", - Codec.BOOL, - false, - ChoiceInput.inputBoolean(Text.literal("")), - value -> Text.literal(value.toString()) + new SimpleValueType<>( + Codec.BOOL, + false, + o -> o instanceof Boolean b ? b : null, + ChoiceInput.inputBoolean(Text.literal("")), + value -> Text.literal(value.toString()) + ) ); - public static ValueType STRING = registerSimple( + public static ValueType STRING = register( "string", - Codec.STRING, - "", - ((player, oldValue) -> TextInput.inputString( - player, - Text.literal("Text"), - oldValue) - ), - value -> Text.literal("\"" + value + "\"") + new SimpleValueType<>( + Codec.STRING, + "", + o -> o instanceof String s ? s : null, + ((player, oldValue) -> TextInput.inputString( + player, + Text.literal("Text"), + oldValue) + ), + value -> Text.literal("\"" + value + "\"") + ) ); - public static ValueType TURN_DIRECTION = registerSimple( + public static ValueType TURN_DIRECTION = register( "turn_direction", - TurnDirection.CODEC, - TurnDirection.RIGHT, - ChoiceInput.createDialogOpener(TurnDirection.values()), - value -> Text.literal(value.name) + new SimpleValueType<>( + TurnDirection.CODEC, + TurnDirection.RIGHT, + o -> o instanceof TurnDirection d ? d : null, + ChoiceInput.createDialogOpener(TurnDirection.values()), + value -> Text.literal(value.name) + ) ); - private static ValueType registerSimple( + private static > T register( String id, - Codec codec, - T defaultValue, - BiFunction> valueDialogOpener, - Function textDisplay + T type ) { Identifier identifier = Identifier.of(Minions.MOD_ID, id); - return Registry.register( + Registry.register( MinionRegistries.VALUE_TYPES, identifier, - new SimpleValueType<>( - codec, - defaultValue, - valueDialogOpener, - textDisplay - ) + type ); + return type; } public static void register() {} diff --git a/src/main/resources/data/minions/lang/en_us.json b/src/main/resources/data/minions/lang/en_us.json index cdfcce3..8dfc25f 100644 --- a/src/main/resources/data/minions/lang/en_us.json +++ b/src/main/resources/data/minions/lang/en_us.json @@ -8,6 +8,7 @@ "minions.gui.ok": "OK", "minions.gui.confirm": "Confirm", "minions.gui.abort": "Abort", + "minions.gui.back": "Back", "minions.gui.look.skin.name": "Name", "minions.gui.look.skin.name.title": "Enter a player name", @@ -39,6 +40,8 @@ "minions.gui.instruction.parameter": "%s: %s", "minions.gui.instruction.argument": "Argument: %s", + "minions.gui.instruction.check.argument_not_set": "Argument %s is not set", + "minions.command.input.int.fail": "Not an integer", "minions.command.input.float.fail": "Not a number", "minions.command.action.details": "Set how to %s",