Runtime & MinionTriggerBlock start

This commit is contained in:
skippyall
2025-11-06 22:48:20 +01:00
parent cc1320d5c6
commit b413592abf
40 changed files with 726 additions and 473 deletions
@@ -1,7 +1,12 @@
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.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 net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.DamageResistantComponent;
import net.minecraft.entity.damage.DamageType;
@@ -14,16 +19,59 @@ import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import java.util.List;
import java.util.function.Function;
import static io.github.skippyall.minions.Minions.MOD_ID;
public class MinionItems {
public static final TagKey<DamageType> MINION_ITEM_RESISTS = TagKey.of(RegistryKeys.DAMAGE_TYPE, Identifier.of(MOD_ID, "minion_item_resists"));
public static final MinionItem MINION_ITEM = registerItem(Identifier.of(MOD_ID, "minion"), settings -> new MinionItem(settings.component(DataComponentTypes.DAMAGE_RESISTANT, new DamageResistantComponent(MINION_ITEM_RESISTS))));
public static final SimplePolymerItem BASIC_UPGRADE_BASE = registerItem(Identifier.of(MOD_ID, "basic_upgrade_base"), settings -> new SimplePolymerItem(settings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE));
public static final SimplePolymerItem ADVANCED_UPGRADE_BASE = registerItem(Identifier.of(MOD_ID, "advanced_upgrade_base"), settings -> new SimplePolymerItem(settings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE));
public static final MinionItem MINION_ITEM = registerItem(
Identifier.of(MOD_ID, "minion"),
settings -> new MinionItem(settings.component(DataComponentTypes.DAMAGE_RESISTANT, new DamageResistantComponent(MINION_ITEM_RESISTS)))
);
public static final SimplePolymerItem BASIC_UPGRADE_BASE = registerItem(
Identifier.of(MOD_ID, "basic_upgrade_base"),
settings -> new SimplePolymerItem(settings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE)
);
public static final SimplePolymerItem ADVANCED_UPGRADE_BASE = registerItem(
Identifier.of(MOD_ID, "advanced_upgrade_base"),
settings -> new SimplePolymerItem(settings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE)
);
public static final SimplePolymerItem MOVE_MODULE = registerModule(
Identifier.of(MOD_ID, "move_module"),
Items.IRON_BOOTS,
List.of(Instructions.WALK, Instructions.TURN)
);
public static final SimplePolymerItem ATTACK_MODULE = registerModule(
Identifier.of(MOD_ID, "attack_module"),
Items.IRON_PICKAXE,
List.of(Instructions.ATTACK)
);
public static final SimplePolymerItem INTERACT_MODULE = registerModule(
Identifier.of(MOD_ID, "interact_module"),
Items.LEVER,
List.of(Instructions.USE)
);
public static final SimplePolymerItem MOB_SPAWNING_MODULE = registerModule(
Identifier.of(MOD_ID, "mob_spawning_module"),
Items.SPAWNER,
List.of(),
List.of("mobSpawning")
);
public static final PolymerBlockItem MINION_TRIGGER_ITEM =
registerItem(
MinionRegistration.MINION_TRIGGER_ID,
settings -> new PolymerBlockItem(MinionRegistration.MINION_TRIGGER_BLOCK, settings, Items.GOLD_BLOCK)
);
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)));
@@ -37,6 +85,23 @@ public class MinionItems {
return registerItem(identifier, constructor, new Item.Settings());
}
public static SimplePolymerItem registerModule(Identifier identifier, Item vanillaItem, List<InstructionType<MinionRuntime>> instructionTypes, List<String> specialEffects) {
return registerItem(
identifier,
settings -> new SimplePolymerItem(settings, vanillaItem),
new Item.Settings().component(MinionModule.COMPONENT_TYPE, new MinionModule(instructionTypes, specialEffects))
);
}
public static SimplePolymerItem registerModule(Identifier identifier, Item vanillaItem, List<InstructionType<MinionRuntime>> instructionTypes) {
return registerModule(
identifier,
vanillaItem,
instructionTypes,
List.of()
);
}
public static void register() {
}
}
@@ -0,0 +1,39 @@
package io.github.skippyall.minions;
import eu.pb4.polymer.core.api.block.PolymerBlockUtils;
import io.github.skippyall.minions.block.MinionTriggerBlock;
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.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.Identifier;
public class MinionRegistration {
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()
)
);
public static final BlockEntityType<MinionTriggerBlockEntity> MINION_TRIGGER_BE_TYPE =
Registry.register(
Registries.BLOCK_ENTITY_TYPE,
MINION_TRIGGER_ID,
FabricBlockEntityTypeBuilder.create(MinionTriggerBlockEntity::new, MINION_TRIGGER_BLOCK).build()
);
public static void register() {
PolymerBlockUtils.registerBlockEntity(MINION_TRIGGER_BE_TYPE);
}
}
@@ -3,8 +3,9 @@ package io.github.skippyall.minions;
import com.mojang.serialization.Lifecycle;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.skin.SkinProvider;
import io.github.skippyall.minions.program.argument.ArgumentType;
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 net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
@@ -13,7 +14,8 @@ import net.minecraft.util.Identifier;
public class MinionRegistries {
public static final Registry<ValueType<?>> VALUE_TYPES = registry("value_type");
public static final Registry<ArgumentType<MinionRuntime>> ARGUMENT_TYPE_REGISTRY = registry("argument_type");
public static final Registry<ValueSupplierType<MinionRuntime>> ARGUMENT_TYPES = registry("argument_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");
@@ -41,6 +41,8 @@ public class Minions implements ModInitializer {
ValueTypes.register();
SkinProviders.register();
MinionRegistration.register();
MinionItems.register();
MinionCreativeTab.registerGroup();
}
@@ -0,0 +1,67 @@
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 net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.block.WireOrientation;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.packettweaker.PacketContext;
public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock {
public static final MapCodec<MinionTriggerBlock> CODEC = createCodec(MinionTriggerBlock::new);
public static final BooleanProperty POWERED = BooleanProperty.of("powered");
public MinionTriggerBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(POWERED, false));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(POWERED);
}
@Override
protected void neighborUpdate(BlockState state, World world, BlockPos pos, Block sourceBlock, @Nullable WireOrientation wireOrientation, boolean notify) {
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);
}
}
@Override
protected boolean hasComparatorOutput(BlockState state) {
return true;
}
@Override
protected int getComparatorOutput(BlockState state, World world, BlockPos pos) {
return world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE).map(MinionTriggerBlockEntity::getComparatorOutput).orElse(0);
}
@Override
protected MapCodec<? extends BlockWithEntity> getCodec() {
return CODEC;
}
@Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new MinionTriggerBlockEntity(pos, state);
}
@Override
public BlockState getPolymerBlockState(BlockState state, PacketContext context) {
return state.get(POWERED) ? Blocks.REDSTONE_BLOCK.getDefaultState() : Blocks.GOLD_BLOCK.getDefaultState();
}
}
@@ -0,0 +1,84 @@
package io.github.skippyall.minions.block;
import io.github.skippyall.minions.MinionRegistration;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import net.minecraft.util.Uuids;
import net.minecraft.util.math.BlockPos;
import java.util.UUID;
public class MinionTriggerBlockEntity extends BlockEntity {
private UUID minionUuid;
private String instructionName = "";
public MinionTriggerBlockEntity(BlockPos pos, BlockState state) {
super(MinionRegistration.MINION_TRIGGER_BE_TYPE, pos, state);
}
public void updatePower() {
boolean powered = getCachedState().get(MinionTriggerBlock.POWERED);
ConfiguredInstruction<MinionRuntime> instruction = getInstruction();
if(instruction != null) {
if(powered) {
instruction.run(getMinion().getInstructionManager());
} else {
instruction.stop(getMinion().getInstructionManager());
}
}
}
public int getComparatorOutput() {
ConfiguredInstruction<MinionRuntime> instruction = getInstruction();
if(instruction != null && instruction.isRunning()) {
return 15;
}
return 0;
}
public MinionFakePlayer getMinion() {
if(minionUuid != null && world != null && world.getPlayerByUuid(minionUuid) instanceof MinionFakePlayer minion) {
return minion;
}
return null;
}
public ConfiguredInstruction<MinionRuntime> getInstruction() {
MinionFakePlayer minion = getMinion();
if(minion == null) {
return null;
}
return minion.getInstructionManager().getInstruction(instructionName);
}
@Override
public void onBlockReplaced(BlockPos pos, BlockState oldState) {
super.onBlockReplaced(pos, oldState);
}
@Override
public void markRemoved() {
super.markRemoved();
}
@Override
protected void readData(ReadView view) {
minionUuid = view.read("minionUuid", Uuids.CODEC).orElse(null);
instructionName = view.getString("instructionName", "");
}
@Override
protected void writeData(WriteView view) {
if(minionUuid != null) {
view.put("minionUuid", Uuids.CODEC, minionUuid);
}
view.putString("instructionName", instructionName);
}
}
@@ -7,13 +7,11 @@ import io.github.skippyall.minions.input.TextInput;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.module.MinionModule;
import io.github.skippyall.minions.program.argument.Argument;
import io.github.skippyall.minions.program.argument.GenericArgumentType;
import io.github.skippyall.minions.program.argument.SpecificArgumentType;
import io.github.skippyall.minions.program.dingenskirchen.SpecificDingenskirchenType;
import io.github.skippyall.minions.program.supplier.ValueSupplier;
import io.github.skippyall.minions.program.supplier.ValueSupplierType;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.program.argument.Parameter;
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;
@@ -45,7 +43,7 @@ public class InstructionGui {
public static void instructionList(MinionFakePlayer minion, ServerPlayerEntity player) {
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false);
for(String instructionName : minion.getInstructionManager().getInstructionNames()) {
ConfiguredInstruction<?> instruction = minion.getInstructionManager().getInstruction(instructionName);
ConfiguredInstruction<MinionRuntime> instruction = minion.getInstructionManager().getInstruction(instructionName);
gui.addSlot(instruction.getInstruction().getDisplay().createElement()
.setName(Text.literal(instructionName))
.setLore(List.of())
@@ -58,7 +56,7 @@ public class InstructionGui {
public static void createNewInstruction(MinionFakePlayer minion, ServerPlayerEntity player) {
selectInstructionModuleMenu(minion, player).thenAccept(instructionType ->
TextInput.inputString(player, Text.translatable("minions.gui.instruction.enter_name"), "Instruction").thenAccept(name -> {
ConfiguredInstruction<?> configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType);
ConfiguredInstruction<MinionRuntime> configuredInstruction = minion.getInstructionManager().createInstruction(name, instructionType);
configureInstructionMenu(name, configuredInstruction, minion, player);
})
);
@@ -73,7 +71,7 @@ public class InstructionGui {
return stillExists;
}
public static void configureInstructionMenu(String name, ConfiguredInstruction<?> instruction, MinionFakePlayer minion, ServerPlayerEntity player) {
public static void configureInstructionMenu(String name, ConfiguredInstruction<MinionRuntime> instruction, MinionFakePlayer minion, ServerPlayerEntity player) {
if(!checkInstructionExists(name, instruction, minion, player)) {
return;
}
@@ -94,12 +92,12 @@ public class InstructionGui {
gui.open();
}
private static void updateRunSlot(ConfiguredInstruction<?> instruction, MinionFakePlayer minion, SimpleGui gui) {
private static void updateRunSlot(ConfiguredInstruction<MinionRuntime> instruction, MinionFakePlayer minion, SimpleGui gui) {
if(!instruction.isRunning()) {
gui.setSlot(26, new GuiElementBuilder(Items.ARROW)
.setName(Text.literal("Run"))
.setCallback(() -> {
instruction.run(minion);
instruction.run(minion.getInstructionManager());
updateRunSlot(instruction, minion, gui);
})
);
@@ -107,14 +105,14 @@ public class InstructionGui {
gui.setSlot(26, new GuiElementBuilder(Items.BARRIER)
.setName(Text.literal("Stop"))
.setCallback(() -> {
instruction.stop(minion);
instruction.stop(minion.getInstructionManager());
updateRunSlot(instruction, minion, gui);
})
);
}
}
public static <T, A extends Argument<T, ? extends SpecificDingenskirchenType<T, A, MinionRuntime>, MinionRuntime>> void configureArgumentMenu(String name, ConfiguredInstruction<?> instruction, Parameter<T> parameter, MinionFakePlayer minion, ServerPlayerEntity player) {
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)) {
return;
}
@@ -123,9 +121,9 @@ public class InstructionGui {
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false);
gui.setSlot(3, new GuiElementBuilder(Items.STICK)
.setName(Text.literal("Type: " + (argument == null ? "Unset" : MinionRegistries.GENERIC_ARGUMENT_TYPE_REGISTRY.getId(argument.getType().getGenericArgumentType()).getPath())))
.setName(Text.literal("Type: " + (argument == null ? "Unset" : MinionRegistries.ARGUMENT_TYPES.getId(argument.getType()).getPath())))
.setCallback(() -> selectArgumentType(player)
.thenApply(type -> type.<T>createTypeSpecific(parameter.type()).openArgumentDialog(player, null)
.thenApply(type -> type.openConfiguration(player, parameter.type(), null)
.thenAccept(newArgument -> {
instruction.getArguments().setArgument(parameter, newArgument);
configureArgumentMenu(name, instruction, parameter, minion, player);
@@ -137,7 +135,7 @@ public class InstructionGui {
if(argument != null) {
gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID)
.setName(Text.literal("Configure"))
.setCallback(() -> argument.getType().openArgumentDialog(player, argument)
.setCallback(() -> argument.getType().openConfiguration(player, argument.getValueType(), argument)
.thenAccept(newArgument -> instruction.getArguments().setArgument(parameter, newArgument))
)
);
@@ -145,12 +143,12 @@ public class InstructionGui {
gui.open();
}
public static CompletableFuture<GenericArgumentType> selectArgumentType(ServerPlayerEntity player) {
CompletableFuture<GenericArgumentType> future = new CompletableFuture<>();
public static CompletableFuture<ValueSupplierType<MinionRuntime>> selectArgumentType(ServerPlayerEntity player) {
CompletableFuture<ValueSupplierType<MinionRuntime>> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false);
for(GenericArgumentType type : MinionRegistries.GENERIC_ARGUMENT_TYPE_REGISTRY) {
for(ValueSupplierType<MinionRuntime> type : MinionRegistries.ARGUMENT_TYPES) {
gui.addSlot(new GuiElementBuilder()
.setName(Text.translatable(TranslationUtil.getTranslationKey(type, MinionRegistries.GENERIC_ARGUMENT_TYPE_REGISTRY)))
.setName(Text.translatable(TranslationUtil.getTranslationKey(type, MinionRegistries.ARGUMENT_TYPES)))
.setCallback(() -> future.complete(type))
);
}
@@ -158,13 +156,13 @@ public class InstructionGui {
return future;
}
public static CompletableFuture<InstructionType<?>> selectInstructionModuleMenu(MinionFakePlayer minion, ServerPlayerEntity 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"));
return CompletableFuture.failedFuture(new NoSuchElementException("No modules"));
}
CompletableFuture<InstructionType<?>> future = new CompletableFuture<>();
CompletableFuture<InstructionType<MinionRuntime>> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false) {
@Override
@@ -190,11 +188,11 @@ public class InstructionGui {
return future;
}
public static CompletableFuture<InstructionType<?>> selectInstructionMenu(MinionModule module, MinionFakePlayer minion, ServerPlayerEntity player) {
CompletableFuture<InstructionType<?>> future = new CompletableFuture<>();
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<?> instructionType : module.instructions()) {
for(InstructionType<MinionRuntime> instructionType : module.instructions()) {
gui.addSlot(createInstructionElement(instructionType)
.setCallback(() -> future.complete(instructionType))
);
@@ -215,10 +213,10 @@ public class InstructionGui {
return instructionBuilder;
}
public static GuiElementBuilder createArgumentElement(Argument<?,?> argument) {
public static GuiElementBuilder createArgumentElement(ValueSupplier<?,?> valueSupplier) {
GuiElementBuilder argumentBuilder;
if(argument != null) {
argumentBuilder = argument.getDisplay().createElement();
if(valueSupplier != null) {
argumentBuilder = valueSupplier.getDisplay().createElement();
} else {
argumentBuilder = new GuiElementBuilder(Items.RED_WOOL)
.setName(Text.translatable("minions.gui.instruction.no_argument_set"));
@@ -1,79 +0,0 @@
package io.github.skippyall.minions.minion;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import io.github.skippyall.minions.program.instruction.InstructionType;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class MinionInstructionManager {
private final MinionFakePlayer minion;
private final Map<String, ConfiguredInstruction<?>> configuredInstructions = new HashMap<>();
public MinionInstructionManager(MinionFakePlayer minion) {
this.minion = minion;
}
public void tick() {
for (ConfiguredInstruction<?> instruction : configuredInstructions.values()) {
instruction.tick(minion);
}
}
public Set<String> getInstructionNames() {
return configuredInstructions.keySet();
}
public <T> ConfiguredInstruction<T> createInstruction(String name, InstructionType<T> instructionType) {
ConfiguredInstruction<T> instruction = new ConfiguredInstruction<>(instructionType, name);
configuredInstructions.put(name, instruction);
return instruction;
}
public void removeInstruction(String name) {
ConfiguredInstruction<?> instruction = getInstruction(name);
instruction.stop(minion);
configuredInstructions.remove(name);
}
public ConfiguredInstruction<?> getInstruction(String name) {
return configuredInstructions.get(name);
}
public boolean hasInstruction(String name) {
return configuredInstructions.containsKey(name);
}
public void save(WriteView view) {
WriteView.ListView list = view.getList("configuredInstructions");
for (Map.Entry<String, ConfiguredInstruction<?>> instruction : configuredInstructions.entrySet()) {
WriteView inner = list.add();
inner.putString("name", instruction.getKey());
instruction.getValue().save(inner, minion);
}
}
public void load(ReadView view) {
ReadView.ListReadView list = view.getListReadView("configuredInstructions");
for (ReadView inner : list) {
Optional<String> name = inner.getOptionalString("name");
if(name.isEmpty()) {
Minions.LOGGER.error("Tried deserializing configured instruction without a name of minion \"{}\":", minion.getGameProfile().getName());
continue;
}
try {
ConfiguredInstruction<?> instruction = ConfiguredInstruction.load(inner, minion, name.get());
configuredInstructions.put(name.get(), instruction);
} catch (Exception e) {
Minions.LOGGER.error("Could not deserialize configured instruction \"{}\" of minion \"{}\":", name.get(), minion.getGameProfile().getName(), e);
}
}
}
}
@@ -1,14 +1,103 @@
package io.github.skippyall.minions.minion;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.GenericArgumentType;
import io.github.skippyall.minions.program.supplier.ValueSupplierType;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.program.consumer.ValueConsumerType;
import net.minecraft.registry.Registry;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class MinionRuntime implements InstructionRuntime<MinionRuntime> {
private final MinionFakePlayer minion;
private final Map<String, ConfiguredInstruction<MinionRuntime>> configuredInstructions = new HashMap<>();
public MinionRuntime(MinionFakePlayer minion) {
this.minion = minion;
}
public MinionFakePlayer getMinion() {
return minion;
}
public void tick() {
for (ConfiguredInstruction<MinionRuntime> instruction : configuredInstructions.values()) {
instruction.tick(this);
}
}
public Set<String> getInstructionNames() {
return configuredInstructions.keySet();
}
public ConfiguredInstruction<MinionRuntime> createInstruction(String name, InstructionType<MinionRuntime> instructionType) {
ConfiguredInstruction<MinionRuntime> instruction = new ConfiguredInstruction<>(instructionType, name);
configuredInstructions.put(name, instruction);
return instruction;
}
public void removeInstruction(String name) {
ConfiguredInstruction<MinionRuntime> instruction = getInstruction(name);
instruction.stop(this);
configuredInstructions.remove(name);
}
public ConfiguredInstruction<MinionRuntime> getInstruction(String name) {
return configuredInstructions.get(name);
}
public boolean hasInstruction(String name) {
return configuredInstructions.containsKey(name);
}
public void save(WriteView view) {
WriteView.ListView list = view.getList("configuredInstructions");
for (Map.Entry<String, ConfiguredInstruction<MinionRuntime>> instruction : configuredInstructions.entrySet()) {
WriteView inner = list.add();
inner.putString("name", instruction.getKey());
instruction.getValue().save(inner, this);
}
}
public void load(ReadView view) {
ReadView.ListReadView list = view.getListReadView("configuredInstructions");
for (ReadView inner : list) {
Optional<String> name = inner.getOptionalString("name");
if(name.isEmpty()) {
Minions.LOGGER.error("Tried deserializing configured instruction without a name of minion \"{}\":", minion.getGameProfile().getName());
continue;
}
try {
ConfiguredInstruction<MinionRuntime> instruction = ConfiguredInstruction.load(inner, this, name.get());
configuredInstructions.put(name.get(), instruction);
} catch (Exception e) {
Minions.LOGGER.error("Could not deserialize configured instruction \"{}\" of minion \"{}\":", name.get(), minion.getGameProfile().getName(), e);
}
}
}
@Override
public Registry<GenericArgumentType<MinionRuntime>> getGenericArgumentTypeRegistry() {
return MinionRegistries.GENERIC_ARGUMENT_TYPE_REGISTRY;
public Registry<ValueSupplierType<MinionRuntime>> getArgumentTypeRegistry() {
return MinionRegistries.ARGUMENT_TYPES;
}
@Override
public Registry<InstructionType<MinionRuntime>> getInstructionTypeRegistry() {
return MinionRegistries.INSTRUCTION_TYPES;
}
@Override
public Registry<ValueConsumerType<MinionRuntime>> getValueConsumerTypeRegistry() {
return MinionRegistries.VALUE_CONSUMER_TYPES;
}
}
@@ -6,7 +6,7 @@ import com.mojang.authlib.properties.PropertyMap;
import io.github.skippyall.minions.MinionItems;
import io.github.skippyall.minions.minion.MinionData;
import io.github.skippyall.minions.gui.MinionGui;
import io.github.skippyall.minions.minion.MinionInstructionManager;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.MinionItem;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.minion.MinionProfileUtils;
@@ -51,11 +51,8 @@ public class MinionFakePlayer extends ServerPlayerEntity {
private EntityPlayerActionPack actionPack;
private float moveForward;
private float moveSideways;
private final ModuleInventory moduleInventory = new ModuleInventory();
private final MinionInstructionManager instructionManager = new MinionInstructionManager(this);
private final MinionRuntime instructionManager = new MinionRuntime(this);
private final MinionData data;
@@ -118,7 +115,7 @@ public class MinionFakePlayer extends ServerPlayerEntity {
return actionPack;
}
public MinionInstructionManager getInstructionManager() {
public MinionRuntime getInstructionManager() {
return instructionManager;
}
@@ -127,11 +124,11 @@ public class MinionFakePlayer extends ServerPlayerEntity {
}
public boolean canSpawnMobs() {
return true;
return moduleInventory.hasAbility("mobSpawning");
}
public boolean canDespawnMobs() {
return true;
return moduleInventory.hasAbility("mobSpawning");
}
@Override
@@ -249,44 +246,6 @@ public class MinionFakePlayer extends ServerPlayerEntity {
return networkHandler.player;
}
/*public void moveForward(float forward) {
this.moveForward += forward;
EntityPlayerActionPack actionPack = getMinionActionPack();
if (moveForward != 0) {
actionPack.setForward(moveForward > 0 ? 1 : -1);
}
}
public void moveSideways(float sideways) {
this.moveSideways += sideways;
EntityPlayerActionPack actionPack = getMinionActionPack();
if (moveSideways != 0) {
actionPack.setStrafing(moveSideways > 0 ? 1 : -1);
}
}
@Override
public void move(MovementType movementType, Vec3d movement) {
float newForward = (float) (moveForward - movement.z);
float newSideways = (float) (moveSideways - movement.x);
Vec3d newMovement = movement;
if ((newForward < 0 && moveForward > 0) || (newForward > 0 && moveForward < 0)) {
newMovement = new Vec3d(newMovement.x, newMovement.y, moveForward);
moveForward = 0;
getMinionActionPack().setForward(0);
}else {
moveForward = newForward;
}
if ((newSideways < 0 && moveSideways > 0) || (newSideways > 0 && moveSideways < 0)) {
newMovement = new Vec3d(newMovement.x, newMovement.y, moveSideways);
moveSideways = 0;
getMinionActionPack().setStrafing(0);
}else {
moveSideways = newSideways;
}
super.move(movementType, newMovement);
}*/
@Override
public void drop(ServerWorld world, DamageSource damageSource) {
super.drop(world, damageSource);
@@ -8,7 +8,6 @@ import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.authlib.GameProfile;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.minion.fakeplayer.NetHandlerPlayServerFake;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.packet.c2s.common.SyncedClientOptions;
import net.minecraft.server.MinecraftServer;
@@ -5,6 +5,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
import eu.pb4.polymer.core.api.other.PolymerComponent;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionType;
import net.minecraft.component.ComponentType;
import net.minecraft.registry.Registries;
@@ -13,10 +14,11 @@ import net.minecraft.util.Identifier;
import java.util.List;
public record MinionModule(List<InstructionType<?>> instructions) {
public record MinionModule(List<InstructionType<MinionRuntime>> instructions, List<String> specialBehaviour) {
public static final Codec<MinionModule> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
MinionRegistries.INSTRUCTION_TYPES.getCodec().listOf().fieldOf("instructions").forGetter(MinionModule::instructions)
MinionRegistries.INSTRUCTION_TYPES.getCodec().listOf().fieldOf("instructions").forGetter(MinionModule::instructions),
Codec.STRING.listOf().fieldOf("specialBehaviour").forGetter(MinionModule::specialBehaviour)
).apply(instance, MinionModule::new)
);
@@ -24,8 +26,13 @@ public record MinionModule(List<InstructionType<?>> instructions) {
public static final MinionModule EMPTY = new MinionModule(List.of());
public MinionModule(List<InstructionType<?>> instructions) {
public MinionModule(List<InstructionType<MinionRuntime>> instructions) {
this(instructions, List.of());
}
public MinionModule(List<InstructionType<MinionRuntime>> instructions, List<String> specialBehaviour) {
this.instructions = List.copyOf(instructions);
this.specialBehaviour = List.copyOf(specialBehaviour);
}
public static void register() {
@@ -19,6 +19,8 @@ import java.util.Set;
public class ModuleInventory extends SimpleInventory {
private final Set<MinionModule> modules = new HashSet<>();
private final Set<String> specialAbilities = new HashSet<>();
public ModuleInventory() {
super(27);
}
@@ -45,10 +47,12 @@ public class ModuleInventory extends SimpleInventory {
public void updateModules() {
modules.clear();
specialAbilities.clear();
for (ItemStack heldStack : heldStacks) {
MinionModule module = heldStack.get(MinionModule.COMPONENT_TYPE);
if(module != null) {
modules.add(module);
specialAbilities.addAll(module.specialBehaviour());
}
}
}
@@ -62,14 +66,14 @@ public class ModuleInventory extends SimpleInventory {
Inventories.writeData(view, heldStacks);
}
public boolean hasModule(MinionModule module) {
return modules.contains(module);
}
public Collection<MinionModule> getModules() {
return modules;
}
public boolean hasAbility(String ability) {
return specialAbilities.contains(ability);
}
public List<InstructionType<?>> getAllInstructions() {
ArrayList<InstructionType<?>> instructionTypes = new ArrayList<>();
for(MinionModule module : modules) {
@@ -11,6 +11,7 @@ import net.minecraft.screen.slot.Slot;
public class ModuleInventoryScreenHandler extends ScreenHandler {
private final int rows = 3;
private final ModuleInventory inventory;
public ModuleInventoryScreenHandler(int syncId, ModuleInventory inventory) {
super(ScreenHandlerType.GENERIC_9X3, syncId);
this.inventory = inventory;
@@ -18,14 +19,13 @@ public class ModuleInventoryScreenHandler extends ScreenHandler {
public ModuleInventoryScreenHandler(int syncId, PlayerInventory playerInventory, ModuleInventory inventory) {
super(ScreenHandlerType.GENERIC_9X3, syncId);
int k;
int j;
GenericContainerScreenHandler.checkSize(inventory, 3 * 9);
this.inventory = inventory;
inventory.onOpen(playerInventory.player);
int i = (rows - 4) * 18;
for (j = 0; j < rows; ++j) {
for (k = 0; k < 9; ++k) {
for (int j = 0; j < rows; ++j) {
for (int k = 0; k < 9; ++k) {
this.addSlot(new Slot(inventory, k + j * 9, 8 + k * 18, 18 + j * 18) {
@Override
public boolean canInsert(ItemStack stack) {
@@ -34,14 +34,8 @@ public class ModuleInventoryScreenHandler extends ScreenHandler {
});
}
}
for (j = 0; j < 3; ++j) {
for (k = 0; k < 9; ++k) {
this.addSlot(new Slot(playerInventory, k + j * 9 + 9, 8 + k * 18, 103 + j * 18 + i));
}
}
for (j = 0; j < 9; ++j) {
this.addSlot(new Slot(playerInventory, j, 8 + j * 18, 161 + i));
}
addPlayerSlots(playerInventory, 8, 85);
}
@Override
@@ -1,30 +1,43 @@
package io.github.skippyall.minions.program;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.argument.Argument;
import io.github.skippyall.minions.program.argument.ArgumentList;
import io.github.skippyall.minions.program.argument.ArgumentType;
import io.github.skippyall.minions.program.argument.Arguments;
import io.github.skippyall.minions.program.supplier.ValueSupplier;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import io.github.skippyall.minions.program.supplier.ValueSupplierType;
import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.program.returnvalue.ValueConsumerType;
import io.github.skippyall.minions.program.consumer.ValueConsumer;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import io.github.skippyall.minions.program.consumer.ValueConsumerType;
import net.minecraft.registry.Registry;
public interface InstructionRuntime<R extends InstructionRuntime<R>> {
Registry<ArgumentType<R>> getArgumentTypeRegistry();
Registry<ValueSupplierType<R>> getArgumentTypeRegistry();
Registry<InstructionType<R>> getInstructionTypeRegistry();
Registry<ValueConsumerType<R>> getValueConsumerRegistry();
Registry<ValueConsumerType<R>> getValueConsumerTypeRegistry();
default Codec<ArgumentType<R>> getArgumentTypeCodec() {
default Codec<ValueSupplierType<R>> getArgumentTypeCodec() {
return getArgumentTypeRegistry().getCodec();
}
default Codec<Argument<?,R>> getArgumentCodec() {
return Arguments.createArgumentCodec(getArgumentTypeCodec());
default Codec<ValueSupplier<?,R>> getArgumentCodec() {
return ValueSupplier.createArgumentCodec(getArgumentTypeCodec());
}
default Codec<ArgumentList<R>> getArgumentListCodec() {
return ArgumentList.getCodec(getArgumentTypeCodec());
default Codec<ValueSupplierList<R>> getArgumentListCodec() {
return ValueSupplierList.getCodec(getArgumentCodec());
}
default Codec<ValueConsumerType<R>> getValueConsumerTypeCodec() {
return getValueConsumerTypeRegistry().getCodec();
}
default Codec<ValueConsumer<?,R>> getValueConsumerCodec() {
return ValueConsumer.createValueConsumerCodec(getValueConsumerTypeCodec());
}
default Codec<ValueConsumerList<R>> getValueConsumerListCodec() {
return ValueConsumerList.getCodec(getValueConsumerCodec());
}
}
@@ -1,31 +0,0 @@
package io.github.skippyall.minions.program.argument;
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;
/**
* An <code>Argument</code> can be supplied to an instruction with a matching parameter.
* Its value is resolved at runtime and can vary between executions.
* <code>Argument</code>s are created exclusively by <code>SpecificArgumentType</code>s.
* @param <T> The type of the <code>Argument</code>'s value
*/
public interface Argument<T, R extends InstructionRuntime<R>> {
T resolve(R minion);
GuiDisplay getDisplay();
ValueType<T> getValueType();
ArgumentType<R> getType();
default <U,A extends Argument<U,R>> @Nullable A cast(ValueType<U> type) {
if(getValueType() == type) {
//noinspection unchecked
return (A) this;
} else {
return null;
}
}
}
@@ -1,52 +0,0 @@
package io.github.skippyall.minions.program.argument;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.InstructionRuntime;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class ArgumentList<R extends InstructionRuntime<R>> {
private final Map<String, Argument<?, R>> arguments;
public ArgumentList() {
arguments = new HashMap<>();
}
public ArgumentList(Map<String, Argument<?,R>> arguments) {
this.arguments = new HashMap<>(arguments);
}
public <T> T getValue(Parameter<T> parameter, R runtime) {
Argument<T,R> argument = getArgument(parameter);
return argument != null ? argument.resolve(runtime) : null;
}
public <T, A extends Argument<T,R>> A getArgument(Parameter<T> parameter) {
Argument<?,R> argument = arguments.get(parameter.name());
return argument == null ? null : argument.cast(parameter.type());
}
public <T> void setArgument(Parameter<T> parameter, Argument<T,R> argument) {
arguments.put(parameter.name(), argument);
}
public boolean hasArgumentFor(Parameter<?> parameter) {
return getArgument(parameter) != null;
}
public boolean hasArgumentForAll(Collection<Parameter<?>> checkParameters) {
for(Parameter<?> parameter : checkParameters) {
if(!hasArgumentFor(parameter)) {
return false;
}
}
return true;
}
public static <R extends InstructionRuntime<R>> Codec<ArgumentList<R>> getCodec(Codec<ArgumentType<R>> genericCodec) {
return Codec.unboundedMap(Codec.STRING, Arguments.createArgumentCodec(genericCodec))
.xmap(ArgumentList::new, list -> list.arguments);
}
}
@@ -1,15 +0,0 @@
package io.github.skippyall.minions.program.argument;
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 abstract class ArgumentType<R extends InstructionRuntime<R>> {
public abstract <T> Codec<? extends Argument<T,R>> getCodec(ValueType<T> type);
public abstract <T> CompletableFuture<? extends Argument<T,R>> openConfiguration(ServerPlayerEntity player, ValueType<T> valueType, @Nullable Argument<T,R> previous);
}
@@ -1,19 +0,0 @@
package io.github.skippyall.minions.program.argument;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.program.InstructionRuntime;
public class Arguments {
public static <R extends InstructionRuntime<R>> Codec<Argument<?,R>> createArgumentCodec(Codec<ArgumentType<R>> codec) {
return codec.dispatch(
"type",
Argument::getType,
type ->
MinionRegistries.VALUE_TYPES.getCodec().<Argument<?,R>>dispatch(
Argument::getValueType,
valueType -> type.getCodec(valueType).fieldOf("valueType")
).fieldOf("valueType")
);
}
}
@@ -0,0 +1,45 @@
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;
/**
* An <code>ValueSupplier</code> can be supplied to an instruction with a matching parameter.
* Its value is resolved at runtime and can vary between executions.
* <code>ValueSupplier</code>s are created exclusively by <code>SpecificArgumentType</code>s.
* @param <T> The type of the <code>ValueSupplier</code>'s value
*/
public interface ValueConsumer<T,R extends InstructionRuntime<R>> {
void consume(T value, R runtime);
GuiDisplay getDisplay();
ValueType<T> getValueType();
ValueConsumerType<R> getType();
default <U,A extends ValueConsumer<U,R>> @Nullable A cast(ValueType<U> type) {
if(getValueType() == type) {
//noinspection unchecked
return (A) this;
} else {
return null;
}
}
static <R extends InstructionRuntime<R>> Codec<ValueConsumer<?,R>> createValueConsumerCodec(Codec<ValueConsumerType<R>> codec) {
return codec.dispatch(
"type",
ValueConsumer::getType,
type ->
MinionRegistries.VALUE_TYPES.getCodec().<ValueConsumer<?,R>>dispatch(
ValueConsumer::getValueType,
valueType -> type.getCodec(valueType).fieldOf("valueType")
).fieldOf("valueType")
);
}
}
@@ -1,8 +1,8 @@
package io.github.skippyall.minions.program.returnvalue;
package io.github.skippyall.minions.program.consumer;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.Parameter;
import io.github.skippyall.minions.program.supplier.Parameter;
import java.util.HashMap;
import java.util.Map;
@@ -27,8 +27,15 @@ public class ValueConsumerList<R extends InstructionRuntime<R>> {
valueConsumers.put(parameter.name(), consumer);
}
public static <R extends InstructionRuntime<R>> Codec<ValueConsumerList<R>> getCodec(Codec<ValueConsumerType<R>> genericCodec) {
return Codec.unboundedMap(Codec.STRING, ValueConsumers.createValueConsumersCodec(genericCodec))
public <T> void setValue(Parameter<T> parameter, T value, R runtime) {
ValueConsumer<T,R> consumer = getValueConsumer(parameter).cast(parameter.type());
if (consumer != null) {
consumer.consume(value, runtime);
}
}
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,8 +1,7 @@
package io.github.skippyall.minions.program.returnvalue;
package io.github.skippyall.minions.program.consumer;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.Argument;
import io.github.skippyall.minions.program.value.ValueType;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
@@ -2,33 +2,36 @@ package io.github.skippyall.minions.program.instruction;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.ArgumentList;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import org.jetbrains.annotations.Nullable;
public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
private final InstructionType<R> instruction;
private final ArgumentList<R> arguments;
private final ValueSupplierList<R> arguments;
private final ValueConsumerList<R> valueConsumers;
private @Nullable InstructionExecution<R> execution;
private final String name;
private ConfiguredInstruction(InstructionType<R> instruction, ArgumentList<R> arguments, @Nullable InstructionExecution<R> execution, String name) {
private ConfiguredInstruction(InstructionType<R> instruction, ValueSupplierList<R> arguments, ValueConsumerList<R> valueConsumers, @Nullable InstructionExecution<R> execution, String name) {
this.instruction = instruction;
this.arguments = arguments;
this.valueConsumers = valueConsumers;
this.execution = execution;
this.name = name;
}
public ConfiguredInstruction(InstructionType<R> instruction, String name) {
this(instruction, new ArgumentList<>(), null, name);
this(instruction, new ValueSupplierList<>(), new ValueConsumerList<>(), null, name);
}
public InstructionType<R> getInstruction() {
return instruction;
}
public ArgumentList<R> getArguments() {
public ValueSupplierList<R> getArguments() {
return arguments;
}
@@ -75,7 +78,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
public void stop(R minion) {
if(isRunning()) {
execution.stop(minion);
execution.stop(minion, valueConsumers);
execution = null;
}
}
@@ -83,6 +86,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
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()) {
execution.save(view.get("execution"), minion);
@@ -92,7 +96,8 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
public static <R extends InstructionRuntime<R>> ConfiguredInstruction<R> load(ReadView view, R minion, String name) {
InstructionType<R> instructionType = view.read("instruction", minion.getInstructionTypeRegistry().getCodec()).orElseThrow();
ArgumentList<R> arguments = view.read("arguments", minion.getArgumentListCodec()).orElseThrow();
ValueSupplierList<R> arguments = view.read("arguments", minion.getArgumentListCodec()).orElseGet(ValueSupplierList::new);
ValueConsumerList<R> valueConsumers = view.read("valueConsumers", minion.getValueConsumerListCodec()).orElseGet(ValueConsumerList::new);
boolean running = view.getBoolean("running", false);
@@ -100,12 +105,12 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
ReadView executionView = view.getReadView("execution");
try {
InstructionExecution<R> execution = instructionType.loadExecution(executionView, minion);
return new ConfiguredInstruction<>(instructionType, arguments, execution, name);
return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, execution, name);
} catch (Exception e) {
Minions.LOGGER.error("Error while loading execution", e);
}
}
return new ConfiguredInstruction<>(instructionType, arguments, null, name);
return new ConfiguredInstruction<>(instructionType, arguments, valueConsumers, null, name);
}
}
@@ -1,7 +1,8 @@
package io.github.skippyall.minions.program.instruction;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.ArgumentList;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
@@ -9,7 +10,7 @@ import net.minecraft.storage.WriteView;
* Responsible for executing instructions.
* When an instruction is executed:
* <li>A new instance is created using the factory</li>
* <li>{@link InstructionExecution#readArguments(ArgumentList, R) readFromParameters} is called</li>
* <li>{@link InstructionExecution#readArguments(ValueSupplierList, R) readFromParameters} is called</li>
* <li>{@link InstructionExecution#start(R) start} is called</li>
*/
public interface InstructionExecution<R extends InstructionRuntime<R>> {
@@ -32,19 +33,18 @@ public interface InstructionExecution<R extends InstructionRuntime<R>> {
/**
* Stops this execution. Is called when isDone returns true, but it may also be called before that.
* In this case, the return value is ignored.
* This should undo changes to the minion unless they are supposed to be permanent.
*
* @param runtime The runtime that was executing this instruction.
*/
void stop(R runtime);
void stop(R runtime, ValueConsumerList<R> valueConsumers);
/**
* Initializes the execution with its arguments.
* Initializes the execution with its parameters. The parameters must be defined by the InstructionType
* @param arguments The arguments to initialize the execution
* @param runtime The runtime should be used to resolve the arguments
*/
void readArguments(ArgumentList<R> arguments, R runtime);
void readArguments(ValueSupplierList<R> arguments, R runtime);
/**
* Saves the execution, e.g. when the server is closed.
@@ -2,9 +2,8 @@ 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.argument.Parameter;
import io.github.skippyall.minions.program.argument.ArgumentList;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.program.supplier.Parameter;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import net.minecraft.storage.ReadView;
import java.util.Collection;
@@ -14,31 +13,29 @@ 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 Supplier<InstructionExecution<R>> executionFactory;
private InstructionType(GuiDisplay display, Supplier<InstructionExecution<R>> executionFactory, Collection<Parameter<?>> parameters) {
public InstructionType(GuiDisplay display, Supplier<InstructionExecution<R>> executionFactory, Collection<Parameter<?>> parameters, Collection<Parameter<?>> returnParameters) {
this.display = display;
this.parameters = parameters;
this.parameters = List.copyOf(parameters);
this.returnParameters = List.copyOf(returnParameters);
this.executionFactory = executionFactory;
}
public static <R extends InstructionRuntime<R>> InstructionType<R> create(GuiDisplay display, Supplier<InstructionExecution<R>> executionFactory, Collection<Parameter<?>> parameters) {
return new InstructionType<>(display, executionFactory, List.copyOf(parameters));
}
public static <Return,R extends InstructionRuntime<R>> InstructionType<R> create(GuiDisplay display, Supplier<InstructionExecution<R>> executionFactory, ValueType<Return> returnType, Parameter<?>... parameters) {
return new InstructionType<>(display, executionFactory, List.of(parameters));
}
public Collection<Parameter<?>> getParameters() {
return parameters;
}
public Collection<Parameter<?>> getReturnParameters() {
return returnParameters;
}
public GuiDisplay getDisplay() {
return display;
}
public InstructionExecution<R> createExecution(ArgumentList<R> parameters, R minion) {
public InstructionExecution<R> createExecution(ValueSupplierList<R> parameters, R minion) {
InstructionExecution<R> execution = executionFactory.get();
execution.readArguments(parameters, minion);
return execution;
@@ -3,47 +3,60 @@ 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.minion.fakeplayer.MinionFakePlayer;
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.argument.Parameter;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.program.value.ValueTypes;
import io.github.skippyall.minions.program.supplier.Parameter;
import io.github.skippyall.minions.util.ModelIdUtil;
import net.minecraft.item.Items;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
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<Void, MinionFakePlayer> WALK = register(
public static final InstructionType<MinionRuntime> WALK = register(
"walk",
base -> new GuiDisplay.ModelBased(ModelIdUtil.getItemModelId(Items.IRON_BOOTS), base, true),
WalkExecution::new,
ValueTypes.VOID,
WalkExecution.blocksToMoveParam
List.of(WalkExecution.blocksToMoveParam)
);
public static final InstructionType<Void, MinionFakePlayer> ATTACK = register(
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> ATTACK = register(
"attack",
base -> new GuiDisplay.ModelBased(ModelIdUtil.getItemModelId(Items.IRON_BOOTS), base, true),
() -> new ActionExecution(EntityPlayerActionPack.ActionType.ATTACK),
ValueTypes.VOID
() -> new ActionExecution(EntityPlayerActionPack.ActionType.ATTACK)
);
public static final InstructionType<Void, MinionFakePlayer> USE = register(
public static final InstructionType<MinionRuntime> USE = register(
"use",
base -> new GuiDisplay.ModelBased(ModelIdUtil.getItemModelId(Items.LEVER), base, true),
() -> new ActionExecution(EntityPlayerActionPack.ActionType.USE),
ValueTypes.VOID
() -> new ActionExecution(EntityPlayerActionPack.ActionType.USE)
);
private static <R> InstructionType<R, MinionFakePlayer> register(String id, Function<String, GuiDisplay> displayFunction, Supplier<InstructionExecution<R,MinionFakePlayer>> factory, ValueType<R> returnType, Parameter<?>... parameters) {
private static InstructionType<MinionRuntime> register(String id, Function<String, GuiDisplay> displayFunction, 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, InstructionType.create(displayFunction.apply(identifier.toTranslationKey("instruction_type")), factory, returnType, parameters));
return Registry.register(MinionRegistries.INSTRUCTION_TYPES, identifier, new InstructionType<>(displayFunction.apply(identifier.toTranslationKey("instruction_type")), 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, Function<String, GuiDisplay> displayFunction, Supplier<InstructionExecution<MinionRuntime>> factory) {
return register(id, displayFunction, factory, List.of(), List.of());
}
public static void register() {
@@ -1,12 +1,13 @@
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.argument.ArgumentList;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
public class ActionExecution implements ContinuousInstructionExecution {
public class ActionExecution implements ContinuousInstructionExecution<MinionRuntime> {
private final EntityPlayerActionPack.ActionType action;
public ActionExecution(EntityPlayerActionPack.ActionType action) {
@@ -14,24 +15,23 @@ public class ActionExecution implements ContinuousInstructionExecution {
}
@Override
public void start(MinionFakePlayer minion) {
minion.getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
public void start(MinionRuntime minion) {
minion.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
}
@Override
public Void stop(MinionFakePlayer minion) {
minion.getMinionActionPack().stop(action);
return null;
public void stop(MinionRuntime minion, ValueConsumerList<MinionRuntime> valueConsumers) {
minion.getMinion().getMinionActionPack().stop(action);
}
@Override
public void readArguments(ArgumentList parameters, MinionFakePlayer minion) {}
public void readArguments(ValueSupplierList<MinionRuntime> parameters, MinionRuntime minion) {}
@Override
public void save(WriteView view, MinionFakePlayer minion) {}
public void save(WriteView view, MinionRuntime minion) {}
@Override
public void load(ReadView view, MinionFakePlayer minion) {
minion.getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
public void load(ReadView view, MinionRuntime minion) {
minion.getMinion().getMinionActionPack().start(action, EntityPlayerActionPack.Action.continuous());
}
}
@@ -1,11 +1,11 @@
package io.github.skippyall.minions.program.instruction.execution;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
public interface ContinuousInstructionExecution extends InstructionExecution<Void,MinionFakePlayer> {
public interface ContinuousInstructionExecution<R extends InstructionRuntime<R>> extends InstructionExecution<R> {
@Override
default boolean isDone(MinionFakePlayer minion) {
default boolean isDone(R minion) {
return false;
}
}
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.program.instruction.execution;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
@@ -10,7 +10,7 @@ import net.minecraft.storage.WriteView;
* The timer must be set with <code>setTimer</code> when reading from parameters.
* Saving and loading of the timer is automatic if the super method is called by the subclass.
*/
public abstract class TimedInstructionExecution<T> implements InstructionExecution<T,MinionFakePlayer> {
public abstract class TimedInstructionExecution<R extends InstructionRuntime<R>> implements InstructionExecution<R> {
int timer;
public int getTimer() {
@@ -22,22 +22,22 @@ public abstract class TimedInstructionExecution<T> implements InstructionExecuti
}
@Override
public void tick(MinionFakePlayer minion) {
public void tick(R minion) {
timer--;
}
@Override
public boolean isDone(MinionFakePlayer minion) {
public boolean isDone(R minion) {
return timer > 0;
}
@Override
public void save(WriteView view, MinionFakePlayer minion) {
public void save(WriteView view, R minion) {
view.putInt("timer", timer);
}
@Override
public void load(ReadView view, MinionFakePlayer minion) {
public void load(ReadView view, R minion) {
timer = view.getInt("timer", 0);
}
}
@@ -3,9 +3,10 @@ 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.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.argument.ArgumentList;
import io.github.skippyall.minions.program.argument.Parameter;
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;
@@ -14,7 +15,7 @@ import net.minecraft.util.StringIdentifiable;
import java.util.UUID;
public class TurnExecution implements InstructionExecution<Void,MinionFakePlayer> {
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);
@@ -25,37 +26,36 @@ public class TurnExecution implements InstructionExecution<Void,MinionFakePlayer
private TurnDirection direction;
@Override
public void tick(MinionFakePlayer minion) {
public void tick(MinionRuntime minion) {
float toRotate = Math.min(anglePerTick, maxAngle - rotatedAngle);
minion.getMinionActionPack().turn(direction.xFactor * toRotate, direction.yFactor * toRotate);
minion.getMinion().getMinionActionPack().turn(direction.xFactor * toRotate, direction.yFactor * toRotate);
rotatedAngle += toRotate;
}
@Override
public boolean isDone(MinionFakePlayer minion) {
public boolean isDone(MinionRuntime minion) {
return Math.abs(maxAngle - rotatedAngle) < 0.001F;
}
@Override
public Void stop(MinionFakePlayer minion) {
return null;
public void stop(MinionRuntime minion, ValueConsumerList<MinionRuntime> valueConsumers) {
}
@Override
public void readArguments(ArgumentList<MinionFakePlayer> arguments, MinionFakePlayer minion) {
public void readArguments(ValueSupplierList<MinionRuntime> arguments, MinionRuntime minion) {
maxAngle = arguments.getValue(ANGLE, minion);
direction = arguments.getValue(DIRECTION, minion);
}
@Override
public void save(WriteView view, MinionFakePlayer minion) {
public void save(WriteView view, MinionRuntime minion) {
view.putFloat("maxAngle", maxAngle);
view.put("direction", TurnDirection.CODEC, direction);
}
@Override
public void load(ReadView view, MinionFakePlayer minion) {
public void load(ReadView view, MinionRuntime minion) {
maxAngle = view.getFloat("maxAngle", 0);
direction = view.read("direction", TurnDirection.CODEC).orElseThrow();
}
@@ -1,52 +1,53 @@
package io.github.skippyall.minions.program.instruction.execution;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
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.argument.Parameter;
import io.github.skippyall.minions.program.argument.ArgumentList;
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.entity.MovementType;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
public class WalkExecution implements InstructionExecution<Void,MinionFakePlayer> {
public class WalkExecution implements InstructionExecution<MinionRuntime> {
public static final Parameter<Float> blocksToMoveParam = new Parameter<>("blocksToMove", ValueTypes.FLOAT);
private final float ACCURACY = 1F / 32F;
private static final float ACCURACY = 1F / 32F;
private float totalBlocksToMove;
private float blocksMoved;
@Override
public void tick(MinionFakePlayer minion) {
float speed = Math.min(minion.getMovementSpeed(), totalBlocksToMove - blocksMoved);
minion.move(MovementType.SELF, minion.getHorizontalFacing().getDoubleVector().normalize().multiply(speed));
public void tick(MinionRuntime minion) {
float speed = Math.min(minion.getMinion().getMovementSpeed(), totalBlocksToMove - blocksMoved);
minion.getMinion().move(MovementType.SELF, minion.getMinion().getHorizontalFacing().getDoubleVector().normalize().multiply(speed));
blocksMoved += speed;
}
@Override
public boolean isDone(MinionFakePlayer minion) {
public boolean isDone(MinionRuntime minion) {
return totalBlocksToMove - blocksMoved < ACCURACY;
}
@Override
public Void stop(MinionFakePlayer minion) {
return null;
public void stop(MinionRuntime minion, ValueConsumerList<MinionRuntime> valueConsumers) {
}
@Override
public void readArguments(ArgumentList<MinionFakePlayer> parameters, MinionFakePlayer minion) {
public void readArguments(ValueSupplierList<MinionRuntime> parameters, MinionRuntime minion) {
totalBlocksToMove = parameters.getValue(blocksToMoveParam, minion);
blocksMoved = 0;
}
@Override
public void save(WriteView view, MinionFakePlayer minion) {
public void save(WriteView view, MinionRuntime minion) {
view.putFloat("totalBlocksToMove", totalBlocksToMove);
view.putFloat("blocksMoved", blocksMoved);
}
@Override
public void load(ReadView view, MinionFakePlayer minion) {
public void load(ReadView view, MinionRuntime minion) {
totalBlocksToMove = view.getFloat("totalBlocksToMove", 0F);
blocksMoved = view.getFloat("blocksMoved", 0F);
}
@@ -1,32 +0,0 @@
package io.github.skippyall.minions.program.returnvalue;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.ArgumentType;
import io.github.skippyall.minions.program.value.ValueType;
import org.jetbrains.annotations.Nullable;
/**
* An <code>Argument</code> can be supplied to an instruction with a matching parameter.
* Its value is resolved at runtime and can vary between executions.
* <code>Argument</code>s are created exclusively by <code>SpecificArgumentType</code>s.
* @param <T> The type of the <code>Argument</code>'s value
*/
public interface ValueConsumer<T,R extends InstructionRuntime<R>> {
void consume(T value, R runtime);
GuiDisplay getDisplay();
ValueType<T> getValueType();
ValueConsumerType<R> getType();
default <U,A extends ValueConsumer<U,R>> @Nullable A cast(ValueType<U> type) {
if(getValueType() == type) {
//noinspection unchecked
return (A) this;
} else {
return null;
}
}
}
@@ -1,24 +0,0 @@
package io.github.skippyall.minions.program.returnvalue;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.argument.Argument;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class ValueConsumers {
public static <R extends InstructionRuntime<R>> Codec<ValueConsumer<?,R>> createValueConsumersCodec(Codec<ValueConsumerType<R>> codec) {
return codec.dispatch(
"type",
ValueConsumer::getType,
type ->
MinionRegistries.VALUE_TYPES.getCodec().<ValueConsumer<?,R>>dispatch(
ValueConsumer::getValueType,
valueType -> type.getCodec(valueType).fieldOf("valueType")
).fieldOf("valueType")
);
}
}
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.program.argument;
package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
@@ -1,14 +1,13 @@
package io.github.skippyall.minions.program.argument;
package io.github.skippyall.minions.program.supplier;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.ValueType;
/**
* An argument that always resolves to a fixed value
* An supplier that always resolves to a fixed value
*/
public class ValueArgument<T, R extends InstructionRuntime<R>> implements Argument<T, R> {
public class ValueArgument<T, R extends InstructionRuntime<R>> implements ValueSupplier<T, R> {
private final ValueArgumentType<R> type;
private final ValueType<T> valueType;
private final T value;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.program.argument;
package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.InstructionRuntime;
@@ -8,17 +8,17 @@ import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class ValueArgumentType<R extends InstructionRuntime<R>> extends ArgumentType<R> {
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 Argument<V,R> previousArgument) {
public <V> CompletableFuture<ValueArgument<V,R>> openConfiguration(ServerPlayerEntity player, ValueType<V> valueType, @Nullable ValueSupplier<V,R> previousValueSupplier) {
return valueType.openValueDialog(
player,
previousArgument instanceof ValueArgument<V,R> val ? val.getValue() : valueType.defaultValue()
previousValueSupplier instanceof ValueArgument<V,R> val ? val.getValue() : valueType.defaultValue()
).thenApply(value -> new ValueArgument<>(this, valueType, value));
}
}
@@ -0,0 +1,45 @@
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;
/**
* An <code>ValueSupplier</code> can be supplied to an instruction with a matching parameter.
* Its value is resolved at runtime and can vary between executions.
* <code>ValueSupplier</code>s are created exclusively by <code>SpecificArgumentType</code>s.
* @param <T> The type of the <code>ValueSupplier</code>'s value
*/
public interface ValueSupplier<T, R extends InstructionRuntime<R>> {
T resolve(R minion);
GuiDisplay getDisplay();
ValueType<T> getValueType();
ValueSupplierType<R> getType();
default <U,A extends ValueSupplier<U,R>> @Nullable A cast(ValueType<U> type) {
if(getValueType() == type) {
//noinspection unchecked
return (A) this;
} else {
return null;
}
}
static <R extends InstructionRuntime<R>> Codec<ValueSupplier<?,R>> createArgumentCodec(Codec<ValueSupplierType<R>> codec) {
return codec.dispatch(
"type",
ValueSupplier::getType,
type ->
MinionRegistries.VALUE_TYPES.getCodec().<ValueSupplier<?,R>>dispatch(
ValueSupplier::getValueType,
valueType -> type.getCodec(valueType).fieldOf("valueType")
).fieldOf("valueType")
);
}
}
@@ -0,0 +1,52 @@
package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.InstructionRuntime;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class ValueSupplierList<R extends InstructionRuntime<R>> {
private final Map<String, ValueSupplier<?, R>> arguments;
public ValueSupplierList() {
arguments = new HashMap<>();
}
public ValueSupplierList(Map<String, ValueSupplier<?,R>> arguments) {
this.arguments = new HashMap<>(arguments);
}
public <T> T getValue(Parameter<T> parameter, R runtime) {
ValueSupplier<T,R> valueSupplier = getArgument(parameter);
return valueSupplier != null ? valueSupplier.resolve(runtime) : null;
}
public <T, A extends ValueSupplier<T,R>> A getArgument(Parameter<T> parameter) {
ValueSupplier<?,R> valueSupplier = arguments.get(parameter.name());
return valueSupplier == null ? null : valueSupplier.cast(parameter.type());
}
public <T> void setArgument(Parameter<T> parameter, ValueSupplier<T,R> valueSupplier) {
arguments.put(parameter.name(), valueSupplier);
}
public boolean hasArgumentFor(Parameter<?> parameter) {
return getArgument(parameter) != null;
}
public boolean hasArgumentForAll(Collection<Parameter<?>> checkParameters) {
for(Parameter<?> parameter : checkParameters) {
if(!hasArgumentFor(parameter)) {
return false;
}
}
return true;
}
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,15 @@
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 abstract class ValueSupplierType<R extends InstructionRuntime<R>> {
public abstract <T> Codec<? extends ValueSupplier<T,R>> getCodec(ValueType<T> type);
public abstract <T> CompletableFuture<? extends ValueSupplier<T,R>> openConfiguration(ServerPlayerEntity player, ValueType<T> valueType, @Nullable ValueSupplier<T,R> previous);
}
@@ -0,0 +1,5 @@
{
"values": [
"minions:minion_trigger"
]
}