Cast, Convert & more

This commit is contained in:
skippyall
2026-03-16 23:01:58 +01:00
parent 9b61dba4c7
commit 7acd083e79
59 changed files with 1207 additions and 178 deletions
@@ -2,7 +2,7 @@ package io.github.skippyall.minions.client;
import eu.pb4.polymer.networking.api.client.PolymerClientNetworking;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.util.PolymerUtil;
import io.github.skippyall.minions.polymer.VersionSync;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap;
import net.minecraft.client.render.BlockRenderLayer;
@@ -12,6 +12,6 @@ public class MinionsClient implements ClientModInitializer {
public void onInitializeClient() {
BlockRenderLayerMap.putBlock(MinionBlocks.MINION_TRIGGER_BLOCK, BlockRenderLayer.TRANSLUCENT);
PolymerClientNetworking.registerCommonHandler(PolymerUtil.VersionSyncPayload.class, (client, handler, payload) -> {});
PolymerClientNetworking.registerCommonHandler(VersionSync.VersionSyncPayload.class, (client, handler, payload) -> {});
}
}
@@ -2,13 +2,16 @@ package io.github.skippyall.minions;
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
import io.github.skippyall.minions.command.MinionsCommand;
import io.github.skippyall.minions.docs.DocsManager;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.registration.MinionRegistration;
import io.github.skippyall.minions.util.PolymerUtil;
import io.github.skippyall.minions.polymer.VersionSync;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.minecraft.resource.ResourceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,7 +26,7 @@ public class Minions implements ModInitializer {
MinionRegistration.register();
PolymerUtil.register();
VersionSync.register();
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
MinionPersistentState.get(server).getMinionData().forEach((uuid, data) -> {
@@ -42,5 +45,7 @@ public class Minions implements ModInitializer {
});*/
PolymerResourcePackUtils.addModAssets(Minions.MOD_ID);
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new DocsManager());
}
}
@@ -0,0 +1,22 @@
package io.github.skippyall.minions.block.input;
import io.github.skippyall.minions.clipboard.ClipboardItem;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class AnalogInputBlock extends Block {
public AnalogInputBlock(Settings settings) {
super(settings);
}
@Override
protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
player.getInventory().offer(ClipboardItem.createBlockPosReference(world, pos), true);
return ActionResult.SUCCESS;
}
}
@@ -0,0 +1,63 @@
package io.github.skippyall.minions.block.instruction_bound;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerBlockEntity;
import io.github.skippyall.minions.clipboard.InstructionClipboard;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.registration.MinionComponentTypes;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public abstract class InstructionBoundBlock extends Block implements BlockEntityProvider {
public InstructionBoundBlock(Settings settings) {
super(settings);
}
protected abstract BlockEntityType<? extends InstructionBoundBlockEntity<?>> getBlockEntityType();
@Override
protected ActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if(stack.get(MinionComponentTypes.REFERENCE) instanceof InstructionClipboard instruction) {
world.getBlockEntity(pos, getBlockEntityType()).ifPresent(be -> {
be.setInstruction(instruction.selectedMinion(), instruction.selectedInstruction());
player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1);
stack.decrement(1);
});
return ActionResult.SUCCESS;
}
return super.onUseWithItem(stack, state, world, pos, player, hand, hit);
}
@Override
protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
if(world.isClient()) {
return ActionResult.CONSUME;
}
world.getBlockEntity(pos, getBlockEntityType()).ifPresent(be -> {
String name = MinionPersistentState.get(world.getServer()).getMinionData(be.getMinionUuid()).name();
player.sendMessage(Text.translatable("minions.reference.instruction.tooltip", name, be.getInstructionName()), true);
});
return ActionResult.SUCCESS;
}
@Override
protected void onStateReplaced(BlockState state, ServerWorld world, BlockPos pos, boolean moved) {
super.onStateReplaced(state, world, pos, moved);
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::removeListener);
}
}
@@ -0,0 +1,71 @@
package io.github.skippyall.minions.block.instruction_bound;
import io.github.skippyall.minions.listener.BlockEntityMinionListener;
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.block.entity.BlockEntityType;
import net.minecraft.util.math.BlockPos;
import java.util.Optional;
import java.util.UUID;
public abstract class InstructionBoundBlockEntity<L extends BlockEntityMinionListener<?>> extends BlockEntity {
protected UUID minionUuid;
protected String instructionName = "";
public InstructionBoundBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}
protected abstract L createListener();
protected abstract Class<L> getListenerClass();
public void removeListener() {
L listener = getListener();
listener.remove(world.getServer());
}
public void addListener() {
L listener = createListener();
listener.add(world.getServer());
}
public void setInstruction(UUID minionUuid, String instructionName) {
removeListener();
this.minionUuid = minionUuid;
this.instructionName = instructionName;
addListener();
markDirty();
}
public Optional<MinionFakePlayer> getMinion() {
if(minionUuid != null && world != null && world.getPlayerByUuid(minionUuid) instanceof MinionFakePlayer minion) {
return Optional.of(minion);
}
return Optional.empty();
}
public UUID getMinionUuid() {
return minionUuid;
}
public String getInstructionName() {
return instructionName;
}
public Optional<ConfiguredInstruction<MinionRuntime>> getInstruction(MinionFakePlayer minion) {
return Optional.ofNullable(minion.getInstructionManager().getInstruction(instructionName));
}
public Optional<ConfiguredInstruction<MinionRuntime>> getInstruction() {
return getMinion().flatMap(this::getInstruction);
}
public L getListener() {
return BlockEntityMinionListener.getListener(world, pos, minionUuid, getListenerClass());
}
}
@@ -8,35 +8,27 @@ import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
import eu.pb4.polymer.virtualentity.api.BlockWithElementHolder;
import eu.pb4.polymer.virtualentity.api.ElementHolder;
import eu.pb4.polymer.virtualentity.api.elements.ItemDisplayElement;
import io.github.skippyall.minions.block.instruction_bound.InstructionBoundBlock;
import io.github.skippyall.minions.block.instruction_bound.InstructionBoundBlockEntity;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.clipboard.InstructionClipboard;
import io.github.skippyall.minions.registration.MinionComponentTypes;
import io.github.skippyall.minions.util.PolymerUtil;
import io.github.skippyall.minions.polymer.VersionSync;
import net.minecraft.block.AbstractRedstoneGateBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.SideShapeType;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
@@ -47,7 +39,7 @@ import net.minecraft.world.block.WireOrientation;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.packettweaker.PacketContext;
public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock, PolymerKeepModel, PolymerClientDecoded, BlockWithElementHolder {
public class MinionTriggerBlock extends InstructionBoundBlock implements PolymerBlock, PolymerKeepModel, PolymerClientDecoded, BlockWithElementHolder {
public static final MapCodec<MinionTriggerBlock> CODEC = createCodec(MinionTriggerBlock::new);
public static final BooleanProperty POWERED = BooleanProperty.of("powered");
@@ -73,44 +65,11 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
return state.isSideSolid(world, pos, Direction.UP, SideShapeType.RIGID);
}
@Override
protected void onStateReplaced(BlockState state, ServerWorld world, BlockPos pos, boolean moved) {
super.onStateReplaced(state, world, pos, moved);
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::removeListener);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(POWERED);
}
@Override
protected ActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if(stack.get(MinionComponentTypes.REFERENCE) instanceof InstructionClipboard instruction) {
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> {
be.setInstruction(instruction.selectedMinion(), instruction.selectedInstruction());
player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1);
stack.decrement(1);
});
return ActionResult.SUCCESS;
}
return super.onUseWithItem(stack, state, world, pos, player, hand, hit);
}
@Override
protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
if(world.isClient()) {
return ActionResult.CONSUME;
}
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> {
String name = MinionPersistentState.get(world.getServer()).getMinionData(be.getMinionUuid()).name();
player.sendMessage(Text.translatable("minions.reference.instruction.tooltip", name, be.getInstructionName()), true);
});
return ActionResult.SUCCESS;
}
@Override
protected void neighborUpdate(BlockState state, World world, BlockPos pos, Block sourceBlock, @Nullable WireOrientation wireOrientation, boolean notify) {
if(!canPlaceAt(state, world, pos)) {
@@ -136,19 +95,19 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
return world.getBlockEntity(pos, MinionBlocks.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
protected BlockEntityType<MinionTriggerBlockEntity> getBlockEntityType() {
return MinionBlocks.MINION_TRIGGER_BE_TYPE;
}
@Override
public BlockState getPolymerBlockState(BlockState state, PacketContext context) {
return PolymerUtil.isOnClient(context) ? state : net.minecraft.block.Blocks.COMPARATOR.getDefaultState().with(AbstractRedstoneGateBlock.POWERED, state.get(POWERED));
return VersionSync.isOnClient(context) ? state : net.minecraft.block.Blocks.COMPARATOR.getDefaultState().with(AbstractRedstoneGateBlock.POWERED, state.get(POWERED));
}
@Override
@@ -1,5 +1,6 @@
package io.github.skippyall.minions.block.miniontrigger;
import io.github.skippyall.minions.block.instruction_bound.InstructionBoundBlockEntity;
import io.github.skippyall.minions.listener.BlockEntityMinionListener;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.minion.MinionRuntime;
@@ -15,28 +16,19 @@ import net.minecraft.util.math.BlockPos;
import java.util.Optional;
import java.util.UUID;
public class MinionTriggerBlockEntity extends BlockEntity {
private UUID minionUuid;
private String instructionName = "";
public class MinionTriggerBlockEntity extends InstructionBoundBlockEntity<MinionTriggerMinionListener> {
public MinionTriggerBlockEntity(BlockPos pos, BlockState state) {
super(MinionBlocks.MINION_TRIGGER_BE_TYPE, pos, state);
}
public void removeListener() {
MinionTriggerMinionListener.removeListener(world, pos, minionUuid, instructionName);
@Override
protected MinionTriggerMinionListener createListener() {
return new MinionTriggerMinionListener(world.getRegistryKey(), pos, minionUuid, instructionName);
}
public void addListener() {
MinionTriggerMinionListener.addListener(world, pos, minionUuid, instructionName);
}
public void setInstruction(UUID minionUuid, String instructionName) {
removeListener();
this.minionUuid = minionUuid;
this.instructionName = instructionName;
addListener();
markDirty();
@Override
protected Class<MinionTriggerMinionListener> getListenerClass() {
return MinionTriggerMinionListener.class;
}
public void updatePower() {
@@ -66,33 +58,6 @@ public class MinionTriggerBlockEntity extends BlockEntity {
return 0;
}
public Optional<MinionFakePlayer> getMinion() {
if(minionUuid != null && world != null && world.getPlayerByUuid(minionUuid) instanceof MinionFakePlayer minion) {
return Optional.of(minion);
}
return Optional.empty();
}
public UUID getMinionUuid() {
return minionUuid;
}
public String getInstructionName() {
return instructionName;
}
public Optional<ConfiguredInstruction<MinionRuntime>> getInstruction(MinionFakePlayer minion) {
return Optional.ofNullable(minion.getInstructionManager().getInstruction(instructionName));
}
public Optional<ConfiguredInstruction<MinionRuntime>> getInstruction() {
return getMinion().flatMap(this::getInstruction);
}
public MinionTriggerMinionListener getListener() {
return BlockEntityMinionListener.getListener(world, pos, minionUuid, MinionTriggerMinionListener.class);
}
@Override
protected void readData(ReadView view) {
minionUuid = view.read("minionUuid", Uuids.CODEC).orElse(null);
@@ -36,23 +36,11 @@ public class MinionTriggerMinionListener extends BlockEntityMinionInstructionLis
boolean runningCache;
boolean incomingPowerCache;
private MinionTriggerMinionListener(RegistryKey<World> worldKey, BlockPos pos, UUID minionUuid, String instructionName) {
MinionTriggerMinionListener(RegistryKey<World> worldKey, BlockPos pos, UUID minionUuid, String instructionName) {
super(worldKey, pos, minionUuid, MinionBlocks.MINION_TRIGGER_BE_TYPE);
this.instructionName = Objects.requireNonNull(instructionName);
}
public static void addListener(World world, BlockPos pos, UUID minion, String instructionName) {
MinionTriggerMinionListener listener = new MinionTriggerMinionListener(world.getRegistryKey(), pos, minion, instructionName);
listener.add(world.getServer());
}
public static void removeListener(World world, BlockPos pos, UUID minion, String instructionName) {
MinionTriggerMinionListener old = getListener(world, pos, minion, MinionTriggerMinionListener.class);
if(old != null) {
old.remove(world.getServer());
}
}
@Override
protected Map<String, ConfiguredInstructionListener> getInstructionListeners() {
return Map.of(instructionName, listener);
@@ -88,7 +76,7 @@ public class MinionTriggerMinionListener extends BlockEntityMinionInstructionLis
}
@Override
protected void add(MinecraftServer server) {
public void add(MinecraftServer server) {
super.add(server);
runningCache = minion.getInstructionManager().getInstruction(instructionName).isRunning();
updateComparatorsIfLoaded(server);
@@ -0,0 +1,33 @@
package io.github.skippyall.minions.clipboard;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.component.ComponentsAccess;
import net.minecraft.item.Item;
import net.minecraft.item.tooltip.TooltipType;
import net.minecraft.registry.RegistryKey;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.function.Consumer;
public record BlockPosClipboard(RegistryKey<World> world, BlockPos pos) implements Clipboard {
public static final MapCodec<BlockPosClipboard> CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(
World.CODEC.fieldOf("world").forGetter(BlockPosClipboard::world),
BlockPos.CODEC.fieldOf("pos").forGetter(BlockPosClipboard::pos)
).apply(instance, BlockPosClipboard::new)
);
@Override
public MapCodec<BlockPosClipboard> getCodec() {
return CODEC;
}
@Override
public void appendTooltip(Item.TooltipContext context, Consumer<Text> textConsumer, TooltipType type, ComponentsAccess components) {
textConsumer.accept(Text.translatable("minions.reference.block.tooltip", pos.toString()));
}
}
@@ -10,6 +10,8 @@ import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.tooltip.TooltipType;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.packettweaker.PacketContext;
@@ -40,4 +42,10 @@ public class ClipboardItem extends Item implements PolymerItem {
stack.set(MinionComponentTypes.REFERENCE, new InstructionClipboard(minion.getUuid(), instructionName, minion.getGameProfile().getName()));
return stack;
}
public static ItemStack createBlockPosReference(World world, BlockPos pos) {
ItemStack stack = new ItemStack(MinionItems.REFERENCE_ITEM);
stack.set(MinionComponentTypes.REFERENCE, new BlockPosClipboard(world.getRegistryKey(), pos));
return stack;
}
}
@@ -0,0 +1,36 @@
package io.github.skippyall.minions.command;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import io.github.skippyall.minions.docs.DocsManager;
import net.minecraft.command.argument.IdentifierArgumentType;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.Identifier;
import java.util.concurrent.CompletableFuture;
import static net.minecraft.server.command.CommandManager.literal;
import static net.minecraft.server.command.CommandManager.argument;
public class DocsSubcommand {
public static final LiteralArgumentBuilder<ServerCommandSource> DOCS = literal("docs")
.then(
argument("docName", IdentifierArgumentType.identifier())
.suggests(DocsSubcommand::getSuggestions)
.executes(DocsSubcommand::execute)
);
public static CompletableFuture<Suggestions> getSuggestions(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) {
DocsManager.getDocsEntryIds().forEach(id -> builder.suggest(id.toString()));
return CompletableFuture.completedFuture(builder.build());
}
public static int execute(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
Identifier id = IdentifierArgumentType.getIdentifier(context, "docName");
DocsManager.showDocsEntry(context.getSource().getPlayerOrThrow(), id);
return 1;
}
}
@@ -13,7 +13,8 @@ public class MinionsCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess access, CommandManager.RegistrationEnvironment environment) {
LiteralArgumentBuilder<ServerCommandSource> builder = literal("minions")
.then(SpawnSubcommand.SPAWN)
.then(ListSubcommand.LIST);
.then(ListSubcommand.LIST)
.then(DocsSubcommand.DOCS);
if(MinionsConfig.get().minion.enableMobCapHacks) {
builder.then(MobCapDebugSubcommand.MOB_CAP_DEBUG);
@@ -0,0 +1,30 @@
package io.github.skippyall.minions.docs;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.registration.MinionRegistries;
import net.minecraft.dialog.body.DialogBody;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.List;
import java.util.function.Function;
public interface DocsEntry {
Codec<DocsEntry> CODEC = MinionRegistries.DOCS_ENTRY_TYPES.getCodec().dispatch(DocsEntry::getCodec, Function.identity());
Metadata getMetadata();
List<DialogBody> getDialog(DynamicRegistryManager manager);
MapCodec<? extends DocsEntry> getCodec();
record Metadata(String titleKey) {
public static final MapCodec<Metadata> CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(
Codec.STRING.fieldOf("title").forGetter(Metadata::titleKey)
).apply(instance, Metadata::new)
);
}
}
@@ -0,0 +1,145 @@
package io.github.skippyall.minions.docs;
import com.google.gson.JsonParseException;
import com.mojang.serialization.JsonOps;
import io.github.skippyall.minions.Minions;
import net.fabricmc.fabric.api.resource.SimpleResourceReloadListener;
import net.minecraft.dialog.AfterAction;
import net.minecraft.dialog.DialogActionButtonData;
import net.minecraft.dialog.DialogButtonData;
import net.minecraft.dialog.DialogCommonData;
import net.minecraft.dialog.action.DynamicRunCommandDialogAction;
import net.minecraft.dialog.action.SimpleDialogAction;
import net.minecraft.dialog.type.MultiActionDialog;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;
import net.minecraft.util.StrictJsonParser;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
public class DocsManager implements SimpleResourceReloadListener<Pair<Map<Identifier, DocsEntry>, DocsTree>> {
private static Map<Identifier, DocsEntry> docs;
private static DocsTree tree;
public static DocsTree getTree() {
return tree;
}
public static void showDocsEntry(ServerPlayerEntity player, Identifier id) {
DocsEntry entry = getDocsEntry(id);
if(entry == null) {
return;
}
List<DialogActionButtonData> buttons = new ArrayList<>();
if(tree != null) {
DocsTree.DocElement element = tree.getElement(id);
if (element.previous() != null) {
Identifier previousId = element.previous().getId();
buttons.add(getDialogButton(Text.literal("<- ").append(Text.translatable(getDocsEntry(previousId).getMetadata().titleKey())), previousId.toString()));
}
if (element.next() != null) {
Identifier nextId = element.next().getId();
buttons.add(getDialogButton(Text.translatable(getDocsEntry(nextId).getMetadata().titleKey()).append(Text.literal(" ->")), nextId.toString()));
}
}
buttons.add(new DialogActionButtonData(
new DialogButtonData(Text.translatable("gui.ok"), 100),
Optional.empty()
));
player.openDialog(RegistryEntry.of(new MultiActionDialog(
new DialogCommonData(
Text.translatable(entry.getMetadata().titleKey()),
Optional.empty(),
true,
false,
AfterAction.CLOSE,
entry.getDialog(player.getRegistryManager()),
List.of()
),
buttons,
Optional.empty(),
2
)));
}
private static DialogActionButtonData getDialogButton(Text text, String dialogToOpen) {
return new DialogActionButtonData(
new DialogButtonData(
text, 100
),
Optional.of(new SimpleDialogAction(new ClickEvent.RunCommand("/minions docs " + dialogToOpen)))
);
}
public static DocsEntry getDocsEntry(Identifier id) {
return docs.get(id);
}
public static Collection<Identifier> getDocsEntryIds() {
return docs.keySet();
}
@Override
public CompletableFuture<Pair<Map<Identifier, DocsEntry>, DocsTree>> load(ResourceManager resourceManager, Executor executor) {
return CompletableFuture.supplyAsync(() -> {
Map<Identifier, Resource> resources = resourceManager.findResources("docs", id -> id.getNamespace().equals(Minions.MOD_ID) && id.getPath().endsWith(".json"));
final DocsTree.BranchElement[] root = {null};
Map<Identifier, DocsEntry> docsEntries = new HashMap<>();
resources.forEach((id, resource) -> {
try(Reader reader = resource.getReader()) {
if(id.getPath().equals("docs/tree.json")) {
DocsTree.BranchElement.CODEC.decode(JsonOps.INSTANCE, StrictJsonParser.parse(reader))
.ifSuccess(entry -> root[0] = entry.getFirst())
.ifError(error -> Minions.LOGGER.warn("Could not parse docs tree {}: {}", id, error.message()));
} else {
Identifier docId = Identifier.of(id.getNamespace(), id.getPath().substring("docs/".length(), id.getPath().length() - ".json".length()));
DocsEntry.CODEC.decode(JsonOps.INSTANCE, StrictJsonParser.parse(reader))
.ifSuccess(entry -> docsEntries.put(docId, entry.getFirst()))
.ifError(error -> Minions.LOGGER.warn("Could not parse docs entry {}: {}", id, error.message()));
}
} catch (IOException | JsonParseException e) {
Minions.LOGGER.warn("Could not read file {}", id, e);
}
});
if(root[0] != null) {
DocsTree tree = new DocsTree(root[0]);
return new Pair<>(docsEntries, tree);
} else {
return new Pair<>(docsEntries, null);
}
}, executor);
}
@Override
public CompletableFuture<Void> apply(Pair<Map<Identifier, DocsEntry>, DocsTree> o, ResourceManager resourceManager, Executor executor) {
return CompletableFuture.supplyAsync(() -> {
docs = o.getLeft();
tree = o.getRight();
return null;
});
}
@Override
public Identifier getFabricId() {
return Identifier.of(Minions.MOD_ID, "docs");
}
}
@@ -0,0 +1,146 @@
package io.github.skippyall.minions.docs;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import net.minecraft.util.Identifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class DocsTree {
private final BranchElement root;
private final Map<Identifier, DocElement> entries = new HashMap<>();
public DocsTree(BranchElement root) {
this.root = root;
initEntries();
}
private void initEntries() {
DocElement element = root.first();
do {
entries.put(element.id, element);
element = element.next();
} while (element != null);
}
public BranchElement getRoot() {
return root;
}
public DocElement getElement(Identifier id) {
return entries.get(id);
}
public static abstract sealed class Element {
private BranchElement parent;
public BranchElement getParent() {
return parent;
}
void setParent(BranchElement parent) {
this.parent = parent;
}
public DocElement next() {
return parent.next(this);
}
public DocElement previous() {
return parent.previous(this);
}
}
public static final class DocElement extends Element {
public static final Codec<DocElement> CODEC = Identifier.CODEC.xmap(DocElement::new, DocElement::getId);
private final Identifier id;
public DocElement(Identifier element) {
this.id = element;
}
public Identifier getId() {
return id;
}
}
public static final class BranchElement extends Element {
public static final Codec<BranchElement> CODEC = Codec.<BranchElement>recursive("DocsBranch", codec ->
Codec.either(codec, DocElement.CODEC)
.xmap(
Either::unwrap,
element -> switch (element) {
case BranchElement branch -> Either.left(branch);
case DocElement doc -> Either.right(doc);
}
).listOf()
.xmap(BranchElement::new, branch -> branch.e)
).xmap(BranchElement::setParents, Function.identity());
private final List<Element> e;
public BranchElement(List<Element> elements) {
e = elements;
}
public DocElement first() {
return switch (e.getFirst()) {
case BranchElement branch -> branch.first();
case DocElement doc -> doc;
};
}
public DocElement last() {
return switch (e.getLast()) {
case BranchElement branch -> branch.last();
case DocElement doc -> doc;
};
}
public DocElement next(Element current) {
int nextIndex = e.indexOf(current) + 1;
if(nextIndex < e.size()) {
return switch (e.get(nextIndex)) {
case BranchElement branch -> branch.first();
case DocElement doc -> doc;
};
} else {
if(getParent() != null) {
return getParent().next(this);
} else {
return null;
}
}
}
public DocElement previous(Element current) {
int previousIndex = e.indexOf(current) - 1;
if(previousIndex >= 0) {
return switch (e.get(previousIndex)) {
case BranchElement branch -> branch.first();
case DocElement doc -> doc;
};
} else {
if(getParent() != null) {
return getParent().previous(this);
} else {
return null;
}
}
}
private BranchElement setParents() {
for(Element element : e) {
element.setParent(this);
if(element instanceof BranchElement branch) {
branch.setParents();
}
}
return this;
}
}
}
@@ -1,14 +1,81 @@
package io.github.skippyall.minions.docs;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.gui.GuiDisplay;
import net.minecraft.dialog.body.DialogBody;
import net.minecraft.dialog.body.ItemDialogBody;
import net.minecraft.dialog.body.PlainMessageDialogBody;
import net.minecraft.item.Item;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.text.Text;
import net.minecraft.text.TextCodecs;
import net.minecraft.util.Identifier;
public record ReferenceEntry(Text shortDescription, Text longDescription) {
public static final Codec<ReferenceEntry> CODEC = RecordCodecBuilder.create(instance ->
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public record ReferenceEntry(Metadata metadata, RegistryKey<?> object, Text shortDescription, Text longDescription) implements DocsEntry {
private static final Codec<RegistryKey<?>> REGISTRY_KEY_CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Identifier.CODEC.fieldOf("registry").forGetter(RegistryKey::getRegistry),
Identifier.CODEC.fieldOf("value").forGetter(RegistryKey::getValue)
).apply(instance, (registry, value) -> RegistryKey.of(RegistryKey.ofRegistry(registry), value))
);
public static final MapCodec<ReferenceEntry> CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(
Metadata.CODEC.fieldOf("metadata").forGetter(ReferenceEntry::getMetadata),
REGISTRY_KEY_CODEC.fieldOf("object").forGetter(ReferenceEntry::object),
TextCodecs.CODEC.fieldOf("shortDescription").forGetter(ReferenceEntry::shortDescription),
TextCodecs.CODEC.fieldOf("longDescription").forGetter(ReferenceEntry::longDescription)
).apply(instance, ReferenceEntry::new));
@Override
public Metadata getMetadata() {
return metadata;
}
@Override
public List<DialogBody> getDialog(DynamicRegistryManager manager) {
List<DialogBody> bodyElements = new ArrayList<>();
GuiDisplay display = getObjectDisplay(manager);
if(display != null) {
bodyElements.add(new ItemDialogBody(display.createItemStack(), Optional.empty(), false, false, 16, 16));
}
bodyElements.add(new PlainMessageDialogBody(Text.translatable(object.getValue().toTranslationKey(object.getRegistry().getPath())), 200));
bodyElements.add(new PlainMessageDialogBody(longDescription.copy().append("\n".repeat(100)), 200));
return bodyElements;
}
public GuiDisplay getObjectDisplay(DynamicRegistryManager manager) {
GuiDisplay display = GuiDisplay.DEFAULT_DISPLAY;
if(object.isOf(RegistryKeys.ITEM) || object.isOf(RegistryKeys.BLOCK)) {
Item item;
if(object.isOf(RegistryKeys.ITEM)) {
item = Registries.ITEM.get(object.getValue());
} else {
item = Registries.BLOCK.get(object.getValue()).asItem();
}
if(item != null) {
display = new GuiDisplay.ItemBased(item);
}
} else {
Identifier displayId = object.getValue().withPrefixedPath(object.getRegistry().getPath() + "/");
display = GuiDisplay.getGuiDisplay(displayId, manager);
}
return display;
}
@Override
public MapCodec<? extends DocsEntry> getCodec() {
return CODEC;
}
}
@@ -100,18 +100,22 @@ public class InstructionGui {
}
public static <T, A extends ValueSupplier<T, MinionRuntime>> void configureArgumentMenu(String instructionName, ConfiguredInstruction<MinionRuntime> instruction, Parameter<T> parameter, MinionFakePlayer minion, ServerPlayerEntity player) {
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 A argument = instruction.getArguments().getArgument(parameter);
@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());
@@ -26,7 +26,7 @@ public abstract class BlockEntityMinionInstructionListener<E extends BlockEntity
}
@Override
protected void add(MinecraftServer server) {
public void add(MinecraftServer server) {
super.add(server);
if(minion != null) {
registerInstructionListeners();
@@ -94,7 +94,7 @@ public abstract class BlockEntityMinionListener<E extends BlockEntity> implement
return false;
}
protected void add(MinecraftServer server) {
public void add(MinecraftServer server) {
MinionPersistentState.get(server).getMinionData(minionUuid).listeners().addListener(this);
MinionPersistentState.get(server).markDirty();
this.minion = (MinionFakePlayer) server.getPlayerManager().getPlayer(minionUuid);
@@ -190,7 +190,7 @@ public class EntityPlayerActionPack
if (onlyRideables)
{
entities = player.getWorld().getOtherEntities(player, player.getBoundingBox().expand(3.0D, 1.0D, 3.0D),
e -> (e instanceof MinecartEntity || e instanceof BoatEntity || e instanceof AbstractHorseEntity) && ((EntityAccessor)e).invokeCanAddPassenger(player));
e -> (e instanceof MinecartEntity || e instanceof BoatEntity || e instanceof AbstractHorseEntity) && ((EntityAccessor)e).minions$canAddPassenger(player));
}
else
{
@@ -23,6 +23,8 @@ import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.vehicle.AbstractBoatEntity;
import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.DisconnectionInfo;
import net.minecraft.network.NetworkSide;
@@ -210,6 +212,21 @@ public class MinionFakePlayer extends ServerPlayerEntity {
}
@Override
public boolean startRiding(Entity entityToRide, boolean force) {
if (super.startRiding(entityToRide, force)) {
// from ClientPacketListener.handleSetEntityPassengersPacket
if (entityToRide instanceof AbstractBoatEntity) {
this.lastYaw = entityToRide.getYaw();
this.setYaw(entityToRide.getYaw());
this.setHeadYaw(entityToRide.getHeadYaw());
}
return true;
} else {
return false;
}
}
private void shakeOff()
{
if (getVehicle() instanceof PlayerEntity) stopRiding();
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction;
package io.github.skippyall.minions.minion.program.instruction;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.EntityPlayerActionPack;
@@ -1,5 +1,5 @@
//partially code from https://github.com/gnembon/fabric-carpet (EntityPlayerActionPack)
package io.github.skippyall.minions.instruction;
package io.github.skippyall.minions.minion.program.instruction;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.EntityPlayerActionPack;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.inventory;
package io.github.skippyall.minions.minion.program.instruction.inventory;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.move;
package io.github.skippyall.minions.minion.program.instruction.move;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.move;
package io.github.skippyall.minions.minion.program.instruction.move;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.move;
package io.github.skippyall.minions.minion.program.instruction.move;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.gui.Displayable;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.move;
package io.github.skippyall.minions.minion.program.instruction.move;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.move;
package io.github.skippyall.minions.minion.program.instruction.move;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.supplier.Parameter;
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.instruction.move;
package io.github.skippyall.minions.minion.program.instruction.move;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
@@ -0,0 +1,100 @@
package io.github.skippyall.minions.minion.program.supplier;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.clipboard.BlockPosClipboard;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.supplier.ValueSupplier;
import io.github.skippyall.minions.program.supplier.ValueSupplierType;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.registration.MinionComponentTypes;
import io.github.skippyall.minions.registration.MinionItems;
import io.github.skippyall.minions.registration.ValueSuppliers;
import io.github.skippyall.minions.registration.ValueTypes;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.RegistryKey;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class AnalogInputSupplier implements ValueSupplier<Long, MinionRuntime> {
public static final Codec<AnalogInputSupplier> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
World.CODEC.fieldOf("analogInputWorld").forGetter(s -> s.analogInputWorld),
BlockPos.CODEC.fieldOf("analogInputPos").forGetter(s -> s.analogInputPos)
).apply(instance, AnalogInputSupplier::new));
private final RegistryKey<World> analogInputWorld;
private final BlockPos analogInputPos;
public AnalogInputSupplier(RegistryKey<World> analogInputWorld, BlockPos analogInputPos) {
this.analogInputWorld = analogInputWorld;
this.analogInputPos = analogInputPos;
}
@Override
public Long resolve(MinionRuntime minion) {
World world = minion.getMinion().getServer().getWorld(analogInputWorld);
if(world != null && world.isPosLoaded(analogInputPos) && world.getBlockState(analogInputPos).isOf(MinionBlocks.ANALOG_INPUT_BLOCK)) {
return (long) world.getReceivedRedstonePower(analogInputPos);
} else {
return 0L;
}
}
@Override
public ValueType<Long> getValueType() {
return ValueTypes.LONG;
}
@Override
public ValueSupplierType<MinionRuntime> getType() {
return ValueSuppliers.ANALOG_INPUT;
}
@Override
public Text getDisplayText() {
return Text.translatable("value_supplier_type.minions.analog_input.display", analogInputPos.toString(), analogInputWorld.getValue());
}
public static class AnalogInputSupplierType extends ValueSupplierType<MinionRuntime> {
@Override
public <T> Codec<? extends ValueSupplier<T, MinionRuntime>> getCodec(ValueType<T> type) {
if(type == ValueTypes.LONG) {
return ValueSupplier.castCodec(CODEC, ValueTypes.LONG, type);
}
return null;
}
@Override
public <T> CompletableFuture<ValueSupplier<T, MinionRuntime>> openConfiguration(ServerPlayerEntity player, ValueType<T> valueType, @Nullable ValueSupplier<T, MinionRuntime> previous) {
if(valueType != ValueTypes.LONG) {
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);
gui.setTitle(Text.translatable("value_supplier_type.minions.analog_input"));
gui.setSlot(4, new GuiElementBuilder(MinionItems.REFERENCE_ITEM)
.setCallback(() -> {
ItemStack cursor = player.currentScreenHandler.getCursorStack();
if(cursor.isOf(MinionItems.REFERENCE_ITEM) && cursor.get(MinionComponentTypes.REFERENCE) instanceof BlockPosClipboard pos) {
future.complete(new AnalogInputSupplier(pos.world(), pos.pos()).cast(valueType));
}
})
.setItemName(Text.translatable("value_supplier_type.minions.analog_input.config.click_with_reference"))
);
return future;
}
}
}
@@ -4,8 +4,13 @@ import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.stream.Stream;
@Mixin(Entity.class)
public interface EntityAccessor {
@Invoker("canAddPassenger")
boolean invokeCanAddPassenger(Entity other);
boolean minions$canAddPassenger(Entity other);
@Invoker("streamIntoPassengers")
Stream<Entity> minions$streamIntoPassengers();
}
@@ -0,0 +1,42 @@
package io.github.skippyall.minions.mixins;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.PistonBlockEntity;
import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(PistonBlockEntity.class)
public abstract class PistonMovingBlockEntityMixin {
@WrapOperation(method = "pushEntities", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/entity/Entity;getPistonBehavior()Lnet/minecraft/block/piston/PistonBehavior;"
))
private static PistonBehavior moveFakePlayers(Entity entity, Operation<PistonBehavior> original, World world, BlockPos pos, float f, PistonBlockEntity pistonBlockEntity)
{
if (entity instanceof MinionFakePlayer && pistonBlockEntity.getPushedBlock().isOf(Blocks.SLIME_BLOCK))
{
Vec3d vec3d = entity.getVelocity();
double x = vec3d.x;
double y = vec3d.y;
double z = vec3d.z;
Direction direction = pistonBlockEntity.getMovementDirection();
switch (direction.getAxis()) {
case X -> x = direction.getOffsetX();
case Y -> y = direction.getOffsetY();
case Z -> z = direction.getOffsetZ();
}
entity.setVelocity(x, y, z);
}
return original.call(entity);
}
}
@@ -1,20 +1,21 @@
package io.github.skippyall.minions.mixins;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.mixinhelper.EntityViewMixinHelper;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.SpawnHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(SpawnHelper.class)
public class SpawnHelperMixin {
@Redirect(method = "spawnEntitiesInChunk(Lnet/minecraft/entity/SpawnGroup;Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/world/chunk/Chunk;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/SpawnHelper$Checker;Lnet/minecraft/world/SpawnHelper$Runner;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;getClosestPlayer(DDDDZ)Lnet/minecraft/entity/player/PlayerEntity;"))
private static PlayerEntity checkMobSpawningMinion(ServerWorld instance, double x, double y, double z, double maxDistance, boolean b) {
return instance.getClosestPlayer(x, y, z, maxDistance, EntityPredicates.EXCEPT_SPECTATOR.and(entity -> {
@WrapOperation(method = "spawnEntitiesInChunk(Lnet/minecraft/entity/SpawnGroup;Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/world/chunk/Chunk;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/SpawnHelper$Checker;Lnet/minecraft/world/SpawnHelper$Runner;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;getClosestPlayer(DDDDZ)Lnet/minecraft/entity/player/PlayerEntity;"))
private static PlayerEntity checkMobSpawningMinion(ServerWorld world, double x, double y, double z, double maxDistance, boolean ignoreCreative, Operation<PlayerEntity> original) {
EntityViewMixinHelper.ADDITIONAL_PREDICATE.set(entity -> {
if(entity instanceof ServerPlayerEntity player) {
if(player instanceof MinionFakePlayer minion) {
return minion.canSpawnMobs();
@@ -22,6 +23,9 @@ public class SpawnHelperMixin {
return true;
}
return false;
}));
});
PlayerEntity player = original.call(world, x, y, z, maxDistance, ignoreCreative);
EntityViewMixinHelper.ADDITIONAL_PREDICATE.remove();
return player;
}
}
@@ -0,0 +1,33 @@
package io.github.skippyall.minions.mixins;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.tick.TickManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(TickManager.class)
public abstract class TickRateManagerMixin {
@Shadow
public abstract boolean shouldTick();
@ModifyReturnValue(method = "shouldSkipTick", at = @At("TAIL"))
private boolean handler(boolean alreadyFrozen, Entity entity) {
if (alreadyFrozen) return true;
if (shouldTick()) return false;
return !isActualPlayer(entity) && // not carrying players
((EntityAccessor) entity)
.minions$streamIntoPassengers()
.noneMatch(TickRateManagerMixin::isActualPlayer);
}
@Unique
private static boolean isActualPlayer(Entity e) {
return e instanceof PlayerEntity && !(e instanceof MinionFakePlayer);
}
}
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.util;
package io.github.skippyall.minions.polymer;
import eu.pb4.polymer.networking.api.PolymerNetworking;
import eu.pb4.polymer.networking.api.server.PolymerServerNetworking;
@@ -9,7 +9,7 @@ import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.util.Identifier;
import xyz.nucleoid.packettweaker.PacketContext;
public class PolymerUtil {
public class VersionSync {
public static final int NETWORK_VERSION = 1;
public static boolean isOnClient(PacketContext context) {
@@ -29,7 +29,7 @@ public class PolymerUtil {
@Override
public Id<? extends CustomPayload> getId() {
return null;
return PACKET_ID;
}
}
@@ -2,6 +2,7 @@ package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.program.value.SimpleValueType;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.program.value.ValueType;
import org.jetbrains.annotations.Nullable;
@@ -1,6 +1,7 @@
package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.ValueType;
@@ -31,14 +32,33 @@ public interface ValueSupplier<T, R extends InstructionRuntime<R>> {
}
}
/**
* WARNING: If originalType is not the type of the value suppliers from the codec, this will leak wrong generics!
*/
static <T, U, R extends InstructionRuntime<R>, A extends ValueSupplier<U,R>, C extends ValueSupplier<T, R>> @Nullable Codec<A> castCodec(Codec<C> codec, ValueType<T> originalType, ValueType<U> newType) {
if(originalType == newType) {
//noinspection unchecked
return (Codec<A>) codec;
} 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")
MinionRegistries.VALUE_TYPES.getCodec().<ValueSupplier<?,R>>partialDispatch(
"type",
s -> DataResult.success(s.getValueType()),
valueType -> {
if(type.getCodec(valueType) != null) {
return DataResult.success(type.getCodec(valueType).fieldOf("valueType"));
} else {
return DataResult.error(() -> "Supplier type " + type + "not available for value type " + valueType);
}
}
).fieldOf("valueType")
);
}
@@ -2,6 +2,8 @@ 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.Cast;
import io.github.skippyall.minions.program.value.Casts;
import java.util.ArrayList;
import java.util.Collection;
@@ -22,17 +24,29 @@ public class ValueSupplierList<R extends InstructionRuntime<R>> {
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 <F, T> T getValue(Parameter<T> parameter, R runtime) {
//noinspection unchecked
ValueSupplier<F,R> valueSupplier = (ValueSupplier<F, R>) getArgument(parameter);
if(valueSupplier == null) {
return 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());
if(valueSupplier.getValueType() == parameter.type()) {
return valueSupplier.cast(parameter.type()).resolve(runtime);
} else {
Cast<F,T> cast = Casts.getCast(valueSupplier.getValueType(), parameter.type());
if(cast != null) {
return cast.cast(valueSupplier.resolve(runtime));
}
}
return null;
}
public <T> void setArgument(Parameter<T> parameter, ValueSupplier<T,R> valueSupplier) {
public ValueSupplier<?,R> getArgument(Parameter<?> parameter) {
return arguments.get(parameter.name());
}
public <T> void setArgument(Parameter<T> parameter, ValueSupplier<?,R> valueSupplier) {
arguments.put(parameter.name(), valueSupplier);
onChange(parameter);
}
@@ -0,0 +1,77 @@
package io.github.skippyall.minions.program.value;
public class Cast<F, T> {
private final ValueType<F> from;
private final ValueType<T> to;
private final Caster<F,T> caster;
private final boolean lossy, canFail;
public Cast(ValueType<F> from, ValueType<T> to, Caster<F,T> caster) {
this(from, to, caster, false, false);
}
private Cast(ValueType<F> from, ValueType<T> to, Caster<F,T> caster, boolean lossy, boolean canFail) {
this.from = from;
this.to = to;
this.caster = caster;
this.lossy = lossy;
this.canFail = canFail;
}
public T cast(F from) {
return caster.cast(from);
}
public ValueType<F> getFrom() {
return from;
}
public ValueType<T> getTo() {
return to;
}
public boolean isLossy() {
return lossy;
}
public boolean canFail() {
return canFail;
}
public static class CastCrafter<F, T> {
private final ValueType<F> from;
private final ValueType<T> to;
private final Caster<F,T> caster;
private boolean lossy, canFail;
public CastCrafter(ValueType<F> from, ValueType<T> to, Caster<F, T> caster) {
this.from = from;
this.to = to;
this.caster = caster;
}
public CastCrafter<F, T> lossy() {
lossy = true;
return this;
}
public CastCrafter<F, T> canFail() {
canFail = true;
return this;
}
public Cast<F, T> craftCast() {
return new Cast<>(from, to, caster, lossy, canFail);
}
}
@FunctionalInterface
public interface Caster<F, T> {
T cast(F from);
}
}
@@ -0,0 +1,36 @@
package io.github.skippyall.minions.program.value;
import io.github.skippyall.minions.registration.ValueTypes;
import org.jetbrains.annotations.Nullable;
public class Casts {
public static <F,T> @Nullable Cast<F,T> getCast(ValueType<F> from, ValueType<T> to) {
if(from == to) {
//noinspection unchecked
return new Cast<>(from, to, v -> (T)v);
}
if(from == ValueTypes.LONG && to == ValueTypes.DOUBLE) {
//noinspection unchecked
return (Cast<F, T>) new Cast<>(ValueTypes.LONG, ValueTypes.DOUBLE, (v) -> (double)v);
}
if(from == ValueTypes.DOUBLE && to == ValueTypes.LONG) {
//noinspection unchecked
return (Cast<F, T>) new Cast.CastCrafter<>(ValueTypes.DOUBLE, ValueTypes.LONG, (v) -> (long)(double)v).lossy().craftCast();
}
if((from == ValueTypes.DOUBLE || from == ValueTypes.LONG) && to == ValueTypes.STRING) {
//noinspection unchecked
return (Cast<F, T>) new Cast.CastCrafter<>(from, ValueTypes.STRING, String::valueOf).craftCast();
}
return null;
}
public static boolean canCastSafely(ValueType<?> from, ValueType<?> to) {
if(from == to) {
return true;
} else {
Cast<?,?> cast = getCast(from, to);
return cast != null && !cast.canFail() && !cast.isLossy();
}
}
}
@@ -0,0 +1,29 @@
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;
}
}
@@ -0,0 +1,22 @@
package io.github.skippyall.minions.program.value;
import com.mojang.serialization.Codec;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public record SimpleValueType<T>(Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener, Function<T, Text> textDisplay) implements ValueType<T> {
@Override
public CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue) {
return valueDialogOpener.apply(player, previousValue);
}
@Override
public Text getDisplayText(T value) {
return textDisplay.apply(value);
}
}
@@ -3,17 +3,18 @@ package io.github.skippyall.minions.program.value;
import com.mojang.serialization.Codec;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public record ValueType<T>(Codec<T> codec, T defaultValue, BiFunction<ServerPlayerEntity, T, CompletableFuture<T>> valueDialogOpener, Function<T, Text> textDisplay) {
public CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue) {
return valueDialogOpener.apply(player, previousValue);
}
public interface ValueType<T> {
CompletableFuture<T> openValueDialog(ServerPlayerEntity player, T previousValue);
public Text getDisplayText(T value) {
return textDisplay.apply(value);
}
Text getDisplayText(T value);
Codec<T> codec();
T defaultValue();
}
@@ -0,0 +1,13 @@
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();
}
@@ -0,0 +1,18 @@
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);
}
@@ -2,6 +2,7 @@ package io.github.skippyall.minions.registration;
import com.mojang.serialization.MapCodec;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.clipboard.BlockPosClipboard;
import io.github.skippyall.minions.clipboard.InstructionClipboard;
import io.github.skippyall.minions.clipboard.Clipboard;
import net.minecraft.registry.Registry;
@@ -14,5 +15,6 @@ public class ClipboardTypes {
static void register() {
register("instruction", InstructionClipboard.CODEC);
register("position", BlockPosClipboard.CODEC);
}
}
@@ -0,0 +1,12 @@
package io.github.skippyall.minions.registration;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.docs.ReferenceEntry;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class DocsEntryTypes {
public static void register() {
Registry.register(MinionRegistries.DOCS_ENTRY_TYPES, Identifier.of(Minions.MOD_ID, "reference_entry"), ReferenceEntry.CODEC);
}
}
@@ -1,17 +1,17 @@
package io.github.skippyall.minions.registration;
import io.github.skippyall.minions.instruction.inventory.SwapItemExecution;
import io.github.skippyall.minions.minion.program.instruction.inventory.SwapItemExecution;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
import io.github.skippyall.minions.program.instruction.InstructionType;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.EntityPlayerActionPack;
import io.github.skippyall.minions.instruction.ActionExecution;
import io.github.skippyall.minions.instruction.MineBlockExecution;
import io.github.skippyall.minions.instruction.move.ContinuousWalkExecution;
import io.github.skippyall.minions.instruction.move.TurnExecution;
import io.github.skippyall.minions.instruction.move.TurnVectorExecution;
import io.github.skippyall.minions.instruction.move.WalkExecution;
import io.github.skippyall.minions.minion.program.instruction.ActionExecution;
import io.github.skippyall.minions.minion.program.instruction.MineBlockExecution;
import io.github.skippyall.minions.minion.program.instruction.move.ContinuousWalkExecution;
import io.github.skippyall.minions.minion.program.instruction.move.TurnExecution;
import io.github.skippyall.minions.minion.program.instruction.move.TurnVectorExecution;
import io.github.skippyall.minions.minion.program.instruction.move.WalkExecution;
import io.github.skippyall.minions.program.supplier.Parameter;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
@@ -2,12 +2,14 @@ package io.github.skippyall.minions.registration;
import eu.pb4.polymer.core.api.block.PolymerBlockUtils;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.block.input.AnalogInputBlock;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerBlock;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerBlockEntity;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
@@ -35,6 +37,15 @@ public class MinionBlocks {
FabricBlockEntityTypeBuilder.create(MinionTriggerBlockEntity::new, MINION_TRIGGER_BLOCK).build()
);
public static final Identifier ANALOG_INPUT_BLOCK_ID = Identifier.of(Minions.MOD_ID, "analog_input");
public static final AnalogInputBlock ANALOG_INPUT_BLOCK = Registry.register(
Registries.BLOCK,
ANALOG_INPUT_BLOCK_ID,
new AnalogInputBlock(AbstractBlock.Settings.create()
.registryKey(RegistryKey.of(RegistryKeys.BLOCK, ANALOG_INPUT_BLOCK_ID))
)
);
public static void register() {
PolymerBlockUtils.registerBlockEntity(MINION_TRIGGER_BE_TYPE);
}
@@ -8,13 +8,13 @@ import net.minecraft.util.Identifier;
import static io.github.skippyall.minions.minion.MinionConfig.booleanOption;
public class MinionConfigOptions {
public static final MinionConfig.Option<Boolean> showInServerList = register(booleanOption(id("showInServerList"), false));
public static final MinionConfig.Option<Boolean> showInTabList = register(booleanOption(id("showInTabList"), false));
public static final MinionConfig.Option<Boolean> sendLoginMessage = register(booleanOption(id("sendLoginMessage"), false));
public static final MinionConfig.Option<Boolean> sendLogoutMessage = register(booleanOption(id("sendLogoutMessage"), false));
public static final MinionConfig.Option<Boolean> countForSleeping = register(booleanOption(id("countForSleeping"), false));
public static final MinionConfig.Option<Boolean> countForPlayerLimit = register(booleanOption(id("countForPlayerLimit"), false));
public static final MinionConfig.Option<Boolean> spawnAndDespawnMobs = register(booleanOption(id("spawnAndDespawnMobs"), false));
public static final MinionConfig.Option<Boolean> showInServerList = register(booleanOption(id("show_in_server_list"), false));
public static final MinionConfig.Option<Boolean> showInTabList = register(booleanOption(id("show_in_tab_list"), false));
public static final MinionConfig.Option<Boolean> sendLoginMessage = register(booleanOption(id("send_login_message"), false));
public static final MinionConfig.Option<Boolean> sendLogoutMessage = register(booleanOption(id("send_logout_message"), false));
public static final MinionConfig.Option<Boolean> countForSleeping = register(booleanOption(id("count_for_sleeping"), false));
public static final MinionConfig.Option<Boolean> countForPlayerLimit = register(booleanOption(id("count_for_player_limit"), false));
public static final MinionConfig.Option<Boolean> spawnAndDespawnMobs = register(booleanOption(id("spawn_and_despawn_mobs"), false));
private static <T> MinionConfig.Option<T> register(MinionConfig.Option<T> option) {
return Registry.register(MinionRegistries.MINION_CONFIG_OPTIONS, option.key(), option);
@@ -23,4 +23,6 @@ public class MinionConfigOptions {
private static Identifier id(String name) {
return Identifier.of(Minions.MOD_ID, name);
}
public static void register() {}
}
@@ -5,10 +5,12 @@ public class MinionRegistration {
MinionRegistries.register();
ClipboardTypes.register();
DocsEntryTypes.register();
GuiDisplayTypes.register();
Instructions.register();
MinionBlocks.register();
MinionComponentTypes.register();
MinionConfigOptions.register();
MinionItems.register();
MinionListeners.register();
SkinProviders.register();
@@ -3,6 +3,7 @@ package io.github.skippyall.minions.registration;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.docs.DocsEntry;
import io.github.skippyall.minions.docs.ReferenceEntry;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.minion.MinionConfig;
@@ -35,9 +36,10 @@ public class MinionRegistries {
public static final Registry<MapCodec<? extends Clipboard>> CLIPBOARD_TYPES = registry("clipboard_type");
public static final Registry<SpecialAbility> SPECIAL_ABILITIES = registry("special_ability");
public static final Registry<MinionConfig.Option<?>> MINION_CONFIG_OPTIONS = registry("minion_config_option");
public static final Registry<MapCodec<? extends DocsEntry>> DOCS_ENTRY_TYPES = registry("docs_entry_type");
public static final RegistryKey<Registry<GuiDisplay>> GUI_DISPLAY = key("gui_display");
public static final RegistryKey<Registry<ReferenceEntry>> REFERENCE_ENTRY = key("reference_entry");
public static final RegistryKey<Registry<ReferenceEntry>> DOCS_ENTRY = key("docs_entry");
private static <T> Registry<T> registry(String id) {
return FabricRegistryBuilder.<T>createSimple(key(id)).attribute(RegistryAttribute.OPTIONAL).buildAndRegister();
@@ -49,6 +51,5 @@ public class MinionRegistries {
public static void register() {
DynamicRegistries.register(GUI_DISPLAY, GuiDisplay.CODEC);
DynamicRegistries.register(REFERENCE_ENTRY, ReferenceEntry.CODEC);
}
}
@@ -1,5 +1,6 @@
package io.github.skippyall.minions.registration;
import io.github.skippyall.minions.minion.program.supplier.AnalogInputSupplier;
import io.github.skippyall.minions.program.supplier.FixedValueSupplierType;
import io.github.skippyall.minions.program.supplier.ValueSupplierType;
import io.github.skippyall.minions.Minions;
@@ -9,6 +10,7 @@ import net.minecraft.util.Identifier;
public class ValueSuppliers {
public static final FixedValueSupplierType<MinionRuntime> FIXED_VALUE_SUPPLIER_TYPE = register("fixed", new FixedValueSupplierType<>());
public static final AnalogInputSupplier.AnalogInputSupplierType ANALOG_INPUT = register("analog_input", new AnalogInputSupplier.AnalogInputSupplierType());
public static <T extends ValueSupplierType<MinionRuntime>> T register(String id, T type) {
return Registry.register(MinionRegistries.VALUE_SUPPLIER_TYPES, Identifier.of(Minions.MOD_ID, id), type);
@@ -1,11 +1,12 @@
package io.github.skippyall.minions.registration;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.program.value.SimpleValueType;
import io.github.skippyall.minions.program.value.ValueType;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.gui.input.ChoiceInput;
import io.github.skippyall.minions.gui.input.TextInput;
import io.github.skippyall.minions.instruction.move.TurnDirection;
import io.github.skippyall.minions.minion.program.instruction.move.TurnDirection;
import net.minecraft.registry.Registry;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
@@ -79,7 +80,7 @@ public class ValueTypes {
return Registry.register(
MinionRegistries.VALUE_TYPES,
identifier,
new ValueType<>(
new SimpleValueType<>(
codec,
defaultValue,
valueDialogOpener,
@@ -0,0 +1,12 @@
{
"type": "minions:reference_entry",
"metadata": {
"title": "Hi"
},
"object": {
"registry": "minions:value_type",
"value": "minions:float"
},
"shortDescription": "Whatever floats your goat",
"longDescription": "A decimal value"
}
@@ -0,0 +1,3 @@
[
"minions:test"
]
@@ -55,6 +55,7 @@
"minions.command.minion.not_present": "This minion does not exist",
"minions.reference.instruction.tooltip": "Linked to instruction %s in %s",
"minions.reference.block.tooltip": "Linked to block at %s",
"instruction_type.minions.walk": "Walk",
"instruction_type.minions.walk.description": "Walk forward a specified amount of blocks",
@@ -70,8 +71,11 @@
"value_type.minions.string": "Text",
"value_type.minions.turn_direction": "Turn Direction",
"value_supplier_type.minions.fixed": "Value",
"value_supplier_type.minions.fixed": "Fixed Value",
"value_supplier_type.minions.fixed.display": "Fixed Value: %s",
"value_supplier_type.minions.analog_input": "Analog Input",
"value_supplier_type.minions.analog_input.display": "Analog Input at %s in %s",
"value_supplier_type.minions.analog_input.config.click_with_reference": "Click here with a position clipboard",
"item.minions.attack_module": "Attack Module",
"item.minions.interact_module": "Interact Module",
+4 -2
View File
@@ -9,20 +9,22 @@
"EntityAccessor",
"EntityMixin",
"EntityViewMixin",
"compat.universal_graves.GraveMixin",
"MinecraftServerMixin",
"MobEntityMixin",
"PistonMovingBlockEntityMixin",
"PlayerListEntryS2CPacket$EntryMixin",
"PlayerListMixin",
"PlayerMixin",
"ServerPlayNetworkHandlerMixin",
"SleepManagerMixin",
"SpawnHelperMixin",
"TickRateManagerMixin",
"antimobcap.ChunkLevelManager$DistanceFromNearestPlayerTrackerMixin",
"antimobcap.ChunkLevelManagerMixin",
"antimobcap.ChunkPosDistanceLevelPropagatorMixin",
"antimobcap.ServerChunkManagerAccessor",
"antimobcap.ServerChunkManagerMixin"
"antimobcap.ServerChunkManagerMixin",
"compat.universal_graves.GraveMixin"
],
"client": [],
"server": [],