From c5b3c883ca830903f631866c13768981159231c3 Mon Sep 17 00:00:00 2001 From: skippyall <121978267+skippyall@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:00:57 +0100 Subject: [PATCH] MinionTriggerBlock & More GUI --- build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../minions/PlayerClipboardAttachment.java | 11 +++ .../minions/block/MinionTriggerBlock.java | 39 +++++++++- .../block/MinionTriggerBlockEntity.java | 74 ++++++++++++------- .../skippyall/minions/gui/CommandsGui.java | 40 ---------- .../skippyall/minions/gui/GuiDisplay.java | 7 +- .../skippyall/minions/gui/InstructionGui.java | 63 +++++++++++++--- .../skippyall/minions/gui/MinionGui.java | 10 +-- .../skippyall/minions/gui/MinionLookGui.java | 2 +- .../minions/gui/input/ChoiceInput.java | 74 +++++++++++++++++++ .../minions/{ => gui}/input/Result.java | 6 +- .../minions/{ => gui}/input/TextInput.java | 3 +- .../skippyall/minions/input/ChoiceInput.java | 37 ---------- .../minions/minion/MinionPersistentState.java | 1 - .../minions/minion/MinionProfileUtils.java | 9 +-- .../minions/minion/MinionRuntime.java | 14 +++- .../minion/skin/Base64SkinProvider.java | 2 +- .../minions/minion/skin/NameSkinProvider.java | 2 +- .../minions/minion/skin/UUIDSkinProvider.java | 8 +- .../instruction/ConfiguredInstruction.java | 20 ++--- .../program/instruction/Instructions.java | 7 +- .../minions/program/value/ValueTypes.java | 5 +- .../skippyall/minions/util/ModelIdUtil.java | 3 +- .../resources/data/minions/lang/en_us.json | 20 ++++- 25 files changed, 282 insertions(+), 181 deletions(-) create mode 100644 src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java delete mode 100644 src/main/java/io/github/skippyall/minions/gui/CommandsGui.java create mode 100644 src/main/java/io/github/skippyall/minions/gui/input/ChoiceInput.java rename src/main/java/io/github/skippyall/minions/{ => gui}/input/Result.java (95%) rename src/main/java/io/github/skippyall/minions/{ => gui}/input/TextInput.java (97%) delete mode 100644 src/main/java/io/github/skippyall/minions/input/ChoiceInput.java diff --git a/build.gradle b/build.gradle index 33abc6b..6d9e49e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + id 'net.fabricmc.fabric-loom-remap' version '1.14-SNAPSHOT' id 'maven-publish' } @@ -40,6 +40,8 @@ repositories { } dependencies { + implementation 'org.jetbrains:annotations:15.0' + // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 82d65ca..3c0465a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1 +1 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip diff --git a/src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java b/src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java new file mode 100644 index 0000000..3b2f718 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java @@ -0,0 +1,11 @@ +package io.github.skippyall.minions; + +import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry; +import net.fabricmc.fabric.api.attachment.v1.AttachmentType; +import net.minecraft.util.Identifier; + +import java.util.UUID; + +public record PlayerClipboardAttachment(UUID selectedMinion, String selectedInstruction) { + public static final AttachmentType TYPE = AttachmentRegistry.create(Identifier.of(Minions.MOD_ID, "clipboard")); +} diff --git a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlock.java b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlock.java index 2493d7d..e631da9 100644 --- a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlock.java +++ b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlock.java @@ -3,32 +3,58 @@ package io.github.skippyall.minions.block; import com.mojang.serialization.MapCodec; import eu.pb4.polymer.core.api.block.PolymerBlock; import io.github.skippyall.minions.MinionRegistration; +import io.github.skippyall.minions.PlayerClipboardAttachment; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.BlockWithEntity; import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; import net.minecraft.state.StateManager; import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.block.WireOrientation; import org.jetbrains.annotations.Nullable; import xyz.nucleoid.packettweaker.PacketContext; +import java.util.Optional; + public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock { public static final MapCodec CODEC = createCodec(MinionTriggerBlock::new); public static final BooleanProperty POWERED = BooleanProperty.of("powered"); + public static final BooleanProperty RUNNING = BooleanProperty.of("running"); public MinionTriggerBlock(Settings settings) { super(settings); - setDefaultState(getDefaultState().with(POWERED, false)); + setDefaultState(getDefaultState().with(POWERED, false).with(RUNNING, false)); } @Override protected void appendProperties(StateManager.Builder builder) { - builder.add(POWERED); + builder.add(POWERED, RUNNING); + } + + @Override + protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { + PlayerClipboardAttachment clipboard = player.getAttached(PlayerClipboardAttachment.TYPE); + if(clipboard != null) { + Optional be = world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE); + if(be.isPresent()) { + be.get().setInstruction(clipboard.selectedMinion(), clipboard.selectedInstruction()); + player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1); + + return ActionResult.SUCCESS; + } + } + return ActionResult.PASS; } @Override @@ -64,4 +90,13 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock public BlockState getPolymerBlockState(BlockState state, PacketContext context) { return state.get(POWERED) ? Blocks.REDSTONE_BLOCK.getDefaultState() : Blocks.GOLD_BLOCK.getDefaultState(); } + + @Override + public @Nullable BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType type) { + if(type == MinionRegistration.MINION_TRIGGER_BE_TYPE) { + return MinionTriggerBlockEntity::tick; + } else { + return null; + } + } } diff --git a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockEntity.java b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockEntity.java index 54bbf54..beea910 100644 --- a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockEntity.java +++ b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockEntity.java @@ -10,62 +10,80 @@ import net.minecraft.storage.ReadView; import net.minecraft.storage.WriteView; import net.minecraft.util.Uuids; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import java.util.Optional; import java.util.UUID; public class MinionTriggerBlockEntity extends BlockEntity { private UUID minionUuid; private String instructionName = ""; + private boolean first = true; + private boolean runningCache = false; + public MinionTriggerBlockEntity(BlockPos pos, BlockState state) { super(MinionRegistration.MINION_TRIGGER_BE_TYPE, pos, state); } - public void updatePower() { - boolean powered = getCachedState().get(MinionTriggerBlock.POWERED); - ConfiguredInstruction instruction = getInstruction(); + public void setInstruction(UUID minionUuid, String instructionName) { + this.minionUuid = minionUuid; + this.instructionName = instructionName; + markDirty(); + } - if(instruction != null) { - if(powered) { - instruction.run(getMinion().getInstructionManager()); - } else { - instruction.stop(getMinion().getInstructionManager()); + public static void tick(World world, BlockPos pos, BlockState state, BlockEntity blockEntity) { + if(!(blockEntity instanceof MinionTriggerBlockEntity triggerBlockEntity)) { + return; + } + if(triggerBlockEntity.first) { + triggerBlockEntity.first = false; + world.updateComparators(pos, MinionRegistration.MINION_TRIGGER_BLOCK); + triggerBlockEntity.runningCache = triggerBlockEntity.getInstruction().map(ConfiguredInstruction::isRunning).orElse(false); + } else { + boolean isRunning = triggerBlockEntity.getInstruction().map(ConfiguredInstruction::isRunning).orElse(false); + if (isRunning != triggerBlockEntity.runningCache) { + world.updateComparators(pos, MinionRegistration.MINION_TRIGGER_BLOCK); + triggerBlockEntity.runningCache = isRunning; } } } + public void updatePower() { + boolean powered = getCachedState().get(MinionTriggerBlock.POWERED); + getMinion().ifPresent(minion -> { + getInstruction().ifPresent(instruction -> { + if(powered) { + instruction.run(minion.getInstructionManager()); + } else { + instruction.stop(minion.getInstructionManager()); + } + }); + }); + + } + public int getComparatorOutput() { - ConfiguredInstruction instruction = getInstruction(); - if(instruction != null && instruction.isRunning()) { + Optional> instruction = getInstruction(); + if(instruction.isPresent() && instruction.get().isRunning()) { return 15; } return 0; } - public MinionFakePlayer getMinion() { + public Optional getMinion() { if(minionUuid != null && world != null && world.getPlayerByUuid(minionUuid) instanceof MinionFakePlayer minion) { - return minion; + return Optional.of(minion); } - return null; + return Optional.empty(); } - public ConfiguredInstruction getInstruction() { - MinionFakePlayer minion = getMinion(); - if(minion == null) { - return null; - } - - return minion.getInstructionManager().getInstruction(instructionName); + public Optional> getInstruction(MinionFakePlayer minion) { + return Optional.ofNullable(minion.getInstructionManager().getInstruction(instructionName)); } - @Override - public void onBlockReplaced(BlockPos pos, BlockState oldState) { - super.onBlockReplaced(pos, oldState); - } - - @Override - public void markRemoved() { - super.markRemoved(); + public Optional> getInstruction() { + return getMinion().flatMap(this::getInstruction); } @Override diff --git a/src/main/java/io/github/skippyall/minions/gui/CommandsGui.java b/src/main/java/io/github/skippyall/minions/gui/CommandsGui.java deleted file mode 100644 index 8fa1f06..0000000 --- a/src/main/java/io/github/skippyall/minions/gui/CommandsGui.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.skippyall.minions.gui; - -public class CommandsGui { - /*public static void openServerModuleCommandGui(ServerPlayerEntity player, MinionFakePlayer minion) { - Collection modules = minion.getModuleInventory().getModules(); - - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); - - gui.setTitle(Text.translatable("minions.gui.module_commands.title")); - - for (ModuleItem module : modules) { - gui.addSlot(new GuiElementBuilder() - .setItem(module.asItem()) - .setCallback(() -> openServerCommandGui(player, minion, module)) - ); - } - - gui.open(); - } - - public static void openServerCommandGui(ServerPlayerEntity player, MinionFakePlayer minion, ModuleItem module) { - List commands = module.getCommands(); - - SimpleGui commandGui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); - - commandGui.setTitle(Text.translatable("minions.gui.commands.title", module.asItem().getName())); - - for(int j = 0; j < commands.size(); j++) { - Command command = commands.get(j); - commandGui.setSlot(j, new GuiElementBuilder() - .setItem(command.getItemRepresentation()) - .setName(command.getName()) - .addLoreLine(command.getDescription()) - .setCallback(() -> command.execute(player, minion)) - ); - } - - commandGui.open(); - }*/ -} diff --git a/src/main/java/io/github/skippyall/minions/gui/GuiDisplay.java b/src/main/java/io/github/skippyall/minions/gui/GuiDisplay.java index cd9e35b..4dbab1e 100644 --- a/src/main/java/io/github/skippyall/minions/gui/GuiDisplay.java +++ b/src/main/java/io/github/skippyall/minions/gui/GuiDisplay.java @@ -1,11 +1,8 @@ package io.github.skippyall.minions.gui; import com.mojang.authlib.properties.PropertyMap; -import com.mojang.datafixers.util.Either; import eu.pb4.sgui.api.elements.GuiElementBuilder; import io.github.skippyall.minions.util.ModelIdUtil; -import net.minecraft.block.SkullBlock; -import net.minecraft.block.entity.SkullBlockEntity; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.LoreComponent; import net.minecraft.component.type.ProfileComponent; @@ -30,6 +27,10 @@ public interface GuiDisplay { this(ModelIdUtil.getItemModelId(model), translationKeyBase, withLore); } + public ModelBased(Item model, String translationKeyBase) { + this(ModelIdUtil.getItemModelId(model), translationKeyBase, false); + } + @Override public ItemStack createItemStack() { ItemStack stack = new ItemStack(Items.BARRIER); diff --git a/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java b/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java index 2249295..98e4e77 100644 --- a/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java @@ -3,7 +3,10 @@ package io.github.skippyall.minions.gui; import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.SimpleGui; import io.github.skippyall.minions.MinionRegistries; -import io.github.skippyall.minions.input.TextInput; +import io.github.skippyall.minions.PlayerClipboardAttachment; +import io.github.skippyall.minions.gui.input.ChoiceInput; +import io.github.skippyall.minions.gui.input.Result; +import io.github.skippyall.minions.gui.input.TextInput; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.module.MinionModule; @@ -17,7 +20,10 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.screen.ScreenHandlerType; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.NoSuchElementException; @@ -28,12 +34,12 @@ public class InstructionGui { SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); gui.setSlot(3, new GuiElementBuilder() .setItem(Items.BOOK) - .setName(Text.literal("Instruction List")) + .setName(Text.translatable("minions.gui.instruction.list")) .setCallback(() -> instructionList(minion, player)) ); gui.setSlot(5, new GuiElementBuilder() .setItem(Items.WRITABLE_BOOK) - .setName(Text.literal("New Instruction")) + .setName(Text.translatable("minions.gui.instruction.create")) .setCallback(() -> createNewInstruction(minion, player)) ); @@ -55,13 +61,22 @@ public class InstructionGui { public static void createNewInstruction(MinionFakePlayer minion, ServerPlayerEntity player) { selectInstructionModuleMenu(minion, player).thenAccept(instructionType -> - TextInput.inputString(player, Text.translatable("minions.gui.instruction.enter_name"), "Instruction").thenAccept(name -> { + inputInstructionName(minion, player, "Instruction").thenAccept(name -> { ConfiguredInstruction configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType); configureInstructionMenu(name, configuredInstruction, minion, player); }) ); } + public static CompletableFuture inputInstructionName(MinionFakePlayer minion, ServerPlayerEntity player, String defaultValue) { + return TextInput.inputSync(player, Text.translatable("minions.gui.instruction.enter_name"), defaultValue, name -> { + if(minion.getInstructionManager().hasInstruction(name)) { + return new Result.Error<>(Text.translatable("minions.gui.instruction.name_already_used")); + } + return new Result.Success<>(name); + }); + } + public static boolean checkInstructionExists(String name, ConfiguredInstruction instruction, MinionFakePlayer minion, ServerPlayerEntity player) { boolean stillExists = minion.getInstructionManager().getInstruction(name) == instruction; if(!stillExists) { @@ -77,16 +92,44 @@ public class InstructionGui { } SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); + gui.setTitle(Text.literal(name)); - gui.setSlot(12, createInstructionElement(instruction.getInstruction())); + gui.setSlot(7, new GuiElementBuilder(Items.ANVIL) + .setName(Text.translatable("minions.gui.instruction.configure.rename")) + .setCallback(() -> inputInstructionName(minion, player, name).thenAccept(newName -> { + minion.getInstructionManager().setInstructionName(name, newName); + configureInstructionMenu(newName, instruction, minion, player); + })) + ); - int slot = 13; + gui.setSlot(8, new GuiElementBuilder(Items.LAVA_BUCKET) + .setName(Text.translatable("minions.gui.instruction.configure.delete")) + .setCallback(() -> ChoiceInput.confirm(player, Text.translatable("minions.gui.instruction.configure.delete.confirm", name)) + .thenAccept(v -> { + minion.getInstructionManager().removeInstruction(name); + instructionList(minion, player); + })) + ); + + gui.setSlot(13, createInstructionElement(instruction.getInstruction())); + + int slot = 14; for(Parameter parameter : instruction.getInstruction().getParameters()) { gui.setSlot(slot, createArgumentElement(instruction.getArguments().getArgument(parameter)) .setCallback(() -> configureArgumentMenu(name, instruction, parameter, minion, player)) ); slot++; } + + gui.setSlot(25, new GuiElementBuilder(Items.FEATHER) + .setName(Text.translatable("minions.gui.instruction.configure.copy")) + .addLoreLine(Text.translatable("minions.gui.instruction.configure.copy.description")) + .setCallback(() -> { + player.setAttached(PlayerClipboardAttachment.TYPE, new PlayerClipboardAttachment(minion.getUuid(), name)); + player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1); + }) + ); + updateRunSlot(instruction, minion, gui); gui.open(); @@ -95,7 +138,7 @@ public class InstructionGui { private static void updateRunSlot(ConfiguredInstruction instruction, MinionFakePlayer minion, SimpleGui gui) { if(!instruction.isRunning()) { gui.setSlot(26, new GuiElementBuilder(Items.ARROW) - .setName(Text.literal("Run")) + .setName(Text.translatable("minions.gui.instruction.run")) .setCallback(() -> { instruction.run(minion.getInstructionManager()); updateRunSlot(instruction, minion, gui); @@ -103,7 +146,7 @@ public class InstructionGui { ); } else { gui.setSlot(26, new GuiElementBuilder(Items.BARRIER) - .setName(Text.literal("Stop")) + .setName(Text.translatable("minions.gui.instruction.stop")) .setCallback(() -> { instruction.stop(minion.getInstructionManager()); updateRunSlot(instruction, minion, gui); @@ -117,11 +160,11 @@ public class InstructionGui { return; } - A argument = instruction.getArguments().getArgument(parameter); + @Nullable A argument = instruction.getArguments().getArgument(parameter); SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); gui.setSlot(3, new GuiElementBuilder(Items.STICK) - .setName(Text.literal("Type: " + (argument == null ? "Unset" : MinionRegistries.ARGUMENT_TYPES.getId(argument.getType()).getPath()))) + .setName(Text.translatable("minions.gui.instruction.argument.configure.type", Text.translatable(TranslationUtil.getTranslationKey(argument != null ? argument.getType() : null, MinionRegistries.ARGUMENT_TYPES, "minions.gui.instruction.argument.configure.type.unset")))) .setCallback(() -> selectArgumentType(player) .thenApply(type -> type.openConfiguration(player, parameter.type(), null) .thenAccept(newArgument -> { diff --git a/src/main/java/io/github/skippyall/minions/gui/MinionGui.java b/src/main/java/io/github/skippyall/minions/gui/MinionGui.java index 6616223..ac69851 100644 --- a/src/main/java/io/github/skippyall/minions/gui/MinionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/MinionGui.java @@ -21,7 +21,7 @@ public class MinionGui { gui.setSlot(1, new GuiElementBuilder() .setItem(Items.COMMAND_BLOCK) - .setName(Text.translatable("minions.gui.main.commands")) + .setName(Text.translatable("minions.gui.main.instructions")) .setCallback((i, clickType, slotActionType) -> { InstructionGui.openInstructionMainMenu(minion, player); }) @@ -50,14 +50,6 @@ public class MinionGui { gui.open(); } - public static void openCommandsGui(ServerPlayerEntity player, MinionFakePlayer minion) { - //CommandsGui.openServerModuleCommandGui(player, minion); - } - - public static void openProgrammingInventory(ServerPlayerEntity player, MinionFakePlayer minion) { - - } - public static void openMinionInventory(ServerPlayerEntity player, MinionFakePlayer minion) { SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X5, player, false); gui.setTitle(Text.translatable("minions.gui.inventory.title", minion.getName())); diff --git a/src/main/java/io/github/skippyall/minions/gui/MinionLookGui.java b/src/main/java/io/github/skippyall/minions/gui/MinionLookGui.java index ca88b79..d2c58b9 100644 --- a/src/main/java/io/github/skippyall/minions/gui/MinionLookGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/MinionLookGui.java @@ -3,7 +3,7 @@ package io.github.skippyall.minions.gui; import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.SimpleGui; import io.github.skippyall.minions.MinionRegistries; -import io.github.skippyall.minions.input.TextInput; +import io.github.skippyall.minions.gui.input.TextInput; import io.github.skippyall.minions.minion.MinionData; import io.github.skippyall.minions.minion.MinionItem; import io.github.skippyall.minions.minion.MinionProfileUtils; diff --git a/src/main/java/io/github/skippyall/minions/gui/input/ChoiceInput.java b/src/main/java/io/github/skippyall/minions/gui/input/ChoiceInput.java new file mode 100644 index 0000000..342fcbf --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/input/ChoiceInput.java @@ -0,0 +1,74 @@ +package io.github.skippyall.minions.gui.input; + +import eu.pb4.sgui.api.elements.GuiElementBuilder; +import eu.pb4.sgui.api.gui.SimpleGui; +import io.github.skippyall.minions.gui.Displayable; +import io.github.skippyall.minions.gui.GuiDisplay; +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; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class ChoiceInput { + public static BiFunction> createDialogOpener(ScreenHandlerType screen, Text title, Function displayFunction, T[] values, @Nullable T fallback) { + return (player, object) -> { + CompletableFuture future = new CompletableFuture<>(); + + SimpleGui gui = new SimpleGui(screen, player, false) { + @Override + public void onClose() { + if(fallback == null) { + future.cancel(false); + } else { + future.complete(fallback); + } + } + }; + + gui.setTitle(title); + + for(T value : values) { + gui.addSlot(displayFunction.apply(value).createElement() + .setCallback(() -> future.complete(value)) + ); + } + gui.open(); + return future; + }; + } + + public static BiFunction> createDialogOpener(T[] values) { + return createDialogOpener(ScreenHandlerType.GENERIC_9X3, Text.empty(), Displayable::getDisplay, values, null); + } + + public static CompletableFuture confirm(ServerPlayerEntity player, Text title) { + CompletableFuture future = new CompletableFuture<>(); + + SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false) { + @Override + public void onClose() { + future.cancel(false); + } + }; + + gui.setTitle(title); + + gui.setSlot(3, new GuiElementBuilder(Items.REDSTONE_BLOCK) + .setName(Text.translatable("minions.gui.abort")) + .setCallback(() -> future.cancel(false)) + ); + + gui.setSlot(5, new GuiElementBuilder(Items.EMERALD_BLOCK) + .setName(Text.translatable("minions.gui.confirm")) + .setCallback(() -> future.complete(null)) + ); + + gui.open(); + return future; + } +} diff --git a/src/main/java/io/github/skippyall/minions/input/Result.java b/src/main/java/io/github/skippyall/minions/gui/input/Result.java similarity index 95% rename from src/main/java/io/github/skippyall/minions/input/Result.java rename to src/main/java/io/github/skippyall/minions/gui/input/Result.java index 9f1539b..24cd9ec 100644 --- a/src/main/java/io/github/skippyall/minions/input/Result.java +++ b/src/main/java/io/github/skippyall/minions/gui/input/Result.java @@ -1,4 +1,4 @@ -package io.github.skippyall.minions.input; +package io.github.skippyall.minions.gui.input; import org.jetbrains.annotations.NotNull; @@ -87,7 +87,7 @@ public interface Result { @Override public @NotNull T getOrThrow() { - throw new RuntimeException("Result was an error: " + message.toString()); + throw new RuntimeException("Result was an error: " + message); } @Override @@ -106,7 +106,7 @@ public interface Result { } @Override - public void ifError(Consumer> handler) { + public void ifError(@NotNull Consumer> handler) { handler.accept(this); } } diff --git a/src/main/java/io/github/skippyall/minions/input/TextInput.java b/src/main/java/io/github/skippyall/minions/gui/input/TextInput.java similarity index 97% rename from src/main/java/io/github/skippyall/minions/input/TextInput.java rename to src/main/java/io/github/skippyall/minions/gui/input/TextInput.java index 14fdb7a..3681183 100644 --- a/src/main/java/io/github/skippyall/minions/input/TextInput.java +++ b/src/main/java/io/github/skippyall/minions/gui/input/TextInput.java @@ -1,4 +1,4 @@ -package io.github.skippyall.minions.input; +package io.github.skippyall.minions.gui.input; import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.AnvilInputGui; @@ -9,7 +9,6 @@ import net.minecraft.text.Text; import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import java.util.function.Predicate; public class TextInput extends AnvilInputGui { private final GuiElementBuilder valid = new GuiElementBuilder() diff --git a/src/main/java/io/github/skippyall/minions/input/ChoiceInput.java b/src/main/java/io/github/skippyall/minions/input/ChoiceInput.java deleted file mode 100644 index 9b1a207..0000000 --- a/src/main/java/io/github/skippyall/minions/input/ChoiceInput.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.skippyall.minions.input; - -import eu.pb4.sgui.api.gui.SimpleGui; -import io.github.skippyall.minions.gui.Displayable; -import io.github.skippyall.minions.gui.GuiDisplay; -import net.minecraft.screen.ScreenHandlerType; -import net.minecraft.server.network.ServerPlayerEntity; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; -import java.util.function.Function; - -public class ChoiceInput { - public static BiFunction> createDialogOpener(Function displayFunction, T[] values) { - return (player, object) -> { - CompletableFuture future = new CompletableFuture<>(); - - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false) { - @Override - public void onClose() { - future.cancel(false); - } - }; - for(T value : values) { - gui.addSlot(displayFunction.apply(value).createElement() - .setCallback(() -> future.complete(value)) - ); - } - gui.open(); - return future; - }; - } - - public static BiFunction> createDialogOpener(T[] values) { - return createDialogOpener(Displayable::getDisplay, values); - } -} diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java b/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java index 02a21d5..0ee429e 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java @@ -1,7 +1,6 @@ package io.github.skippyall.minions.minion; import com.mojang.serialization.Codec; -import io.github.skippyall.minions.Minions; import net.minecraft.server.MinecraftServer; import net.minecraft.world.PersistentState; import net.minecraft.world.PersistentStateType; diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java b/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java index 799f0bd..7bc56a0 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java @@ -1,20 +1,13 @@ package io.github.skippyall.minions.minion; import com.mojang.authlib.GameProfile; -import com.mojang.authlib.ProfileLookupCallback; import com.mojang.authlib.properties.PropertyMap; -import com.mojang.authlib.yggdrasil.ProfileResult; import com.mojang.brigadier.StringReader; -import io.github.skippyall.minions.input.Result; -import net.minecraft.block.entity.SkullBlockEntity; -import net.minecraft.server.MinecraftServer; +import io.github.skippyall.minions.gui.input.Result; import net.minecraft.text.Text; import net.minecraft.util.StringHelper; -import org.jetbrains.annotations.Nullable; import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ForkJoinPool; import static io.github.skippyall.minions.Minions.LOGGER; diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionRuntime.java b/src/main/java/io/github/skippyall/minions/minion/MinionRuntime.java index a4297d9..ebb0f32 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionRuntime.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionRuntime.java @@ -40,7 +40,11 @@ public class MinionRuntime implements InstructionRuntime { } public ConfiguredInstruction createInstruction(String name, InstructionType instructionType) { - ConfiguredInstruction instruction = new ConfiguredInstruction<>(instructionType, name); + if(configuredInstructions.containsKey(name)) { + return null; + } + + ConfiguredInstruction instruction = new ConfiguredInstruction<>(instructionType); configuredInstructions.put(name, instruction); return instruction; } @@ -59,6 +63,12 @@ public class MinionRuntime implements InstructionRuntime { return configuredInstructions.containsKey(name); } + public void setInstructionName(String oldName, String newName) { + if(!configuredInstructions.containsKey(newName)) { + configuredInstructions.put(newName, configuredInstructions.remove(oldName)); + } + } + public void save(WriteView view) { WriteView.ListView list = view.getList("configuredInstructions"); for (Map.Entry> instruction : configuredInstructions.entrySet()) { @@ -78,7 +88,7 @@ public class MinionRuntime implements InstructionRuntime { } try { - ConfiguredInstruction instruction = ConfiguredInstruction.load(inner, this, name.get()); + ConfiguredInstruction instruction = ConfiguredInstruction.load(inner, this); configuredInstructions.put(name.get(), instruction); } catch (Exception e) { Minions.LOGGER.error("Could not deserialize configured instruction \"{}\" of minion \"{}\":", name.get(), minion.getGameProfile().getName(), e); diff --git a/src/main/java/io/github/skippyall/minions/minion/skin/Base64SkinProvider.java b/src/main/java/io/github/skippyall/minions/minion/skin/Base64SkinProvider.java index 6251307..2662893 100644 --- a/src/main/java/io/github/skippyall/minions/minion/skin/Base64SkinProvider.java +++ b/src/main/java/io/github/skippyall/minions/minion/skin/Base64SkinProvider.java @@ -2,7 +2,7 @@ package io.github.skippyall.minions.minion.skin; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; -import io.github.skippyall.minions.input.TextInput; +import io.github.skippyall.minions.gui.input.TextInput; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; diff --git a/src/main/java/io/github/skippyall/minions/minion/skin/NameSkinProvider.java b/src/main/java/io/github/skippyall/minions/minion/skin/NameSkinProvider.java index 9b0d6a2..47a3948 100644 --- a/src/main/java/io/github/skippyall/minions/minion/skin/NameSkinProvider.java +++ b/src/main/java/io/github/skippyall/minions/minion/skin/NameSkinProvider.java @@ -2,7 +2,7 @@ package io.github.skippyall.minions.minion.skin; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.PropertyMap; -import io.github.skippyall.minions.input.TextInput; +import io.github.skippyall.minions.gui.input.TextInput; import net.minecraft.block.entity.SkullBlockEntity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; diff --git a/src/main/java/io/github/skippyall/minions/minion/skin/UUIDSkinProvider.java b/src/main/java/io/github/skippyall/minions/minion/skin/UUIDSkinProvider.java index 520957c..8eaae8d 100644 --- a/src/main/java/io/github/skippyall/minions/minion/skin/UUIDSkinProvider.java +++ b/src/main/java/io/github/skippyall/minions/minion/skin/UUIDSkinProvider.java @@ -2,16 +2,10 @@ package io.github.skippyall.minions.minion.skin; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.PropertyMap; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import io.github.skippyall.minions.input.TextInput; -import io.github.skippyall.minions.minion.MinionProfileUtils; +import io.github.skippyall.minions.gui.input.TextInput; import net.minecraft.block.entity.SkullBlockEntity; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; -import net.minecraft.util.Uuids; -import net.minecraft.util.dynamic.Codecs; import java.util.Optional; import java.util.UUID; 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 aa56bf1..c8b4f37 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 @@ -13,18 +13,16 @@ public class ConfiguredInstruction> { private final ValueSupplierList arguments; private final ValueConsumerList valueConsumers; private @Nullable InstructionExecution execution; - private final String name; - private ConfiguredInstruction(InstructionType instruction, ValueSupplierList arguments, ValueConsumerList valueConsumers, @Nullable InstructionExecution execution, String name) { + private ConfiguredInstruction(InstructionType instruction, ValueSupplierList arguments, ValueConsumerList valueConsumers, @Nullable InstructionExecution execution) { this.instruction = instruction; this.arguments = arguments; this.valueConsumers = valueConsumers; this.execution = execution; - this.name = name; } - public ConfiguredInstruction(InstructionType instruction, String name) { - this(instruction, new ValueSupplierList<>(), new ValueConsumerList<>(), null, name); + public ConfiguredInstruction(InstructionType instruction) { + this(instruction, new ValueSupplierList<>(), new ValueConsumerList<>(), null); } public InstructionType getInstruction() { @@ -35,10 +33,6 @@ public class ConfiguredInstruction> { return arguments; } - public String getName() { - return name; - } - public boolean canRun() { return instruction != null && arguments != null && arguments.hasArgumentForAll(instruction.getParameters()); } @@ -57,7 +51,7 @@ public class ConfiguredInstruction> { execution = instruction.createExecution(arguments, minion); execution.start(minion); } catch (Exception e) { - Minions.LOGGER.error("An error occurred while executing configured Instruction {}", name, e); + Minions.LOGGER.error("An error occurred while executing configured Instruction", e); } } @@ -93,7 +87,7 @@ public class ConfiguredInstruction> { } } - public static > ConfiguredInstruction load(ReadView view, R minion, String name) { + public static > ConfiguredInstruction load(ReadView view, R minion) { InstructionType instructionType = view.read("instruction", minion.getInstructionTypeRegistry().getCodec()).orElseThrow(); ValueSupplierList arguments = view.read("arguments", minion.getArgumentListCodec()).orElseGet(ValueSupplierList::new); @@ -105,12 +99,12 @@ public class ConfiguredInstruction> { ReadView executionView = view.getReadView("execution"); try { InstructionExecution execution = instructionType.loadExecution(executionView, minion); - return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution, name); + return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution); } catch (Exception e) { Minions.LOGGER.error("Error while loading execution", e); } } - return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null, name); + return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null); } } diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/Instructions.java b/src/main/java/io/github/skippyall/minions/program/instruction/Instructions.java index 24d46cf..d1367d1 100644 --- a/src/main/java/io/github/skippyall/minions/program/instruction/Instructions.java +++ b/src/main/java/io/github/skippyall/minions/program/instruction/Instructions.java @@ -9,7 +9,6 @@ import io.github.skippyall.minions.program.instruction.execution.ActionExecution import io.github.skippyall.minions.program.instruction.execution.TurnExecution; import io.github.skippyall.minions.program.instruction.execution.WalkExecution; import io.github.skippyall.minions.program.supplier.Parameter; -import io.github.skippyall.minions.util.ModelIdUtil; import net.minecraft.item.Items; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; @@ -22,7 +21,7 @@ import java.util.function.Supplier; public class Instructions { public static final InstructionType WALK = register( "walk", - base -> new GuiDisplay.ModelBased(ModelIdUtil.getItemModelId(Items.IRON_BOOTS), base, true), + base -> new GuiDisplay.ModelBased(Items.IRON_BOOTS, base, true), WalkExecution::new, List.of(WalkExecution.blocksToMoveParam) ); @@ -36,13 +35,13 @@ public class Instructions { public static final InstructionType ATTACK = register( "attack", - base -> new GuiDisplay.ModelBased(ModelIdUtil.getItemModelId(Items.IRON_BOOTS), base, true), + base -> new GuiDisplay.ModelBased(Items.IRON_PICKAXE, base, true), () -> new ActionExecution(EntityPlayerActionPack.ActionType.ATTACK) ); public static final InstructionType USE = register( "use", - base -> new GuiDisplay.ModelBased(ModelIdUtil.getItemModelId(Items.LEVER), base, true), + base -> new GuiDisplay.ModelBased(Items.LEVER, base, true), () -> new ActionExecution(EntityPlayerActionPack.ActionType.USE) ); diff --git a/src/main/java/io/github/skippyall/minions/program/value/ValueTypes.java b/src/main/java/io/github/skippyall/minions/program/value/ValueTypes.java index d50122b..ffe175b 100644 --- a/src/main/java/io/github/skippyall/minions/program/value/ValueTypes.java +++ b/src/main/java/io/github/skippyall/minions/program/value/ValueTypes.java @@ -3,10 +3,9 @@ package io.github.skippyall.minions.program.value; import com.mojang.serialization.Codec; import io.github.skippyall.minions.MinionRegistries; import io.github.skippyall.minions.Minions; -import io.github.skippyall.minions.gui.Displayable; import io.github.skippyall.minions.gui.GuiDisplay; -import io.github.skippyall.minions.input.ChoiceInput; -import io.github.skippyall.minions.input.TextInput; +import io.github.skippyall.minions.gui.input.ChoiceInput; +import io.github.skippyall.minions.gui.input.TextInput; import io.github.skippyall.minions.program.instruction.execution.TurnExecution; import net.minecraft.item.Items; import net.minecraft.registry.Registry; diff --git a/src/main/java/io/github/skippyall/minions/util/ModelIdUtil.java b/src/main/java/io/github/skippyall/minions/util/ModelIdUtil.java index 10cfed5..78f383c 100644 --- a/src/main/java/io/github/skippyall/minions/util/ModelIdUtil.java +++ b/src/main/java/io/github/skippyall/minions/util/ModelIdUtil.java @@ -6,7 +6,6 @@ import net.minecraft.util.Identifier; public class ModelIdUtil { public static Identifier getItemModelId(Item item) { - Identifier identifier = Registries.ITEM.getId(item); - return identifier.withPrefixedPath("item/"); + return Registries.ITEM.getId(item); } } diff --git a/src/main/resources/data/minions/lang/en_us.json b/src/main/resources/data/minions/lang/en_us.json index ac46830..08416e2 100644 --- a/src/main/resources/data/minions/lang/en_us.json +++ b/src/main/resources/data/minions/lang/en_us.json @@ -1,13 +1,16 @@ { - "minions.gui.main.commands": "Commands", - "minions.gui.main.programming": "Programming", + "minions.gui.main.instructions": "Instructions", "minions.gui.main.modules": "Modules", "minions.gui.main.inventory": "Inventory", + "minions.gui.main.pickup": "Pick up", "minions.gui.inventory.title": "%s's Inventory", "minions.gui.module_commands.title": "Commands", "minions.gui.commands.title": "%s's Commands", "minions.gui.modules.title": "%s's Modules", "minions.gui.ok": "OK", + "minions.gui.confirm": "Confirm", + "minions.gui.abort": "Abort", + "minions.gui.look.skin.name": "Name", "minions.gui.look.skin.name.title": "Enter a player name", "minions.gui.look.skin.uuid": "UUID", @@ -15,10 +18,23 @@ "minions.gui.look.skin.base64": "Base64", "minions.gui.look.skin.base64.title": "Enter a skin in base64 encoding", "minions.gui.look.rename.title": "Enter a name", + + "minions.gui.instruction.list": "Instruction List", + "minions.gui.instruction.create": "New Instruction", "minions.gui.instruction.no_instruction_set": "No instruction set", "minions.gui.instruction.enter_name": "Enter a name", + "minions.gui.instruction.name_already_used": "Name already used", "minions.gui.instruction.removed": "This instruction no longer exists", "minions.gui.instruction.no_argument_set": "No argument set", + "minions.gui.instruction.configure.rename": "Rename", + "minions.gui.instruction.configure.delete": "Delete", + "minions.gui.instruction.configure.delete.confirm": "Delete %s?", + "minions.gui.instruction.configure.copy": "Copy Reference", + "minions.gui.instruction.configure.copy.description": "Click here and then use a Minion Trigger Block to bind it", + "minions.gui.instruction.run": "Run", + "minions.gui.instruction.stop": "Stop", + "minions.gui.instruction.argument.configure.type": "Type: %s", + "minions.gui.instruction.argument.configure.type.unset": "Unset", "minions.command.input.int.fail": "Not an integer", "minions.command.input.float.fail": "Not a number",