Listen up!

This commit is contained in:
skippyall
2026-01-05 13:12:40 +01:00
parent c5b3c883ca
commit 3f2a52fd0a
87 changed files with 2191 additions and 492 deletions
+18 -8
View File
@@ -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
}
}
+1 -1
View File
@@ -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
@@ -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) -> {});
}
}
@@ -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<MinionTriggerBlockEntity> MINION_TRIGGER_BE_TYPE =
@@ -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 extends Item> T registerItem(Identifier identifier, Function<Item.Settings, T> constructor, Item.Settings settings) {
T item = constructor.apply(settings.registryKey(RegistryKey.of(RegistryKeys.ITEM, identifier)));
@@ -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<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> 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) {
}
}
@@ -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<ValueType<?>> VALUE_TYPES = registry("value_type");
public static final Registry<ValueSupplierType<MinionRuntime>> ARGUMENT_TYPES = registry("argument_type");
public static final Registry<ValueSupplierType<MinionRuntime>> VALUE_SUPPLIER_TYPES = registry("value_supplier_type");
public static final Registry<ValueConsumerType<MinionRuntime>> VALUE_CONSUMER_TYPES = registry("value_consumer_type");
public static final Registry<InstructionType<MinionRuntime>> INSTRUCTION_TYPES = registry("instruction_type");
public static final Registry<SkinProvider> SKIN_PROVIDERS = registry("skin_providers");
public static final Registry<SkinProvider> SKIN_PROVIDERS = registry("skin_provider");
public static final Registry<Codec<? extends GuiDisplay>> GUI_DISPLAY_TYPE = registry("gui_display_type");
public static final Registry<Codec<? extends ConfiguredInstructionListener>> INSTRUCTION_LISTENER_CODECS = registry("instruction_listener_codec");
public static final Registry<Codec<? extends MinionListener>> MINION_LISTENER_CODECS = registry("minion_listener_codec");
public static final Registry<MapCodec<? extends Reference>> REFERENCE_CODEC = registry("reference_codec");
public static final RegistryKey<Registry<GuiDisplay>> GUI_DISPLAY = key("gui_display");
public static final RegistryKey<Registry<ReferenceEntry>> REFERENCE_ENTRY = key("reference_entry");
private static <T> Registry<T> registry(String id) {
return new SimpleRegistry<>(RegistryKey.ofRegistry(Identifier.of(Minions.MOD_ID, id)), Lifecycle.stable());
return FabricRegistryBuilder.<T>createSimple(key(id)).attribute(RegistryAttribute.OPTIONAL).buildAndRegister();
}
private static <T> RegistryKey<Registry<T>> 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);
}
}
@@ -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);
}
}
@@ -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<PlayerClipboardAttachment> TYPE = AttachmentRegistry.create(Identifier.of(Minions.MOD_ID, "clipboard"));
}
@@ -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<World> worldKey;
protected BlockPos pos;
public BlockEntityMinionListener(RegistryKey<World> worldKey, BlockPos pos) {
this.worldKey = worldKey;
this.pos = pos;
}
public static final Codec<BlockEntityMinionListener> 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<MinionListeningBlockEntity> 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
}
}
@@ -0,0 +1,7 @@
package io.github.skippyall.minions.block;
import io.github.skippyall.minions.minion.MinionListener;
public interface MinionListeningBlockEntity extends MinionListener {
}
@@ -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<MinionTriggerBlock> 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<Block, BlockState> 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<MinionTriggerBlockEntity> 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 <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> 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;
}
}
@@ -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<ConfiguredInstruction<MinionRuntime>> getInstruction(MinionFakePlayer minion) {
return Optional.ofNullable(minion.getInstructionManager().getInstruction(instructionName));
}
@@ -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;
}
}
}
@@ -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<ReferenceEntry> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
TextCodecs.CODEC.fieldOf("shortDescription").forGetter(ReferenceEntry::shortDescription),
TextCodecs.CODEC.fieldOf("longDescription").forGetter(ReferenceEntry::longDescription)
).apply(instance, ReferenceEntry::new));
}
@@ -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<MinionRuntime> configuredInstruction, String name) {
super(type, player, minion, configuredInstruction);
this.name = name;
init();
}
public static void configureInstructionMenu(String name, ConfiguredInstruction<MinionRuntime> 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--;
}
}
}
@@ -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<GuiDisplay> 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 <T> GuiDisplay getGuiDisplayFor(Registry<T> 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 <T> ItemStack getDisplayStack(Registry<T> registry, T element, DynamicRegistryManager manager) {
return getGuiDisplayFor(registry, element, manager).createItemStack();
}
static <T> ItemStack getDisplayStackWithName(Registry<T> 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<? extends GuiDisplay> getCodec();
public ModelBased(Item model, String translationKeyBase) {
this(ModelIdUtil.getItemModelId(model), translationKeyBase, false);
class ModelBased implements GuiDisplay {
public static final Codec<ModelBased> 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<? extends GuiDisplay> getCodec() {
return CODEC;
}
}
record HeadBased(UUID uuid, String translationKeyBase, boolean withLore) implements GuiDisplay {
class ItemBased implements GuiDisplay {
public static final Codec<ItemBased> 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<? extends GuiDisplay> getCodec() {
return CODEC;
}
}
class HeadBased implements GuiDisplay {
public static final Codec<HeadBased> 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<? extends GuiDisplay> getCodec() {
return CODEC;
}
}
class StackBased implements GuiDisplay {
public static final Codec<StackBased> 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<? extends GuiDisplay> 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);
}
}
@@ -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<MinionRuntime> instruction;
public InstructionBoundSimpleGui(ScreenHandlerType<?> type, ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> 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);
}
}
@@ -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<MinionRuntime> 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<MinionRuntime> 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<MinionRuntime> configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType);
configureInstructionMenu(name, configuredInstruction, minion, player);
if (!minion.isRemoved() && !minion.isDisconnected()) {
ConfiguredInstruction<MinionRuntime> configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType);
ConfigureInstructionGui.configureInstructionMenu(name, configuredInstruction, minion, player);
}
})
);
}
public static CompletableFuture<String> 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<MinionRuntime> 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<MinionRuntime> 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 <T, A extends ValueSupplier<T, MinionRuntime>> void configureArgumentMenu(String name, ConfiguredInstruction<MinionRuntime> instruction, Parameter<T> parameter, MinionFakePlayer minion, ServerPlayerEntity player) {
if(!checkInstructionExists(name, instruction, minion, player)) {
public static <T, A extends ValueSupplier<T, MinionRuntime>> void configureArgumentMenu(String instructionName, ConfiguredInstruction<MinionRuntime> instruction, Parameter<T> 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<ValueSupplierType<MinionRuntime>> selectArgumentType(ServerPlayerEntity player) {
public static CompletableFuture<ValueSupplierType<MinionRuntime>> selectArgumentType(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> instruction) {
CompletableFuture<ValueSupplierType<MinionRuntime>> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false);
for(ValueSupplierType<MinionRuntime> 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<MinionRuntime> 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 <T> void configureTypeAndValue(String name, ConfiguredInstruction<MinionRuntime> instruction, Parameter<T> 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<InstructionType<MinionRuntime>> 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<InstructionType<MinionRuntime>> 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<InstructionType<MinionRuntime>> selectInstructionMenu(MinionModule module, MinionFakePlayer minion, ServerPlayerEntity player) {
CompletableFuture<InstructionType<MinionRuntime>> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false);
for(InstructionType<MinionRuntime> 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<MinionRuntime> 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<MinionRuntime> 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<?, MinionRuntime> 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;
}
}
}
@@ -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;
}
}
@@ -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();
}
@@ -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 <T extends Displayable> BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> 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<Void> confirm(ServerPlayerEntity player, Text title) {
@@ -47,12 +47,12 @@ public class TextInput<T> extends AnvilInputGui {
return inputSync(player, title, defaultValue, Result.Success::new);
}
public static CompletableFuture<Integer> 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<Long> 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<Float> 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<Double> 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
@@ -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) {}
}
@@ -46,6 +46,7 @@ public class MinionRuntime implements InstructionRuntime<MinionRuntime> {
ConfiguredInstruction<MinionRuntime> 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<MinionRuntime> {
ConfiguredInstruction<MinionRuntime> instruction = getInstruction(name);
instruction.stop(this);
configuredInstructions.remove(name);
instruction.onInstructionRemove();
minion.forEachMinionListener(listener -> listener.onInstructionsUpdate(minion));
}
public ConfiguredInstruction<MinionRuntime> getInstruction(String name) {
@@ -64,8 +68,16 @@ public class MinionRuntime implements InstructionRuntime<MinionRuntime> {
}
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<MinionRuntime> 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<MinionRuntime> {
@Override
public Registry<ValueSupplierType<MinionRuntime>> getArgumentTypeRegistry() {
return MinionRegistries.ARGUMENT_TYPES;
return MinionRegistries.VALUE_SUPPLIER_TYPES;
}
@Override
@@ -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<ActionType, Action> 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
{
@@ -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<MinionListener> 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<MinionListener> 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);
}
}
@@ -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<Boolean> 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
}
}
@@ -21,7 +21,7 @@ import java.util.concurrent.Executor;
@Mixin(ChunkLevelManager.class)
public class ChunkLevelManagerMixin implements ChunkLevelManagerAccessor {
@Shadow @Final private Long2ObjectMap<ObjectSet<ServerPlayerEntity>> playersByChunkPos;
@Shadow @Final Long2ObjectMap<ObjectSet<ServerPlayerEntity>> playersByChunkPos;
@Shadow @Final private ChunkLevelManager.DistanceFromNearestPlayerTracker distanceFromNearestPlayerTracker;
@Unique
ChunkLevelManager.DistanceFromNearestPlayerTracker minionless;
@@ -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;<init>(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) {
@@ -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<T,R extends InstructionRuntime<R>> {
void consume(T value, R runtime);
GuiDisplay getDisplay();
ValueType<T> getValueType();
ValueConsumerType<R> getType();
@@ -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<R extends InstructionRuntime<R>> {
private final Map<String, ValueConsumer<?, R>> valueConsumers;
private final List<Consumer<Parameter<?>>> changeListeners = new ArrayList<>();
public ValueConsumerList() {
valueConsumers = new HashMap<>();
@@ -25,15 +29,30 @@ public class ValueConsumerList<R extends InstructionRuntime<R>> {
public <T> void setValueConsumer(Parameter<T> parameter, ValueConsumer<?,R> consumer) {
valueConsumers.put(parameter.name(), consumer);
onChange(parameter);
}
public <T> void setValue(Parameter<T> parameter, T value, R runtime) {
public <T> void consumeValue(Parameter<T> parameter, T value, R runtime) {
ValueConsumer<T,R> consumer = getValueConsumer(parameter).cast(parameter.type());
if (consumer != null) {
consumer.consume(value, runtime);
}
}
private void onChange(Parameter<?> parameter) {
for (Consumer<Parameter<?>> listener : changeListeners) {
listener.accept(parameter);
}
}
public void addListener(Consumer<Parameter<?>> listener) {
changeListeners.add(listener);
}
public void removeListener(Consumer<Parameter<?>> listener) {
changeListeners.remove(listener);
}
public static <R extends InstructionRuntime<R>> Codec<ValueConsumerList<R>> getCodec(Codec<ValueConsumer<?,R>> valueConsumerCodec) {
return Codec.unboundedMap(Codec.STRING, valueConsumerCodec)
.xmap(ValueConsumerList::new, list -> list.valueConsumers);
@@ -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<R extends InstructionRuntime<R>> {
private final ValueConsumerList<R> valueConsumers;
private @Nullable InstructionExecution<R> execution;
private SerializableListenerManager<ConfiguredInstructionListener> listeners;
private ConfiguredInstruction(InstructionType<R> instruction, ValueSupplierList<R> arguments, ValueConsumerList<R> valueConsumers, @Nullable InstructionExecution<R> execution, SerializableListenerManager<ConfiguredInstructionListener> listeners) {
this(instruction, arguments, valueConsumers, execution);
this.listeners = listeners;
}
private ConfiguredInstruction(InstructionType<R> instruction, ValueSupplierList<R> arguments, ValueConsumerList<R> valueConsumers, @Nullable InstructionExecution<R> execution) {
this.instruction = instruction;
this.arguments = arguments;
this.valueConsumers = valueConsumers;
this.execution = execution;
arguments.addListener(this::onSupplierChange);
valueConsumers.addListener(this::onConsumerChange);
}
public ConfiguredInstruction(InstructionType<R> instruction) {
@@ -52,13 +65,14 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
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<R extends InstructionRuntime<R>> {
}
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 <R extends InstructionRuntime<R>> ConfiguredInstruction<R> load(ReadView view, R minion) {
@@ -95,16 +132,19 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
boolean running = view.getBoolean("running", false);
SerializableListenerManager<ConfiguredInstructionListener> listeners = new SerializableListenerManager<>(MinionRegistries.INSTRUCTION_LISTENER_CODECS);
listeners.load(view);
if(running) {
ReadView executionView = view.getReadView("execution");
try {
InstructionExecution<R> 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);
}
}
@@ -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) {}
}
@@ -37,7 +37,7 @@ public interface InstructionExecution<R extends InstructionRuntime<R>> {
*
* @param runtime The runtime that was executing this instruction.
*/
void stop(R runtime, ValueConsumerList<R> valueConsumers);
default void stop(R runtime, ValueConsumerList<R> valueConsumers) {}
/**
* Initializes the execution with its parameters. The parameters must be defined by the InstructionType
@@ -55,4 +55,15 @@ public interface InstructionExecution<R extends InstructionRuntime<R>> {
* Loads the execution, e.g. when the server is started.
*/
void load(ReadView view, R runtime);
interface Stateless<R extends InstructionRuntime<R>> extends InstructionExecution<R> {
@Override
default void readArguments(ValueSupplierList<R> arguments, R runtime) {}
@Override
default void save(WriteView view, R runtime) {}
@Override
default void load(ReadView view, R runtime) {}
}
}
@@ -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<R extends InstructionRuntime<R>> {
private final GuiDisplay display;
private final Collection<Parameter<?>> parameters;
private final Collection<Parameter<?>> returnParameters;
private final List<Parameter<?>> parameters;
private final List<Parameter<?>> returnParameters;
private final Supplier<InstructionExecution<R>> executionFactory;
public InstructionType(GuiDisplay display, Supplier<InstructionExecution<R>> executionFactory, Collection<Parameter<?>> parameters, Collection<Parameter<?>> returnParameters) {
this.display = display;
public InstructionType(Supplier<InstructionExecution<R>> executionFactory, Collection<Parameter<?>> parameters, Collection<Parameter<?>> returnParameters) {
this.parameters = List.copyOf(parameters);
this.returnParameters = List.copyOf(returnParameters);
this.executionFactory = executionFactory;
}
public Collection<Parameter<?>> getParameters() {
public List<Parameter<?>> getParameters() {
return parameters;
}
public Collection<Parameter<?>> getReturnParameters() {
public List<Parameter<?>> getReturnParameters() {
return returnParameters;
}
public GuiDisplay getDisplay() {
return display;
}
public InstructionExecution<R> createExecution(ValueSupplierList<R> parameters, R minion) {
InstructionExecution<R> execution = executionFactory.get();
execution.readArguments(parameters, minion);
@@ -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<MinionRuntime> WALK = register(
"walk",
base -> new GuiDisplay.ModelBased(Items.IRON_BOOTS, base, true),
WalkExecution::new,
List.of(WalkExecution.blocksToMoveParam)
);
public static final InstructionType<MinionRuntime> WALK_CONTINUOUS = register(
"walk_continuous",
ContinuousWalkExecution::new
);
public static final InstructionType<MinionRuntime> TURN = register(
"turn",
base -> new GuiDisplay.ModelBased(Items.STRUCTURE_VOID, base, true),
TurnExecution::new,
List.of(TurnExecution.ANGLE, TurnExecution.DIRECTION)
);
public static final InstructionType<MinionRuntime> TURN_VECTOR = register(
"turn_vector",
TurnVectorExecution::new,
List.of(TurnVectorExecution.X, TurnVectorExecution.Y, TurnVectorExecution.Z)
);
public static final InstructionType<MinionRuntime> ATTACK = register(
"attack",
base -> new GuiDisplay.ModelBased(Items.IRON_PICKAXE, base, true),
() -> new ActionExecution(EntityPlayerActionPack.ActionType.ATTACK)
);
public static final InstructionType<MinionRuntime> MINE_BLOCK = register(
"mine_block",
MineBlockExecution::new
);
public static final InstructionType<MinionRuntime> USE = register(
"use",
base -> new GuiDisplay.ModelBased(Items.LEVER, base, true),
() -> new ActionExecution(EntityPlayerActionPack.ActionType.USE)
);
private static InstructionType<MinionRuntime> register(String id, Function<String, GuiDisplay> displayFunction, Supplier<InstructionExecution<MinionRuntime>> factory, Collection<Parameter<?>> parameters, Collection<Parameter<?>> returnParameters) {
private static InstructionType<MinionRuntime> register(String id, Supplier<InstructionExecution<MinionRuntime>> factory, Collection<Parameter<?>> parameters, Collection<Parameter<?>> 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<MinionRuntime> register(String id, Function<String, GuiDisplay> displayFunction, Supplier<InstructionExecution<MinionRuntime>> factory, Collection<Parameter<?>> parameters) {
return register(id, displayFunction, factory, parameters, List.of());
private static InstructionType<MinionRuntime> register(String id, Supplier<InstructionExecution<MinionRuntime>> factory, Collection<Parameter<?>> parameters) {
return register(id, factory, parameters, List.of());
}
private static InstructionType<MinionRuntime> register(String id, Function<String, GuiDisplay> displayFunction, Supplier<InstructionExecution<MinionRuntime>> factory) {
return register(id, displayFunction, factory, List.of(), List.of());
private static InstructionType<MinionRuntime> register(String id, Supplier<InstructionExecution<MinionRuntime>> factory) {
return register(id, factory, List.of(), List.of());
}
public static void register() {
@@ -16,7 +16,10 @@ public class ActionExecution implements ContinuousInstructionExecution<MinionRun
@Override
public void start(MinionRuntime minion) {
minion.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
EntityPlayerActionPack ap = minion.getMinion().getMinionActionPack();
if(!ap.hasAction(action)) {
minion.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.startContinuous());
}
}
@Override
@@ -0,0 +1,140 @@
//partially code from https://github.com/gnembon/fabric-carpet (EntityPlayerActionPack)
package io.github.skippyall.minions.program.instruction.execution;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.EntityPlayerActionPack;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import net.minecraft.block.BlockState;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
public class MineBlockExecution implements InstructionExecution<MinionRuntime> {
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<MinionRuntime> 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<MinionRuntime> 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;
}
}
}
@@ -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<MinionRuntime> {
public static final Parameter<Float> ANGLE = new Parameter<>("maxAngle", ValueTypes.FLOAT);
public static final Parameter<TurnDirection> 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<MinionRuntime> valueConsumers) {
}
@Override
public void readArguments(ValueSupplierList<MinionRuntime> 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<TurnDirection> 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);
};
}
}
}
@@ -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<MinionRuntime> {
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);
}
}
@@ -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<MinionRuntime>, InstructionExecution.Stateless<MinionRuntime> {
@Override
public void tick(MinionRuntime minion) {
minion.getMinion().move(MovementType.SELF, minion.getMinion().getHorizontalFacing().getDoubleVector().normalize().multiply(minion.getMinion().getMovementSpeed()));
}
}
@@ -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<TurnDirection> 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);
};
}
}
@@ -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<Double> ANGLE = new Parameter<>("maxAngle", ValueTypes.DOUBLE);
public static final Parameter<TurnDirection> DIRECTION = new Parameter<>("direction", ValueTypes.TURN_DIRECTION);
@Override
public void readArguments(ValueSupplierList<MinionRuntime> 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;
}
}
@@ -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<Double> X = new Parameter<>("x", ValueTypes.DOUBLE);
public static final Parameter<Double> Y = new Parameter<>("y", ValueTypes.DOUBLE);
public static final Parameter<Double> Z = new Parameter<>("z", ValueTypes.DOUBLE);
@Override
public void readArguments(ValueSupplierList<MinionRuntime> 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);
}
}
@@ -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<MinionRuntime> {
public static final Parameter<Float> blocksToMoveParam = new Parameter<>("blocksToMove", ValueTypes.FLOAT);
public static final Parameter<Double> 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<MinionRuntime> {
return totalBlocksToMove - blocksMoved < ACCURACY;
}
@Override
public void stop(MinionRuntime minion, ValueConsumerList<MinionRuntime> valueConsumers) {
}
@Override
public void readArguments(ValueSupplierList<MinionRuntime> 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);
}
}
@@ -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<T, R extends InstructionRuntime<R>> implements ValueSupplier<T, R> {
private final ValueArgumentType<R> type;
public class FixedValueSupplier<T, R extends InstructionRuntime<R>> implements ValueSupplier<T, R> {
private final FixedValueSupplierType<R> type;
private final ValueType<T> valueType;
private final T value;
public ValueArgument(ValueArgumentType<R> type, ValueType<T> valueType, T value) {
public FixedValueSupplier(FixedValueSupplierType<R> type, ValueType<T> valueType, T value) {
this.type = type;
this.valueType = valueType;
this.value = value;
@@ -27,18 +26,13 @@ public class ValueArgument<T, R extends InstructionRuntime<R>> implements ValueS
return value;
}
@Override
public GuiDisplay getDisplay() {
return getValueType().display();
}
@Override
public ValueType<T> getValueType() {
return valueType;
}
@Override
public ValueArgumentType<R> getType() {
public FixedValueSupplierType<R> getType() {
return type;
}
}
@@ -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<R extends InstructionRuntime<R>> extends ValueSupplierType<R> {
@Override
public <T> Codec<FixedValueSupplier<T,R>> getCodec(ValueType<T> valueType) {
return valueType.codec().xmap(value -> new FixedValueSupplier<>(this, valueType, value), FixedValueSupplier::getValue);
}
@Override
public <V> CompletableFuture<FixedValueSupplier<V,R>> openConfiguration(ServerPlayerEntity player, ValueType<V> valueType, @Nullable ValueSupplier<V,R> previousValueSupplier) {
return valueType.openValueDialog(
player,
previousValueSupplier instanceof FixedValueSupplier<V,R> val ? val.getValue() : valueType.defaultValue()
).thenApply(value -> new FixedValueSupplier<>(this, valueType, value));
}
}
@@ -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<R extends InstructionRuntime<R>> extends ValueSupplierType<R> {
@Override
public <T> Codec<ValueArgument<T,R>> getCodec(ValueType<T> valueType) {
return valueType.codec().xmap(value -> new ValueArgument<>(this, valueType, value), ValueArgument::getValue);
}
@Override
public <V> CompletableFuture<ValueArgument<V,R>> openConfiguration(ServerPlayerEntity player, ValueType<V> valueType, @Nullable ValueSupplier<V,R> previousValueSupplier) {
return valueType.openValueDialog(
player,
previousValueSupplier instanceof ValueArgument<V,R> val ? val.getValue() : valueType.defaultValue()
).thenApply(value -> new ValueArgument<>(this, valueType, value));
}
}
@@ -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, R extends InstructionRuntime<R>> {
T resolve(R minion);
GuiDisplay getDisplay();
ValueType<T> getValueType();
ValueSupplierType<R> getType();
@@ -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<R extends InstructionRuntime<R>> {
private final Map<String, ValueSupplier<?, R>> arguments;
private final List<Consumer<Parameter<?>>> changeListeners = new ArrayList<>();
public ValueSupplierList() {
arguments = new HashMap<>();
@@ -30,10 +34,11 @@ public class ValueSupplierList<R extends InstructionRuntime<R>> {
public <T> void setArgument(Parameter<T> parameter, ValueSupplier<T,R> 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<Parameter<?>> checkParameters) {
@@ -45,6 +50,21 @@ public class ValueSupplierList<R extends InstructionRuntime<R>> {
return true;
}
private void onChange(Parameter<?> parameter) {
for (Consumer<Parameter<?>> listener : changeListeners) {
listener.accept(parameter);
}
}
public void addListener(Consumer<Parameter<?>> listener) {
changeListeners.add(listener);
}
public void removeListener(Consumer<Parameter<?>> listener) {
changeListeners.remove(listener);
}
public static <R extends InstructionRuntime<R>> Codec<ValueSupplierList<R>> getCodec(Codec<ValueSupplier<?,R>> argumentCodec) {
return Codec.unboundedMap(Codec.STRING, argumentCodec)
.xmap(ValueSupplierList::new, list -> list.arguments);
@@ -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<MinionRuntime> FIXED_VALUE_SUPPLIER_TYPE = register("fixed", new FixedValueSupplierType<>());
public static <T extends ValueSupplierType<MinionRuntime>> T register(String id, T type) {
return Registry.register(MinionRegistries.VALUE_SUPPLIER_TYPES, Identifier.of(Minions.MOD_ID, id), type);
}
public static void register() {}
}
@@ -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<T> {
Result<Void, Text> validate(T value);
default boolean matches(T value) {
return validate(value).isSuccess();
}
}
@@ -0,0 +1,6 @@
package io.github.skippyall.minions.program.value;
import java.util.Collection;
public record TypeRestriction<T>(ValueType<T> type, Collection<RestrictionRule<T>> rules) {
}
@@ -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<T>(Codec<T> codec, GuiDisplay display, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener) {
public record ValueType<T>(Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener) {
public CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue) {
return valueDialogOpener.apply(player, previousValue);
}
@@ -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> INTEGER = registerSimple(
"integer",
Codec.INT,
key -> new GuiDisplay.ModelBased(Items.NETHERITE_SCRAP, key, true),
0,
(player, oldValue) -> TextInput.inputInt(
public static ValueType<Long> LONG = registerSimple(
"long",
Codec.LONG,
0L,
(player, oldValue) -> TextInput.inputLong(
player,
Text.literal("Integer"),
String.valueOf(oldValue)
)
);
public static ValueType<Float> FLOAT = registerSimple(
"float",
Codec.FLOAT,
key -> new GuiDisplay.ModelBased(Items.BAMBOO_RAFT, key, true),
0F,
(player, oldValue) -> TextInput.inputFloat(
public static ValueType<Double> 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> 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<TurnExecution.TurnDirection> TURN_DIRECTION = registerSimple(
public static ValueType<TurnDirection> 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> VOID = registerSimple(
"void",
Codec.unit(null),
key -> new GuiDisplay.ModelBased(Items.BARRIER, key, false),
null,
(player, oldValue) -> CompletableFuture.completedFuture(null)
);
private static <T> ValueType<T> registerSimple(String id, Codec<T> codec, Function<String, GuiDisplay> displayFunction, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener) {
private static <T> ValueType<T> registerSimple(String id, Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> 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
)
@@ -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<InstructionReference> 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<? extends Reference> getCodec() {
return CODEC;
}
@Override
public void appendTooltip(Item.TooltipContext context, Consumer<Text> textConsumer, TooltipType type, ComponentsAccess components) {
textConsumer.accept(Text.translatable("minions.reference.instruction.tooltip", selectedInstruction, visualMinionName));
}
}
@@ -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<Reference> CODEC = MinionRegistries.REFERENCE_CODEC.getCodec().dispatch(Reference::getCodec, Function.identity());
ComponentType<Reference> COMPONENT_TYPE = ComponentType.<Reference>builder().codec(CODEC).build();
MapCodec<? extends Reference> 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);
}
}
@@ -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;
}
}
@@ -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<T> {
protected final List<T> listeners = new CopyOnWriteArrayList<>();
public void forEachListener(Consumer<T> listenerConsumer) {
for(T listener : listeners) {
listenerConsumer.accept(listener);
}
}
public void addListener(T listener) {
listeners.add(listener);
}
public void removeListener(T listener) {
listeners.remove(listener);
}
}
@@ -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<VersionSyncPayload> PACKET_ID = new CustomPayload.Id<>(Identifier.of(Minions.MOD_ID, "version_sync"));
@Override
public Id<? extends CustomPayload> getId() {
return null;
}
}
public static void register() {
PolymerNetworking.registerS2CVersioned(VersionSyncPayload.PACKET_ID, NETWORK_VERSION, PacketCodec.unit(null));
}
}
@@ -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<T extends SerializableListenerManager.SerializableListener> extends ListenerManager<T> {
private final Registry<Codec<? extends T>> registry;
public SerializableListenerManager(Registry<Codec<? extends T>> 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<? extends T> codec = registry.get(listener.getCodecId().get());
listenerView.put("id", Identifier.CODEC, listener.getCodecId().get());
//noinspection unchecked
listenerView.put("data", (Codec<T>) codec, listener);
}
}
}
public void load(ReadView view) {
ReadView.ListReadView listView = view.getListReadView("listeners");
for (ReadView listenerView : listView) {
Optional<Identifier> id = listenerView.read("id", Identifier.CODEC);
if(id.isEmpty()) {
continue;
}
Codec<? extends T> codec = registry.get(id.get());
Optional<? extends T> listener = listenerView.read("data", codec);
if(listener.isPresent()) {
listeners.add(listener.get());
}
}
}
public interface SerializableListener {
default Optional<Identifier> getCodecId() {
return Optional.empty();
}
}
}
@@ -0,0 +1,10 @@
{
"variants": {
"powered=false": {
"model": "minions:block/minion_trigger_inactive"
},
"powered=true": {
"model": "minions:block/minion_trigger_active"
}
}
}
@@ -0,0 +1,6 @@
{
"model": {
"type": "minecraft:model",
"model": "minions:block/minion_trigger_active"
}
}
@@ -0,0 +1,6 @@
{
"model": {
"type": "minecraft:model",
"model": "minions:block/minion_trigger_no_plate_active"
}
}
@@ -0,0 +1,6 @@
{
"model": {
"type": "minecraft:model",
"model": "minions:block/minion_trigger_no_plate_inactive"
}
}
@@ -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]
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "minions:block/minion_trigger",
"textures": {
"torch": "block/redstone_torch"
}
}
@@ -0,0 +1,6 @@
{
"parent": "minions:block/minion_trigger",
"textures": {
"torch": "block/redstone_torch_off"
}
}
@@ -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]
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "minions:block/minion_trigger_no_plate",
"textures": {
"torch": "block/redstone_torch"
}
}
@@ -0,0 +1,6 @@
{
"parent": "minions:block/minion_trigger_no_plate",
"textures": {
"torch": "block/redstone_torch_off"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

@@ -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",
@@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minions:minion_trigger"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:iron_pickaxe"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:structure_void"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:lever"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:iron_boots"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:redstone_block"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:bamboo_raft"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:netherite_scrap"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:string"
}
@@ -0,0 +1,4 @@
{
"type": "minions:item",
"data": "minecraft:structure_void"
}
+3
View File
@@ -12,6 +12,9 @@
"icon": "assets/minions/icon.png",
"environment": "*",
"entrypoints": {
"client": [
"io.github.skippyall.minions.client.MinionsClient"
],
"main": [
"io.github.skippyall.minions.Minions"
]
+2 -1
View File
@@ -1,4 +1,5 @@
accessWidener v2 named
accessible class net/minecraft/server/world/ChunkLevelManager$DistanceFromNearestPlayerTracker
accessible method net/minecraft/server/world/ChunkLevelManager$DistanceFromNearestPlayerTracker <init> (Lnet/minecraft/server/world/ChunkLevelManager;I)V
accessible method net/minecraft/server/world/ChunkLevelManager$DistanceFromNearestPlayerTracker <init> (Lnet/minecraft/server/world/ChunkLevelManager;I)V
accessible class net/minecraft/screen/slot/ArmorSlot
+3 -2
View File
@@ -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"
}