Convert even more

This commit is contained in:
skippyall
2026-04-05 01:17:53 +02:00
parent 7acd083e79
commit 324fea04a9
34 changed files with 858 additions and 278 deletions
@@ -0,0 +1,54 @@
package io.github.skippyall.minions.gui;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
public abstract class MinionsGui {
protected final @Nullable MinionsGui parent;
protected final ServerPlayerEntity viewer;
private @Nullable MinionsGui child = null;
private boolean open = true;
public MinionsGui(MinionsGui parent) {
this.viewer = parent.viewer;
this.parent = parent;
parent.child = this;
open();
}
public MinionsGui(ServerPlayerEntity viewer) {
this.viewer = viewer;
this.parent = null;
open();
}
protected void open() {}
protected void reopen() {
open();
}
public void onBackingClosed() {
if(child != null && child.open) {
return;
}
close();
}
public void close() {
if(open) {
if(child != null) {
child.close();
}
if(parent != null) {
parent.child = null;
parent.reopen();
}
onClose();
open = false;
}
}
protected void onClose() {}
}
@@ -0,0 +1,59 @@
package io.github.skippyall.minions.gui;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SlotGuiInterface;
import io.github.skippyall.minions.gui.minion.MinionGui;
import net.minecraft.item.Items;
import net.minecraft.text.Text;
import net.minecraft.util.collection.IndexedIterable;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public class PaginatedList {
public static void createList(SlotGuiInterface gui, int size, Function<Integer, GuiElementBuilder> display, Runnable onBack) {
AtomicInteger page = new AtomicInteger(0);
addItems(page, gui, size, display);
gui.setSlot(27, MinionGui.backButton(onBack));
}
public static <T> void createList(SlotGuiInterface gui, List<T> list, Function<T, GuiElementBuilder> display, Runnable onBack) {
createList(gui, list.size(), i -> display.apply(list.get(i)), onBack);
}
public static <T> void createList(SlotGuiInterface gui, IndexedIterable<T> list, Function<T, GuiElementBuilder> display, Runnable onBack) {
createList(gui, list.size(), i -> display.apply(list.get(i)), onBack);
}
private static void addItems(AtomicInteger page, SlotGuiInterface gui, int size, Function<Integer, GuiElementBuilder> display) {
for(int i = 27 * page.get(); i < Math.min(27 * (page.get() + 1), size); i++) {
gui.addSlot(display.apply(i));
}
if(page.get() > 0) {
gui.setSlot(30, new GuiElementBuilder(Items.SPECTRAL_ARROW)
.setItemName(Text.translatable("book.page_button.previous"))
.setCallback(() -> {
page.decrementAndGet();
addItems(page, gui, size, display);
})
);
} else {
gui.clearSlot(30);
}
if(27 * (page.get() + 1) < size) {
gui.setSlot(32, new GuiElementBuilder(Items.ARROW)
.setItemName(Text.translatable("book.page_button.next"))
.setCallback(() -> {
page.incrementAndGet();
addItems(page, gui, size, display);
})
);
} else {
gui.clearSlot(32);
}
}
}
@@ -25,11 +25,11 @@ public interface Result<T, E> {
boolean isSuccess(); boolean isSuccess();
@NotNull T getOrDefault(@NotNull T defaultValue); T getOrDefault(T defaultValue);
@NotNull T getOrThrow(); T getOrThrow();
@NotNull E getErrorOrThrow(); E getErrorOrThrow();
@NotNull Optional<T> getOptional(); @NotNull Optional<T> getOptional();
@@ -37,24 +37,24 @@ public interface Result<T, E> {
void ifError(@NotNull Consumer<Error<T, E>> handler); void ifError(@NotNull Consumer<Error<T, E>> handler);
record Success<T, E>(@NotNull T result) implements Result<T, E> { record Success<T, E>(T result) implements Result<T, E> {
@Override @Override
public boolean isSuccess() { public boolean isSuccess() {
return true; return true;
} }
@Override @Override
public @NotNull T getOrDefault(@NotNull T defaultValue) { public T getOrDefault(T defaultValue) {
return result; return result;
} }
@Override @Override
public @NotNull T getOrThrow() { public T getOrThrow() {
return result; return result;
} }
@Override @Override
public @NotNull E getErrorOrThrow() { public E getErrorOrThrow() {
throw new RuntimeException("Result was not an Error"); throw new RuntimeException("Result was not an Error");
} }
@@ -74,24 +74,25 @@ public interface Result<T, E> {
} }
} }
record Error<T, E>(@NotNull E message) implements Result<T, E> { record Error<T, E>(E message) implements Result<T, E> {
@Override @Override
public boolean isSuccess() { public boolean isSuccess() {
return false; return false;
} }
@Override @Override
public @NotNull T getOrDefault(@NotNull T defaultValue) { public T getOrDefault(
T defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public @NotNull T getOrThrow() { public T getOrThrow() {
throw new RuntimeException("Result was an error: " + message); throw new RuntimeException("Result was an error: " + message);
} }
@Override @Override
public @NotNull E getErrorOrThrow() { public E getErrorOrThrow() {
return message; return message;
} }
@@ -0,0 +1,81 @@
package io.github.skippyall.minions.gui.instruction;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import io.github.skippyall.minions.program.supplier.Parameter;
import io.github.skippyall.minions.program.supplier.ValueSupplier;
import io.github.skippyall.minions.program.supplier.ValueSupplierType;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.util.TranslationUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class ArgumentGui {
public static <T, A extends ValueSupplier<T, MinionRuntime>> void configureArgumentMenu(String instructionName, ConfiguredInstruction<MinionRuntime> instruction, Parameter<?> parameter, MinionFakePlayer minion, ServerPlayerEntity player) {
if (!InstructionGui.checkInstructionExists(instructionName, instruction, minion, player)) {
return;
}
@Nullable ValueSupplier<?, MinionRuntime> argument = instruction.getArguments().getArgument(parameter);
if(argument == null) {
configureTypeAndValue(instructionName, instruction, parameter, minion, player);
return;
}
configureArgumentHelper(instructionName, instruction, parameter, argument, minion, player);
}
private static <F, T> void configureArgumentHelper(String instructionName, ConfiguredInstruction<MinionRuntime> instruction, Parameter<T> parameter, ValueSupplier<F, MinionRuntime> argument, MinionFakePlayer minion, ServerPlayerEntity player) {
SimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion, instruction);
ItemStack displayStack = GuiDisplay.getDisplayStack(MinionRegistries.VALUE_SUPPLIER_TYPES, argument.getType(), player.getRegistryManager());
gui.setSlot(3, new GuiElementBuilder(displayStack)
.setName(Text.translatable("minions.gui.instruction.argument.configure.type", Text.translatable(TranslationUtil.getTranslationKey(argument.getType(), MinionRegistries.VALUE_SUPPLIER_TYPES, "minions.gui.instruction.argument.configure.type.unset"))))
.setCallback(() -> configureTypeAndValue(instructionName, instruction, parameter, minion, player))
);
gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID)
.setName(Text.literal("Configure"))
.setCallback(() -> argument.getType().openConfiguration(player, argument.getValueType(), argument)
.thenAccept(newArgument -> {
instruction.getArguments().setArgument(parameter, newArgument);
configureArgumentMenu(instructionName, instruction, parameter, minion, player);
})
)
);
gui.open();
}
public static CompletableFuture<ValueSupplierType<MinionRuntime>> selectArgumentType(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> instruction) {
CompletableFuture<ValueSupplierType<MinionRuntime>> future = new CompletableFuture<>();
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))
);
}
gui.open();
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(v -> {
instruction.getArguments().setArgument(parameter, v);
configureArgumentMenu(name, instruction, parameter, minion, player);
})
);
}
}
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.gui; package io.github.skippyall.minions.gui.instruction;
import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.elements.GuiElementBuilder;
import io.github.skippyall.minions.gui.input.ChoiceInput; import io.github.skippyall.minions.gui.input.ChoiceInput;
@@ -108,7 +108,7 @@ public class ConfigureInstructionGui extends InstructionBoundSimpleGui {
int slot = 12; int slot = 12;
for(Parameter<?> parameter : instruction.getInstruction().getParameters().reversed()) { for(Parameter<?> parameter : instruction.getInstruction().getParameters().reversed()) {
setSlot(slot, InstructionGui.createParameterElement(parameter, instruction.getArguments().getArgument(parameter), player.getRegistryManager()) setSlot(slot, InstructionGui.createParameterElement(parameter, instruction.getArguments().getArgument(parameter), player.getRegistryManager())
.setCallback(() -> InstructionGui.configureArgumentMenu(name, instruction, parameter, minion, player)) .setCallback(() -> ArgumentGui.configureArgumentMenu(name, instruction, parameter, minion, player))
); );
slot--; slot--;
} }
@@ -0,0 +1,71 @@
package io.github.skippyall.minions.gui.instruction;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.gui.PaginatedList;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.conversion.ValueConverter;
import io.github.skippyall.minions.program.conversion.ValueConverterType;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import io.github.skippyall.minions.program.supplier.Parameter;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.registration.MinionRegistries;
import net.minecraft.item.Items;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
public class ConverterGui {
public static void configureConvertersMenu(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> instruction, Parameter<?> parameter, Runnable back) {
InstructionBoundSimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X4, player, minion, instruction);
ValueSupplierList.ValueSupplierEntry<?,?,MinionRuntime> entry = instruction.getArguments().getEntry(parameter);
PaginatedList.createList(
gui,
entry.getConverters(),
converter -> createConverterElement(converter, player.getRegistryManager())
.setCallback(() -> configureConverter(player, minion, instruction, parameter, entry, converter)),
back
);
gui.open();
}
public static GuiElementBuilder createConverterElement(ValueConverter<?,?> converter, DynamicRegistryManager manager) {
GuiElementBuilder builder = new GuiElementBuilder(GuiDisplay.getDisplayStack(MinionRegistries.VALUE_CONVERTER_TYPES, converter.getType(), manager));
builder.addLoreLine(converter.getDisplayText());
return builder;
}
public static void configureConverter(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> instruction, Parameter<?> parameter, ValueSupplierList.ValueSupplierEntry<?,?,MinionRuntime> entry, @Nullable ValueConverter<?,?> converter) {
InstructionBoundSimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion, instruction);
gui.setSlot(3, new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_CONVERTER_TYPES, converter.getType(), player.getRegistryManager()))
.setCallback(() -> {
InstructionBoundSimpleGui chooseTypeGui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X4, player, minion, instruction);
PaginatedList.createList(
chooseTypeGui,
MinionRegistries.VALUE_CONVERTER_TYPES,
type -> new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_CONVERTER_TYPES, type, player.getRegistryManager()))
.setCallback(() -> {
entry.set
}),
() -> configureConverter(player, minion, instruction, parameter, entry, converter)
);
}));
gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID));
gui.open();
}
public static <F,T> void selectConverterMenu(ValueType<F> from, ValueType<T> to, ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> instruction) {
InstructionBoundSimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_9X3, player, minion, instruction);
for(ValueConverterType<?> valueConverterType : MinionRegistries.VALUE_CONVERTER_TYPES) {
gui.addSlot(new GuiElementBuilder(GuiDisplay.getDisplayStackWithName(MinionRegistries.VALUE_CONVERTER_TYPES, valueConverterType, player.getRegistryManager()))
.setCallback(() -> valueConverterType.configure(player, from, to, null).thenAccept(c -> )));
}
gui.open();
}
}
@@ -1,5 +1,6 @@
package io.github.skippyall.minions.gui; package io.github.skippyall.minions.gui.instruction;
import io.github.skippyall.minions.gui.MinionBoundSimpleGui;
import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction; import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
@@ -1,7 +1,9 @@
package io.github.skippyall.minions.gui; package io.github.skippyall.minions.gui.instruction;
import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui; import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.gui.MinionBoundSimpleGui;
import io.github.skippyall.minions.registration.MinionComponentTypes; import io.github.skippyall.minions.registration.MinionComponentTypes;
import io.github.skippyall.minions.registration.MinionRegistries; import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.gui.input.Result; import io.github.skippyall.minions.gui.input.Result;
@@ -10,7 +12,6 @@ import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.module.MinionModule; import io.github.skippyall.minions.module.MinionModule;
import io.github.skippyall.minions.program.supplier.ValueSupplier; 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.ConfiguredInstruction;
import io.github.skippyall.minions.program.instruction.InstructionType; import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.program.supplier.Parameter; import io.github.skippyall.minions.program.supplier.Parameter;
@@ -99,62 +100,6 @@ public class InstructionGui {
return stillExists; return stillExists;
} }
public static <T, A extends ValueSupplier<T, MinionRuntime>> void configureArgumentMenu(String instructionName, ConfiguredInstruction<MinionRuntime> instruction, Parameter<?> parameter, MinionFakePlayer minion, ServerPlayerEntity player) {
if (!checkInstructionExists(instructionName, instruction, minion, player)) {
return;
}
@Nullable ValueSupplier<?, MinionRuntime> argument = instruction.getArguments().getArgument(parameter);
if(argument == null) {
configureTypeAndValue(instructionName, instruction, parameter, minion, player);
return;
}
configureArgumentHelper(instructionName, instruction, parameter, argument, minion, player);
}
private static <T> void configureArgumentHelper(String instructionName, ConfiguredInstruction<MinionRuntime> instruction, Parameter<?> parameter, ValueSupplier<T, MinionRuntime> argument, MinionFakePlayer minion, ServerPlayerEntity player) {
SimpleGui gui = new InstructionBoundSimpleGui(ScreenHandlerType.GENERIC_3X3, player, minion, instruction);
ItemStack displayStack = GuiDisplay.getDisplayStack(MinionRegistries.VALUE_SUPPLIER_TYPES, argument.getType(), player.getRegistryManager());
gui.setSlot(3, new GuiElementBuilder(displayStack)
.setName(Text.translatable("minions.gui.instruction.argument.configure.type", Text.translatable(TranslationUtil.getTranslationKey(argument.getType(), MinionRegistries.VALUE_SUPPLIER_TYPES, "minions.gui.instruction.argument.configure.type.unset"))))
.setCallback(() -> configureTypeAndValue(instructionName, instruction, parameter, minion, player))
);
gui.setSlot(5, new GuiElementBuilder(Items.STRUCTURE_VOID)
.setName(Text.literal("Configure"))
.setCallback(() -> argument.getType().openConfiguration(player, argument.getValueType(), argument)
.thenAccept(newArgument -> instruction.getArguments().setArgument(parameter, newArgument))
)
);
gui.open();
}
public static CompletableFuture<ValueSupplierType<MinionRuntime>> selectArgumentType(ServerPlayerEntity player, MinionFakePlayer minion, ConfiguredInstruction<MinionRuntime> instruction) {
CompletableFuture<ValueSupplierType<MinionRuntime>> future = new CompletableFuture<>();
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))
);
}
gui.open();
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) { public static CompletableFuture<InstructionType<MinionRuntime>> selectInstructionModuleMenu(MinionFakePlayer minion, ServerPlayerEntity player) {
if (minion.getModuleInventory().getModules().isEmpty()) { if (minion.getModuleInventory().getModules().isEmpty()) {
player.sendMessage(Text.translatable("minions.gui.instruction.no_modules")); player.sendMessage(Text.translatable("minions.gui.instruction.no_modules"));
@@ -0,0 +1,93 @@
package io.github.skippyall.minions.gui.minion;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.gui.MinionsGui;
import io.github.skippyall.minions.gui.instruction.InstructionGui;
import io.github.skippyall.minions.minion.MinionListener;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.module.ModuleInventory;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.slot.ArmorSlot;
import net.minecraft.screen.slot.Slot;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public class MinionGui extends MinionsGui implements MinionListener {
private final MinionFakePlayer minion;
private SimpleGui gui;
public MinionGui(ServerPlayerEntity viewer, MinionFakePlayer minion) {
super(viewer);
this.minion = minion;
minion.addMinionListener(this);
}
public MinionFakePlayer getMinion() {
return minion;
}
@Override
protected void open() {
gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, viewer, false) {
@Override
public void onClose() {
onBackingClosed();
}
};
gui.setTitle(minion.getName());
gui.setSlot(1, new GuiElementBuilder()
.setItem(Items.COMMAND_BLOCK)
.setName(Text.translatable("minions.gui.main.instructions"))
.setCallback(() -> {
InstructionGui.openInstructionMainMenu(minion, viewer);
})
);
gui.setSlot(3, new GuiElementBuilder()
.setItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE)
.setName(Text.translatable("minions.gui.main.modules"))
.setCallback(() -> {
ModuleInventory.openModuleInventory(viewer, minion);
})
);
gui.setSlot(5, new GuiElementBuilder()
.setItem(Items.CHEST)
.setName(Text.translatable("minions.gui.main.inventory"))
.setCallback(() -> new MinionInventoryGui(this))
);
gui.setSlot(7, new GuiElementBuilder()
.setItem(Items.BARRIER)
.setName(Text.translatable("minions.gui.main.pickup"))
.setCallback(() -> minion.kill(minion.getWorld()))
);
gui.open();
}
@Override
protected void reopen() {
gui.open();
}
@Override
protected void onClose() {
gui.close();
minion.removeMinionListener(this);
}
@Override
public void onMinionRemove(MinionFakePlayer minion) {
close();
}
public static GuiElementBuilder backButton(Runnable onBack) {
return new GuiElementBuilder(Items.COMPASS)
.setItemName(Text.translatable("gui.back"))
.setCallback(onBack);
}
}
@@ -1,9 +1,8 @@
package io.github.skippyall.minions.gui; package io.github.skippyall.minions.gui.minion;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui; import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.gui.MinionsGui;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.module.ModuleInventory;
import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -11,51 +10,28 @@ import net.minecraft.item.Items;
import net.minecraft.screen.ScreenHandlerType; import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.slot.ArmorSlot; import net.minecraft.screen.slot.ArmorSlot;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text; import net.minecraft.text.Text;
public class MinionGui { public class MinionInventoryGui extends MinionsGui {
public static void openInventory(ServerPlayerEntity player, MinionFakePlayer minion) { protected final MinionGui parent;
openServerSideInventory(player, minion); private final MinionFakePlayer minion;
private SimpleGui gui;
public MinionInventoryGui(MinionGui parent) {
super(parent);
this.parent = parent;
this.minion = parent.getMinion();
} }
public static void openServerSideInventory(ServerPlayerEntity player, MinionFakePlayer minion) { @Override
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); protected void open() {
gui.setTitle(minion.getName()); gui = new SimpleGui(ScreenHandlerType.GENERIC_9X6, viewer, false) {
@Override
gui.setSlot(1, new GuiElementBuilder() public void onClose() {
.setItem(Items.COMMAND_BLOCK) onBackingClosed();
.setName(Text.translatable("minions.gui.main.instructions")) }
.setCallback((i, clickType, slotActionType) -> { };
InstructionGui.openInstructionMainMenu(minion, player);
})
);
gui.setSlot(3, new GuiElementBuilder()
.setItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE)
.setName(Text.translatable("minions.gui.main.modules"))
.setCallback(() -> {
ModuleInventory.openModuleInventory(player, minion);
})
);
gui.setSlot(5, new GuiElementBuilder()
.setItem(Items.CHEST)
.setName(Text.translatable("minions.gui.main.inventory"))
.setCallback(() -> {
openMinionInventory(player, minion);
})
);
gui.setSlot(7, new GuiElementBuilder()
.setItem(Items.BARRIER)
.setName(Text.translatable("minions.gui.main.pickup"))
.setCallback(() -> {
minion.kill(minion.getWorld());
})
);
gui.open();
}
public static void openMinionInventory(ServerPlayerEntity player, MinionFakePlayer minion) {
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X6, player, false);
gui.setTitle(Text.translatable("minions.gui.inventory.title")); gui.setTitle(Text.translatable("minions.gui.inventory.title"));
for(int i = 0; i < 18; i++) { for(int i = 0; i < 18; i++) {
@@ -83,4 +59,9 @@ public class MinionGui {
} }
gui.open(); gui.open();
} }
@Override
protected void onClose() {
gui.close();
}
} }
@@ -7,7 +7,7 @@ import io.github.skippyall.minions.registration.MinionConfigOptions;
import io.github.skippyall.minions.registration.MinionItems; import io.github.skippyall.minions.registration.MinionItems;
import io.github.skippyall.minions.minion.MinionListener; import io.github.skippyall.minions.minion.MinionListener;
import io.github.skippyall.minions.minion.MinionData; import io.github.skippyall.minions.minion.MinionData;
import io.github.skippyall.minions.gui.MinionGui; import io.github.skippyall.minions.gui.minion.MinionGui;
import io.github.skippyall.minions.minion.MinionRuntime; import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.MinionItem; import io.github.skippyall.minions.minion.MinionItem;
import io.github.skippyall.minions.minion.MinionPersistentState; import io.github.skippyall.minions.minion.MinionPersistentState;
@@ -24,7 +24,6 @@ import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.HungerManager; import net.minecraft.entity.player.HungerManager;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.vehicle.AbstractBoatEntity; import net.minecraft.entity.vehicle.AbstractBoatEntity;
import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.DisconnectionInfo; import net.minecraft.network.DisconnectionInfo;
import net.minecraft.network.NetworkSide; import net.minecraft.network.NetworkSide;
@@ -156,7 +155,7 @@ public class MinionFakePlayer extends ServerPlayerEntity {
@Override @Override
public ActionResult interact(PlayerEntity player, Hand hand) { public ActionResult interact(PlayerEntity player, Hand hand) {
if(player instanceof ServerPlayerEntity spe) { if(player instanceof ServerPlayerEntity spe) {
MinionGui.openInventory(spe, this); new MinionGui(spe, this);
} }
return ActionResult.CONSUME; return ActionResult.CONSUME;
} }
@@ -62,7 +62,7 @@ public class AnalogInputSupplier implements ValueSupplier<Long, MinionRuntime> {
@Override @Override
public Text getDisplayText() { public Text getDisplayText() {
return Text.translatable("value_supplier_type.minions.analog_input.display", analogInputPos.toString(), analogInputWorld.getValue()); return Text.translatable("value_supplier_type.minions.analog_input.display", analogInputPos.toString(), analogInputWorld.getValue().toString());
} }
public static class AnalogInputSupplierType extends ValueSupplierType<MinionRuntime> { public static class AnalogInputSupplierType extends ValueSupplierType<MinionRuntime> {
@@ -75,12 +75,8 @@ public class AnalogInputSupplier implements ValueSupplier<Long, MinionRuntime> {
} }
@Override @Override
public <T> CompletableFuture<ValueSupplier<T, MinionRuntime>> openConfiguration(ServerPlayerEntity player, ValueType<T> valueType, @Nullable ValueSupplier<T, MinionRuntime> previous) { public <T> CompletableFuture<ValueSupplier<?, MinionRuntime>> openConfiguration(ServerPlayerEntity player, ValueType<T> valueType, @Nullable ValueSupplier<T, MinionRuntime> previous) {
if(valueType != ValueTypes.LONG) { CompletableFuture<ValueSupplier<?, MinionRuntime>> future = new CompletableFuture<>();
return CompletableFuture.failedFuture(new IllegalArgumentException("Value type " + valueType + " not allowed"));
}
CompletableFuture<ValueSupplier<T, MinionRuntime>> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false);
gui.setTitle(Text.translatable("value_supplier_type.minions.analog_input")); gui.setTitle(Text.translatable("value_supplier_type.minions.analog_input"));
@@ -89,11 +85,12 @@ public class AnalogInputSupplier implements ValueSupplier<Long, MinionRuntime> {
.setCallback(() -> { .setCallback(() -> {
ItemStack cursor = player.currentScreenHandler.getCursorStack(); ItemStack cursor = player.currentScreenHandler.getCursorStack();
if(cursor.isOf(MinionItems.REFERENCE_ITEM) && cursor.get(MinionComponentTypes.REFERENCE) instanceof BlockPosClipboard pos) { if(cursor.isOf(MinionItems.REFERENCE_ITEM) && cursor.get(MinionComponentTypes.REFERENCE) instanceof BlockPosClipboard pos) {
future.complete(new AnalogInputSupplier(pos.world(), pos.pos()).cast(valueType)); future.complete(new AnalogInputSupplier(pos.world(), pos.pos()));
} }
}) })
.setItemName(Text.translatable("value_supplier_type.minions.analog_input.config.click_with_reference")) .setItemName(Text.translatable("value_supplier_type.minions.analog_input.config.click_with_reference"))
); );
gui.open();
return future; return future;
} }
} }
@@ -1,4 +1,6 @@
package io.github.skippyall.minions.program.value; package io.github.skippyall.minions.program.conversion;
import io.github.skippyall.minions.program.value.ValueType;
public class Cast<F, T> { public class Cast<F, T> {
private final ValueType<F> from; private final ValueType<F> from;
@@ -0,0 +1,64 @@
package io.github.skippyall.minions.program.conversion;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.registration.ValueConverters;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class CastConverter<F,T> implements ValueConverter<F,T> {
private static final MapCodec<CastConverter<?,?>> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
MinionRegistries.VALUE_TYPES.getCodec().fieldOf("from").forGetter(CastConverter::getFrom),
MinionRegistries.VALUE_TYPES.getCodec().fieldOf("to").forGetter(CastConverter::getTo)
).apply(instance, CastConverter::new)
);
private final ValueType<F> from;
private final ValueType<T> to;
public CastConverter(ValueType<F> from, ValueType<T> to) {
this.from = from;
this.to = to;
}
@Override
public T convert(F fromValue) {
return Casts.getCast(from, to).cast(fromValue);
}
@Override
public ValueType<F> getFrom() {
return from;
}
@Override
public ValueType<T> getTo() {
return to;
}
@Override
public ValueConverterType<?> getType() {
return ValueConverters.CAST_CONVERTER;
}
public static class Type implements ValueConverterType<CastConverter<?,?>> {
@Override
public MapCodec<CastConverter<?, ?>> getCodec() {
return CastConverter.CODEC;
}
@Override
public boolean isSupportedConversion(ValueType<?> from, ValueType<?> to) {
return Casts.getCast(from, to) != null;
}
@Override
public <F,T> CompletableFuture<CastConverter<?,?>> configure(ServerPlayerEntity player, ValueType<F> from, ValueType<T> to, @Nullable CastConverter<?, ?> old) {
return CompletableFuture.completedFuture(new CastConverter<>(from, to));
}
}
}
@@ -1,5 +1,6 @@
package io.github.skippyall.minions.program.value; package io.github.skippyall.minions.program.conversion;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.registration.ValueTypes; import io.github.skippyall.minions.registration.ValueTypes;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -0,0 +1,73 @@
package io.github.skippyall.minions.program.conversion;
import com.mojang.serialization.MapCodec;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.registration.ValueTypes;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class EqualityConverter<F> implements ValueConverter<F, Boolean> {
public static final MapCodec<EqualityConverter<?>> CODEC = MinionRegistries.VALUE_TYPES.getCodec().dispatchMap(
EqualityConverter::getFrom,
EqualityConverter::getCodec
);
private ValueType<F> fromType;
private F compareValue;
public EqualityConverter(ValueType<F> fromType, F compareValue) {
this.fromType = fromType;
this.compareValue = compareValue;
}
@Override
public Boolean convert(F from) {
return compareValue.equals(from);
}
@Override
public ValueType<F> getFrom() {
return fromType;
}
@Override
public ValueType<Boolean> getTo() {
return ValueTypes.BOOLEAN;
}
@Override
public ValueConverterType<?> getType() {
return null;
}
private static <F> MapCodec<EqualityConverter<F>> getCodec(ValueType<F> fromType) {
return fromType.codec().fieldOf("compareValue")
.xmap(compareValue -> new EqualityConverter<>(fromType, compareValue), converter -> converter.compareValue);
}
public static class EqualityConverterType implements ValueConverterType<EqualityConverter<?>> {
@Override
public MapCodec<EqualityConverter<?>> getCodec() {
return CODEC;
}
@Override
public boolean isSupportedConversion(ValueType<?> from, ValueType<?> to) {
return to == ValueTypes.BOOLEAN;
}
@Override
public <F,T> CompletableFuture<EqualityConverter<?>> configure(ServerPlayerEntity player, ValueType<F> from, ValueType<T> to, @Nullable EqualityConverter<?> old) {
if(to == ValueTypes.BOOLEAN) {
//noinspection unchecked
return from.openValueDialog(player, old != null && old.fromType == from ? (F) old.compareValue : null)
.thenApply(compareValue -> new EqualityConverter<>(from, compareValue));
} else {
return CompletableFuture.failedFuture(new IllegalArgumentException("EqualityConverter does not support converting to " + to));
}
}
}
}
@@ -0,0 +1,29 @@
package io.github.skippyall.minions.program.conversion;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.registration.MinionRegistries;
import net.minecraft.text.Text;
public interface ValueConverter<F,T> {
Codec<ValueConverter<?,?>> CODEC = MinionRegistries.VALUE_CONVERTER_TYPES.getCodec().dispatch(ValueConverter::getType, ValueConverterType::getCodec);
T convert(F from);
ValueType<F> getFrom();
ValueType<T> getTo();
ValueConverterType<?> getType();
Text getDisplayText();
default <F2,T2> ValueConverter<F2,T2> cast(ValueType<F2> from, ValueType<T2> to) {
if(from == getFrom() && to == getTo()) {
//noinspection unchecked
return (ValueConverter<F2, T2>) this;
} else {
return null;
}
}
}
@@ -0,0 +1,16 @@
package io.github.skippyall.minions.program.conversion;
import com.mojang.serialization.MapCodec;
import io.github.skippyall.minions.program.value.ValueType;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public interface ValueConverterType<C extends ValueConverter<?,?>> {
MapCodec<C> getCodec();
boolean isSupportedConversion(ValueType<?> from, ValueType<?> to);
<F,T> CompletableFuture<C> configure(ServerPlayerEntity player, ValueType<F> from, ValueType<T> to, @Nullable C old);
}
@@ -11,6 +11,10 @@ import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView; import net.minecraft.storage.WriteView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/**
* Holds an instruction, its configuration and is responsible for executing the instruction
* @param <R> The runtime holding this object
*/
public class ConfiguredInstruction<R extends InstructionRuntime<R>> { public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
private final InstructionType<R> instruction; private final InstructionType<R> instruction;
private final ValueSupplierList<R> arguments; private final ValueSupplierList<R> arguments;
@@ -48,7 +52,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
} }
public boolean canRun() { public boolean canRun() {
return instruction != null && arguments != null && arguments.hasArgumentForAll(instruction.getParameters()); return instruction != null && arguments != null && arguments.checkRun(instruction).isSuccess();
} }
public boolean isRunning() { public boolean isRunning() {
@@ -9,6 +9,11 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
/**
* Defines the semantics of an instruction and creates {@link InstructionExecution}
* InstructionTypes for minions can be registered to {@link io.github.skippyall.minions.registration.MinionRegistries#INSTRUCTION_TYPES}
* @param <R> The runtime that this instruction can be executed in
*/
public class InstructionType<R extends InstructionRuntime<R>> { public class InstructionType<R extends InstructionRuntime<R>> {
private final List<Parameter<?>> parameters; private final List<Parameter<?>> parameters;
private final List<Parameter<?>> returnParameters; private final List<Parameter<?>> returnParameters;
@@ -1,6 +1,7 @@
package io.github.skippyall.minions.program.supplier; package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.program.value.SimpleValueType; import io.github.skippyall.minions.program.value.SimpleValueType;
import io.github.skippyall.minions.registration.MinionRegistries; import io.github.skippyall.minions.registration.MinionRegistries;
@@ -8,11 +9,12 @@ import io.github.skippyall.minions.program.value.ValueType;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public record Parameter<T>(String name, ValueType<T> type) { public record Parameter<T>(String name, ValueType<T> type) {
public static final Codec<Parameter<?>> CODEC = RecordCodecBuilder.create(instance -> public static final MapCodec<Parameter<?>> MAP_CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group( instance.group(
Codec.STRING.fieldOf("name").forGetter(Parameter::name), Codec.STRING.fieldOf("name").forGetter(Parameter::name),
MinionRegistries.VALUE_TYPES.getCodec().fieldOf("type").forGetter(Parameter::type) MinionRegistries.VALUE_TYPES.getCodec().fieldOf("type").forGetter(Parameter::type)
).apply(instance, Parameter::new)); ).apply(instance, Parameter::new));
public static final Codec<Parameter<?>> CODEC = MAP_CODEC.codec();
public <U> @Nullable Parameter<U> cast(ValueType<U> type) { public <U> @Nullable Parameter<U> cast(ValueType<U> type) {
if(this.type == type) { if(this.type == type) {
@@ -1,69 +1,100 @@
package io.github.skippyall.minions.program.supplier; package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.gui.input.Result;
import io.github.skippyall.minions.program.InstructionRuntime; import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.Cast; import io.github.skippyall.minions.program.conversion.Cast;
import io.github.skippyall.minions.program.value.Casts; import io.github.skippyall.minions.program.conversion.Casts;
import io.github.skippyall.minions.program.conversion.ValueConverter;
import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.program.value.ValueType;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
public class ValueSupplierList<R extends InstructionRuntime<R>> { public class ValueSupplierList<R extends InstructionRuntime<R>> {
private final Map<String, ValueSupplier<?, R>> arguments; private final Map<Parameter<?>, ValueSupplierEntry<?,?,R>> arguments = new HashMap<>();
private final List<Consumer<Parameter<?>>> changeListeners = new ArrayList<>(); private final List<Consumer<Parameter<?>>> changeListeners = new ArrayList<>();
public ValueSupplierList() { public ValueSupplierList() {
arguments = new HashMap<>();
} }
public ValueSupplierList(Map<String, ValueSupplier<?,R>> arguments) { private ValueSupplierList(List<ValueSupplierEntry<?,?,R>> arguments) {
this.arguments = new HashMap<>(arguments); for(ValueSupplierEntry<?,?,R> argument : arguments) {
this.arguments.put(argument.parameter, argument);
}
} }
public <F, T> T getValue(Parameter<T> parameter, R runtime) { private List<ValueSupplierEntry<?,?,R>> toCodecList() {
//noinspection unchecked return List.copyOf(arguments.values());
ValueSupplier<F,R> valueSupplier = (ValueSupplier<F, R>) getArgument(parameter); }
if(valueSupplier == null) {
return null;
}
if(valueSupplier.getValueType() == parameter.type()) { public <T> T getValue(Parameter<T> parameter, R runtime) {
return valueSupplier.cast(parameter.type()).resolve(runtime); ValueSupplierEntry<?,?,R> entry = getEntry(parameter);
} else { return parameter.type().checkedCast(entry.getValue(runtime));
Cast<F,T> cast = Casts.getCast(valueSupplier.getValueType(), parameter.type());
if(cast != null) {
return cast.cast(valueSupplier.resolve(runtime));
}
}
return null;
} }
public ValueSupplier<?,R> getArgument(Parameter<?> parameter) { public ValueSupplier<?,R> getArgument(Parameter<?> parameter) {
return arguments.get(parameter.name()); if(arguments.containsKey(parameter)) {
return arguments.get(parameter).supplier;
} else {
return null;
}
} }
public <T> void setArgument(Parameter<T> parameter, ValueSupplier<?,R> valueSupplier) { public <P> ValueSupplierEntry<P,?,R> getEntry(Parameter<P> parameter) {
arguments.put(parameter.name(), valueSupplier); //noinspection unchecked
return (ValueSupplierEntry<P, ?, R>) arguments.get(parameter);
}
public void setArgument(Parameter<?> parameter, ValueSupplier<?,R> valueSupplier) {
arguments.put(parameter, new ValueSupplierEntry<>(parameter, valueSupplier, List.of()));
onChange(parameter);
}
public void setArgument(Parameter<?> parameter, ValueSupplier<?,R> valueSupplier, List<ValueConverter<?, ?>> converter) {
arguments.put(parameter, new ValueSupplierEntry<>(parameter, valueSupplier, converter));
onChange(parameter); onChange(parameter);
} }
public boolean hasArgumentFor(Parameter<?> parameter) { public boolean hasArgumentFor(Parameter<?> parameter) {
return arguments.containsKey(parameter.name()); return arguments.containsKey(parameter);
} }
public boolean hasArgumentForAll(Collection<Parameter<?>> checkParameters) { public Result<@Nullable Void, Text> checkHasArguments(Collection<Parameter<?>> checkParameters) {
for(Parameter<?> parameter : checkParameters) { for(Parameter<?> parameter : checkParameters) {
if(!hasArgumentFor(parameter)) { if(!hasArgumentFor(parameter)) {
return false; return new Result.Error<>(Text.translatable("minions.gui.instruction.check.argument_not_set", parameter.name()));
} }
} }
return true; return new Result.Success<>(null);
} }
public Result<@Nullable Void, Text> checkRun(InstructionType<R> instructionType) {
Result<@Nullable Void, Text> checkResult = checkHasArguments(instructionType.getParameters());
if(!checkResult.isSuccess()) {
return checkResult;
}
for(ValueSupplierEntry<?,?,R> entry : arguments.values()) {
checkResult = entry.check();
if(!checkResult.isSuccess()) {
return checkResult;
}
}
return new Result.Success<>(null);
}
private void onChange(Parameter<?> parameter) { private void onChange(Parameter<?> parameter) {
for (Consumer<Parameter<?>> listener : changeListeners) { for (Consumer<Parameter<?>> listener : changeListeners) {
@@ -80,7 +111,98 @@ public class ValueSupplierList<R extends InstructionRuntime<R>> {
} }
public static <R extends InstructionRuntime<R>> Codec<ValueSupplierList<R>> getCodec(Codec<ValueSupplier<?,R>> argumentCodec) { public static <R extends InstructionRuntime<R>> Codec<ValueSupplierList<R>> getCodec(Codec<ValueSupplier<?,R>> argumentCodec) {
return Codec.unboundedMap(Codec.STRING, argumentCodec) return ValueSupplierEntry.getCodec(argumentCodec)
.xmap(ValueSupplierList::new, list -> list.arguments); .codec()
.listOf()
.xmap(ValueSupplierList::new, ValueSupplierList::toCodecList);
}
public static class ValueSupplierEntry<S, P, R extends InstructionRuntime<R>> {
private Parameter<P> parameter;
private ValueSupplier<S,R> supplier;
private List<ValueConverter<?,?>> converters;
public ValueSupplierEntry(Parameter<P> parameter, ValueSupplier<S, R> supplier, Collection<ValueConverter<?, ?>> converters) {
this.parameter = parameter;
this.supplier = supplier;
this.converters = new LinkedList<>(converters);
}
public List<ValueConverter<?,?>> getConverters() {
return converters;
}
public Parameter<P> getParameter() {
return parameter;
}
public ValueSupplier<S, R> getSupplier() {
return supplier;
}
public void setParameter(Parameter<P> parameter) {
this.parameter = parameter;
}
public void setSupplier(ValueSupplier<S, R> supplier) {
this.supplier = supplier;
}
public void setConverters(List<ValueConverter<?, ?>> converters) {
this.converters = converters;
}
public @Nullable P getValue(R runtime) {
S value = supplier.resolve(runtime);
Iterator<ValueConverter<?,?>> iterator = converters.iterator();
Object convertedValue = convert(supplier.getValueType(), value, iterator.next(), iterator);
return finalCast(convertedValue);
}
private <F> @Nullable P finalCast(Object value) {
//noinspection unchecked
ValueType<F> lastConvertedType = (ValueType<F>) converters.getLast().getTo();
F convertedValue = lastConvertedType.checkedCast(value);
if(convertedValue == null) {
return null;
}
Cast<F,P> cast = Casts.getCast(lastConvertedType, parameter.type());
if(cast != null) {
return cast.cast(convertedValue);
}
return null;
}
private <F,I,T> @Nullable Object convert(ValueType<F> fromType, F from, ValueConverter<I,T> converter, Iterator<ValueConverter<?,?>> iterator) {
Cast<F, I> cast = Casts.getCast(fromType, converter.getFrom());
if(cast == null) {
return null;
}
I inter = cast.cast(from);
if(inter == null) {
return null;
}
T to = converter.convert(inter);
if(iterator.hasNext() && to != null) {
return convert(converter.getTo(), to, iterator.next(), iterator);
} else {
return to;
}
}
public Result<@Nullable Void, Text> check() {
return new Result.Success<>(null);
}
public static <R extends InstructionRuntime<R>> MapCodec<ValueSupplierEntry<?,?,R>> getCodec(Codec<ValueSupplier<?,R>> argumentCodec) {
return RecordCodecBuilder.mapCodec(instance -> instance.group(
Parameter.CODEC.fieldOf("parameter").forGetter(e -> e.parameter),
argumentCodec.fieldOf("argument").forGetter(e -> e.supplier),
ValueConverter.CODEC.listOf().optionalFieldOf("converter", List.of()).forGetter(e -> e.converters)
).apply(instance, ValueSupplierEntry::new));
}
} }
} }
@@ -11,5 +11,5 @@ import java.util.concurrent.CompletableFuture;
public abstract class ValueSupplierType<R extends InstructionRuntime<R>> { public abstract class ValueSupplierType<R extends InstructionRuntime<R>> {
public abstract <T> Codec<? extends ValueSupplier<T,R>> getCodec(ValueType<T> type); 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); public abstract <T> CompletableFuture<? extends ValueSupplier<?,R>> openConfiguration(ServerPlayerEntity player, ValueType<T> valueType, @Nullable ValueSupplier<T,R> previous);
} }
@@ -1,29 +0,0 @@
package io.github.skippyall.minions.program.value;
import com.mojang.serialization.Codec;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import java.util.concurrent.CompletableFuture;
public class LongValueType implements ValueType<Long> {
@Override
public CompletableFuture<Long> openValueDialog(ServerPlayerEntity player, Long previousValue) {
return null;
}
@Override
public Text getDisplayText(Long value) {
return null;
}
@Override
public Codec<Long> codec() {
return null;
}
@Override
public Long defaultValue() {
return 0L;
}
}
@@ -9,7 +9,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
public record SimpleValueType<T>(Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener, Function<T, Text> textDisplay) implements ValueType<T> { public record SimpleValueType<T>(Codec<T> codec, T defaultValue, Function<Object, T> checkedCast, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener, Function<T, Text> textDisplay) implements ValueType<T> {
@Override @Override
public CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue) { public CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue) {
return valueDialogOpener.apply(player, previousValue); return valueDialogOpener.apply(player, previousValue);
@@ -19,4 +19,9 @@ public record SimpleValueType<T>(Codec<T> codec, T defaultValue, BiFunction<Serv
public Text getDisplayText(T value) { public Text getDisplayText(T value) {
return textDisplay.apply(value); return textDisplay.apply(value);
} }
@Override
public T checkedCast(Object value) {
return checkedCast.apply(value);
}
} }
@@ -17,4 +17,10 @@ public interface ValueType<T> {
Codec<T> codec(); Codec<T> codec();
T defaultValue(); T defaultValue();
@Nullable T checkedCast(Object value);
default boolean isOf(Object value) {
return checkedCast(value) != null;
}
} }
@@ -1,13 +0,0 @@
package io.github.skippyall.minions.program.value.conversion;
import io.github.skippyall.minions.program.value.ValueType;
public interface ValueConverter<F,T> {
T convert(F from);
ValueType<F> getFrom();
ValueType<T> getTo();
ValueConverterType<?> getType();
}
@@ -1,18 +0,0 @@
package io.github.skippyall.minions.program.value.conversion;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.value.ValueType;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public interface ValueConverterType<C extends ValueConverter<?,?>> {
Codec<C> getCodec(ValueType<?> from, ValueType<?> to);
boolean isSupportedFromType(ValueType<?> type);
boolean isSupportedToType(ValueType<?> type);
CompletableFuture<C> configure(ServerPlayerEntity player, ValueType<?> from, ValueType<?> to, @Nullable C old);
}
@@ -16,7 +16,7 @@ import java.util.UUID;
public class MinionComponentTypes { public class MinionComponentTypes {
public static final ComponentType<UUID> MINION_DATA = register("minion_data", ComponentType.<UUID>builder().codec(Uuids.CODEC).build()); public static final ComponentType<UUID> MINION_DATA = register("minion_data", ComponentType.<UUID>builder().codec(Uuids.CODEC).build());
public static final ComponentType<MinionModule> MODULE = register("minion_module", ComponentType.<MinionModule>builder().codec(MinionModule.CODEC).build()); public static final ComponentType<MinionModule> MODULE = register("minion_module", ComponentType.<MinionModule>builder().codec(MinionModule.CODEC).build());
public static final ComponentType<Clipboard> REFERENCE = ComponentType.<Clipboard>builder().codec(Clipboard.CODEC).build(); public static final ComponentType<Clipboard> REFERENCE = register("reference", ComponentType.<Clipboard>builder().codec(Clipboard.CODEC).build());
private static <T extends ComponentType<?>> T register(String name, T type) { private static <T extends ComponentType<?>> T register(String name, T type) {
Registry.register(Registries.DATA_COMPONENT_TYPE, Identifier.of(Minions.MOD_ID, name), type); Registry.register(Registries.DATA_COMPONENT_TYPE, Identifier.of(Minions.MOD_ID, name), type);
@@ -15,6 +15,7 @@ public class MinionRegistration {
MinionListeners.register(); MinionListeners.register();
SkinProviders.register(); SkinProviders.register();
SpecialAbilities.register(); SpecialAbilities.register();
ValueConverters.register();
ValueSuppliers.register(); ValueSuppliers.register();
ValueTypes.register(); ValueTypes.register();
@@ -17,6 +17,7 @@ import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.program.consumer.ValueConsumerType; import io.github.skippyall.minions.program.consumer.ValueConsumerType;
import io.github.skippyall.minions.program.value.ValueType; import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.clipboard.Clipboard; import io.github.skippyall.minions.clipboard.Clipboard;
import io.github.skippyall.minions.program.conversion.ValueConverterType;
import net.fabricmc.fabric.api.event.registry.DynamicRegistries; import net.fabricmc.fabric.api.event.registry.DynamicRegistries;
import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
import net.fabricmc.fabric.api.event.registry.RegistryAttribute; import net.fabricmc.fabric.api.event.registry.RegistryAttribute;
@@ -29,6 +30,8 @@ public class MinionRegistries {
public static final Registry<ValueSupplierType<MinionRuntime>> VALUE_SUPPLIER_TYPES = registry("value_supplier_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<ValueConsumerType<MinionRuntime>> VALUE_CONSUMER_TYPES = registry("value_consumer_type");
public static final Registry<InstructionType<MinionRuntime>> INSTRUCTION_TYPES = registry("instruction_type"); public static final Registry<InstructionType<MinionRuntime>> INSTRUCTION_TYPES = registry("instruction_type");
public static final Registry<ValueConverterType<?>> VALUE_CONVERTER_TYPES = registry("value_converter_type");
public static final Registry<SkinProvider> SKIN_PROVIDERS = registry("skin_provider"); 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 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 ConfiguredInstructionListener>> INSTRUCTION_LISTENER_CODECS = registry("instruction_listener_codec");
@@ -0,0 +1,19 @@
package io.github.skippyall.minions.registration;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.program.conversion.CastConverter;
import io.github.skippyall.minions.program.conversion.EqualityConverter;
import io.github.skippyall.minions.program.conversion.ValueConverterType;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class ValueConverters {
public static final EqualityConverter.EqualityConverterType EQUALITY_CONVERTER = register("equality_converter", new EqualityConverter.EqualityConverterType());
public static final CastConverter.Type CAST_CONVERTER = register("cast_converter", new CastConverter.Type());
private static <T extends ValueConverterType<?>> T register(String name, T type) {
return Registry.register(MinionRegistries.VALUE_CONVERTER_TYPES, Identifier.of(Minions.MOD_ID, name), type);
}
public static void register() {}
}
@@ -8,85 +8,88 @@ import io.github.skippyall.minions.gui.input.ChoiceInput;
import io.github.skippyall.minions.gui.input.TextInput; import io.github.skippyall.minions.gui.input.TextInput;
import io.github.skippyall.minions.minion.program.instruction.move.TurnDirection; import io.github.skippyall.minions.minion.program.instruction.move.TurnDirection;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public class ValueTypes { public class ValueTypes {
public static ValueType<Long> LONG = registerSimple( public static ValueType<Long> LONG = register(
"long", "long",
Codec.LONG, new SimpleValueType<>(
0L, Codec.LONG,
(player, oldValue) -> TextInput.inputLong( 0L,
player, o -> o instanceof Long l ? l : null,
Text.literal("Integer"), (player, oldValue) -> TextInput.inputLong(
String.valueOf(oldValue) player,
), Text.literal("Integer"),
value -> Text.literal(value.toString()) String.valueOf(oldValue)
),
value -> Text.literal(value.toString())
)
); );
public static ValueType<Double> DOUBLE = registerSimple( public static ValueType<Double> DOUBLE = register(
"double", "double",
Codec.DOUBLE, new SimpleValueType<>(
0D, Codec.DOUBLE,
(player, oldValue) -> TextInput.inputDouble( 0D,
player, o -> o instanceof Double d ? d : null,
Text.literal("Number"), (player, oldValue) -> TextInput.inputDouble(
String.valueOf(oldValue) player,
), Text.literal("Number"),
value -> Text.literal(value.toString()) String.valueOf(oldValue)
),
value -> Text.literal(value.toString())
)
); );
public static ValueType<Boolean> BOOLEAN = registerSimple( public static ValueType<Boolean> BOOLEAN = register(
"boolean", "boolean",
Codec.BOOL, new SimpleValueType<>(
false, Codec.BOOL,
ChoiceInput.inputBoolean(Text.literal("")), false,
value -> Text.literal(value.toString()) o -> o instanceof Boolean b ? b : null,
ChoiceInput.inputBoolean(Text.literal("")),
value -> Text.literal(value.toString())
)
); );
public static ValueType<String> STRING = registerSimple( public static ValueType<String> STRING = register(
"string", "string",
Codec.STRING, new SimpleValueType<>(
"", Codec.STRING,
((player, oldValue) -> TextInput.inputString( "",
player, o -> o instanceof String s ? s : null,
Text.literal("Text"), ((player, oldValue) -> TextInput.inputString(
oldValue) player,
), Text.literal("Text"),
value -> Text.literal("\"" + value + "\"") oldValue)
),
value -> Text.literal("\"" + value + "\"")
)
); );
public static ValueType<TurnDirection> TURN_DIRECTION = registerSimple( public static ValueType<TurnDirection> TURN_DIRECTION = register(
"turn_direction", "turn_direction",
TurnDirection.CODEC, new SimpleValueType<>(
TurnDirection.RIGHT, TurnDirection.CODEC,
ChoiceInput.createDialogOpener(TurnDirection.values()), TurnDirection.RIGHT,
value -> Text.literal(value.name) o -> o instanceof TurnDirection d ? d : null,
ChoiceInput.createDialogOpener(TurnDirection.values()),
value -> Text.literal(value.name)
)
); );
private static <T> ValueType<T> registerSimple( private static <T extends ValueType<?>> T register(
String id, String id,
Codec<T> codec, T type
T defaultValue,
BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener,
Function<T, Text> textDisplay
) { ) {
Identifier identifier = Identifier.of(Minions.MOD_ID, id); Identifier identifier = Identifier.of(Minions.MOD_ID, id);
return Registry.register( Registry.register(
MinionRegistries.VALUE_TYPES, MinionRegistries.VALUE_TYPES,
identifier, identifier,
new SimpleValueType<>( type
codec,
defaultValue,
valueDialogOpener,
textDisplay
)
); );
return type;
} }
public static void register() {} public static void register() {}
@@ -8,6 +8,7 @@
"minions.gui.ok": "OK", "minions.gui.ok": "OK",
"minions.gui.confirm": "Confirm", "minions.gui.confirm": "Confirm",
"minions.gui.abort": "Abort", "minions.gui.abort": "Abort",
"minions.gui.back": "Back",
"minions.gui.look.skin.name": "Name", "minions.gui.look.skin.name": "Name",
"minions.gui.look.skin.name.title": "Enter a player name", "minions.gui.look.skin.name.title": "Enter a player name",
@@ -39,6 +40,8 @@
"minions.gui.instruction.parameter": "%s: %s", "minions.gui.instruction.parameter": "%s: %s",
"minions.gui.instruction.argument": "Argument: %s", "minions.gui.instruction.argument": "Argument: %s",
"minions.gui.instruction.check.argument_not_set": "Argument %s is not set",
"minions.command.input.int.fail": "Not an integer", "minions.command.input.int.fail": "Not an integer",
"minions.command.input.float.fail": "Not a number", "minions.command.input.float.fail": "Not a number",
"minions.command.action.details": "Set how to %s", "minions.command.action.details": "Set how to %s",