From 3f2a52fd0a093862dd3d08d9de049ecf0de68b21 Mon Sep 17 00:00:00 2001 From: skippyall <121978267+skippyall@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:12:40 +0100 Subject: [PATCH] Listen up! --- build.gradle | 26 +- gradle.properties | 2 +- .../minions/client/MinionsClient.java | 17 ++ ...ionRegistration.java => MinionBlocks.java} | 11 +- .../github/skippyall/minions/MinionItems.java | 12 +- .../minions/MinionMixinConfigPlugin.java | 49 ++++ .../skippyall/minions/MinionRegistries.java | 34 ++- .../io/github/skippyall/minions/Minions.java | 23 +- .../minions/PlayerClipboardAttachment.java | 11 - .../block/BlockEntityMinionListener.java | 55 +++++ .../block/MinionListeningBlockEntity.java | 7 + .../minions/block/MinionTriggerBlock.java | 116 +++++++-- .../block/MinionTriggerBlockEntity.java | 16 +- .../minions/block/MinionTriggerBlockItem.java | 25 ++ .../minions/docs/ReferenceEntry.java | 14 ++ .../minions/gui/ConfigureInstructionGui.java | 117 +++++++++ .../skippyall/minions/gui/GuiDisplay.java | 132 ++++++++-- .../gui/InstructionBoundSimpleGui.java | 29 +++ .../skippyall/minions/gui/InstructionGui.java | 233 ++++++++---------- .../minions/gui/MinionBoundSimpleGui.java | 37 +++ .../skippyall/minions/gui/MinionGui.java | 33 ++- .../minions/gui/input/ChoiceInput.java | 4 +- .../minions/gui/input/TextInput.java | 8 +- .../minions/minion/MinionListener.java | 15 ++ .../minions/minion/MinionRuntime.java | 18 +- .../fakeplayer/EntityPlayerActionPack.java | 38 ++- .../minion/fakeplayer/MinionFakePlayer.java | 31 ++- .../mixins/ChunkTicketManagerFixMixin.java | 12 +- .../antimobcap/ChunkLevelManagerMixin.java | 2 +- .../universal_graves/GraveMixin.java} | 4 +- .../program/consumer/ValueConsumer.java | 3 - .../program/consumer/ValueConsumerList.java | 21 +- .../instruction/ConfiguredInstruction.java | 52 +++- .../ConfiguredInstructionListener.java | 16 ++ .../instruction/InstructionExecution.java | 13 +- .../program/instruction/InstructionType.java | 17 +- .../program/instruction/Instructions.java | 41 +-- .../execution/ActionExecution.java | 5 +- .../execution/MineBlockExecution.java | 140 +++++++++++ .../instruction/execution/TurnExecution.java | 101 -------- .../execution/move/AbstractTurnExecution.java | 38 +++ .../move/ContinuousWalkExecution.java | 13 + .../execution/move/TurnDirection.java | 47 ++++ .../execution/move/TurnExecution.java | 23 ++ .../execution/move/TurnVectorExecution.java | 36 +++ .../execution/{ => move}/WalkExecution.java | 26 +- ...eArgument.java => FixedValueSupplier.java} | 16 +- .../supplier/FixedValueSupplierType.java | 24 ++ .../program/supplier/ValueArgumentType.java | 24 -- .../program/supplier/ValueSupplier.java | 3 - .../program/supplier/ValueSupplierList.java | 22 +- .../program/supplier/ValueSuppliers.java | 17 ++ .../program/value/RestrictionRule.java | 12 + .../program/value/TypeRestriction.java | 6 + .../minions/program/value/ValueType.java | 4 +- .../minions/program/value/ValueTypes.java | 48 ++-- .../reference/InstructionReference.java | 32 +++ .../minions/reference/Reference.java | 31 +++ .../minions/reference/ReferenceItem.java | 35 +++ .../minions/util/ListenerManager.java | 23 ++ .../skippyall/minions/util/PolymerUtil.java | 39 +++ .../util/SerializableListenerManager.java | 54 ++++ .../minions/blockstates/minion_trigger.json | 10 + .../assets/minions/items/minion_trigger.json | 6 + .../items/minion_trigger_no_plate_active.json | 6 + .../minion_trigger_no_plate_inactive.json | 6 + .../minions/models/block/minion_trigger.json | 218 ++++++++++++++++ .../models/block/minion_trigger_active.json | 6 + .../models/block/minion_trigger_inactive.json | 6 + .../models/block/minion_trigger_no_plate.json | 205 +++++++++++++++ .../block/minion_trigger_no_plate_active.json | 6 + .../minion_trigger_no_plate_inactive.json | 6 + .../assets/minions/textures/block/wave.png | Bin 0 -> 89 bytes .../resources/data/minions/lang/en_us.json | 29 ++- .../loot_table/blocks/minion_trigger.json | 19 ++ .../gui_display/instruction_type/attack.json | 4 + .../gui_display/instruction_type/turn.json | 4 + .../gui_display/instruction_type/use.json | 4 + .../gui_display/instruction_type/walk.json | 4 + .../value_supplier_type/fixed.json | 4 + .../gui_display/value_type/double.json | 4 + .../minions/gui_display/value_type/long.json | 4 + .../gui_display/value_type/string.json | 4 + .../value_type/turn_direction.json | 4 + src/main/resources/fabric.mod.json | 3 + src/main/resources/minions.accesswidener | 3 +- src/main/resources/minions.mixins.json | 5 +- 87 files changed, 2191 insertions(+), 492 deletions(-) create mode 100644 src/client/java/io/github/skippyall/minions/client/MinionsClient.java rename src/main/java/io/github/skippyall/minions/{MinionRegistration.java => MinionBlocks.java} (85%) create mode 100644 src/main/java/io/github/skippyall/minions/MinionMixinConfigPlugin.java delete mode 100644 src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java create mode 100644 src/main/java/io/github/skippyall/minions/block/BlockEntityMinionListener.java create mode 100644 src/main/java/io/github/skippyall/minions/block/MinionListeningBlockEntity.java create mode 100644 src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockItem.java create mode 100644 src/main/java/io/github/skippyall/minions/docs/ReferenceEntry.java create mode 100644 src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java create mode 100644 src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java create mode 100644 src/main/java/io/github/skippyall/minions/gui/MinionBoundSimpleGui.java create mode 100644 src/main/java/io/github/skippyall/minions/minion/MinionListener.java rename src/main/java/io/github/skippyall/minions/mixins/{GraveCompatMixin.java => compat/universal_graves/GraveMixin.java} (90%) create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstructionListener.java create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/MineBlockExecution.java delete mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/TurnExecution.java create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/move/AbstractTurnExecution.java create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/move/ContinuousWalkExecution.java create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnDirection.java create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnExecution.java create mode 100644 src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnVectorExecution.java rename src/main/java/io/github/skippyall/minions/program/instruction/execution/{ => move}/WalkExecution.java (65%) rename src/main/java/io/github/skippyall/minions/program/supplier/{ValueArgument.java => FixedValueSupplier.java} (56%) create mode 100644 src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplierType.java delete mode 100644 src/main/java/io/github/skippyall/minions/program/supplier/ValueArgumentType.java create mode 100644 src/main/java/io/github/skippyall/minions/program/supplier/ValueSuppliers.java create mode 100644 src/main/java/io/github/skippyall/minions/program/value/RestrictionRule.java create mode 100644 src/main/java/io/github/skippyall/minions/program/value/TypeRestriction.java create mode 100644 src/main/java/io/github/skippyall/minions/reference/InstructionReference.java create mode 100644 src/main/java/io/github/skippyall/minions/reference/Reference.java create mode 100644 src/main/java/io/github/skippyall/minions/reference/ReferenceItem.java create mode 100644 src/main/java/io/github/skippyall/minions/util/ListenerManager.java create mode 100644 src/main/java/io/github/skippyall/minions/util/PolymerUtil.java create mode 100644 src/main/java/io/github/skippyall/minions/util/SerializableListenerManager.java create mode 100644 src/main/resources/assets/minions/blockstates/minion_trigger.json create mode 100644 src/main/resources/assets/minions/items/minion_trigger.json create mode 100644 src/main/resources/assets/minions/items/minion_trigger_no_plate_active.json create mode 100644 src/main/resources/assets/minions/items/minion_trigger_no_plate_inactive.json create mode 100644 src/main/resources/assets/minions/models/block/minion_trigger.json create mode 100644 src/main/resources/assets/minions/models/block/minion_trigger_active.json create mode 100644 src/main/resources/assets/minions/models/block/minion_trigger_inactive.json create mode 100644 src/main/resources/assets/minions/models/block/minion_trigger_no_plate.json create mode 100644 src/main/resources/assets/minions/models/block/minion_trigger_no_plate_active.json create mode 100644 src/main/resources/assets/minions/models/block/minion_trigger_no_plate_inactive.json create mode 100644 src/main/resources/assets/minions/textures/block/wave.png create mode 100644 src/main/resources/data/minions/loot_table/blocks/minion_trigger.json create mode 100644 src/main/resources/data/minions/minions/gui_display/instruction_type/attack.json create mode 100644 src/main/resources/data/minions/minions/gui_display/instruction_type/turn.json create mode 100644 src/main/resources/data/minions/minions/gui_display/instruction_type/use.json create mode 100644 src/main/resources/data/minions/minions/gui_display/instruction_type/walk.json create mode 100644 src/main/resources/data/minions/minions/gui_display/value_supplier_type/fixed.json create mode 100644 src/main/resources/data/minions/minions/gui_display/value_type/double.json create mode 100644 src/main/resources/data/minions/minions/gui_display/value_type/long.json create mode 100644 src/main/resources/data/minions/minions/gui_display/value_type/string.json create mode 100644 src/main/resources/data/minions/minions/gui_display/value_type/turn_direction.json diff --git a/build.gradle b/build.gradle index 6d9e49e..d91c610 100644 --- a/build.gradle +++ b/build.gradle @@ -6,16 +6,24 @@ plugins { version = project.mod_version group = project.maven_group -loom { - accessWidenerPath = file("src/main/resources/minions.accesswidener") -} - base { archivesName = project.archives_base_name } loom { accessWidenerPath = file("src/main/resources/minions.accesswidener") + splitEnvironmentSourceSets() + + mods { + minions { + sourceSet sourceSets.main + sourceSet sourceSets.client + } + } + + runs.forEach { + it.vmArg("-XX:+AllowEnhancedClassRedefinition") + } } repositories { @@ -40,8 +48,6 @@ 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" @@ -49,7 +55,11 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + modImplementation "eu.pb4:polymer-core:${project.polymer_version}" + modImplementation "eu.pb4:polymer-virtual-entity:${project.polymer_version}" + modImplementation "eu.pb4:polymer-resource-pack:${project.polymer_version}" + modLocalRuntime "eu.pb4:polymer-autohost:${project.polymer_version}" modImplementation include("eu.pb4:sgui:${project.sgui_version}") modImplementation include("xyz.nucleoid:server-translations-api:${project.server_translations_version}") @@ -94,14 +104,14 @@ java { jar { from("LICENSE") { - rename { "${it}_${project.archivesBaseName}"} + rename { "${it}_${project.archives_base_name}"} } } // configure the maven publication publishing { publications { - minions (MavenPublication) { + maven (MavenPublication) { from components.java } } diff --git a/gradle.properties b/gradle.properties index ba462ed..e44fdc0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx1G yarn_mappings=1.21.7+build.2 # Mod Properties - mod_version = 0.2.1-SNAPSHOT + mod_version = 1.0.0-SNAPSHOT-1 maven_group = io.github.skippyall archives_base_name = Minions diff --git a/src/client/java/io/github/skippyall/minions/client/MinionsClient.java b/src/client/java/io/github/skippyall/minions/client/MinionsClient.java new file mode 100644 index 0000000..721d8b3 --- /dev/null +++ b/src/client/java/io/github/skippyall/minions/client/MinionsClient.java @@ -0,0 +1,17 @@ +package io.github.skippyall.minions.client; + +import eu.pb4.polymer.networking.api.client.PolymerClientNetworking; +import io.github.skippyall.minions.MinionBlocks; +import io.github.skippyall.minions.util.PolymerUtil; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap; +import net.minecraft.client.render.BlockRenderLayer; + +public class MinionsClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + BlockRenderLayerMap.putBlock(MinionBlocks.MINION_TRIGGER_BLOCK, BlockRenderLayer.TRANSLUCENT); + + PolymerClientNetworking.registerCommonHandler(PolymerUtil.VersionSyncPayload.class, (client, handler, payload) -> {}); + } +} diff --git a/src/main/java/io/github/skippyall/minions/MinionRegistration.java b/src/main/java/io/github/skippyall/minions/MinionBlocks.java similarity index 85% rename from src/main/java/io/github/skippyall/minions/MinionRegistration.java rename to src/main/java/io/github/skippyall/minions/MinionBlocks.java index 3e92b69..5e98c02 100644 --- a/src/main/java/io/github/skippyall/minions/MinionRegistration.java +++ b/src/main/java/io/github/skippyall/minions/MinionBlocks.java @@ -6,6 +6,7 @@ import io.github.skippyall.minions.block.MinionTriggerBlockEntity; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; import net.minecraft.block.AbstractBlock; import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.piston.PistonBehavior; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; @@ -13,17 +14,17 @@ import net.minecraft.registry.RegistryKeys; import net.minecraft.sound.BlockSoundGroup; import net.minecraft.util.Identifier; -public class MinionRegistration { +public class MinionBlocks { public static final Identifier MINION_TRIGGER_ID = Identifier.of(Minions.MOD_ID, "minion_trigger"); public static final MinionTriggerBlock MINION_TRIGGER_BLOCK = Registry.register( Registries.BLOCK, MINION_TRIGGER_ID, new MinionTriggerBlock(AbstractBlock.Settings.create() .registryKey(RegistryKey.of(RegistryKeys.BLOCK, MINION_TRIGGER_ID)) - .hardness(5) - .strength(5.0F, 6.0F) - .sounds(BlockSoundGroup.IRON) - .requiresTool() + .nonOpaque() + .breakInstantly() + .sounds(BlockSoundGroup.STONE) + .pistonBehavior(PistonBehavior.DESTROY) ) ); public static final BlockEntityType MINION_TRIGGER_BE_TYPE = diff --git a/src/main/java/io/github/skippyall/minions/MinionItems.java b/src/main/java/io/github/skippyall/minions/MinionItems.java index dea80b3..a0d0fe6 100644 --- a/src/main/java/io/github/skippyall/minions/MinionItems.java +++ b/src/main/java/io/github/skippyall/minions/MinionItems.java @@ -2,11 +2,13 @@ package io.github.skippyall.minions; import eu.pb4.polymer.core.api.item.PolymerBlockItem; import eu.pb4.polymer.core.api.item.SimplePolymerItem; +import io.github.skippyall.minions.block.MinionTriggerBlockItem; import io.github.skippyall.minions.minion.MinionItem; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.module.MinionModule; import io.github.skippyall.minions.program.instruction.InstructionType; import io.github.skippyall.minions.program.instruction.Instructions; +import io.github.skippyall.minions.reference.ReferenceItem; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.DamageResistantComponent; import net.minecraft.entity.damage.DamageType; @@ -45,13 +47,13 @@ public class MinionItems { public static final SimplePolymerItem MOVE_MODULE = registerModule( Identifier.of(MOD_ID, "move_module"), Items.IRON_BOOTS, - List.of(Instructions.WALK, Instructions.TURN) + List.of(Instructions.WALK, Instructions.WALK_CONTINUOUS, Instructions.TURN, Instructions.TURN_VECTOR) ); public static final SimplePolymerItem ATTACK_MODULE = registerModule( Identifier.of(MOD_ID, "attack_module"), Items.IRON_PICKAXE, - List.of(Instructions.ATTACK) + List.of(Instructions.ATTACK, Instructions.MINE_BLOCK) ); public static final SimplePolymerItem INTERACT_MODULE = registerModule( @@ -69,10 +71,12 @@ public class MinionItems { public static final PolymerBlockItem MINION_TRIGGER_ITEM = registerItem( - MinionRegistration.MINION_TRIGGER_ID, - settings -> new PolymerBlockItem(MinionRegistration.MINION_TRIGGER_BLOCK, settings, Items.GOLD_BLOCK) + MinionBlocks.MINION_TRIGGER_ID, + settings -> new MinionTriggerBlockItem(MinionBlocks.MINION_TRIGGER_BLOCK, settings, Items.COMPARATOR) ); + public static final ReferenceItem REFERENCE_ITEM = registerItem(Identifier.of(MOD_ID, "reference"), ReferenceItem::new); + public static T registerItem(Identifier identifier, Function constructor, Item.Settings settings) { T item = constructor.apply(settings.registryKey(RegistryKey.of(RegistryKeys.ITEM, identifier))); diff --git a/src/main/java/io/github/skippyall/minions/MinionMixinConfigPlugin.java b/src/main/java/io/github/skippyall/minions/MinionMixinConfigPlugin.java new file mode 100644 index 0000000..639f43c --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/MinionMixinConfigPlugin.java @@ -0,0 +1,49 @@ +package io.github.skippyall.minions; + +import net.fabricmc.loader.api.FabricLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class MinionMixinConfigPlugin implements IMixinConfigPlugin { + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if(mixinClassName.startsWith("io.github.skippyall.mixins.compat.universal_graves.")) { + return FabricLoader.getInstance().isModLoaded("universal-universal_graves"); + } + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/src/main/java/io/github/skippyall/minions/MinionRegistries.java b/src/main/java/io/github/skippyall/minions/MinionRegistries.java index 4976f69..528a829 100644 --- a/src/main/java/io/github/skippyall/minions/MinionRegistries.java +++ b/src/main/java/io/github/skippyall/minions/MinionRegistries.java @@ -1,25 +1,49 @@ package io.github.skippyall.minions; -import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import io.github.skippyall.minions.docs.ReferenceEntry; +import io.github.skippyall.minions.gui.GuiDisplay; +import io.github.skippyall.minions.minion.MinionListener; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.skin.SkinProvider; +import io.github.skippyall.minions.program.instruction.ConfiguredInstructionListener; import io.github.skippyall.minions.program.supplier.ValueSupplierType; 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.reference.Reference; +import net.fabricmc.fabric.api.event.registry.DynamicRegistries; +import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.fabricmc.fabric.api.event.registry.RegistryAttribute; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.SimpleRegistry; import net.minecraft.util.Identifier; public class MinionRegistries { public static final Registry> VALUE_TYPES = registry("value_type"); - public static final Registry> ARGUMENT_TYPES = registry("argument_type"); + 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 SKIN_PROVIDERS = registry("skin_providers"); + 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"); + public static final Registry> MINION_LISTENER_CODECS = registry("minion_listener_codec"); + public static final Registry> REFERENCE_CODEC = registry("reference_codec"); + + public static final RegistryKey> GUI_DISPLAY = key("gui_display"); + public static final RegistryKey> REFERENCE_ENTRY = key("reference_entry"); private static Registry registry(String id) { - return new SimpleRegistry<>(RegistryKey.ofRegistry(Identifier.of(Minions.MOD_ID, id)), Lifecycle.stable()); + return FabricRegistryBuilder.createSimple(key(id)).attribute(RegistryAttribute.OPTIONAL).buildAndRegister(); + } + + private static RegistryKey> key(String name) { + return RegistryKey.ofRegistry(Identifier.of(Minions.MOD_ID, name)); + } + + public static void register() { + DynamicRegistries.register(GUI_DISPLAY, GuiDisplay.CODEC); + DynamicRegistries.register(REFERENCE_ENTRY, ReferenceEntry.CODEC); } } diff --git a/src/main/java/io/github/skippyall/minions/Minions.java b/src/main/java/io/github/skippyall/minions/Minions.java index c53187f..d8f5d72 100644 --- a/src/main/java/io/github/skippyall/minions/Minions.java +++ b/src/main/java/io/github/skippyall/minions/Minions.java @@ -1,13 +1,17 @@ package io.github.skippyall.minions; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; import io.github.skippyall.minions.command.MinionsCommand; +import io.github.skippyall.minions.gui.GuiDisplay; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.minion.MinionData; import io.github.skippyall.minions.minion.MinionPersistentState; import io.github.skippyall.minions.minion.skin.SkinProviders; import io.github.skippyall.minions.module.MinionModule; import io.github.skippyall.minions.program.instruction.Instructions; +import io.github.skippyall.minions.program.supplier.ValueSuppliers; import io.github.skippyall.minions.program.value.ValueTypes; +import io.github.skippyall.minions.util.PolymerUtil; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -21,11 +25,23 @@ public class Minions implements ModInitializer { @Override public void onInitialize() { + MinionRegistries.register(); + Instructions.register(); + ValueSuppliers.register(); + ValueTypes.register(); + SkinProviders.register(); + GuiDisplay.register(); MinionData.register(); MinionModule.register(); + MinionBlocks.register(); + MinionItems.register(); + MinionCreativeTab.registerGroup(); + + PolymerUtil.register(); + ServerLifecycleEvents.SERVER_STARTED.register(server -> { MinionPersistentState.create(server); MinionPersistentState.INSTANCE.getMinionData().forEach((uuid, data) -> { @@ -39,11 +55,6 @@ public class Minions implements ModInitializer { MinionsCommand.register(commandDispatcher); }); - ValueTypes.register(); - SkinProviders.register(); - - MinionRegistration.register(); - MinionItems.register(); - MinionCreativeTab.registerGroup(); + PolymerResourcePackUtils.addModAssets(Minions.MOD_ID); } } diff --git a/src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java b/src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java deleted file mode 100644 index 3b2f718..0000000 --- a/src/main/java/io/github/skippyall/minions/PlayerClipboardAttachment.java +++ /dev/null @@ -1,11 +0,0 @@ -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/BlockEntityMinionListener.java b/src/main/java/io/github/skippyall/minions/block/BlockEntityMinionListener.java new file mode 100644 index 0000000..2ba6c67 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/block/BlockEntityMinionListener.java @@ -0,0 +1,55 @@ +package io.github.skippyall.minions.block; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.github.skippyall.minions.minion.MinionListener; +import net.minecraft.registry.RegistryKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.Optional; + +public class BlockEntityMinionListener implements MinionListener { + protected RegistryKey worldKey; + protected BlockPos pos; + + public BlockEntityMinionListener(RegistryKey worldKey, BlockPos pos) { + this.worldKey = worldKey; + this.pos = pos; + } + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> + instance.group( + World.CODEC.fieldOf("world").forGetter(listener -> listener.worldKey), + BlockPos.CODEC.fieldOf("pos").forGetter(listener -> listener.pos) + ).apply(instance, BlockEntityMinionListener::new)); + + + private BlockEntityState getBlockEntityState(MinecraftServer server) { + World world = server.getWorld(worldKey); + if(world == null || !world.isPosLoaded(pos)) { + return BlockEntityState.UNLOADED; + } + + if(world.getBlockEntity(pos) instanceof MinionListeningBlockEntity) { + return BlockEntityState.LOADED; + } else { + return BlockEntityState.REMOVED; + } + } + + public Optional getBlockEntity(MinecraftServer server) { + World world = server.getWorld(worldKey); + if(world != null && world.isPosLoaded(pos) && world.getBlockEntity(pos) instanceof MinionListeningBlockEntity be) { + return Optional.of(be); + } + return Optional.empty(); + } + + public enum BlockEntityState { + LOADED, + REMOVED, + UNLOADED + } +} diff --git a/src/main/java/io/github/skippyall/minions/block/MinionListeningBlockEntity.java b/src/main/java/io/github/skippyall/minions/block/MinionListeningBlockEntity.java new file mode 100644 index 0000000..6235355 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/block/MinionListeningBlockEntity.java @@ -0,0 +1,7 @@ +package io.github.skippyall.minions.block; + +import io.github.skippyall.minions.minion.MinionListener; + +public interface MinionListeningBlockEntity extends MinionListener { + +} 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 e631da9..cd60786 100644 --- a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlock.java +++ b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlock.java @@ -2,67 +2,118 @@ 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 eu.pb4.polymer.core.api.utils.PolymerClientDecoded; +import eu.pb4.polymer.core.api.utils.PolymerKeepModel; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; +import eu.pb4.polymer.virtualentity.api.BlockWithElementHolder; +import eu.pb4.polymer.virtualentity.api.ElementHolder; +import eu.pb4.polymer.virtualentity.api.elements.ItemDisplayElement; +import io.github.skippyall.minions.MinionBlocks; +import io.github.skippyall.minions.Minions; +import io.github.skippyall.minions.minion.MinionPersistentState; +import io.github.skippyall.minions.reference.InstructionReference; +import io.github.skippyall.minions.reference.Reference; +import io.github.skippyall.minions.util.PolymerUtil; +import net.minecraft.block.AbstractRedstoneGateBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.BlockWithEntity; import net.minecraft.block.Blocks; +import net.minecraft.block.ShapeContext; +import net.minecraft.block.SideShapeType; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityTicker; import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.component.DataComponentTypes; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; import net.minecraft.state.StateManager; import net.minecraft.state.property.BooleanProperty; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; import net.minecraft.world.World; +import net.minecraft.world.WorldView; 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 class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock, PolymerKeepModel, PolymerClientDecoded, BlockWithElementHolder { 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 static final VoxelShape SHAPE = Block.createColumnShape(16.0, 0.0, 2.0); public MinionTriggerBlock(Settings settings) { super(settings); - setDefaultState(getDefaultState().with(POWERED, false).with(RUNNING, false)); + setDefaultState(getDefaultState().with(POWERED, false)); + } + + @Override + protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return SHAPE; + } + + @Override + protected boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) { + BlockPos blockPos = pos.down(); + return this.canPlaceAbove(world, blockPos, world.getBlockState(blockPos)); + } + + protected boolean canPlaceAbove(WorldView world, BlockPos pos, BlockState state) { + return state.isSideSolid(world, pos, Direction.UP, SideShapeType.RIGID); } @Override protected void appendProperties(StateManager.Builder builder) { - builder.add(POWERED, RUNNING); + builder.add(POWERED); + } + + @Override + protected ActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if(stack.get(Reference.COMPONENT_TYPE) instanceof InstructionReference instruction) { + world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> { + be.setInstruction(instruction.selectedMinion(), instruction.selectedInstruction()); + player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1); + stack.decrement(1); + }); + return ActionResult.SUCCESS; + } + + return super.onUseWithItem(stack, state, world, pos, player, hand, hit); } @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; + world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> { + String name = MinionPersistentState.INSTANCE.getMinionData(be.getMinionUuid()).name(); + player.sendMessage(Text.translatable("minions.reference.instruction.tooltip", name, be.getInstructionName()), true); + }); + return ActionResult.SUCCESS; } @Override protected void neighborUpdate(BlockState state, World world, BlockPos pos, Block sourceBlock, @Nullable WireOrientation wireOrientation, boolean notify) { + if(!canPlaceAt(state, world, pos)) { + dropStacks(state, world, pos); + world.removeBlock(pos, false); + } + boolean newPower = world.isReceivingRedstonePower(pos); if(state.get(POWERED) != newPower) { world.setBlockState(pos, state.with(POWERED, newPower)); - world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::updatePower); + world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::updatePower); } } @@ -73,7 +124,7 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock @Override protected int getComparatorOutput(BlockState state, World world, BlockPos pos) { - return world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE).map(MinionTriggerBlockEntity::getComparatorOutput).orElse(0); + return world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).map(MinionTriggerBlockEntity::getComparatorOutput).orElse(0); } @Override @@ -88,15 +139,34 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock @Override public BlockState getPolymerBlockState(BlockState state, PacketContext context) { - return state.get(POWERED) ? Blocks.REDSTONE_BLOCK.getDefaultState() : Blocks.GOLD_BLOCK.getDefaultState(); + return PolymerUtil.isOnClient(context) ? state : Blocks.COMPARATOR.getDefaultState().with(AbstractRedstoneGateBlock.POWERED, state.get(POWERED)); } @Override public @Nullable BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType type) { - if(type == MinionRegistration.MINION_TRIGGER_BE_TYPE) { + if(type == MinionBlocks.MINION_TRIGGER_BE_TYPE) { return MinionTriggerBlockEntity::tick; } else { return null; } } + + @Override + public @Nullable ElementHolder createElementHolder(ServerWorld world, BlockPos pos, BlockState initialBlockState) { + ElementHolder holder = new ElementHolder() { + @Override + public boolean startWatching(ServerPlayNetworkHandler player) { + if(PolymerResourcePackUtils.hasMainPack(player)) { + return super.startWatching(player); + } else { + return false; + } + } + }; + ItemStack stack = new ItemStack(Items.BARRIER); + stack.set(DataComponentTypes.ITEM_MODEL, Identifier.of(Minions.MOD_ID, "minion_trigger_no_plate_" + (initialBlockState.get(MinionTriggerBlock.POWERED) ? "active" : "inactive"))); + + holder.addElement(new ItemDisplayElement(stack)); + return holder; + } } 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 beea910..96abda4 100644 --- a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockEntity.java +++ b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockEntity.java @@ -1,6 +1,6 @@ package io.github.skippyall.minions.block; -import io.github.skippyall.minions.MinionRegistration; +import io.github.skippyall.minions.MinionBlocks; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; @@ -23,7 +23,7 @@ public class MinionTriggerBlockEntity extends BlockEntity { private boolean runningCache = false; public MinionTriggerBlockEntity(BlockPos pos, BlockState state) { - super(MinionRegistration.MINION_TRIGGER_BE_TYPE, pos, state); + super(MinionBlocks.MINION_TRIGGER_BE_TYPE, pos, state); } public void setInstruction(UUID minionUuid, String instructionName) { @@ -38,12 +38,12 @@ public class MinionTriggerBlockEntity extends BlockEntity { } if(triggerBlockEntity.first) { triggerBlockEntity.first = false; - world.updateComparators(pos, MinionRegistration.MINION_TRIGGER_BLOCK); + world.updateComparators(pos, MinionBlocks.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); + world.updateComparators(pos, MinionBlocks.MINION_TRIGGER_BLOCK); triggerBlockEntity.runningCache = isRunning; } } @@ -78,6 +78,14 @@ public class MinionTriggerBlockEntity extends BlockEntity { return Optional.empty(); } + public UUID getMinionUuid() { + return minionUuid; + } + + public String getInstructionName() { + return instructionName; + } + public Optional> getInstruction(MinionFakePlayer minion) { return Optional.ofNullable(minion.getInstructionManager().getInstruction(instructionName)); } diff --git a/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockItem.java b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockItem.java new file mode 100644 index 0000000..55392b6 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/block/MinionTriggerBlockItem.java @@ -0,0 +1,25 @@ +package io.github.skippyall.minions.block; + +import eu.pb4.polymer.core.api.item.PolymerBlockItem; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; +import xyz.nucleoid.packettweaker.PacketContext; + +public class MinionTriggerBlockItem extends PolymerBlockItem { + public MinionTriggerBlockItem(Block block, Settings settings, Item polymerItem) { + super(block, settings, polymerItem, true); + } + + @Override + public @Nullable Identifier getPolymerItemModel(ItemStack stack, PacketContext context) { + if(PolymerResourcePackUtils.hasMainPack(context)) { + return super.getPolymerItemModel(stack, context); + } else { + return null; + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/docs/ReferenceEntry.java b/src/main/java/io/github/skippyall/minions/docs/ReferenceEntry.java new file mode 100644 index 0000000..4d024b5 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/docs/ReferenceEntry.java @@ -0,0 +1,14 @@ +package io.github.skippyall.minions.docs; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.text.Text; +import net.minecraft.text.TextCodecs; + +public record ReferenceEntry(Text shortDescription, Text longDescription) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> + instance.group( + TextCodecs.CODEC.fieldOf("shortDescription").forGetter(ReferenceEntry::shortDescription), + TextCodecs.CODEC.fieldOf("longDescription").forGetter(ReferenceEntry::longDescription) + ).apply(instance, ReferenceEntry::new)); +} diff --git a/src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java b/src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java new file mode 100644 index 0000000..bc71991 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/ConfigureInstructionGui.java @@ -0,0 +1,117 @@ +package io.github.skippyall.minions.gui; + +import eu.pb4.sgui.api.elements.GuiElementBuilder; +import io.github.skippyall.minions.gui.input.ChoiceInput; +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.reference.ReferenceItem; +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; + +public class ConfigureInstructionGui extends InstructionBoundSimpleGui { + private String name; + + private ConfigureInstructionGui(ScreenHandlerType type, ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction configuredInstruction, String name) { + super(type, player, minion, configuredInstruction); + this.name = name; + init(); + } + + public static void configureInstructionMenu(String name, ConfiguredInstruction instruction, MinionFakePlayer minion, ServerPlayerEntity player) { + if(!InstructionGui.checkInstructionExists(name, instruction, minion, player)) { + return; + } + + ConfigureInstructionGui gui = new ConfigureInstructionGui(ScreenHandlerType.GENERIC_9X3, player, minion, instruction, name); + + gui.open(); + } + + private void init() { + setTitle(Text.literal(name)); + + setSlot(7, new GuiElementBuilder(Items.ANVIL) + .setName(Text.translatable("minions.gui.instruction.configure.rename")) + .setCallback(() -> InstructionGui.inputInstructionName(minion, player, name).thenAccept(newName -> { + minion.getInstructionManager().setInstructionName(name, newName); + configureInstructionMenu(newName, instruction, minion, player); + })) + ); + + 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); + InstructionGui.instructionList(minion, player); + })) + ); + + updateSuppliers(); + + setSlot(13, InstructionGui.createInstructionElement(instruction.getInstruction(), player.getRegistryManager())); + + 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.getInventory().offer(ReferenceItem.createInstructionReference(minion, name), true); + player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1); + }) + ); + + updateRunSlot(); + } + + @Override + public void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction instruction, String newName) { + this.setTitle(Text.literal(newName)); + name = newName; + } + + @Override + public void onRun(ConfiguredInstruction instruction) { + updateRunSlot(); + } + + @Override + public void onStop(ConfiguredInstruction instruction) { + updateRunSlot(); + } + + @Override + public void onSupplierChange(ConfiguredInstruction instruction, Parameter parameter) { + updateSuppliers(); + } + + private void updateRunSlot() { + if(!instruction.isRunning()) { + setSlot(26, new GuiElementBuilder(Items.ARROW) + .setName(Text.translatable("minions.gui.instruction.run")) + .setCallback(() -> instruction.run(minion.getInstructionManager())) + ); + } else { + setSlot(26, new GuiElementBuilder(Items.BARRIER) + .setName(Text.translatable("minions.gui.instruction.stop")) + .setCallback(() -> instruction.stop(minion.getInstructionManager())) + ); + } + } + + private void updateSuppliers() { + int slot = 11; + for(Parameter parameter : instruction.getInstruction().getParameters().reversed()) { + setSlot(slot, InstructionGui.createParameterElement(parameter, player.getRegistryManager()) + .setCallback(() -> InstructionGui.configureArgumentMenu(name, instruction, parameter, minion, player)) + ); + slot--; + } + } + +} 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 4dbab1e..f98d39c 100644 --- a/src/main/java/io/github/skippyall/minions/gui/GuiDisplay.java +++ b/src/main/java/io/github/skippyall/minions/gui/GuiDisplay.java @@ -1,59 +1,151 @@ package io.github.skippyall.minions.gui; import com.mojang.authlib.properties.PropertyMap; -import eu.pb4.sgui.api.elements.GuiElementBuilder; -import io.github.skippyall.minions.util.ModelIdUtil; +import com.mojang.serialization.Codec; +import io.github.skippyall.minions.MinionRegistries; +import io.github.skippyall.minions.Minions; +import io.github.skippyall.minions.util.TranslationUtil; +import it.unimi.dsi.fastutil.objects.ReferenceSortedSets; import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.LoreComponent; import net.minecraft.component.type.ProfileComponent; +import net.minecraft.component.type.TooltipDisplayComponent; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; import net.minecraft.text.Text; import net.minecraft.util.Identifier; +import net.minecraft.util.Rarity; +import net.minecraft.util.Uuids; import java.util.Optional; import java.util.UUID; public interface GuiDisplay { - default GuiElementBuilder createElement() { - return new GuiElementBuilder(createItemStack()); + Codec CODEC = MinionRegistries.GUI_DISPLAY_TYPE.getCodec().dispatch(GuiDisplay::getCodec, codec -> codec.fieldOf("data")); + GuiDisplay DEFAULT_DISPLAY = new ItemBased(Items.BARRIER); + + static GuiDisplay getGuiDisplay(Identifier id, DynamicRegistryManager manager) { + return manager.getOptional(MinionRegistries.GUI_DISPLAY).map(registry -> registry.get(id)).orElse(DEFAULT_DISPLAY); + } + + static GuiDisplay getGuiDisplayFor(Registry registry, T element, DynamicRegistryManager manager) { + Identifier elementId = registry.getId(element); + if(elementId == null) { + return DEFAULT_DISPLAY; + } + Identifier displayId = elementId.withPrefixedPath(registry.getKey().getValue().getPath() + "/"); + + return getGuiDisplay(displayId, manager); + } + + static ItemStack getDisplayStack(Registry registry, T element, DynamicRegistryManager manager) { + return getGuiDisplayFor(registry, element, manager).createItemStack(); + } + + static ItemStack getDisplayStackWithName(Registry registry, T element, DynamicRegistryManager manager) { + ItemStack stack = getDisplayStack(registry, element, manager); + stack.set(DataComponentTypes.ITEM_NAME, Text.translatable(TranslationUtil.getTranslationKey(element, registry))); + return stack; } ItemStack createItemStack(); - record ModelBased(Identifier model, String translationKeyBase, boolean withLore) implements GuiDisplay { - public ModelBased(Item model, String translationKeyBase, boolean withLore) { - this(ModelIdUtil.getItemModelId(model), translationKeyBase, withLore); - } + Codec getCodec(); - public ModelBased(Item model, String translationKeyBase) { - this(ModelIdUtil.getItemModelId(model), translationKeyBase, false); + class ModelBased implements GuiDisplay { + public static final Codec CODEC = Identifier.CODEC.xmap(ModelBased::new, display -> display.model); + + private final Identifier model; + + public ModelBased(Identifier model) { + this.model = model; } @Override public ItemStack createItemStack() { ItemStack stack = new ItemStack(Items.BARRIER); stack.set(DataComponentTypes.ITEM_MODEL, model); - stack.set(DataComponentTypes.ITEM_NAME, Text.translatable(translationKeyBase + ".name")); - if(withLore) { - stack.set(DataComponentTypes.LORE, LoreComponent.DEFAULT.with(Text.translatable(translationKeyBase + ".description"))); - } return stack; } + + @Override + public Codec getCodec() { + return CODEC; + } } - record HeadBased(UUID uuid, String translationKeyBase, boolean withLore) implements GuiDisplay { + class ItemBased implements GuiDisplay { + public static final Codec CODEC = Registries.ITEM.getCodec().xmap(ItemBased::new, display -> display.item); + + private final Item item; + + public ItemBased(Item item) { + this.item = item; + } + + @Override + public ItemStack createItemStack() { + ItemStack stack = new ItemStack(item); + stack.set(DataComponentTypes.TOOLTIP_DISPLAY, new TooltipDisplayComponent(true, ReferenceSortedSets.emptySet())); + stack.set(DataComponentTypes.RARITY, Rarity.COMMON); + return stack; + } + + @Override + public Codec getCodec() { + return CODEC; + } + } + + class HeadBased implements GuiDisplay { + public static final Codec CODEC = Uuids.CODEC.xmap(HeadBased::new, display -> display.uuid); + + private final UUID uuid; + + public HeadBased(UUID uuid) { + this.uuid = uuid; + } @Override public ItemStack createItemStack() { ItemStack stack = new ItemStack(Items.PLAYER_HEAD); stack.set(DataComponentTypes.PROFILE, new ProfileComponent(Optional.empty(), Optional.of(uuid), new PropertyMap())); - stack.set(DataComponentTypes.ITEM_NAME, Text.translatable(translationKeyBase + ".name")); - if(withLore) { - stack.set(DataComponentTypes.LORE, LoreComponent.DEFAULT.with(Text.translatable(translationKeyBase + ".description"))); - } return stack; } + + @Override + public Codec getCodec() { + return CODEC; + } + } + + class StackBased implements GuiDisplay { + public static final Codec CODEC = ItemStack.CODEC.xmap(StackBased::new, StackBased::createItemStack); + + private final ItemStack stack; + + public StackBased(ItemStack stack) { + this.stack = stack; + } + + @Override + public ItemStack createItemStack() { + return stack; + } + + @Override + public Codec getCodec() { + return CODEC; + } + } + + static void register() { + Registry.register(MinionRegistries.GUI_DISPLAY_TYPE, Identifier.of(Minions.MOD_ID, "item"), ItemBased.CODEC); + Registry.register(MinionRegistries.GUI_DISPLAY_TYPE, Identifier.of(Minions.MOD_ID, "model"), ModelBased.CODEC); + Registry.register(MinionRegistries.GUI_DISPLAY_TYPE, Identifier.of(Minions.MOD_ID, "head"), HeadBased.CODEC); + Registry.register(MinionRegistries.GUI_DISPLAY_TYPE, Identifier.of(Minions.MOD_ID, "stack"), StackBased.CODEC); } } diff --git a/src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java b/src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java new file mode 100644 index 0000000..fd7bfe0 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/InstructionBoundSimpleGui.java @@ -0,0 +1,29 @@ +package io.github.skippyall.minions.gui; + +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.instruction.ConfiguredInstructionListener; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.server.network.ServerPlayerEntity; + +public class InstructionBoundSimpleGui extends MinionBoundSimpleGui implements ConfiguredInstructionListener { + protected final ConfiguredInstruction instruction; + + public InstructionBoundSimpleGui(ScreenHandlerType type, ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction) { + super(type, player, minion); + this.instruction = instruction; + instruction.addListener(this); + } + + @Override + public void onInstructionRemove(ConfiguredInstruction instruction) { + close(); + } + + @Override + public void onClose() { + super.onClose(); + instruction.removeListener(this); + } +} 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 98e4e77..8d024e2 100644 --- a/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/InstructionGui.java @@ -3,8 +3,6 @@ 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.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; @@ -18,20 +16,20 @@ import io.github.skippyall.minions.program.supplier.Parameter; import io.github.skippyall.minions.util.TranslationUtil; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.registry.DynamicRegistryManager; 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; import java.util.concurrent.CompletableFuture; public class InstructionGui { public static void openInstructionMainMenu(MinionFakePlayer minion, ServerPlayerEntity player) { - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); + SimpleGui gui = new MinionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion); + gui.setTitle(Text.translatable("minions.gui.instruction.title")); + gui.setSlot(3, new GuiElementBuilder() .setItem(Items.BOOK) .setName(Text.translatable("minions.gui.instruction.list")) @@ -47,30 +45,44 @@ public class InstructionGui { } public static void instructionList(MinionFakePlayer minion, ServerPlayerEntity player) { - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); - for(String instructionName : minion.getInstructionManager().getInstructionNames()) { - ConfiguredInstruction instruction = minion.getInstructionManager().getInstruction(instructionName); - gui.addSlot(instruction.getInstruction().getDisplay().createElement() - .setName(Text.literal(instructionName)) - .setLore(List.of()) - .setCallback(() -> configureInstructionMenu(instructionName, instruction, minion, player)) - ); - } + SimpleGui gui = new MinionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion) { + @Override + public void onInstructionsUpdate(MinionFakePlayer minion) { + resetInstructionList(this, minion, player); + } + }; + gui.setTitle(Text.translatable("minions.gui.instruction.title")); + resetInstructionList(gui, minion, player); + gui.open(); } + private static void resetInstructionList(SimpleGui gui, MinionFakePlayer minion, ServerPlayerEntity player) { + int i = 0; + for (String instructionName : minion.getInstructionManager().getInstructionNames()) { + ConfiguredInstruction instruction = minion.getInstructionManager().getInstruction(instructionName); + gui.setSlot(i, new GuiElementBuilder(GuiDisplay.getGuiDisplayFor(MinionRegistries.INSTRUCTION_TYPES, instruction.getInstruction(), player.getRegistryManager()).createItemStack()) + .setName(Text.literal(instructionName)) + .setCallback(() -> ConfigureInstructionGui.configureInstructionMenu(instructionName, instruction, minion, player)) + ); + i++; + } + } + public static void createNewInstruction(MinionFakePlayer minion, ServerPlayerEntity player) { selectInstructionModuleMenu(minion, player).thenAccept(instructionType -> inputInstructionName(minion, player, "Instruction").thenAccept(name -> { - ConfiguredInstruction configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType); - configureInstructionMenu(name, configuredInstruction, minion, player); + if (!minion.isRemoved() && !minion.isDisconnected()) { + ConfiguredInstruction configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType); + ConfigureInstructionGui.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)) { + if (minion.getInstructionManager().hasInstruction(name)) { return new Result.Error<>(Text.translatable("minions.gui.instruction.name_already_used")); } return new Result.Success<>(name); @@ -78,120 +90,49 @@ public class InstructionGui { } public static boolean checkInstructionExists(String name, ConfiguredInstruction instruction, MinionFakePlayer minion, ServerPlayerEntity player) { - boolean stillExists = minion.getInstructionManager().getInstruction(name) == instruction; - if(!stillExists) { + boolean stillExists = !minion.isRemoved() && !minion.isDisconnected() && minion.getInstructionManager().getInstruction(name) == instruction; + if (!stillExists) { player.closeHandledScreen(); player.sendMessage(Text.translatable("minions.gui.instruction.removed")); } return stillExists; } - public static void configureInstructionMenu(String name, ConfiguredInstruction instruction, MinionFakePlayer minion, ServerPlayerEntity player) { - if(!checkInstructionExists(name, instruction, minion, player)) { - return; - } - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); - gui.setTitle(Text.literal(name)); - - 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); - })) - ); - - 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(); - } - - private static void updateRunSlot(ConfiguredInstruction instruction, MinionFakePlayer minion, SimpleGui gui) { - if(!instruction.isRunning()) { - gui.setSlot(26, new GuiElementBuilder(Items.ARROW) - .setName(Text.translatable("minions.gui.instruction.run")) - .setCallback(() -> { - instruction.run(minion.getInstructionManager()); - updateRunSlot(instruction, minion, gui); - }) - ); - } else { - gui.setSlot(26, new GuiElementBuilder(Items.BARRIER) - .setName(Text.translatable("minions.gui.instruction.stop")) - .setCallback(() -> { - instruction.stop(minion.getInstructionManager()); - updateRunSlot(instruction, minion, gui); - }) - ); - } - } - - public static > void configureArgumentMenu(String name, ConfiguredInstruction instruction, Parameter parameter, MinionFakePlayer minion, ServerPlayerEntity player) { - if(!checkInstructionExists(name, instruction, minion, player)) { + public static > void configureArgumentMenu(String instructionName, ConfiguredInstruction instruction, Parameter parameter, MinionFakePlayer minion, ServerPlayerEntity player) { + if (!checkInstructionExists(instructionName, instruction, minion, player)) { return; } @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.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 -> { - instruction.getArguments().setArgument(parameter, newArgument); - configureArgumentMenu(name, instruction, parameter, minion, player); - }) - ) + if(argument == null) { + configureTypeAndValue(instructionName, instruction, parameter, minion, player); + return; + } + + 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)) ) ); - - if(argument != null) { - 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) { + public static CompletableFuture> selectArgumentType(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction instruction) { CompletableFuture> future = new CompletableFuture<>(); - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); - for(ValueSupplierType type : MinionRegistries.ARGUMENT_TYPES) { - gui.addSlot(new GuiElementBuilder() - .setName(Text.translatable(TranslationUtil.getTranslationKey(type, MinionRegistries.ARGUMENT_TYPES))) + 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)) ); } @@ -199,28 +140,40 @@ public class InstructionGui { 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.literal("This minion has no modules")); + if (minion.getModuleInventory().getModules().isEmpty()) { + player.sendMessage(Text.translatable("minions.gui.instruction.no_modules")); return CompletableFuture.failedFuture(new NoSuchElementException("No modules")); } CompletableFuture> future = new CompletableFuture<>(); - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false) { + SimpleGui gui = new MinionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion) { @Override public void onClose() { - if(!future.isDone()) { + if (!future.isDone()) { future.cancel(false); } } }; + gui.setTitle(Text.translatable("minions.gui.instruction.select_instruction")); - for(int i = 0; i < minion.getModuleInventory().size(); i++) { - ItemStack module = minion.getModuleInventory().getStack(i); - if(module.contains(MinionModule.COMPONENT_TYPE)) { - gui.addSlot(new GuiElementBuilder(module) - .setCallback(() -> selectInstructionMenu(module.get(MinionModule.COMPONENT_TYPE), minion, player) + for (int i = 0; i < minion.getModuleInventory().size(); i++) { + ItemStack moduleItem = minion.getModuleInventory().getStack(i); + MinionModule module = moduleItem.get(MinionModule.COMPONENT_TYPE); + if (module != null && !module.instructions().isEmpty()) { + gui.addSlot(new GuiElementBuilder(moduleItem) + .setCallback(() -> selectInstructionMenu(module, minion, player) .thenApply(future::complete) ) ); @@ -234,9 +187,18 @@ public class InstructionGui { public static CompletableFuture> selectInstructionMenu(MinionModule module, MinionFakePlayer minion, ServerPlayerEntity player) { CompletableFuture> future = new CompletableFuture<>(); - SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); - for(InstructionType instructionType : module.instructions()) { - gui.addSlot(createInstructionElement(instructionType) + SimpleGui gui = new MinionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion) { + @Override + public void onClose() { + if (!future.isDone()) { + future.cancel(false); + } + } + }; + gui.setTitle(Text.translatable("minions.gui.instruction.select_instruction")); + + for (InstructionType instructionType : module.instructions()) { + gui.addSlot(createInstructionElement(instructionType, player.getRegistryManager()) .setCallback(() -> future.complete(instructionType)) ); } @@ -245,10 +207,10 @@ public class InstructionGui { return future; } - public static GuiElementBuilder createInstructionElement(InstructionType instructionType) { + public static GuiElementBuilder createInstructionElement(InstructionType instructionType, DynamicRegistryManager manager) { GuiElementBuilder instructionBuilder; - if(instructionType != null) { - instructionBuilder = instructionType.getDisplay().createElement(); + if (instructionType != null) { + instructionBuilder = new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.INSTRUCTION_TYPES, instructionType, manager)); } else { instructionBuilder = new GuiElementBuilder(Items.RED_WOOL) .setName(Text.translatable("minions.gui.instruction.no_instruction_set")); @@ -256,14 +218,19 @@ public class InstructionGui { return instructionBuilder; } - public static GuiElementBuilder createArgumentElement(ValueSupplier valueSupplier) { + public static GuiElementBuilder createParameterElement(Parameter parameter, DynamicRegistryManager manager) { + return new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_TYPES, parameter.type(), manager)) + .setName(Text.translatable("minions.gui.instruction.parameter", parameter.name(), Text.translatable(TranslationUtil.getTranslationKey(parameter.type(), MinionRegistries.VALUE_TYPES)))); + } + + public static GuiElementBuilder createArgumentElement(ValueSupplier valueSupplier, DynamicRegistryManager manager) { GuiElementBuilder argumentBuilder; - if(valueSupplier != null) { - argumentBuilder = valueSupplier.getDisplay().createElement(); + if (valueSupplier != null) { + argumentBuilder = new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_SUPPLIER_TYPES, valueSupplier.getType(), manager)); } else { argumentBuilder = new GuiElementBuilder(Items.RED_WOOL) .setName(Text.translatable("minions.gui.instruction.no_argument_set")); } return argumentBuilder; } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/skippyall/minions/gui/MinionBoundSimpleGui.java b/src/main/java/io/github/skippyall/minions/gui/MinionBoundSimpleGui.java new file mode 100644 index 0000000..7ea084b --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/MinionBoundSimpleGui.java @@ -0,0 +1,37 @@ +package io.github.skippyall.minions.gui; + +import eu.pb4.sgui.api.gui.SimpleGui; +import io.github.skippyall.minions.minion.MinionListener; +import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.server.network.ServerPlayerEntity; + +public class MinionBoundSimpleGui extends SimpleGui implements MinionListener { + protected final MinionFakePlayer minion; + + public MinionBoundSimpleGui(ScreenHandlerType type, ServerPlayerEntity player, MinionFakePlayer minion) { + super(type, player, false); + this.minion = minion; + minion.addMinionListener(this); + } + + public MinionFakePlayer getMinion() { + return minion; + } + + @Override + public void onMinionRemove(MinionFakePlayer minion) { + close(); + } + + @Override + public void onClose() { + minion.removeMinionListener(this); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof MinionBoundSimpleGui that)) return false; + return minion == that.minion && player == that.player; + } +} 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 ac69851..9694a74 100644 --- a/src/main/java/io/github/skippyall/minions/gui/MinionGui.java +++ b/src/main/java/io/github/skippyall/minions/gui/MinionGui.java @@ -4,8 +4,12 @@ import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.SimpleGui; 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; @@ -51,10 +55,31 @@ public class MinionGui { } 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())); - for (int i = 0; i < minion.getInventory().size(); i++) { - gui.setSlotRedirect(i, new Slot(minion.getInventory(), i, 0, 0)); + SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X6, player, false); + gui.setTitle(Text.translatable("minions.gui.inventory.title")); + + for(int i = 0; i < 18; i++) { + gui.setSlot(i, new ItemStack(Items.BARRIER)); + } + + gui.setSlot(2, new ItemStack(Items.LEATHER_HELMET)); + gui.setSlot(3, new ItemStack(Items.LEATHER_CHESTPLATE)); + gui.setSlot(4, new ItemStack(Items.LEATHER_LEGGINGS)); + gui.setSlot(5, new ItemStack(Items.LEATHER_BOOTS)); + gui.setSlot(6, new ItemStack(Items.SHIELD)); + + gui.setSlotRedirect(2 + 9, new ArmorSlot(minion.getInventory(), minion, EquipmentSlot.HEAD, EquipmentSlot.HEAD.getOffsetEntitySlotId(PlayerInventory.MAIN_SIZE), 0, 0, null)); + gui.setSlotRedirect(3 + 9, new ArmorSlot(minion.getInventory(), minion, EquipmentSlot.CHEST, EquipmentSlot.CHEST.getOffsetEntitySlotId(PlayerInventory.MAIN_SIZE), 0, 0, null)); + gui.setSlotRedirect(4 + 9, new ArmorSlot(minion.getInventory(), minion, EquipmentSlot.LEGS, EquipmentSlot.LEGS.getOffsetEntitySlotId(PlayerInventory.MAIN_SIZE), 0, 0, null)); + gui.setSlotRedirect(5 + 9, new ArmorSlot(minion.getInventory(), minion, EquipmentSlot.FEET, EquipmentSlot.FEET.getOffsetEntitySlotId(PlayerInventory.MAIN_SIZE), 0, 0, null)); + gui.setSlotRedirect(6 + 9, new Slot(minion.getInventory(), PlayerInventory.OFF_HAND_SLOT, 0, 0)); + + for (int i = PlayerInventory.HOTBAR_SIZE; i < PlayerInventory.MAIN_SIZE; i++) { + gui.setSlotRedirect(i + 9, new Slot(minion.getInventory(), i, 0, 0)); + } + + for (int i = 0; i < PlayerInventory.HOTBAR_SIZE; i++) { + gui.setSlotRedirect(i + 45, new Slot(minion.getInventory(), i, 0, 0)); } gui.open(); } 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 index 342fcbf..994cec4 100644 --- a/src/main/java/io/github/skippyall/minions/gui/input/ChoiceInput.java +++ b/src/main/java/io/github/skippyall/minions/gui/input/ChoiceInput.java @@ -33,7 +33,7 @@ public class ChoiceInput { gui.setTitle(title); for(T value : values) { - gui.addSlot(displayFunction.apply(value).createElement() + gui.addSlot(new GuiElementBuilder(displayFunction.apply(value).createItemStack()) .setCallback(() -> future.complete(value)) ); } @@ -43,7 +43,7 @@ public class ChoiceInput { } public static BiFunction> createDialogOpener(T[] values) { - return createDialogOpener(ScreenHandlerType.GENERIC_9X3, Text.empty(), Displayable::getDisplay, values, null); + return createDialogOpener(ScreenHandlerType.GENERIC_9X3, Text.empty(), t -> t != null ? t.getDisplay() : null, values, null); } public static CompletableFuture confirm(ServerPlayerEntity player, Text title) { diff --git a/src/main/java/io/github/skippyall/minions/gui/input/TextInput.java b/src/main/java/io/github/skippyall/minions/gui/input/TextInput.java index 3681183..9add272 100644 --- a/src/main/java/io/github/skippyall/minions/gui/input/TextInput.java +++ b/src/main/java/io/github/skippyall/minions/gui/input/TextInput.java @@ -47,12 +47,12 @@ public class TextInput extends AnvilInputGui { return inputSync(player, title, defaultValue, Result.Success::new); } - public static CompletableFuture inputInt(ServerPlayerEntity player, Text title, String defaultValue) { - return inputSync(player, title, defaultValue, string -> Result.wrapCustomError(() -> Integer.valueOf(string), Text.translatable("minions.command.input.int.fail"))); + public static CompletableFuture inputLong(ServerPlayerEntity player, Text title, String defaultValue) { + return inputSync(player, title, defaultValue, string -> Result.wrapCustomError(() -> Long.valueOf(string), Text.translatable("minions.command.input.int.fail"))); } - public static CompletableFuture inputFloat(ServerPlayerEntity player, Text title, String defaultValue) { - return inputSync(player, title, defaultValue, string -> Result.wrapCustomError(() -> Float.valueOf(string), Text.translatable("minions.command.input.float.fail"))); + public static CompletableFuture inputDouble(ServerPlayerEntity player, Text title, String defaultValue) { + return inputSync(player, title, defaultValue, string -> Result.wrapCustomError(() -> Double.valueOf(string), Text.translatable("minions.command.input.float.fail"))); } @Override diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionListener.java b/src/main/java/io/github/skippyall/minions/minion/MinionListener.java new file mode 100644 index 0000000..5548270 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/minion/MinionListener.java @@ -0,0 +1,15 @@ +package io.github.skippyall.minions.minion; + +import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; +import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; +import io.github.skippyall.minions.util.SerializableListenerManager; + +public interface MinionListener extends SerializableListenerManager.SerializableListener { + default void onMinionSpawn(MinionFakePlayer minion) {} + + default void onMinionRemove(MinionFakePlayer minion) {} + + default void onInstructionsUpdate(MinionFakePlayer minion) {} + + default void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction instruction, String newName) {} +} 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 ebb0f32..df7b21e 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionRuntime.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionRuntime.java @@ -46,6 +46,7 @@ public class MinionRuntime implements InstructionRuntime { ConfiguredInstruction instruction = new ConfiguredInstruction<>(instructionType); configuredInstructions.put(name, instruction); + minion.forEachMinionListener(listener -> listener.onInstructionsUpdate(minion)); return instruction; } @@ -53,6 +54,9 @@ public class MinionRuntime implements InstructionRuntime { ConfiguredInstruction instruction = getInstruction(name); instruction.stop(this); configuredInstructions.remove(name); + + instruction.onInstructionRemove(); + minion.forEachMinionListener(listener -> listener.onInstructionsUpdate(minion)); } public ConfiguredInstruction getInstruction(String name) { @@ -64,8 +68,16 @@ public class MinionRuntime implements InstructionRuntime { } public void setInstructionName(String oldName, String newName) { - if(!configuredInstructions.containsKey(newName)) { - configuredInstructions.put(newName, configuredInstructions.remove(oldName)); + if(!configuredInstructions.containsKey(newName) && configuredInstructions.containsKey(oldName)) { + ConfiguredInstruction instruction = configuredInstructions.get(oldName); + configuredInstructions.remove(oldName); + configuredInstructions.put(newName, instruction); + + + minion.forEachMinionListener(minionListener -> { + minionListener.onInstructionRename(minion , instruction, newName); + minionListener.onInstructionsUpdate(minion); + }); } } @@ -98,7 +110,7 @@ public class MinionRuntime implements InstructionRuntime { @Override public Registry> getArgumentTypeRegistry() { - return MinionRegistries.ARGUMENT_TYPES; + return MinionRegistries.VALUE_SUPPLIER_TYPES; } @Override diff --git a/src/main/java/io/github/skippyall/minions/minion/fakeplayer/EntityPlayerActionPack.java b/src/main/java/io/github/skippyall/minions/minion/fakeplayer/EntityPlayerActionPack.java index f03d9e0..cdfda5e 100644 --- a/src/main/java/io/github/skippyall/minions/minion/fakeplayer/EntityPlayerActionPack.java +++ b/src/main/java/io/github/skippyall/minions/minion/fakeplayer/EntityPlayerActionPack.java @@ -7,7 +7,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.mojang.serialization.Codec; import io.github.skippyall.minions.mixins.EntityAccessor; import net.minecraft.block.BlockState; import net.minecraft.command.argument.EntityAnchorArgumentType; @@ -18,7 +17,6 @@ import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.entity.vehicle.MinecartEntity; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; @@ -41,7 +39,7 @@ public class EntityPlayerActionPack private final Map actions = new EnumMap<>(ActionType.class); private BlockPos currentBlock; - private int blockHitDelay; + public int blockHitDelay; private boolean isHittingBlock; private float curBlockDamageMP; @@ -73,6 +71,14 @@ public class EntityPlayerActionPack itemUseCooldown = other.itemUseCooldown; } + public Action getAction(ActionType type) { + return actions.get(type); + } + + public boolean hasAction(ActionType type) { + return actions.containsKey(type); + } + public EntityPlayerActionPack start(ActionType type, Action action) { Action previous = actions.remove(type); @@ -255,9 +261,13 @@ public class EntityPlayerActionPack if (strafing != 0.0F || player instanceof MinionFakePlayer) { player.sidewaysSpeed = strafing * vel; } + + if(blockHitDelay > 0) { + blockHitDelay--; + } } - static HitResult getTarget(ServerPlayerEntity player) + public static HitResult getTarget(ServerPlayerEntity player) { double reach = player.interactionManager.isCreative() ? 5 : 4.5f; return Tracer.rayTrace(player, 1, reach, false); @@ -382,7 +392,7 @@ public class EntityPlayerActionPack switch (hit.getType()) { case ENTITY: { EntityHitResult entityHit = (EntityHitResult) hit; - if (!action.isContinuous) + if (!action.isContinuous || action.first) { player.attack(entityHit.getEntity()); player.swingHand(Hand.MAIN_HAND); @@ -395,7 +405,6 @@ public class EntityPlayerActionPack EntityPlayerActionPack ap = player.getMinionActionPack(); if (ap.blockHitDelay > 0) { - ap.blockHitDelay--; return false; } BlockHitResult blockHit = (BlockHitResult) hit; @@ -550,8 +559,9 @@ public class EntityPlayerActionPack private int count; private int next; private final boolean isContinuous; + boolean first = true; - private Action(int limit, int interval, int offset, boolean continuous) + private Action(int limit, int interval, int offset, boolean continuous, boolean first) { this.limit = limit; this.interval = interval; @@ -562,22 +572,27 @@ public class EntityPlayerActionPack public static Action once() { - return new Action(1, 1, 0, false); + return new Action(1, 1, 0, false, false); + } + + public static Action startContinuous() + { + return new Action(-1, 1, 0, true, true); } public static Action continuous() { - return new Action(-1, 1, 0, true); + return new Action(-1, 1, 0, true, false); } public static Action interval(int interval) { - return new Action(-1, interval, 0, false); + return new Action(-1, interval, 0, false, false); } public static Action interval(int interval, int offset) { - return new Action(-1, interval, offset, false); + return new Action(-1, interval, offset, false, false); } Boolean tick(EntityPlayerActionPack actionPack, ActionType type) @@ -608,6 +623,7 @@ public class EntityPlayerActionPack return cancel; } next = interval; + first = false; } else { 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 bf2015a..4f94dd5 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 @@ -4,6 +4,8 @@ package io.github.skippyall.minions.minion.fakeplayer; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.PropertyMap; import io.github.skippyall.minions.MinionItems; +import io.github.skippyall.minions.MinionRegistries; +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.minion.MinionRuntime; @@ -11,6 +13,7 @@ import io.github.skippyall.minions.minion.MinionItem; import io.github.skippyall.minions.minion.MinionPersistentState; import io.github.skippyall.minions.minion.MinionProfileUtils; import io.github.skippyall.minions.module.ModuleInventory; +import io.github.skippyall.minions.util.SerializableListenerManager; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; @@ -45,12 +48,14 @@ import net.minecraft.world.TeleportTarget; import org.jetbrains.annotations.Nullable; import java.util.Set; +import java.util.function.Consumer; public class MinionFakePlayer extends ServerPlayerEntity { public Runnable fixStartingPosition = () -> {}; private EntityPlayerActionPack actionPack; + private final SerializableListenerManager minionListeners = new SerializableListenerManager<>(MinionRegistries.MINION_LISTENER_CODECS); private final ModuleInventory moduleInventory = new ModuleInventory(); private final MinionRuntime instructionManager = new MinionRuntime(this); @@ -90,9 +95,11 @@ public class MinionFakePlayer extends ServerPlayerEntity { instance.interactionManager.changeGameMode(GameMode.SURVIVAL); server.getPlayerManager().sendToDimension(new EntitySetHeadYawS2CPacket(instance, (byte) (instance.headYaw * 256 / 360)), level.getRegistryKey());//instance.dimension); server.getPlayerManager().sendToDimension(EntityPositionSyncS2CPacket.create(instance), level.getRegistryKey());//instance.dimension); - //instance.world.getChunkManager(). updatePosition(instance); + instance.getWorld().getChunkManager().updatePosition(instance); instance.dataTracker.set(PLAYER_MODEL_PARTS, (byte) 0x7f); // show all model layers (incl. capes) instance.getAbilities().flying = false; + + instance.minionListeners.forEachListener(listener -> listener.onMinionSpawn(instance)); } public static MinionFakePlayer respawnFake(MinecraftServer server, ServerWorld level, GameProfile profile, SyncedClientOptions cli, MinionData data) @@ -123,6 +130,18 @@ public class MinionFakePlayer extends ServerPlayerEntity { return data; } + public void addMinionListener(MinionListener listener) { + minionListeners.addListener(listener); + } + + public void removeMinionListener(MinionListener listener) { + minionListeners.removeListener(listener); + } + + public void forEachMinionListener(Consumer listenerConsumer) { + minionListeners.forEachListener(listenerConsumer); + } + public boolean canSpawnMobs() { return moduleInventory.hasAbility("mobSpawning"); } @@ -150,14 +169,10 @@ public class MinionFakePlayer extends ServerPlayerEntity { if (!isUsingItem()) super.onEquipStack(slot, previous, stack); } - /*@Override - public void kill() - { - kill(Text.literal("Killed")); - }*/ - public void kill(Text reason) { + minionListeners.forEachListener(listener -> listener.onMinionRemove(this)); + shakeOff(); if (reason.getContent() instanceof TranslatableTextContent text && text.getKey().equals("multiplayer.disconnect.duplicate_login")) { @@ -266,6 +281,7 @@ public class MinionFakePlayer extends ServerPlayerEntity { super.writeCustomData(view); moduleInventory.writeData(view.get("modules")); instructionManager.save(view.get("instructionManager")); + minionListeners.save(view); } @Override @@ -273,5 +289,6 @@ public class MinionFakePlayer extends ServerPlayerEntity { super.readCustomData(view); moduleInventory.readData(view.getReadView("modules")); instructionManager.load(view.getReadView("instructionManager")); + minionListeners.load(view); } } \ No newline at end of file diff --git a/src/main/java/io/github/skippyall/minions/mixins/ChunkTicketManagerFixMixin.java b/src/main/java/io/github/skippyall/minions/mixins/ChunkTicketManagerFixMixin.java index 1602f2a..a0dd38b 100644 --- a/src/main/java/io/github/skippyall/minions/mixins/ChunkTicketManagerFixMixin.java +++ b/src/main/java/io/github/skippyall/minions/mixins/ChunkTicketManagerFixMixin.java @@ -4,18 +4,26 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import it.unimi.dsi.fastutil.objects.ObjectSet; import net.minecraft.server.world.ChunkLevelManager; -import net.minecraft.server.world.ChunkTicketManager; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @Mixin(value = ChunkLevelManager.class) public class ChunkTicketManagerFixMixin { + @Shadow + @Final + private static Logger LOGGER; + @WrapOperation(method = "handleChunkLeave", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/ObjectSet;remove(Ljava/lang/Object;)Z", remap = false)) public boolean filterIfNull(ObjectSet instance, Object o, Operation original) { if (instance != null) { return original.call(instance, o); } + LOGGER.error("Prevented NPE in handleChunkLeave"); + return false;//Unused } @@ -25,6 +33,8 @@ public class ChunkTicketManagerFixMixin { return original.call(instance); } + LOGGER.error("Prevented NPE in handleChunkLeave"); + return true;//Unused } } diff --git a/src/main/java/io/github/skippyall/minions/mixins/antimobcap/ChunkLevelManagerMixin.java b/src/main/java/io/github/skippyall/minions/mixins/antimobcap/ChunkLevelManagerMixin.java index 84c2fd2..bd54b7e 100644 --- a/src/main/java/io/github/skippyall/minions/mixins/antimobcap/ChunkLevelManagerMixin.java +++ b/src/main/java/io/github/skippyall/minions/mixins/antimobcap/ChunkLevelManagerMixin.java @@ -21,7 +21,7 @@ import java.util.concurrent.Executor; @Mixin(ChunkLevelManager.class) public class ChunkLevelManagerMixin implements ChunkLevelManagerAccessor { - @Shadow @Final private Long2ObjectMap> playersByChunkPos; + @Shadow @Final Long2ObjectMap> playersByChunkPos; @Shadow @Final private ChunkLevelManager.DistanceFromNearestPlayerTracker distanceFromNearestPlayerTracker; @Unique ChunkLevelManager.DistanceFromNearestPlayerTracker minionless; diff --git a/src/main/java/io/github/skippyall/minions/mixins/GraveCompatMixin.java b/src/main/java/io/github/skippyall/minions/mixins/compat/universal_graves/GraveMixin.java similarity index 90% rename from src/main/java/io/github/skippyall/minions/mixins/GraveCompatMixin.java rename to src/main/java/io/github/skippyall/minions/mixins/compat/universal_graves/GraveMixin.java index 02a9d7f..0139843 100644 --- a/src/main/java/io/github/skippyall/minions/mixins/GraveCompatMixin.java +++ b/src/main/java/io/github/skippyall/minions/mixins/compat/universal_graves/GraveMixin.java @@ -1,4 +1,4 @@ -package io.github.skippyall.minions.mixins; +package io.github.skippyall.minions.mixins.compat.universal_graves; import com.llamalad7.mixinextras.sugar.Local; import eu.pb4.graves.grave.Grave; @@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; @Mixin(Grave.class) -public class GraveCompatMixin { +public class GraveMixin { @ModifyArg(method = "createBlock", at = @At(value = "INVOKE", target = "Leu/pb4/graves/grave/Grave;(JLcom/mojang/authlib/GameProfile;BLnet/minecraft/util/Arm;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Identifier;Leu/pb4/graves/grave/GraveType;JJILnet/minecraft/text/Text;Ljava/util/Collection;Ljava/util/Collection;ZI)V")) private static boolean createGrave(boolean profile, @Local(argsOnly = true) ServerPlayerEntity player) { if(player instanceof MinionFakePlayer) { diff --git a/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumer.java b/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumer.java index 29a8e15..cdccb72 100644 --- a/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumer.java +++ b/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumer.java @@ -2,7 +2,6 @@ package io.github.skippyall.minions.program.consumer; import com.mojang.serialization.Codec; import io.github.skippyall.minions.MinionRegistries; -import io.github.skippyall.minions.gui.GuiDisplay; import io.github.skippyall.minions.program.InstructionRuntime; import io.github.skippyall.minions.program.value.ValueType; import org.jetbrains.annotations.Nullable; @@ -16,8 +15,6 @@ import org.jetbrains.annotations.Nullable; public interface ValueConsumer> { void consume(T value, R runtime); - GuiDisplay getDisplay(); - ValueType getValueType(); ValueConsumerType getType(); diff --git a/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumerList.java b/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumerList.java index e39f140..74981eb 100644 --- a/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumerList.java +++ b/src/main/java/io/github/skippyall/minions/program/consumer/ValueConsumerList.java @@ -4,11 +4,15 @@ import com.mojang.serialization.Codec; import io.github.skippyall.minions.program.InstructionRuntime; import io.github.skippyall.minions.program.supplier.Parameter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Consumer; public class ValueConsumerList> { private final Map> valueConsumers; + private final List>> changeListeners = new ArrayList<>(); public ValueConsumerList() { valueConsumers = new HashMap<>(); @@ -25,15 +29,30 @@ public class ValueConsumerList> { public void setValueConsumer(Parameter parameter, ValueConsumer consumer) { valueConsumers.put(parameter.name(), consumer); + onChange(parameter); } - public void setValue(Parameter parameter, T value, R runtime) { + public void consumeValue(Parameter parameter, T value, R runtime) { ValueConsumer consumer = getValueConsumer(parameter).cast(parameter.type()); if (consumer != null) { consumer.consume(value, runtime); } } + private void onChange(Parameter parameter) { + for (Consumer> listener : changeListeners) { + listener.accept(parameter); + } + } + + public void addListener(Consumer> listener) { + changeListeners.add(listener); + } + + public void removeListener(Consumer> listener) { + changeListeners.remove(listener); + } + public static > Codec> getCodec(Codec> valueConsumerCodec) { return Codec.unboundedMap(Codec.STRING, valueConsumerCodec) .xmap(ValueConsumerList::new, list -> list.valueConsumers); 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 c8b4f37..b638174 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 @@ -1,9 +1,12 @@ package io.github.skippyall.minions.program.instruction; +import io.github.skippyall.minions.MinionRegistries; import io.github.skippyall.minions.Minions; import io.github.skippyall.minions.program.InstructionRuntime; +import io.github.skippyall.minions.program.supplier.Parameter; import io.github.skippyall.minions.program.supplier.ValueSupplierList; import io.github.skippyall.minions.program.consumer.ValueConsumerList; +import io.github.skippyall.minions.util.SerializableListenerManager; import net.minecraft.storage.ReadView; import net.minecraft.storage.WriteView; import org.jetbrains.annotations.Nullable; @@ -14,11 +17,21 @@ public class ConfiguredInstruction> { private final ValueConsumerList valueConsumers; private @Nullable InstructionExecution execution; + private SerializableListenerManager listeners; + + private ConfiguredInstruction(InstructionType instruction, ValueSupplierList arguments, ValueConsumerList valueConsumers, @Nullable InstructionExecution execution, SerializableListenerManager listeners) { + this(instruction, arguments, valueConsumers, execution); + this.listeners = listeners; + } + private ConfiguredInstruction(InstructionType instruction, ValueSupplierList arguments, ValueConsumerList valueConsumers, @Nullable InstructionExecution execution) { this.instruction = instruction; this.arguments = arguments; this.valueConsumers = valueConsumers; this.execution = execution; + + arguments.addListener(this::onSupplierChange); + valueConsumers.addListener(this::onConsumerChange); } public ConfiguredInstruction(InstructionType instruction) { @@ -52,13 +65,14 @@ public class ConfiguredInstruction> { execution.start(minion); } catch (Exception e) { Minions.LOGGER.error("An error occurred while executing configured Instruction", e); - } + + listeners.forEachListener(listener -> listener.onRun(this)); } } public void tick(R minion) { - if(isRunning()) { + if(execution != null) { if(execution.isDone(minion)) { stop(minion); } else { @@ -71,20 +85,43 @@ public class ConfiguredInstruction> { } public void stop(R minion) { - if(isRunning()) { + if(execution != null) { execution.stop(minion, valueConsumers); execution = null; + listeners.forEachListener(listener -> listener.onStop(this)); } } + private void onSupplierChange(Parameter parameter) { + listeners.forEachListener(listener -> listener.onSupplierChange(this, parameter)); + } + + private void onConsumerChange(Parameter parameter) { + listeners.forEachListener(listener -> listener.onConsumerChange(this, parameter)); + } + + public void onInstructionRemove() { + listeners.forEachListener(listener -> listener.onInstructionRemove(this)); + } + + public void addListener(ConfiguredInstructionListener listener) { + listeners.addListener(listener); + } + + public void removeListener(ConfiguredInstructionListener listener) { + listeners.removeListener(listener); + } + public void save(WriteView view, R minion) { view.put("instruction", minion.getInstructionTypeRegistry().getCodec(), instruction); view.put("arguments", minion.getArgumentListCodec(), arguments); view.put("valueConsumers", minion.getValueConsumerListCodec(), valueConsumers); view.putBoolean("running", isRunning()); - if(isRunning()) { + if(execution != null) { execution.save(view.get("execution"), minion); } + + listeners.save(view); } public static > ConfiguredInstruction load(ReadView view, R minion) { @@ -95,16 +132,19 @@ public class ConfiguredInstruction> { boolean running = view.getBoolean("running", false); + SerializableListenerManager listeners = new SerializableListenerManager<>(MinionRegistries.INSTRUCTION_LISTENER_CODECS); + listeners.load(view); + if(running) { ReadView executionView = view.getReadView("execution"); try { InstructionExecution execution = instructionType.loadExecution(executionView, minion); - return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution); + return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution, listeners); } catch (Exception e) { Minions.LOGGER.error("Error while loading execution", e); } } - return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null); + return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null, listeners); } } diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstructionListener.java b/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstructionListener.java new file mode 100644 index 0000000..13e863f --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/instruction/ConfiguredInstructionListener.java @@ -0,0 +1,16 @@ +package io.github.skippyall.minions.program.instruction; + +import io.github.skippyall.minions.program.supplier.Parameter; +import io.github.skippyall.minions.util.SerializableListenerManager; + +public interface ConfiguredInstructionListener extends SerializableListenerManager.SerializableListener { + default void onRun(ConfiguredInstruction instruction) {} + + default void onStop(ConfiguredInstruction instruction) {} + + default void onSupplierChange(ConfiguredInstruction instruction, Parameter parameter) {} + + default void onConsumerChange(ConfiguredInstruction instruction, Parameter parameter) {} + + default void onInstructionRemove(ConfiguredInstruction instruction) {} +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/InstructionExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/InstructionExecution.java index eb1ba34..5d6b567 100644 --- a/src/main/java/io/github/skippyall/minions/program/instruction/InstructionExecution.java +++ b/src/main/java/io/github/skippyall/minions/program/instruction/InstructionExecution.java @@ -37,7 +37,7 @@ public interface InstructionExecution> { * * @param runtime The runtime that was executing this instruction. */ - void stop(R runtime, ValueConsumerList valueConsumers); + default void stop(R runtime, ValueConsumerList valueConsumers) {} /** * Initializes the execution with its parameters. The parameters must be defined by the InstructionType @@ -55,4 +55,15 @@ public interface InstructionExecution> { * Loads the execution, e.g. when the server is started. */ void load(ReadView view, R runtime); + + interface Stateless> extends InstructionExecution { + @Override + default void readArguments(ValueSupplierList arguments, R runtime) {} + + @Override + default void save(WriteView view, R runtime) {} + + @Override + default void load(ReadView view, R runtime) {} + } } 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 c684524..7c17a88 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 @@ -1,6 +1,5 @@ package io.github.skippyall.minions.program.instruction; -import io.github.skippyall.minions.gui.GuiDisplay; import io.github.skippyall.minions.program.InstructionRuntime; import io.github.skippyall.minions.program.supplier.Parameter; import io.github.skippyall.minions.program.supplier.ValueSupplierList; @@ -11,30 +10,24 @@ import java.util.List; import java.util.function.Supplier; public class InstructionType> { - private final GuiDisplay display; - private final Collection> parameters; - private final Collection> returnParameters; + private final List> parameters; + private final List> returnParameters; private final Supplier> executionFactory; - public InstructionType(GuiDisplay display, Supplier> executionFactory, Collection> parameters, Collection> returnParameters) { - this.display = display; + public InstructionType(Supplier> executionFactory, Collection> parameters, Collection> returnParameters) { this.parameters = List.copyOf(parameters); this.returnParameters = List.copyOf(returnParameters); this.executionFactory = executionFactory; } - public Collection> getParameters() { + public List> getParameters() { return parameters; } - public Collection> getReturnParameters() { + public List> getReturnParameters() { return returnParameters; } - public GuiDisplay getDisplay() { - return display; - } - public InstructionExecution createExecution(ValueSupplierList parameters, R minion) { InstructionExecution execution = executionFactory.get(); execution.readArguments(parameters, minion); 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 d1367d1..115dc61 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 @@ -2,60 +2,71 @@ package io.github.skippyall.minions.program.instruction; import io.github.skippyall.minions.MinionRegistries; import io.github.skippyall.minions.Minions; -import io.github.skippyall.minions.gui.GuiDisplay; import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.fakeplayer.EntityPlayerActionPack; 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.instruction.execution.MineBlockExecution; +import io.github.skippyall.minions.program.instruction.execution.move.ContinuousWalkExecution; +import io.github.skippyall.minions.program.instruction.execution.move.TurnExecution; +import io.github.skippyall.minions.program.instruction.execution.move.TurnVectorExecution; +import io.github.skippyall.minions.program.instruction.execution.move.WalkExecution; import io.github.skippyall.minions.program.supplier.Parameter; -import net.minecraft.item.Items; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import java.util.Collection; import java.util.List; -import java.util.function.Function; import java.util.function.Supplier; public class Instructions { public static final InstructionType WALK = register( "walk", - base -> new GuiDisplay.ModelBased(Items.IRON_BOOTS, base, true), WalkExecution::new, List.of(WalkExecution.blocksToMoveParam) ); + public static final InstructionType WALK_CONTINUOUS = register( + "walk_continuous", + ContinuousWalkExecution::new + ); public static final InstructionType TURN = register( "turn", - base -> new GuiDisplay.ModelBased(Items.STRUCTURE_VOID, base, true), TurnExecution::new, List.of(TurnExecution.ANGLE, TurnExecution.DIRECTION) ); + public static final InstructionType TURN_VECTOR = register( + "turn_vector", + TurnVectorExecution::new, + List.of(TurnVectorExecution.X, TurnVectorExecution.Y, TurnVectorExecution.Z) + ); + public static final InstructionType ATTACK = register( "attack", - base -> new GuiDisplay.ModelBased(Items.IRON_PICKAXE, base, true), () -> new ActionExecution(EntityPlayerActionPack.ActionType.ATTACK) ); + public static final InstructionType MINE_BLOCK = register( + "mine_block", + MineBlockExecution::new + ); + public static final InstructionType USE = register( "use", - base -> new GuiDisplay.ModelBased(Items.LEVER, base, true), () -> new ActionExecution(EntityPlayerActionPack.ActionType.USE) ); - private static InstructionType register(String id, Function displayFunction, Supplier> factory, Collection> parameters, Collection> returnParameters) { + private static InstructionType register(String id, Supplier> factory, Collection> parameters, Collection> returnParameters) { Identifier identifier = Identifier.of(Minions.MOD_ID, id); - return Registry.register(MinionRegistries.INSTRUCTION_TYPES, identifier, new InstructionType<>(displayFunction.apply(identifier.toTranslationKey("instruction_type")), factory, parameters, returnParameters)); + return Registry.register(MinionRegistries.INSTRUCTION_TYPES, identifier, new InstructionType<>(factory, parameters, returnParameters)); } - private static InstructionType register(String id, Function displayFunction, Supplier> factory, Collection> parameters) { - return register(id, displayFunction, factory, parameters, List.of()); + private static InstructionType register(String id, Supplier> factory, Collection> parameters) { + return register(id, factory, parameters, List.of()); } - private static InstructionType register(String id, Function displayFunction, Supplier> factory) { - return register(id, displayFunction, factory, List.of(), List.of()); + private static InstructionType register(String id, Supplier> factory) { + return register(id, factory, List.of(), List.of()); } public static void register() { diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/ActionExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/ActionExecution.java index 3c100cc..6019c3a 100644 --- a/src/main/java/io/github/skippyall/minions/program/instruction/execution/ActionExecution.java +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/ActionExecution.java @@ -16,7 +16,10 @@ public class ActionExecution implements ContinuousInstructionExecution { + private BlockPos currentBlock; + private float currentBlockDamage = 0; + private boolean first = true; + private boolean done = false; + private boolean success = false; + + @Override + public void start(MinionRuntime runtime) { + MinionFakePlayer player = runtime.getMinion(); + if(EntityPlayerActionPack.getTarget(player) instanceof BlockHitResult hit) { + this.currentBlock = hit.getBlockPos(); + + EntityPlayerActionPack ap = player.getMinionActionPack(); + if (ap.blockHitDelay > 0) { + ap.blockHitDelay--; + done = true; + return; + } + if (player.isBlockBreakingRestricted(player.getWorld(), hit.getBlockPos(), player.interactionManager.getGameMode())) { + done = true; + return; + } + } else { + done = true; + } + } + + @Override + public void tick(MinionRuntime runtime) { + if(done) { + return; + } + + MinionFakePlayer player = runtime.getMinion(); + EntityPlayerActionPack ap = player.getMinionActionPack(); + + HitResult newHit = EntityPlayerActionPack.getTarget(player); + if(!(newHit instanceof BlockHitResult newBlockHit)) { + done = true; + return; + } + + BlockPos newPos = newBlockHit.getBlockPos(); + if(!newPos.equals(currentBlock)) { + done = true; + return; + } + + if (player.getWorld().getBlockState(currentBlock).isAir()) { + done = true; + return; + } + BlockState state = player.getWorld().getBlockState(currentBlock); + boolean blockBroken = false; + if (first) { + first = false; + player.interactionManager.processBlockBreakingAction(currentBlock, PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, newBlockHit.getSide(), player.getWorld().getTopYInclusive(), -1); + boolean notAir = !state.isAir(); + if (notAir) + { + state.onBlockBreakStart(player.getWorld(), currentBlock, player); + } + if (notAir && state.calcBlockBreakingDelta(player, player.getWorld(), currentBlock) >= 1) + { + //instamine?? + blockBroken = true; + } + } else { + currentBlockDamage += state.calcBlockBreakingDelta(player, player.getWorld(), currentBlock); + if (currentBlockDamage >= 1) { + player.interactionManager.processBlockBreakingAction(currentBlock, PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, newBlockHit.getSide(), player.getWorld().getTopYInclusive(), -1); + ap.blockHitDelay = 5; + blockBroken = true; + } + player.getWorld().setBlockBreakingInfo(-1, currentBlock, (int) (currentBlockDamage * 10)); + + } + player.updateLastActionTime(); + player.swingHand(Hand.MAIN_HAND); + + if(blockBroken) { + done = true; + success = true; + } + } + + @Override + public boolean isDone(MinionRuntime runtime) { + return done; + } + + @Override + public void stop(MinionRuntime runtime, ValueConsumerList valueConsumers) { + MinionFakePlayer player = runtime.getMinion(); + EntityPlayerActionPack ap = player.getMinionActionPack(); + + player.getWorld().setBlockBreakingInfo(-1, currentBlock, -1); + player.interactionManager.processBlockBreakingAction(currentBlock, PlayerActionC2SPacket.Action.ABORT_DESTROY_BLOCK, Direction.DOWN, player.getWorld().getTopYInclusive(), -1); + } + + @Override + public void readArguments(ValueSupplierList arguments, MinionRuntime runtime) { + + } + + @Override + public void save(WriteView view, MinionRuntime runtime) { + view.put("currentBlock", BlockPos.CODEC, currentBlock); + view.putFloat("currentBlockDamage", currentBlockDamage); + } + + @Override + public void load(ReadView view, MinionRuntime runtime) { + currentBlock = view.read("currentBlock", BlockPos.CODEC).orElse(null); + currentBlockDamage = view.getFloat("currentBlockDamage", 0); + if(currentBlock == null) { + done = true; + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/TurnExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/TurnExecution.java deleted file mode 100644 index 24b5aed..0000000 --- a/src/main/java/io/github/skippyall/minions/program/instruction/execution/TurnExecution.java +++ /dev/null @@ -1,101 +0,0 @@ -package io.github.skippyall.minions.program.instruction.execution; - -import com.mojang.serialization.Codec; -import io.github.skippyall.minions.gui.Displayable; -import io.github.skippyall.minions.gui.GuiDisplay; -import io.github.skippyall.minions.minion.MinionRuntime; -import io.github.skippyall.minions.program.consumer.ValueConsumerList; -import io.github.skippyall.minions.program.supplier.ValueSupplierList; -import io.github.skippyall.minions.program.supplier.Parameter; -import io.github.skippyall.minions.program.instruction.InstructionExecution; -import io.github.skippyall.minions.program.value.ValueTypes; -import net.minecraft.storage.ReadView; -import net.minecraft.storage.WriteView; -import net.minecraft.util.StringIdentifiable; - -import java.util.UUID; - -public class TurnExecution implements InstructionExecution { - public static final Parameter ANGLE = new Parameter<>("maxAngle", ValueTypes.FLOAT); - public static final Parameter DIRECTION = new Parameter<>("direction", ValueTypes.TURN_DIRECTION); - - private static final float anglePerTick = 5; - - private float maxAngle; - private float rotatedAngle; - private TurnDirection direction; - - @Override - public void tick(MinionRuntime minion) { - float toRotate = Math.min(anglePerTick, maxAngle - rotatedAngle); - minion.getMinion().getMinionActionPack().turn(direction.xFactor * toRotate, direction.yFactor * toRotate); - - rotatedAngle += toRotate; - } - - @Override - public boolean isDone(MinionRuntime minion) { - return Math.abs(maxAngle - rotatedAngle) < 0.001F; - } - - @Override - public void stop(MinionRuntime minion, ValueConsumerList valueConsumers) { - } - - @Override - public void readArguments(ValueSupplierList arguments, MinionRuntime minion) { - maxAngle = arguments.getValue(ANGLE, minion); - direction = arguments.getValue(DIRECTION, minion); - } - - @Override - public void save(WriteView view, MinionRuntime minion) { - view.putFloat("maxAngle", maxAngle); - view.put("direction", TurnDirection.CODEC, direction); - } - - @Override - public void load(ReadView view, MinionRuntime minion) { - maxAngle = view.getFloat("maxAngle", 0); - direction = view.read("direction", TurnDirection.CODEC).orElseThrow(); - } - - public enum TurnDirection implements StringIdentifiable, Displayable { - LEFT("left", -1, 0), - UP("up", 0, -1), - RIGHT("right", 1, 0), - DOWN("down", 0, 1); - - public static final Codec CODEC = StringIdentifiable.createCodec(TurnDirection::values); - - private static final UUID MHF_ArrowLeft = UUID.fromString("a68f0b64-8d14-4000-a95f-4b9ba14f8df9"); - private static final UUID MHF_ArrowUp = UUID.fromString("fef039ef-e6cd-4987-9c84-26a3e6134277"); - private static final UUID MHF_ArrowRight = UUID.fromString("50c8510b-5ea0-4d60-be9a-7d542d6cd156"); - private static final UUID MHF_ArrowDown = UUID.fromString("68f59b9b-5b0b-4b05-a9f2-e1d1405aa348"); - - public final String name; - public final int xFactor; - public final int yFactor; - - TurnDirection(String name, int xFactor, int yFactor) { - this.xFactor = xFactor; - this.yFactor = yFactor; - this.name = name; - } - - @Override - public String asString() { - return name; - } - - @Override - public GuiDisplay getDisplay() { - return switch (this) { - case LEFT -> new GuiDisplay.HeadBased(MHF_ArrowLeft, "minions.direction.left", false); - case UP -> new GuiDisplay.HeadBased(MHF_ArrowUp, "minions.direction.up", false); - case RIGHT -> new GuiDisplay.HeadBased(MHF_ArrowRight, "minions.direction.right", false); - case DOWN -> new GuiDisplay.HeadBased(MHF_ArrowDown, "minions.direction.down", false); - }; - } - } -} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/AbstractTurnExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/AbstractTurnExecution.java new file mode 100644 index 0000000..69733e3 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/AbstractTurnExecution.java @@ -0,0 +1,38 @@ +package io.github.skippyall.minions.program.instruction.execution.move; + +import io.github.skippyall.minions.minion.MinionRuntime; +import io.github.skippyall.minions.program.instruction.InstructionExecution; +import net.minecraft.storage.ReadView; +import net.minecraft.storage.WriteView; + +public abstract class AbstractTurnExecution implements InstructionExecution { + protected float targetYaw; + protected float targetPitch; + + private static final float anglePerTick = 10; + + @Override + public void tick(MinionRuntime minion) { + float rotateYaw = targetYaw - minion.getMinion().getYaw(); + float rotatePitch = targetPitch - minion.getMinion().getPitch(); + + minion.getMinion().getMinionActionPack().turn(Math.min(rotateYaw, anglePerTick), Math.min(rotatePitch, anglePerTick)); + } + + @Override + public boolean isDone(MinionRuntime minion) { + return Math.abs(targetYaw - minion.getMinion().getYaw()) < 0.001F && Math.abs(targetPitch - minion.getMinion().getPitch()) < 0.001F; + } + + @Override + public void save(WriteView view, MinionRuntime runtime) { + view.putFloat("targetYaw", targetYaw); + view.putFloat("targetPitch", targetPitch); + } + + @Override + public void load(ReadView view, MinionRuntime runtime) { + targetYaw = view.getFloat("targetYaw", 0); + targetPitch = view.getFloat("targetPitch", 0); + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/ContinuousWalkExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/ContinuousWalkExecution.java new file mode 100644 index 0000000..190e6a3 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/ContinuousWalkExecution.java @@ -0,0 +1,13 @@ +package io.github.skippyall.minions.program.instruction.execution.move; + +import io.github.skippyall.minions.minion.MinionRuntime; +import io.github.skippyall.minions.program.instruction.InstructionExecution; +import io.github.skippyall.minions.program.instruction.execution.ContinuousInstructionExecution; +import net.minecraft.entity.MovementType; + +public class ContinuousWalkExecution implements ContinuousInstructionExecution, InstructionExecution.Stateless { + @Override + public void tick(MinionRuntime minion) { + minion.getMinion().move(MovementType.SELF, minion.getMinion().getHorizontalFacing().getDoubleVector().normalize().multiply(minion.getMinion().getMovementSpeed())); + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnDirection.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnDirection.java new file mode 100644 index 0000000..0b7abee --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnDirection.java @@ -0,0 +1,47 @@ +package io.github.skippyall.minions.program.instruction.execution.move; + +import com.mojang.serialization.Codec; +import io.github.skippyall.minions.gui.Displayable; +import io.github.skippyall.minions.gui.GuiDisplay; +import net.minecraft.util.StringIdentifiable; + +import java.util.UUID; + +public enum TurnDirection implements StringIdentifiable, Displayable { + LEFT("left", -1, 0), + UP("up", 0, -1), + RIGHT("right", 1, 0), + DOWN("down", 0, 1); + + public static final Codec CODEC = StringIdentifiable.createCodec(TurnDirection::values); + + private static final UUID MHF_ArrowLeft = UUID.fromString("a68f0b64-8d14-4000-a95f-4b9ba14f8df9"); + private static final UUID MHF_ArrowUp = UUID.fromString("fef039ef-e6cd-4987-9c84-26a3e6134277"); + private static final UUID MHF_ArrowRight = UUID.fromString("50c8510b-5ea0-4d60-be9a-7d542d6cd156"); + private static final UUID MHF_ArrowDown = UUID.fromString("68f59b9b-5b0b-4b05-a9f2-e1d1405aa348"); + + public final String name; + public final int xFactor; + public final int yFactor; + + TurnDirection(String name, int xFactor, int yFactor) { + this.xFactor = xFactor; + this.yFactor = yFactor; + this.name = name; + } + + @Override + public String asString() { + return name; + } + + @Override + public GuiDisplay getDisplay() { + return switch (this) { + case LEFT -> new GuiDisplay.HeadBased(MHF_ArrowLeft); + case UP -> new GuiDisplay.HeadBased(MHF_ArrowUp); + case RIGHT -> new GuiDisplay.HeadBased(MHF_ArrowRight); + case DOWN -> new GuiDisplay.HeadBased(MHF_ArrowDown); + }; + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnExecution.java new file mode 100644 index 0000000..59085b8 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnExecution.java @@ -0,0 +1,23 @@ +package io.github.skippyall.minions.program.instruction.execution.move; + +import io.github.skippyall.minions.minion.MinionRuntime; +import io.github.skippyall.minions.program.supplier.ValueSupplierList; +import io.github.skippyall.minions.program.supplier.Parameter; +import io.github.skippyall.minions.program.value.ValueTypes; + +public class TurnExecution extends AbstractTurnExecution { + public static final Parameter ANGLE = new Parameter<>("maxAngle", ValueTypes.DOUBLE); + public static final Parameter DIRECTION = new Parameter<>("direction", ValueTypes.TURN_DIRECTION); + + @Override + public void readArguments(ValueSupplierList arguments, MinionRuntime minion) { + float maxAngle = arguments.getValue(ANGLE, minion).floatValue(); + TurnDirection direction = arguments.getValue(DIRECTION, minion); + + float turnYaw = maxAngle * direction.xFactor; + float turnPitch = maxAngle * direction.yFactor; + + targetYaw = minion.getMinion().getYaw() + turnYaw; + targetPitch = minion.getMinion().getPitch() + turnPitch; + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnVectorExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnVectorExecution.java new file mode 100644 index 0000000..7d4e1a4 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/TurnVectorExecution.java @@ -0,0 +1,36 @@ +package io.github.skippyall.minions.program.instruction.execution.move; + +import io.github.skippyall.minions.minion.MinionRuntime; +import io.github.skippyall.minions.program.supplier.Parameter; +import io.github.skippyall.minions.program.supplier.ValueSupplierList; +import io.github.skippyall.minions.program.value.ValueTypes; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec2f; +import net.minecraft.util.math.Vec3d; + +public class TurnVectorExecution extends AbstractTurnExecution { + public static final Parameter X = new Parameter<>("x", ValueTypes.DOUBLE); + public static final Parameter Y = new Parameter<>("y", ValueTypes.DOUBLE); + public static final Parameter Z = new Parameter<>("z", ValueTypes.DOUBLE); + + @Override + public void readArguments(ValueSupplierList arguments, MinionRuntime runtime) { + double x = arguments.getValue(X, runtime); + double y = arguments.getValue(Y, runtime); + double z = arguments.getValue(Z, runtime); + + Vec3d vector = new Vec3d(x, y, z); + Vec2f rotation = vectorToRotation(vector); + targetYaw = rotation.x; + targetPitch = rotation.y; + } + + //copied from Entity#lookAt (why no helper, Mojang?) + public static Vec2f vectorToRotation(Vec3d vector) { + double g = Math.sqrt(vector.x * vector.x + vector.z * vector.z); + float pitch = MathHelper.wrapDegrees((float)(-(MathHelper.atan2(vector.y, g) * 180.0F / (float)Math.PI))); + float yaw = MathHelper.wrapDegrees((float)(MathHelper.atan2(vector.z, vector.x) * 180.0F / (float)Math.PI) - 90.0F); + + return new Vec2f(yaw, pitch); + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/instruction/execution/WalkExecution.java b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/WalkExecution.java similarity index 65% rename from src/main/java/io/github/skippyall/minions/program/instruction/execution/WalkExecution.java rename to src/main/java/io/github/skippyall/minions/program/instruction/execution/move/WalkExecution.java index 9be8b0f..6a05b80 100644 --- a/src/main/java/io/github/skippyall/minions/program/instruction/execution/WalkExecution.java +++ b/src/main/java/io/github/skippyall/minions/program/instruction/execution/move/WalkExecution.java @@ -1,7 +1,6 @@ -package io.github.skippyall.minions.program.instruction.execution; +package io.github.skippyall.minions.program.instruction.execution.move; import io.github.skippyall.minions.minion.MinionRuntime; -import io.github.skippyall.minions.program.consumer.ValueConsumerList; import io.github.skippyall.minions.program.instruction.InstructionExecution; import io.github.skippyall.minions.program.supplier.Parameter; import io.github.skippyall.minions.program.supplier.ValueSupplierList; @@ -11,15 +10,15 @@ import net.minecraft.storage.ReadView; import net.minecraft.storage.WriteView; public class WalkExecution implements InstructionExecution { - public static final Parameter blocksToMoveParam = new Parameter<>("blocksToMove", ValueTypes.FLOAT); + public static final Parameter blocksToMoveParam = new Parameter<>("blocksToMove", ValueTypes.DOUBLE); private static final float ACCURACY = 1F / 32F; - private float totalBlocksToMove; - private float blocksMoved; + private double totalBlocksToMove; + private double blocksMoved; @Override public void tick(MinionRuntime minion) { - float speed = Math.min(minion.getMinion().getMovementSpeed(), totalBlocksToMove - blocksMoved); + double speed = Math.min(minion.getMinion().getMovementSpeed(), totalBlocksToMove - blocksMoved); minion.getMinion().move(MovementType.SELF, minion.getMinion().getHorizontalFacing().getDoubleVector().normalize().multiply(speed)); blocksMoved += speed; } @@ -29,26 +28,21 @@ public class WalkExecution implements InstructionExecution { return totalBlocksToMove - blocksMoved < ACCURACY; } - @Override - public void stop(MinionRuntime minion, ValueConsumerList valueConsumers) { - - } - @Override public void readArguments(ValueSupplierList parameters, MinionRuntime minion) { - totalBlocksToMove = parameters.getValue(blocksToMoveParam, minion); + totalBlocksToMove = parameters.getValue(blocksToMoveParam, minion).floatValue(); blocksMoved = 0; } @Override public void save(WriteView view, MinionRuntime minion) { - view.putFloat("totalBlocksToMove", totalBlocksToMove); - view.putFloat("blocksMoved", blocksMoved); + view.putDouble("totalBlocksToMove", totalBlocksToMove); + view.putDouble("blocksMoved", blocksMoved); } @Override public void load(ReadView view, MinionRuntime minion) { - totalBlocksToMove = view.getFloat("totalBlocksToMove", 0F); - blocksMoved = view.getFloat("blocksMoved", 0F); + totalBlocksToMove = view.getDouble("totalBlocksToMove", 0F); + blocksMoved = view.getDouble("blocksMoved", 0F); } } diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/ValueArgument.java b/src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplier.java similarity index 56% rename from src/main/java/io/github/skippyall/minions/program/supplier/ValueArgument.java rename to src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplier.java index e168ede..9940a84 100644 --- a/src/main/java/io/github/skippyall/minions/program/supplier/ValueArgument.java +++ b/src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplier.java @@ -1,18 +1,17 @@ package io.github.skippyall.minions.program.supplier; -import io.github.skippyall.minions.gui.GuiDisplay; import io.github.skippyall.minions.program.InstructionRuntime; import io.github.skippyall.minions.program.value.ValueType; /** - * An supplier that always resolves to a fixed value + * A supplier that always resolves to a fixed value */ -public class ValueArgument> implements ValueSupplier { - private final ValueArgumentType type; +public class FixedValueSupplier> implements ValueSupplier { + private final FixedValueSupplierType type; private final ValueType valueType; private final T value; - public ValueArgument(ValueArgumentType type, ValueType valueType, T value) { + public FixedValueSupplier(FixedValueSupplierType type, ValueType valueType, T value) { this.type = type; this.valueType = valueType; this.value = value; @@ -27,18 +26,13 @@ public class ValueArgument> implements ValueS return value; } - @Override - public GuiDisplay getDisplay() { - return getValueType().display(); - } - @Override public ValueType getValueType() { return valueType; } @Override - public ValueArgumentType getType() { + public FixedValueSupplierType getType() { return type; } } diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplierType.java b/src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplierType.java new file mode 100644 index 0000000..8908367 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/supplier/FixedValueSupplierType.java @@ -0,0 +1,24 @@ +package io.github.skippyall.minions.program.supplier; + +import com.mojang.serialization.Codec; +import io.github.skippyall.minions.program.InstructionRuntime; +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 class FixedValueSupplierType> extends ValueSupplierType { + @Override + public Codec> getCodec(ValueType valueType) { + return valueType.codec().xmap(value -> new FixedValueSupplier<>(this, valueType, value), FixedValueSupplier::getValue); + } + + @Override + public CompletableFuture> openConfiguration(ServerPlayerEntity player, ValueType valueType, @Nullable ValueSupplier previousValueSupplier) { + return valueType.openValueDialog( + player, + previousValueSupplier instanceof FixedValueSupplier val ? val.getValue() : valueType.defaultValue() + ).thenApply(value -> new FixedValueSupplier<>(this, valueType, value)); + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/ValueArgumentType.java b/src/main/java/io/github/skippyall/minions/program/supplier/ValueArgumentType.java deleted file mode 100644 index 148a1b9..0000000 --- a/src/main/java/io/github/skippyall/minions/program/supplier/ValueArgumentType.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.skippyall.minions.program.supplier; - -import com.mojang.serialization.Codec; -import io.github.skippyall.minions.program.InstructionRuntime; -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 class ValueArgumentType> extends ValueSupplierType { - @Override - public Codec> getCodec(ValueType valueType) { - return valueType.codec().xmap(value -> new ValueArgument<>(this, valueType, value), ValueArgument::getValue); - } - - @Override - public CompletableFuture> openConfiguration(ServerPlayerEntity player, ValueType valueType, @Nullable ValueSupplier previousValueSupplier) { - return valueType.openValueDialog( - player, - previousValueSupplier instanceof ValueArgument val ? val.getValue() : valueType.defaultValue() - ).thenApply(value -> new ValueArgument<>(this, valueType, value)); - } -} diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplier.java b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplier.java index 32b3e24..ad6eb5b 100644 --- a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplier.java +++ b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSupplier.java @@ -2,7 +2,6 @@ package io.github.skippyall.minions.program.supplier; import com.mojang.serialization.Codec; import io.github.skippyall.minions.MinionRegistries; -import io.github.skippyall.minions.gui.GuiDisplay; import io.github.skippyall.minions.program.InstructionRuntime; import io.github.skippyall.minions.program.value.ValueType; import org.jetbrains.annotations.Nullable; @@ -16,8 +15,6 @@ import org.jetbrains.annotations.Nullable; public interface ValueSupplier> { T resolve(R minion); - GuiDisplay getDisplay(); - ValueType getValueType(); ValueSupplierType getType(); 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 8d778f0..0a9844d 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 @@ -3,12 +3,16 @@ package io.github.skippyall.minions.program.supplier; import com.mojang.serialization.Codec; import io.github.skippyall.minions.program.InstructionRuntime; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Consumer; public class ValueSupplierList> { private final Map> arguments; + private final List>> changeListeners = new ArrayList<>(); public ValueSupplierList() { arguments = new HashMap<>(); @@ -30,10 +34,11 @@ public class ValueSupplierList> { public void setArgument(Parameter parameter, ValueSupplier valueSupplier) { arguments.put(parameter.name(), valueSupplier); + onChange(parameter); } public boolean hasArgumentFor(Parameter parameter) { - return getArgument(parameter) != null; + return arguments.containsKey(parameter.name()); } public boolean hasArgumentForAll(Collection> checkParameters) { @@ -45,6 +50,21 @@ public class ValueSupplierList> { return true; } + + private void onChange(Parameter parameter) { + for (Consumer> listener : changeListeners) { + listener.accept(parameter); + } + } + + public void addListener(Consumer> listener) { + changeListeners.add(listener); + } + + public void removeListener(Consumer> listener) { + changeListeners.remove(listener); + } + public static > Codec> getCodec(Codec> argumentCodec) { return Codec.unboundedMap(Codec.STRING, argumentCodec) .xmap(ValueSupplierList::new, list -> list.arguments); diff --git a/src/main/java/io/github/skippyall/minions/program/supplier/ValueSuppliers.java b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSuppliers.java new file mode 100644 index 0000000..cdb7883 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/supplier/ValueSuppliers.java @@ -0,0 +1,17 @@ +package io.github.skippyall.minions.program.supplier; + +import io.github.skippyall.minions.MinionRegistries; +import io.github.skippyall.minions.Minions; +import io.github.skippyall.minions.minion.MinionRuntime; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +public class ValueSuppliers { + public static final FixedValueSupplierType FIXED_VALUE_SUPPLIER_TYPE = register("fixed", new FixedValueSupplierType<>()); + + public static > T register(String id, T type) { + return Registry.register(MinionRegistries.VALUE_SUPPLIER_TYPES, Identifier.of(Minions.MOD_ID, id), type); + } + + public static void register() {} +} diff --git a/src/main/java/io/github/skippyall/minions/program/value/RestrictionRule.java b/src/main/java/io/github/skippyall/minions/program/value/RestrictionRule.java new file mode 100644 index 0000000..79eda44 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/value/RestrictionRule.java @@ -0,0 +1,12 @@ +package io.github.skippyall.minions.program.value; + +import io.github.skippyall.minions.gui.input.Result; +import net.minecraft.text.Text; + +public interface RestrictionRule { + Result validate(T value); + + default boolean matches(T value) { + return validate(value).isSuccess(); + } +} diff --git a/src/main/java/io/github/skippyall/minions/program/value/TypeRestriction.java b/src/main/java/io/github/skippyall/minions/program/value/TypeRestriction.java new file mode 100644 index 0000000..4e747a4 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/program/value/TypeRestriction.java @@ -0,0 +1,6 @@ +package io.github.skippyall.minions.program.value; + +import java.util.Collection; + +public record TypeRestriction(ValueType type, Collection> rules) { +} diff --git a/src/main/java/io/github/skippyall/minions/program/value/ValueType.java b/src/main/java/io/github/skippyall/minions/program/value/ValueType.java index c7c7e39..d8e6cc8 100644 --- a/src/main/java/io/github/skippyall/minions/program/value/ValueType.java +++ b/src/main/java/io/github/skippyall/minions/program/value/ValueType.java @@ -1,14 +1,12 @@ package io.github.skippyall.minions.program.value; import com.mojang.serialization.Codec; -import io.github.skippyall.minions.gui.Displayable; -import io.github.skippyall.minions.gui.GuiDisplay; import net.minecraft.server.network.ServerPlayerEntity; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; -public record ValueType(Codec codec, GuiDisplay display, T defaultValue, BiFunction> valueDialogOpener) { +public record ValueType(Codec codec, T defaultValue, BiFunction> valueDialogOpener) { public CompletableFuture openValueDialog(ServerPlayerEntity player, T previousValue) { return valueDialogOpener.apply(player, previousValue); } 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 ffe175b..2c04d1d 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,11 +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.GuiDisplay; 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 io.github.skippyall.minions.program.instruction.execution.move.TurnDirection; import net.minecraft.registry.Registry; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; @@ -15,27 +13,24 @@ 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 INTEGER = registerSimple( - "integer", - Codec.INT, - key -> new GuiDisplay.ModelBased(Items.NETHERITE_SCRAP, key, true), - 0, - (player, oldValue) -> TextInput.inputInt( + public static ValueType LONG = registerSimple( + "long", + Codec.LONG, + 0L, + (player, oldValue) -> TextInput.inputLong( player, Text.literal("Integer"), String.valueOf(oldValue) ) ); - public static ValueType FLOAT = registerSimple( - "float", - Codec.FLOAT, - key -> new GuiDisplay.ModelBased(Items.BAMBOO_RAFT, key, true), - 0F, - (player, oldValue) -> TextInput.inputFloat( + public static ValueType DOUBLE = registerSimple( + "double", + Codec.DOUBLE, + 0D, + (player, oldValue) -> TextInput.inputDouble( player, Text.literal("Number"), String.valueOf(oldValue) @@ -45,7 +40,6 @@ public class ValueTypes { public static ValueType STRING = registerSimple( "string", Codec.STRING, - key -> new GuiDisplay.ModelBased(Items.STRING, key, true), "", ((player, oldValue) -> TextInput.inputString( player, @@ -54,30 +48,20 @@ public class ValueTypes { ) ); - public static ValueType TURN_DIRECTION = registerSimple( + public static ValueType TURN_DIRECTION = registerSimple( "turn_direction", - TurnExecution.TurnDirection.CODEC, - base -> new GuiDisplay.ModelBased(Items.STRUCTURE_VOID, base, true), - TurnExecution.TurnDirection.RIGHT, - ChoiceInput.createDialogOpener(TurnExecution.TurnDirection.values()) + TurnDirection.CODEC, + TurnDirection.RIGHT, + ChoiceInput.createDialogOpener(TurnDirection.values()) ); - public static ValueType VOID = registerSimple( - "void", - Codec.unit(null), - key -> new GuiDisplay.ModelBased(Items.BARRIER, key, false), - null, - (player, oldValue) -> CompletableFuture.completedFuture(null) - ); - - private static ValueType registerSimple(String id, Codec codec, Function displayFunction, T defaultValue, BiFunction> valueDialogOpener) { + private static ValueType registerSimple(String id, Codec codec, T defaultValue, BiFunction> valueDialogOpener) { Identifier identifier = Identifier.of(Minions.MOD_ID, id); return Registry.register( MinionRegistries.VALUE_TYPES, identifier, new ValueType<>( codec, - displayFunction.apply(identifier.toTranslationKey("value_type")), defaultValue, valueDialogOpener ) diff --git a/src/main/java/io/github/skippyall/minions/reference/InstructionReference.java b/src/main/java/io/github/skippyall/minions/reference/InstructionReference.java new file mode 100644 index 0000000..47cdc1a --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/reference/InstructionReference.java @@ -0,0 +1,32 @@ +package io.github.skippyall.minions.reference; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.component.ComponentsAccess; +import net.minecraft.item.Item; +import net.minecraft.item.tooltip.TooltipType; +import net.minecraft.text.Text; +import net.minecraft.util.Uuids; + +import java.util.UUID; +import java.util.function.Consumer; + +public record InstructionReference(UUID selectedMinion, String selectedInstruction, String visualMinionName) implements Reference { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + Uuids.CODEC.fieldOf("selectedMinion").forGetter(InstructionReference::selectedMinion), + Codec.STRING.fieldOf("selectedInstruction").forGetter(InstructionReference::selectedInstruction), + Codec.STRING.fieldOf("visualMinionName").forGetter(InstructionReference::visualMinionName) + ).apply(instance, InstructionReference::new)); + + @Override + public MapCodec getCodec() { + return CODEC; + } + + @Override + public void appendTooltip(Item.TooltipContext context, Consumer textConsumer, TooltipType type, ComponentsAccess components) { + textConsumer.accept(Text.translatable("minions.reference.instruction.tooltip", selectedInstruction, visualMinionName)); + } +} diff --git a/src/main/java/io/github/skippyall/minions/reference/Reference.java b/src/main/java/io/github/skippyall/minions/reference/Reference.java new file mode 100644 index 0000000..0b01d58 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/reference/Reference.java @@ -0,0 +1,31 @@ +package io.github.skippyall.minions.reference; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import eu.pb4.polymer.core.api.other.PolymerComponent; +import io.github.skippyall.minions.MinionRegistries; +import io.github.skippyall.minions.Minions; +import net.fabricmc.fabric.api.item.v1.ComponentTooltipAppenderRegistry; +import net.minecraft.component.ComponentType; +import net.minecraft.item.tooltip.TooltipAppender; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +import java.util.function.Function; + +public interface Reference extends TooltipAppender { + Codec CODEC = MinionRegistries.REFERENCE_CODEC.getCodec().dispatch(Reference::getCodec, Function.identity()); + ComponentType COMPONENT_TYPE = ComponentType.builder().codec(CODEC).build(); + + MapCodec getCodec(); + + static void register() { + Registry.register(MinionRegistries.REFERENCE_CODEC, Identifier.of(Minions.MOD_ID, "instruction"), InstructionReference.CODEC); + + Registry.register(Registries.DATA_COMPONENT_TYPE, Identifier.of(Minions.MOD_ID, "reference"), COMPONENT_TYPE); + PolymerComponent.registerDataComponent(COMPONENT_TYPE); + + ComponentTooltipAppenderRegistry.addFirst(COMPONENT_TYPE); + } +} diff --git a/src/main/java/io/github/skippyall/minions/reference/ReferenceItem.java b/src/main/java/io/github/skippyall/minions/reference/ReferenceItem.java new file mode 100644 index 0000000..bbf74c7 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/reference/ReferenceItem.java @@ -0,0 +1,35 @@ +package io.github.skippyall.minions.reference; + +import eu.pb4.polymer.core.api.item.PolymerItem; +import io.github.skippyall.minions.MinionItems; +import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.tooltip.TooltipType; +import xyz.nucleoid.packettweaker.PacketContext; + +public class ReferenceItem extends Item implements PolymerItem { + public ReferenceItem(Settings settings) { + super(settings); + } + + @Override + public Item getPolymerItem(ItemStack itemStack, PacketContext context) { + return Items.PAPER; + } + + @Override + public ItemStack getPolymerItemStack(ItemStack itemStack, TooltipType tooltipType, PacketContext context) { + ItemStack stack = PolymerItem.super.getPolymerItemStack(itemStack, tooltipType, context); + stack.set(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); + return stack; + } + + public static ItemStack createInstructionReference(MinionFakePlayer minion, String instructionName) { + ItemStack stack = new ItemStack(MinionItems.REFERENCE_ITEM); + stack.set(Reference.COMPONENT_TYPE, new InstructionReference(minion.getUuid(), instructionName, minion.getGameProfile().getName())); + return stack; + } +} diff --git a/src/main/java/io/github/skippyall/minions/util/ListenerManager.java b/src/main/java/io/github/skippyall/minions/util/ListenerManager.java new file mode 100644 index 0000000..588a14f --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/util/ListenerManager.java @@ -0,0 +1,23 @@ +package io.github.skippyall.minions.util; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; + +public class ListenerManager { + protected final List listeners = new CopyOnWriteArrayList<>(); + + public void forEachListener(Consumer listenerConsumer) { + for(T listener : listeners) { + listenerConsumer.accept(listener); + } + } + + public void addListener(T listener) { + listeners.add(listener); + } + + public void removeListener(T listener) { + listeners.remove(listener); + } +} diff --git a/src/main/java/io/github/skippyall/minions/util/PolymerUtil.java b/src/main/java/io/github/skippyall/minions/util/PolymerUtil.java new file mode 100644 index 0000000..8818db2 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/util/PolymerUtil.java @@ -0,0 +1,39 @@ +package io.github.skippyall.minions.util; + +import eu.pb4.polymer.networking.api.PolymerNetworking; +import eu.pb4.polymer.networking.api.server.PolymerServerNetworking; +import io.github.skippyall.minions.Minions; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.packet.CustomPayload; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.util.Identifier; +import xyz.nucleoid.packettweaker.PacketContext; + +public class PolymerUtil { + public static final int NETWORK_VERSION = 1; + + public static boolean isOnClient(PacketContext context) { + if(context.getPlayer() != null) { + return isOnClient(context.getPlayer().networkHandler); + } + return false; + } + + public static boolean isOnClient(ServerPlayNetworkHandler player) { + return PolymerServerNetworking.getSupportedVersion(player, VersionSyncPayload.PACKET_ID.id()) == NETWORK_VERSION; + } + + public static class VersionSyncPayload implements CustomPayload { + + public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Identifier.of(Minions.MOD_ID, "version_sync")); + + @Override + public Id getId() { + return null; + } + } + + public static void register() { + PolymerNetworking.registerS2CVersioned(VersionSyncPayload.PACKET_ID, NETWORK_VERSION, PacketCodec.unit(null)); + } +} diff --git a/src/main/java/io/github/skippyall/minions/util/SerializableListenerManager.java b/src/main/java/io/github/skippyall/minions/util/SerializableListenerManager.java new file mode 100644 index 0000000..a7232a2 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/util/SerializableListenerManager.java @@ -0,0 +1,54 @@ +package io.github.skippyall.minions.util; + +import com.mojang.serialization.Codec; +import net.minecraft.registry.Registry; +import net.minecraft.storage.ReadView; +import net.minecraft.storage.WriteView; +import net.minecraft.util.Identifier; + +import java.util.Optional; + +public class SerializableListenerManager extends ListenerManager { + private final Registry> registry; + + public SerializableListenerManager(Registry> registry) { + this.registry = registry; + } + + public void save(WriteView view) { + WriteView.ListView listView = view.getList("listeners"); + for (T listener : listeners) { + if(listener.getCodecId().isPresent()) { + WriteView listenerView = listView.add(); + Codec codec = registry.get(listener.getCodecId().get()); + listenerView.put("id", Identifier.CODEC, listener.getCodecId().get()); + //noinspection unchecked + listenerView.put("data", (Codec) codec, listener); + } + } + } + + public void load(ReadView view) { + ReadView.ListReadView listView = view.getListReadView("listeners"); + for (ReadView listenerView : listView) { + Optional id = listenerView.read("id", Identifier.CODEC); + if(id.isEmpty()) { + continue; + } + + Codec codec = registry.get(id.get()); + + Optional listener = listenerView.read("data", codec); + + if(listener.isPresent()) { + listeners.add(listener.get()); + } + } + } + + public interface SerializableListener { + default Optional getCodecId() { + return Optional.empty(); + } + } +} diff --git a/src/main/resources/assets/minions/blockstates/minion_trigger.json b/src/main/resources/assets/minions/blockstates/minion_trigger.json new file mode 100644 index 0000000..14edd5e --- /dev/null +++ b/src/main/resources/assets/minions/blockstates/minion_trigger.json @@ -0,0 +1,10 @@ +{ + "variants": { + "powered=false": { + "model": "minions:block/minion_trigger_inactive" + }, + "powered=true": { + "model": "minions:block/minion_trigger_active" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/items/minion_trigger.json b/src/main/resources/assets/minions/items/minion_trigger.json new file mode 100644 index 0000000..716deef --- /dev/null +++ b/src/main/resources/assets/minions/items/minion_trigger.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "minions:block/minion_trigger_active" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/items/minion_trigger_no_plate_active.json b/src/main/resources/assets/minions/items/minion_trigger_no_plate_active.json new file mode 100644 index 0000000..df3dc67 --- /dev/null +++ b/src/main/resources/assets/minions/items/minion_trigger_no_plate_active.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "minions:block/minion_trigger_no_plate_active" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/items/minion_trigger_no_plate_inactive.json b/src/main/resources/assets/minions/items/minion_trigger_no_plate_inactive.json new file mode 100644 index 0000000..6737333 --- /dev/null +++ b/src/main/resources/assets/minions/items/minion_trigger_no_plate_inactive.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "minions:block/minion_trigger_no_plate_inactive" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/models/block/minion_trigger.json b/src/main/resources/assets/minions/models/block/minion_trigger.json new file mode 100644 index 0000000..cf23afc --- /dev/null +++ b/src/main/resources/assets/minions/models/block/minion_trigger.json @@ -0,0 +1,218 @@ +{ + "parent": "block/block", + "format_version": "1.21.6", + "credit": "Made with Blockbench", + "ambientocclusion": false, + "textures": { + "2": "minions:block/wave", + "smooth_stone": "block/smooth_stone", + "particle": "#torch" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 2, 16], + "faces": { + "north": {"texture": "#smooth_stone"}, + "east": {"texture": "#smooth_stone"}, + "south": {"texture": "#smooth_stone"}, + "west": {"texture": "#smooth_stone"}, + "up": {"texture": "#smooth_stone"}, + "down": {"texture": "#smooth_stone"} + } + }, + { + "from": [5, 10, 11], + "to": [11, 11, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 10, 1]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [5, 10, 4], + "to": [11, 11, 5], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 21, 4]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [4, 10, 5], + "to": [5, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [5, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [11, 10, 5], + "to": [12, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [5, 10, 14], + "to": [11, 11, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 10, 4]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [5, 10, 1], + "to": [11, 11, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 21, 1]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [1, 10, 5], + "to": [2, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [2, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [14, 10, 5], + "to": [15, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [7, 2, 7], + "to": [9, 12, 9], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "north": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "east": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "south": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "west": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "up": {"uv": [7, 6, 9, 8], "texture": "#torch"}, + "down": {"uv": [7, 13, 9, 15], "texture": "#torch", "cullface": "down"} + } + }, + { + "from": [6.5, 9.5, 6.5], + "to": [9.5, 9.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "up": {"uv": [8, 5, 9, 6], "texture": "#torch"} + } + }, + { + "from": [6.5, 12.5, 6.5], + "to": [9.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "down": {"uv": [7, 5, 8, 6], "texture": "#torch"} + } + }, + { + "from": [6.5, 9.5, 6.5], + "to": [9.5, 12.5, 6.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "south": {"uv": [9, 6, 10, 7], "texture": "#torch"} + } + }, + { + "from": [9.5, 9.5, 6.5], + "to": [9.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "west": {"uv": [6, 7, 7, 8], "texture": "#torch"} + } + }, + { + "from": [6.5, 9.5, 9.5], + "to": [9.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "north": {"uv": [6, 6, 7, 7], "texture": "#torch"} + } + }, + { + "from": [6.5, 9.5, 6.5], + "to": [6.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "east": {"uv": [9, 7, 10, 8], "texture": "#torch"} + } + } + ], + "groups": [ + 0, + { + "name": "ring", + "origin": [12, 21, 0], + "color": 0, + "children": [1, 2, 3, 4] + }, + { + "name": "ring", + "origin": [12, 21, 0], + "color": 0, + "children": [5, 6, 7, 8] + }, + { + "name": "template_redstone_torch", + "origin": [8, 8, 8], + "color": 0, + "children": [9, 10, 11, 12, 13, 14, 15] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/models/block/minion_trigger_active.json b/src/main/resources/assets/minions/models/block/minion_trigger_active.json new file mode 100644 index 0000000..997a828 --- /dev/null +++ b/src/main/resources/assets/minions/models/block/minion_trigger_active.json @@ -0,0 +1,6 @@ +{ + "parent": "minions:block/minion_trigger", + "textures": { + "torch": "block/redstone_torch" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/models/block/minion_trigger_inactive.json b/src/main/resources/assets/minions/models/block/minion_trigger_inactive.json new file mode 100644 index 0000000..9eb127e --- /dev/null +++ b/src/main/resources/assets/minions/models/block/minion_trigger_inactive.json @@ -0,0 +1,6 @@ +{ + "parent": "minions:block/minion_trigger", + "textures": { + "torch": "block/redstone_torch_off" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/models/block/minion_trigger_no_plate.json b/src/main/resources/assets/minions/models/block/minion_trigger_no_plate.json new file mode 100644 index 0000000..e9cc12c --- /dev/null +++ b/src/main/resources/assets/minions/models/block/minion_trigger_no_plate.json @@ -0,0 +1,205 @@ +{ + "parent": "block/block", + "format_version": "1.21.6", + "credit": "Made with Blockbench", + "ambientocclusion": false, + "textures": { + "2": "minions:block/wave", + "particle": "#torch" + }, + "elements": [ + { + "from": [5, 10, 11], + "to": [11, 11, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 10, 1]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [5, 10, 4], + "to": [11, 11, 5], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 21, 4]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [4, 10, 5], + "to": [5, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [5, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [11, 10, 5], + "to": [12, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [5, 10, 14], + "to": [11, 11, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 10, 4]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [5, 10, 1], + "to": [11, 11, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 21, 1]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#2"} + } + }, + { + "from": [1, 10, 5], + "to": [2, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [2, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [14, 10, 5], + "to": [15, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 21, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "rotation": 90, "texture": "#2"}, + "east": {"uv": [0, 0, 2, 2], "rotation": 180, "texture": "#2"}, + "south": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"}, + "down": {"uv": [0, 0, 2, 2], "rotation": 270, "texture": "#2"} + } + }, + { + "from": [7, 2, 7], + "to": [9, 12, 9], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "north": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "east": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "south": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "west": {"uv": [7, 6, 9, 16], "texture": "#torch"}, + "up": {"uv": [7, 6, 9, 8], "texture": "#torch"}, + "down": {"uv": [7, 13, 9, 15], "texture": "#torch", "cullface": "down"} + } + }, + { + "from": [6.5, 9.5, 6.5], + "to": [9.5, 9.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "up": {"uv": [8, 5, 9, 6], "texture": "#torch"} + } + }, + { + "from": [6.5, 12.5, 6.5], + "to": [9.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "down": {"uv": [7, 5, 8, 6], "texture": "#torch"} + } + }, + { + "from": [6.5, 9.5, 6.5], + "to": [9.5, 12.5, 6.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "south": {"uv": [9, 6, 10, 7], "texture": "#torch"} + } + }, + { + "from": [9.5, 9.5, 6.5], + "to": [9.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "west": {"uv": [6, 7, 7, 8], "texture": "#torch"} + } + }, + { + "from": [6.5, 9.5, 9.5], + "to": [9.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "north": {"uv": [6, 6, 7, 7], "texture": "#torch"} + } + }, + { + "from": [6.5, 9.5, 6.5], + "to": [6.5, 12.5, 9.5], + "shade": false, + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "east": {"uv": [9, 7, 10, 8], "texture": "#torch"} + } + } + ], + "groups": [ + 0, + { + "name": "ring", + "origin": [12, 21, 0], + "color": 0, + "children": [1, 2, 3, 4] + }, + { + "name": "ring", + "origin": [12, 21, 0], + "color": 0, + "children": [5, 6, 7, 8] + }, + { + "name": "template_redstone_torch", + "origin": [8, 8, 8], + "color": 0, + "children": [9, 10, 11, 12, 13, 14, 15] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/models/block/minion_trigger_no_plate_active.json b/src/main/resources/assets/minions/models/block/minion_trigger_no_plate_active.json new file mode 100644 index 0000000..a080947 --- /dev/null +++ b/src/main/resources/assets/minions/models/block/minion_trigger_no_plate_active.json @@ -0,0 +1,6 @@ +{ + "parent": "minions:block/minion_trigger_no_plate", + "textures": { + "torch": "block/redstone_torch" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/models/block/minion_trigger_no_plate_inactive.json b/src/main/resources/assets/minions/models/block/minion_trigger_no_plate_inactive.json new file mode 100644 index 0000000..10a70af --- /dev/null +++ b/src/main/resources/assets/minions/models/block/minion_trigger_no_plate_inactive.json @@ -0,0 +1,6 @@ +{ + "parent": "minions:block/minion_trigger_no_plate", + "textures": { + "torch": "block/redstone_torch_off" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minions/textures/block/wave.png b/src/main/resources/assets/minions/textures/block/wave.png new file mode 100644 index 0000000000000000000000000000000000000000..80bf65a11e33bef9ce88f08be79af90975586a28 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3Z5>GAr}70f6gBWIPbuiAY1mc9>yCdz+&b14F}A@sIXtg3Lg*44$rjF6*2UngDbk7O(&S literal 0 HcmV?d00001 diff --git a/src/main/resources/data/minions/lang/en_us.json b/src/main/resources/data/minions/lang/en_us.json index 08416e2..91046a1 100644 --- a/src/main/resources/data/minions/lang/en_us.json +++ b/src/main/resources/data/minions/lang/en_us.json @@ -3,10 +3,8 @@ "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.inventory.title": "Inventory", + "minions.gui.modules.title": "Modules", "minions.gui.ok": "OK", "minions.gui.confirm": "Confirm", "minions.gui.abort": "Abort", @@ -19,8 +17,11 @@ "minions.gui.look.skin.base64.title": "Enter a skin in base64 encoding", "minions.gui.look.rename.title": "Enter a name", + "minions.gui.instruction.title": "Instructions", "minions.gui.instruction.list": "Instruction List", "minions.gui.instruction.create": "New Instruction", + "minions.gui.instruction.no_modules": "This minion has no modules", + "minions.gui.instruction.select_instruction": "Select 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", @@ -35,6 +36,7 @@ "minions.gui.instruction.stop": "Stop", "minions.gui.instruction.argument.configure.type": "Type: %s", "minions.gui.instruction.argument.configure.type.unset": "Unset", + "minions.gui.instruction.parameter": "%s: %s", "minions.command.input.int.fail": "Not an integer", "minions.command.input.float.fail": "Not a number", @@ -51,6 +53,24 @@ "minions.command.minion.not_present": "This minion does not exist", + "minions.reference.instruction.tooltip": "Linked to instruction %s in %s", + + "instruction_type.minions.walk": "Walk", + "instruction_type.minions.walk.description": "Walk forward a specified amount of blocks", + "instruction_type.minions.turn": "Turn", + "instruction_type.minions.turn.description": "Turn the head by specific angle", + "instruction_type.minions.attack": "Attack", + "instruction_type.minions.attack.description": "Attack and mine blocks\n Test", + "instruction_type.minions.use": "Use", + "instruction_type.minions.use.description": "Use and place blocks", + + "value_type.minions.integer": "Integer", + "value_type.minions.float": "Decimal", + "value_type.minions.string": "Text", + "value_type.minions.turn_direction": "Turn Direction", + + "value_supplier_type.minions.fixed": "Value", + "item.minions.attack_module": "Attack Module", "item.minions.interact_module": "Interact Module", "item.minions.mob_spawning_module": "Mob Spawning Module", @@ -59,6 +79,7 @@ "item.minions.minion": "Minion", "item.minions.basic_upgrade_base": "Basic Upgrade Base", "item.minions.advanced_upgrade_base": "Advanced Upgrade Base", + "item.minions.reference": "Reference", "minions.minion_item.tooltip": "Name: %s", "minions.generic.name.invalid_char": "Name contains invalid character", diff --git a/src/main/resources/data/minions/loot_table/blocks/minion_trigger.json b/src/main/resources/data/minions/loot_table/blocks/minion_trigger.json new file mode 100644 index 0000000..88b7673 --- /dev/null +++ b/src/main/resources/data/minions/loot_table/blocks/minion_trigger.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "minions:minion_trigger" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/instruction_type/attack.json b/src/main/resources/data/minions/minions/gui_display/instruction_type/attack.json new file mode 100644 index 0000000..0680a52 --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/instruction_type/attack.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:iron_pickaxe" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/instruction_type/turn.json b/src/main/resources/data/minions/minions/gui_display/instruction_type/turn.json new file mode 100644 index 0000000..22d6b5e --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/instruction_type/turn.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:structure_void" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/instruction_type/use.json b/src/main/resources/data/minions/minions/gui_display/instruction_type/use.json new file mode 100644 index 0000000..ae2a2a4 --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/instruction_type/use.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:lever" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/instruction_type/walk.json b/src/main/resources/data/minions/minions/gui_display/instruction_type/walk.json new file mode 100644 index 0000000..2e7fa4a --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/instruction_type/walk.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:iron_boots" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/value_supplier_type/fixed.json b/src/main/resources/data/minions/minions/gui_display/value_supplier_type/fixed.json new file mode 100644 index 0000000..b5abb64 --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/value_supplier_type/fixed.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:redstone_block" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/value_type/double.json b/src/main/resources/data/minions/minions/gui_display/value_type/double.json new file mode 100644 index 0000000..ca1ace8 --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/value_type/double.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:bamboo_raft" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/value_type/long.json b/src/main/resources/data/minions/minions/gui_display/value_type/long.json new file mode 100644 index 0000000..b324d48 --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/value_type/long.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:netherite_scrap" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/value_type/string.json b/src/main/resources/data/minions/minions/gui_display/value_type/string.json new file mode 100644 index 0000000..e1e3e96 --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/value_type/string.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:string" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/minions/gui_display/value_type/turn_direction.json b/src/main/resources/data/minions/minions/gui_display/value_type/turn_direction.json new file mode 100644 index 0000000..22d6b5e --- /dev/null +++ b/src/main/resources/data/minions/minions/gui_display/value_type/turn_direction.json @@ -0,0 +1,4 @@ +{ + "type": "minions:item", + "data": "minecraft:structure_void" +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 02ce27e..6268593 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -12,6 +12,9 @@ "icon": "assets/minions/icon.png", "environment": "*", "entrypoints": { + "client": [ + "io.github.skippyall.minions.client.MinionsClient" + ], "main": [ "io.github.skippyall.minions.Minions" ] diff --git a/src/main/resources/minions.accesswidener b/src/main/resources/minions.accesswidener index 0810614..80f9817 100644 --- a/src/main/resources/minions.accesswidener +++ b/src/main/resources/minions.accesswidener @@ -1,4 +1,5 @@ accessWidener v2 named accessible class net/minecraft/server/world/ChunkLevelManager$DistanceFromNearestPlayerTracker -accessible method net/minecraft/server/world/ChunkLevelManager$DistanceFromNearestPlayerTracker (Lnet/minecraft/server/world/ChunkLevelManager;I)V \ No newline at end of file +accessible method net/minecraft/server/world/ChunkLevelManager$DistanceFromNearestPlayerTracker (Lnet/minecraft/server/world/ChunkLevelManager;I)V +accessible class net/minecraft/screen/slot/ArmorSlot \ No newline at end of file diff --git a/src/main/resources/minions.mixins.json b/src/main/resources/minions.mixins.json index c599b87..6d38d5b 100644 --- a/src/main/resources/minions.mixins.json +++ b/src/main/resources/minions.mixins.json @@ -9,7 +9,7 @@ "EntityAccessor", "EntityMixin", "EntityViewMixin", - "GraveCompatMixin", + "compat.universal_graves.GraveMixin", "MinecraftServerMixin", "MobEntityMixin", "PlayerListEntryS2CPacket$EntryMixin", @@ -28,5 +28,6 @@ "server": [], "injectors": { "defaultRequire": 1 - } + }, + "plugin": "io.github.skippyall.minions.MinionMixinConfigPlugin" } \ No newline at end of file