Listen up!

This commit is contained in:
skippyall
2026-01-05 13:12:40 +01:00
parent c5b3c883ca
commit 3f2a52fd0a
87 changed files with 2191 additions and 492 deletions
@@ -0,0 +1,55 @@
package io.github.skippyall.minions.block;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.minion.MinionListener;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.Optional;
public class BlockEntityMinionListener implements MinionListener {
protected RegistryKey<World> worldKey;
protected BlockPos pos;
public BlockEntityMinionListener(RegistryKey<World> worldKey, BlockPos pos) {
this.worldKey = worldKey;
this.pos = pos;
}
public static final Codec<BlockEntityMinionListener> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
World.CODEC.fieldOf("world").forGetter(listener -> listener.worldKey),
BlockPos.CODEC.fieldOf("pos").forGetter(listener -> listener.pos)
).apply(instance, BlockEntityMinionListener::new));
private BlockEntityState getBlockEntityState(MinecraftServer server) {
World world = server.getWorld(worldKey);
if(world == null || !world.isPosLoaded(pos)) {
return BlockEntityState.UNLOADED;
}
if(world.getBlockEntity(pos) instanceof MinionListeningBlockEntity) {
return BlockEntityState.LOADED;
} else {
return BlockEntityState.REMOVED;
}
}
public Optional<MinionListeningBlockEntity> getBlockEntity(MinecraftServer server) {
World world = server.getWorld(worldKey);
if(world != null && world.isPosLoaded(pos) && world.getBlockEntity(pos) instanceof MinionListeningBlockEntity be) {
return Optional.of(be);
}
return Optional.empty();
}
public enum BlockEntityState {
LOADED,
REMOVED,
UNLOADED
}
}
@@ -0,0 +1,7 @@
package io.github.skippyall.minions.block;
import io.github.skippyall.minions.minion.MinionListener;
public interface MinionListeningBlockEntity extends MinionListener {
}
@@ -2,67 +2,118 @@ package io.github.skippyall.minions.block;
import com.mojang.serialization.MapCodec;
import eu.pb4.polymer.core.api.block.PolymerBlock;
import io.github.skippyall.minions.MinionRegistration;
import io.github.skippyall.minions.PlayerClipboardAttachment;
import eu.pb4.polymer.core.api.utils.PolymerClientDecoded;
import eu.pb4.polymer.core.api.utils.PolymerKeepModel;
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
import eu.pb4.polymer.virtualentity.api.BlockWithElementHolder;
import eu.pb4.polymer.virtualentity.api.ElementHolder;
import eu.pb4.polymer.virtualentity.api.elements.ItemDisplayElement;
import io.github.skippyall.minions.MinionBlocks;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.reference.InstructionReference;
import io.github.skippyall.minions.reference.Reference;
import io.github.skippyall.minions.util.PolymerUtil;
import net.minecraft.block.AbstractRedstoneGateBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.Blocks;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.SideShapeType;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldView;
import net.minecraft.world.block.WireOrientation;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.packettweaker.PacketContext;
import java.util.Optional;
public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock {
public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock, PolymerKeepModel, PolymerClientDecoded, BlockWithElementHolder {
public static final MapCodec<MinionTriggerBlock> CODEC = createCodec(MinionTriggerBlock::new);
public static final BooleanProperty POWERED = BooleanProperty.of("powered");
public static final BooleanProperty RUNNING = BooleanProperty.of("running");
public static final VoxelShape SHAPE = Block.createColumnShape(16.0, 0.0, 2.0);
public MinionTriggerBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(POWERED, false).with(RUNNING, false));
setDefaultState(getDefaultState().with(POWERED, false));
}
@Override
protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return SHAPE;
}
@Override
protected boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
BlockPos blockPos = pos.down();
return this.canPlaceAbove(world, blockPos, world.getBlockState(blockPos));
}
protected boolean canPlaceAbove(WorldView world, BlockPos pos, BlockState state) {
return state.isSideSolid(world, pos, Direction.UP, SideShapeType.RIGID);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(POWERED, RUNNING);
builder.add(POWERED);
}
@Override
protected ActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if(stack.get(Reference.COMPONENT_TYPE) instanceof InstructionReference instruction) {
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> {
be.setInstruction(instruction.selectedMinion(), instruction.selectedInstruction());
player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1);
stack.decrement(1);
});
return ActionResult.SUCCESS;
}
return super.onUseWithItem(stack, state, world, pos, player, hand, hit);
}
@Override
protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
PlayerClipboardAttachment clipboard = player.getAttached(PlayerClipboardAttachment.TYPE);
if(clipboard != null) {
Optional<MinionTriggerBlockEntity> be = world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE);
if(be.isPresent()) {
be.get().setInstruction(clipboard.selectedMinion(), clipboard.selectedInstruction());
player.playSoundToPlayer(SoundEvents.BLOCK_NOTE_BLOCK_CHIME.value(), SoundCategory.BLOCKS, 1, 1);
return ActionResult.SUCCESS;
}
}
return ActionResult.PASS;
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> {
String name = MinionPersistentState.INSTANCE.getMinionData(be.getMinionUuid()).name();
player.sendMessage(Text.translatable("minions.reference.instruction.tooltip", name, be.getInstructionName()), true);
});
return ActionResult.SUCCESS;
}
@Override
protected void neighborUpdate(BlockState state, World world, BlockPos pos, Block sourceBlock, @Nullable WireOrientation wireOrientation, boolean notify) {
if(!canPlaceAt(state, world, pos)) {
dropStacks(state, world, pos);
world.removeBlock(pos, false);
}
boolean newPower = world.isReceivingRedstonePower(pos);
if(state.get(POWERED) != newPower) {
world.setBlockState(pos, state.with(POWERED, newPower));
world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::updatePower);
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::updatePower);
}
}
@@ -73,7 +124,7 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock
@Override
protected int getComparatorOutput(BlockState state, World world, BlockPos pos) {
return world.getBlockEntity(pos, MinionRegistration.MINION_TRIGGER_BE_TYPE).map(MinionTriggerBlockEntity::getComparatorOutput).orElse(0);
return world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).map(MinionTriggerBlockEntity::getComparatorOutput).orElse(0);
}
@Override
@@ -88,15 +139,34 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock
@Override
public BlockState getPolymerBlockState(BlockState state, PacketContext context) {
return state.get(POWERED) ? Blocks.REDSTONE_BLOCK.getDefaultState() : Blocks.GOLD_BLOCK.getDefaultState();
return PolymerUtil.isOnClient(context) ? state : Blocks.COMPARATOR.getDefaultState().with(AbstractRedstoneGateBlock.POWERED, state.get(POWERED));
}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
if(type == MinionRegistration.MINION_TRIGGER_BE_TYPE) {
if(type == MinionBlocks.MINION_TRIGGER_BE_TYPE) {
return MinionTriggerBlockEntity::tick;
} else {
return null;
}
}
@Override
public @Nullable ElementHolder createElementHolder(ServerWorld world, BlockPos pos, BlockState initialBlockState) {
ElementHolder holder = new ElementHolder() {
@Override
public boolean startWatching(ServerPlayNetworkHandler player) {
if(PolymerResourcePackUtils.hasMainPack(player)) {
return super.startWatching(player);
} else {
return false;
}
}
};
ItemStack stack = new ItemStack(Items.BARRIER);
stack.set(DataComponentTypes.ITEM_MODEL, Identifier.of(Minions.MOD_ID, "minion_trigger_no_plate_" + (initialBlockState.get(MinionTriggerBlock.POWERED) ? "active" : "inactive")));
holder.addElement(new ItemDisplayElement(stack));
return holder;
}
}
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.block;
import io.github.skippyall.minions.MinionRegistration;
import io.github.skippyall.minions.MinionBlocks;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
@@ -23,7 +23,7 @@ public class MinionTriggerBlockEntity extends BlockEntity {
private boolean runningCache = false;
public MinionTriggerBlockEntity(BlockPos pos, BlockState state) {
super(MinionRegistration.MINION_TRIGGER_BE_TYPE, pos, state);
super(MinionBlocks.MINION_TRIGGER_BE_TYPE, pos, state);
}
public void setInstruction(UUID minionUuid, String instructionName) {
@@ -38,12 +38,12 @@ public class MinionTriggerBlockEntity extends BlockEntity {
}
if(triggerBlockEntity.first) {
triggerBlockEntity.first = false;
world.updateComparators(pos, MinionRegistration.MINION_TRIGGER_BLOCK);
world.updateComparators(pos, MinionBlocks.MINION_TRIGGER_BLOCK);
triggerBlockEntity.runningCache = triggerBlockEntity.getInstruction().map(ConfiguredInstruction::isRunning).orElse(false);
} else {
boolean isRunning = triggerBlockEntity.getInstruction().map(ConfiguredInstruction::isRunning).orElse(false);
if (isRunning != triggerBlockEntity.runningCache) {
world.updateComparators(pos, MinionRegistration.MINION_TRIGGER_BLOCK);
world.updateComparators(pos, MinionBlocks.MINION_TRIGGER_BLOCK);
triggerBlockEntity.runningCache = isRunning;
}
}
@@ -78,6 +78,14 @@ public class MinionTriggerBlockEntity extends BlockEntity {
return Optional.empty();
}
public UUID getMinionUuid() {
return minionUuid;
}
public String getInstructionName() {
return instructionName;
}
public Optional<ConfiguredInstruction<MinionRuntime>> getInstruction(MinionFakePlayer minion) {
return Optional.ofNullable(minion.getInstructionManager().getInstruction(instructionName));
}
@@ -0,0 +1,25 @@
package io.github.skippyall.minions.block;
import eu.pb4.polymer.core.api.item.PolymerBlockItem;
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.packettweaker.PacketContext;
public class MinionTriggerBlockItem extends PolymerBlockItem {
public MinionTriggerBlockItem(Block block, Settings settings, Item polymerItem) {
super(block, settings, polymerItem, true);
}
@Override
public @Nullable Identifier getPolymerItemModel(ItemStack stack, PacketContext context) {
if(PolymerResourcePackUtils.hasMainPack(context)) {
return super.getPolymerItemModel(stack, context);
} else {
return null;
}
}
}