From 5500b78e53800c12527bf3558a52f266942ed88a Mon Sep 17 00:00:00 2001 From: XevianLight <63034748+XevianLight@users.noreply.github.com> Date: Tue, 20 Jan 2026 18:05:35 -0700 Subject: [PATCH] Multiblocks now use dummy blocks. BaseMultiblockDummyBlock created for all dummies to extend from. --- .../net/xevianlight/aphelion/Aphelion.java | 3 +- .../block/custom/ArcFurnaceCasingBlock.java | 42 ++-- .../custom/VacuumArcFurnaceController.java | 11 +- .../block/dummy/BaseMultiblockDummyBlock.java | 173 ++++++++++++++++ .../block/dummy/VAFMultiblockDummyBlock.java | 18 ++ .../BaseMultiblockDummyBlockEntity.java | 185 ++++++++++++++++++ .../entity/VAFMultiblockDummyBlockEntity.java | 12 ++ .../renderer/MultiblockDummyRenderer.java | 52 +++++ .../block/entity/custom/EAFPartEntity.java | 18 ++ .../VacuumArcFurnaceControllerEntity.java | 77 +++++--- .../aphelion/core/init/ModBlockEntities.java | 6 + .../aphelion/core/init/ModBlocks.java | 2 + .../aphelion/core/init/ModItems.java | 2 + .../aphelion/event/ModBusEvents.java | 5 + .../aphelion/util/IMultiblockController.java | 18 ++ .../aphelion/util/IMultiblockPart.java | 9 +- .../aphelion/util/MultiblockHelper.java | 139 ++++++++----- .../resources/assets/aphelion/lang/en_us.json | 2 + 18 files changed, 663 insertions(+), 111 deletions(-) create mode 100644 src/main/java/net/xevianlight/aphelion/block/dummy/BaseMultiblockDummyBlock.java create mode 100644 src/main/java/net/xevianlight/aphelion/block/dummy/VAFMultiblockDummyBlock.java create mode 100644 src/main/java/net/xevianlight/aphelion/block/dummy/entity/BaseMultiblockDummyBlockEntity.java create mode 100644 src/main/java/net/xevianlight/aphelion/block/dummy/entity/VAFMultiblockDummyBlockEntity.java create mode 100644 src/main/java/net/xevianlight/aphelion/block/dummy/renderer/MultiblockDummyRenderer.java diff --git a/src/main/java/net/xevianlight/aphelion/Aphelion.java b/src/main/java/net/xevianlight/aphelion/Aphelion.java index a942b09..80bfa74 100644 --- a/src/main/java/net/xevianlight/aphelion/Aphelion.java +++ b/src/main/java/net/xevianlight/aphelion/Aphelion.java @@ -6,6 +6,7 @@ import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; +import net.xevianlight.aphelion.block.dummy.renderer.MultiblockDummyRenderer; import net.xevianlight.aphelion.client.AphelionConfig; import net.xevianlight.aphelion.core.init.*; import net.xevianlight.aphelion.fluid.BaseFluidType; @@ -114,7 +115,7 @@ public class Aphelion { @SubscribeEvent public static void registerBER(EntityRenderersEvent.RegisterRenderers event) { - + event.registerBlockEntityRenderer(ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), MultiblockDummyRenderer::new); } @SubscribeEvent diff --git a/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java b/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java index 6400cc7..4808409 100644 --- a/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java +++ b/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java @@ -9,6 +9,7 @@ import net.minecraft.world.SimpleMenuProvider; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; @@ -19,10 +20,11 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.phys.BlockHitResult; +import net.xevianlight.aphelion.block.dummy.BaseMultiblockDummyBlock; import net.xevianlight.aphelion.block.entity.custom.EAFPartEntity; -import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.IMultiblockController; import net.xevianlight.aphelion.util.MultiblockHelper; import org.jetbrains.annotations.Nullable; @@ -44,7 +46,7 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { public static Properties getProperties() { return Properties .of() - .sound(SoundType.ANVIL) + .sound(SoundType.NETHERITE_BLOCK) .destroyTime(2f) .explosionResistance(10f) .requiresCorrectToolForDrops(); @@ -66,6 +68,12 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { // return new EAFPartEntity(blockPos, blockState); // } + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + super.onPlace(state, level, pos, oldState, movedByPiston); + if (!level.isClientSide()) pingNearbyController(level, pos); + } + private void pingNearbyController(Level level, BlockPos pos) { int r = 5; BlockPos.MutableBlockPos mp = new BlockPos.MutableBlockPos(); @@ -75,39 +83,17 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { for (int dz=-r; dz<=r; dz++) { mp.set(pos.getX()+dx, pos.getY()+dy, pos.getZ()+dz); BlockEntity be = level.getBlockEntity(mp); - if (be instanceof VacuumArcFurnaceControllerEntity vaf) { - if (level.getBlockState(vaf.getBlockPos()).getBlock() instanceof VacuumArcFurnaceController) { - MultiblockHelper.tryForm(level, vaf.getBlockState(), vaf.getBlockPos(), VacuumArcFurnaceControllerEntity.SHAPE, AphelionBlockStateProperties.FORMED); - } + if (be instanceof IMultiblockController controller) { + controller.markDirty(); + } } } - @Override - public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { - if (state.getValue(AphelionBlockStateProperties.FORMED)) { - if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof EAFPartEntity eafPartEntity) { - if (eafPartEntity.getControllerPos() != null) - if (level.getBlockEntity(eafPartEntity.getControllerPos()) instanceof VacuumArcFurnaceControllerEntity) - serverPlayer.openMenu(new SimpleMenuProvider((VacuumArcFurnaceControllerEntity) level.getBlockEntity(eafPartEntity.getControllerPos()), Component.literal("Vacuum Arc Furnace")), eafPartEntity.getControllerPos()); - } - return InteractionResult.sidedSuccess(level.isClientSide); - } - - return InteractionResult.FAIL; - } - - @Override - public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { - super.onPlace(state, level, pos, oldState, movedByPiston); - if (!level.isClientSide()) pingNearbyController(level, pos); - } - @Override public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { - if (!level.isClientSide() && state.getBlock() != newState.getBlock()) { + if (!level.isClientSide() && state.getBlock() != newState.getBlock()) pingNearbyController(level, pos); - } super.onRemove(state, level, pos, newState, movedByPiston); } diff --git a/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java b/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java index d036da0..fb84222 100644 --- a/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java +++ b/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java @@ -25,9 +25,11 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.phys.BlockHitResult; import net.neoforged.neoforge.items.ItemStackHandler; +import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; import net.xevianlight.aphelion.core.init.ModBlockEntities; import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.IMultiblockController; import net.xevianlight.aphelion.util.MultiblockHelper; import org.jetbrains.annotations.Nullable; @@ -43,6 +45,8 @@ public class VacuumArcFurnaceController extends BaseEntityBlock { public VacuumArcFurnaceController(Properties properties) { super(properties); this.registerDefaultState(this.getStateDefinition().any() + .setValue(FACING, Direction.NORTH) + .setValue(LIT, false) .setValue(FORMED, false)); } @@ -61,7 +65,7 @@ public class VacuumArcFurnaceController extends BaseEntityBlock { public static Properties getProperties() { return Properties .of() - .sound(SoundType.METAL) + .sound(SoundType.NETHERITE_BLOCK) .destroyTime(2f) .explosionResistance(10f) .requiresCorrectToolForDrops(); @@ -73,7 +77,6 @@ public class VacuumArcFurnaceController extends BaseEntityBlock { @Override public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { - if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) { if (vacuumArcFurnaceEntity.isFormed()) serverPlayer.openMenu(new SimpleMenuProvider(vacuumArcFurnaceEntity, Component.literal("Vacuum Arc Furnace")), pos); @@ -110,8 +113,8 @@ public class VacuumArcFurnaceController extends BaseEntityBlock { super.onPlace(state, level, pos, oldState, movedByPiston); if (!level.isClientSide() && oldState.getBlock() != state.getBlock()) { BlockEntity blockEntity= level.getBlockEntity(pos); - if (blockEntity instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) { - MultiblockHelper.tryForm(level, state, pos, vacuumArcFurnaceEntity.SHAPE, AphelionBlockStateProperties.FORMED); + if (blockEntity instanceof IMultiblockController mbController) { + mbController.markDirty(); } } } diff --git a/src/main/java/net/xevianlight/aphelion/block/dummy/BaseMultiblockDummyBlock.java b/src/main/java/net/xevianlight/aphelion/block/dummy/BaseMultiblockDummyBlock.java new file mode 100644 index 0000000..fdb6e7e --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/dummy/BaseMultiblockDummyBlock.java @@ -0,0 +1,173 @@ +package net.xevianlight.aphelion.block.dummy; + +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuConstructor; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.xevianlight.aphelion.util.IMultiblockController; +import net.xevianlight.aphelion.util.IMultiblockPart; +import org.jetbrains.annotations.Nullable; + +public class BaseMultiblockDummyBlock extends BaseEntityBlock { + + public static final MapCodec CODEC = simpleCodec(BaseMultiblockDummyBlock::new); + + public BaseMultiblockDummyBlock(Properties properties) { + super(properties); + } + + @Override + protected MapCodec codec() { + return CODEC; + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return null; + } + + public static Item.Properties getItemProperties() { + return new Item.Properties(); + } + + public static Properties getProperties() { + return Properties + .of() + .sound(SoundType.NETHERITE_BLOCK) + .destroyTime(2f) + .explosionResistance(10f) + .requiresCorrectToolForDrops(); + } + + @Override + protected RenderShape getRenderShape(BlockState state) { + return RenderShape.INVISIBLE; + } + + + @Override + public int getLightBlock(BlockState state, BlockGetter level, BlockPos pos) { + return 0; + } + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter level, BlockPos pos) { + return true; + } + + private void pingNearbyController(Level level, BlockPos pos) { + int r = 5; + BlockPos.MutableBlockPos mp = new BlockPos.MutableBlockPos(); + + for (int dx=-r; dx<=r; dx++) + for (int dy=-r; dy<=r; dy++) + for (int dz=-r; dz<=r; dz++) { + mp.set(pos.getX()+dx, pos.getY()+dy, pos.getZ()+dz); + BlockEntity be = level.getBlockEntity(mp); + if (be instanceof IMultiblockController controller) { + controller.markDirty(); + } + } + } + +// @Override +// public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { +// if (!level.isClientSide && player instanceof ServerPlayer serverPlayer) { +// if (level.getBlockEntity(pos) instanceof IMultiblockPart multiblockPartEntity) { +//// if (multiblockPartEntity.getControllerPos() != null) { +// if (level.getBlockEntity(multiblockPartEntity.getControllerPos()) instanceof IMultiblockController controller) +// serverPlayer.openMenu(new SimpleMenuProvider((MenuConstructor) level.getBlockEntity(multiblockPartEntity.getControllerPos()), Component.literal(controller.getMenuTitle())), multiblockPartEntity.getControllerPos()); +//// } else { +//// if (level.getBlockEntity(pos) instanceof IMultiblockPart mbp) { +//// level.setBlock(pos, mbp.getMimicing(), UPDATE_ALL); +//// } else { +//// level.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_ALL); +//// } +//// } +// return InteractionResult.sidedSuccess(level.isClientSide); +// } +// } +// +// return InteractionResult.FAIL; +// } + + + @Override + public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { + + if (!level.isClientSide && player instanceof ServerPlayer serverPlayer) { + if (level.getBlockEntity(pos) instanceof IMultiblockPart mbPartEntity) { + if (mbPartEntity.getControllerPos() != null) { + if (level.getBlockEntity(mbPartEntity.getControllerPos()) instanceof IMultiblockController controller) { + serverPlayer.openMenu(new SimpleMenuProvider((MenuConstructor) level.getBlockEntity(mbPartEntity.getControllerPos()), Component.literal(controller.getMenuTitle())), mbPartEntity.getControllerPos()); + return InteractionResult.sidedSuccess(level.isClientSide); + } + } else { + if (level.getBlockEntity(pos) instanceof IMultiblockPart mbp) { + level.setBlock(pos, mbp.getMimicing(), UPDATE_ALL); + } else { + level.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_ALL); + } + return InteractionResult.FAIL; + } + } + } + return InteractionResult.FAIL; + } + + @Override + public ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, + Player player, InteractionHand hand, BlockHitResult hit) { + // Always handle dummy interaction, even with an item in hand + if (!level.isClientSide && player instanceof ServerPlayer serverPlayer + && level.getBlockEntity(pos) instanceof IMultiblockPart part) { + + if (part.getControllerPos() != null) { + BlockEntity cbe = level.getBlockEntity(part.getControllerPos()); + if (cbe instanceof IMultiblockController controller) { + serverPlayer.openMenu( + new SimpleMenuProvider((MenuConstructor) cbe, Component.literal(controller.getMenuTitle())), + part.getControllerPos() + ); + } + } else { + level.setBlock(pos, part.getMimicing() != null ? part.getMimicing() : Blocks.AIR.defaultBlockState(), UPDATE_ALL); + } + + return ItemInteractionResult.CONSUME; // <- prevents item use/placement + } + + return ItemInteractionResult.SUCCESS; + } + + @Override + protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!level.isClientSide() && state.getBlock() != newState.getBlock()) { + if (level.getBlockEntity(pos) instanceof IMultiblockPart mbPart) + mbPart.onDummyBroken(); + } +// super.onRemove(state, level, pos, newState, movedByPiston); + } +} diff --git a/src/main/java/net/xevianlight/aphelion/block/dummy/VAFMultiblockDummyBlock.java b/src/main/java/net/xevianlight/aphelion/block/dummy/VAFMultiblockDummyBlock.java new file mode 100644 index 0000000..915e1a4 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/dummy/VAFMultiblockDummyBlock.java @@ -0,0 +1,18 @@ +package net.xevianlight.aphelion.block.dummy; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity; +import org.jetbrains.annotations.Nullable; + +public class VAFMultiblockDummyBlock extends BaseMultiblockDummyBlock { + public VAFMultiblockDummyBlock(Properties properties) { + super(properties); + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new VAFMultiblockDummyBlockEntity(blockPos, blockState); + } +} diff --git a/src/main/java/net/xevianlight/aphelion/block/dummy/entity/BaseMultiblockDummyBlockEntity.java b/src/main/java/net/xevianlight/aphelion/block/dummy/entity/BaseMultiblockDummyBlockEntity.java new file mode 100644 index 0000000..19504ea --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/dummy/entity/BaseMultiblockDummyBlockEntity.java @@ -0,0 +1,185 @@ +package net.xevianlight.aphelion.block.dummy.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.energy.IEnergyStorage; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.xevianlight.aphelion.Aphelion; +import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage; +import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.util.IMultiblockController; +import net.xevianlight.aphelion.util.IMultiblockPart; +import net.xevianlight.aphelion.util.SidedSlotHandler; +import org.jetbrains.annotations.Nullable; + +public class BaseMultiblockDummyBlockEntity extends BlockEntity implements IMultiblockPart { + @Nullable private BlockPos controllerPos; + private BlockState mimicing = Blocks.AIR.defaultBlockState(); + + @Nullable + private ItemStackHandler getControllerInventory() { + if (level == null) return null; + + BlockPos cPos = getControllerPos(); + if (cPos == null) return null; + + BlockEntity be = level.getBlockEntity(cPos); + if (be instanceof IMultiblockController controller) { + return controller.getInventory(); + } + return null; + } + + public IItemHandler getItemHandler(@Nullable Direction direction) { + ItemStackHandler inv = getControllerInventory(); + if (inv == null) return new ItemStackHandler(0); // or null, depending on your needs + + // IMPORTANT: your indices are almost certainly 0..3, not 1..4 + return new SidedSlotHandler(inv, new int[]{0,1,2,3}, true, true); + } + + @Nullable + private ModEnergyStorage getControllerEnergy() { + if (level == null) return null; + + BlockPos cPos = getControllerPos(); + if (cPos == null) return null; + + BlockEntity be = level.getBlockEntity(cPos); + if (be instanceof IMultiblockController controller) { + return controller.getEnergy(); + } + return null; + } + + public IEnergyStorage getEnergyStorage(@Nullable Direction direction) { + ModEnergyStorage nrg = getControllerEnergy(); + if (nrg == null) return new ModEnergyStorage(0, 0) { + @Override + public void onEnergyChanged() { + + } + }; + + return nrg; + } + +// public IEnergyStorage getEnergyStorage(@Nullable Direction direction) { +// if (direction == null) +// return isFormed() ? ENERGY_STORAGE : NULL_ENERGY_STORAGE; +// return isFormed() ? ENERGY_STORAGE : null; +// } + + public BaseMultiblockDummyBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } + + public void setControllerPos(@Nullable BlockPos pos) { + controllerPos = pos; + setChanged(); + if (level != null) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); + invalidateCapabilities(); + } + } + + public @Nullable BlockPos getControllerPos() { + return controllerPos; + } + + @Override + public void onDummyBroken() { + if (level == null || level.isClientSide) return; + + if (controllerPos == null) { + level.setBlock(getBlockPos(), getMimicing(), Block.UPDATE_ALL); + return; + } + + BlockEntity be = level.getBlockEntity(controllerPos); + if (be instanceof IMultiblockController controller) { + controller.markDirty(); + } + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + if (controllerPos != null) tag.putLong("controller", controllerPos.asLong()); + tag.put("mimic", NbtUtils.writeBlockState(mimicing)); + } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + controllerPos = tag.contains("controller") ? BlockPos.of(tag.getLong("controller")) : null; + if (tag.contains("mimic")) { + // Depending on your version, this line may need the registry lookup variant. + setMimicing(NbtUtils.readBlockState(registries.lookupOrThrow(net.minecraft.core.registries.Registries.BLOCK), tag.getCompound("mimic"))); + } + } + + @Override + public void onDataPacket(net.minecraft.network.Connection net, + ClientboundBlockEntityDataPacket pkt, + HolderLookup.Provider registries) { + + CompoundTag tag = pkt.getTag(); + if (tag != null) { + loadAdditional(tag, registries); + } + + // Force rerender on client + if (level != null) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); + requestModelDataUpdate(); // if you rely on model data + } + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + CompoundTag tag = new CompoundTag(); + saveAdditional(tag, registries); + return tag; + } + + @Override + public void handleUpdateTag(CompoundTag tag, HolderLookup.Provider registries) { + loadAdditional(tag, registries); + + // CLIENT: force rerender + if (level != null) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL); + } + } + + @Override + public BlockState getMimicing() { + return mimicing; + } + + @Override + public void setMimicing(BlockState newState) { + mimicing = newState; + setChanged(); + if (level != null) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); + requestModelDataUpdate(); // only if you use model data + } + } +} diff --git a/src/main/java/net/xevianlight/aphelion/block/dummy/entity/VAFMultiblockDummyBlockEntity.java b/src/main/java/net/xevianlight/aphelion/block/dummy/entity/VAFMultiblockDummyBlockEntity.java new file mode 100644 index 0000000..7d5cbc4 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/dummy/entity/VAFMultiblockDummyBlockEntity.java @@ -0,0 +1,12 @@ +package net.xevianlight.aphelion.block.dummy.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.xevianlight.aphelion.core.init.ModBlockEntities; + +public class VAFMultiblockDummyBlockEntity extends BaseMultiblockDummyBlockEntity { + public VAFMultiblockDummyBlockEntity(BlockPos pos, BlockState blockState) { + super(ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), pos, blockState); + } +} diff --git a/src/main/java/net/xevianlight/aphelion/block/dummy/renderer/MultiblockDummyRenderer.java b/src/main/java/net/xevianlight/aphelion/block/dummy/renderer/MultiblockDummyRenderer.java new file mode 100644 index 0000000..1c12643 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/dummy/renderer/MultiblockDummyRenderer.java @@ -0,0 +1,52 @@ +package net.xevianlight.aphelion.block.dummy.renderer; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.client.model.data.ModelData; +import net.xevianlight.aphelion.block.dummy.entity.BaseMultiblockDummyBlockEntity; +import net.xevianlight.aphelion.block.entity.custom.EAFPartEntity; + +public class MultiblockDummyRenderer implements BlockEntityRenderer { + + public MultiblockDummyRenderer(BlockEntityRendererProvider.Context context) { + + } + + @Override + public void render(BaseMultiblockDummyBlockEntity be, float partialTick, PoseStack poseStack, + MultiBufferSource buffer, int packedLight, int packedOverlay) { + + Level level = be.getLevel(); + if (level == null) return; + + BlockState mimic = be.getMimicing(); // <-- use stored state + if (mimic == null) return; // or default to AIR/stone + + BlockRenderDispatcher brd = Minecraft.getInstance().getBlockRenderer(); + + for (RenderType rt : brd.getBlockModel(mimic).getRenderTypes(mimic, level.random, ModelData.EMPTY)) { + brd.renderBatched( + mimic, + be.getBlockPos(), + level, + poseStack, + buffer.getBuffer(rt), + false, + level.random, + ModelData.EMPTY, + rt + ); + } + } +} diff --git a/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java b/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java index 7243475..5f9d098 100644 --- a/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java +++ b/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java @@ -3,10 +3,13 @@ package net.xevianlight.aphelion.block.entity.custom; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.util.IMultiblockController; import net.xevianlight.aphelion.util.IMultiblockPart; import org.jetbrains.annotations.Nullable; @@ -22,6 +25,21 @@ public class EAFPartEntity extends BlockEntity implements IMultiblockPart { setChanged(); } + @Override + public BlockState getMimicing() { + return null; + } + + @Override + public void setMimicing(BlockState newState) { + + } + + @Override + public void onDummyBroken() { + Aphelion.LOGGER.error("I SHOULDNT BE CALLED!!!"); + } + public @Nullable BlockPos getControllerPos() { return controllerPos; } diff --git a/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java b/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java index dcb7a50..9353ceb 100644 --- a/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java +++ b/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java @@ -61,6 +61,13 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men public static final int OUTPUT_SLOT = 2; public static final int ENERGY_SLOT = 3; + private boolean dirty = false; + + @Override + public String getMenuTitle() { + return "Vacuum Arc Furnace"; + } + public VacuumArcFurnaceControllerEntity(BlockPos pos, BlockState blockState) { super(ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), pos, blockState); this.data = new ContainerData() { @@ -109,6 +116,14 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men public void tick(Level level, BlockPos pos, BlockState blockState) { + if (dirty) { + dirty = false; + BlockState current = level.getBlockState(pos); + MultiblockHelper.tryForm(level, current, pos, SHAPE, AphelionBlockStateProperties.FORMED); + } + + BlockState newBlockState = level.getBlockState(pos); + if (!blockState.getValue(AphelionBlockStateProperties.FORMED)) return; @@ -120,8 +135,8 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men // Recipe detected! We have enough energy to process progress++; useEnergy(); - level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true)); - setChanged(level, pos, blockState); + level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, true)); + setChanged(level, pos, newBlockState); if (hasCraftingFinished()) { outputBlastingResult(INPUT_SLOT, OUTPUT_SLOT); @@ -129,14 +144,14 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men } } else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) { // Recipe detected but we ran out of power - level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); - setChanged(level, pos, blockState); + level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, newBlockState); progress = progress > 0 ? progress - 1 : 0; } else { // Invalid recipe resetProgress(); - level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); - setChanged(level, pos, blockState); + level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, newBlockState); } } else { // Secondary slot is NOT empty, try alloying recipes @@ -145,8 +160,8 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men // Alloy recipe detected! We have enough energy to process progress++; useEnergy(); - level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true)); - setChanged(level, pos, blockState); + level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, true)); + setChanged(level, pos, newBlockState); if (hasCraftingFinished()) { outputAlloyingResult(INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT); @@ -154,15 +169,15 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men } } else { // Recipe detected but we ran out of power - level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); - setChanged(level, pos, blockState); + level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, newBlockState); progress = progress > 0 ? progress - 1 : 0; } } else { // Invalid recipe resetProgress(); - level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); - setChanged(level, pos, blockState); + level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, newBlockState); } } @@ -328,6 +343,11 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men return isFormed() ? fullHandler : emptyJeiHandler; } + @Override + public ModEnergyStorage getEnergy() { + return ENERGY_STORAGE; + } + public IEnergyStorage getEnergyStorage(@Nullable Direction direction) { if (direction == null) return isFormed() ? ENERGY_STORAGE : NULL_ENERGY_STORAGE; @@ -351,6 +371,7 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men }; } + @Override public ItemStackHandler getInventory() { return inventory; } @@ -416,15 +437,20 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men }; } + @Override + public MultiblockHelper.ShapePart[] getMultiblockShape() { + return SHAPE; + } + public static final MultiblockHelper.ShapePart[] SHAPE = new MultiblockHelper.ShapePart[] { //Layer -1 - new MultiblockHelper.ShapePart(new BlockPos(1,-1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,-1,0), s -> s.is(ModBlocks.BLOCK_STEEL)), new MultiblockHelper.ShapePart(new BlockPos(1,-1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), - new MultiblockHelper.ShapePart(new BlockPos(1,-1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), - new MultiblockHelper.ShapePart(new BlockPos(-1,-1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,-1,2), s -> s.is(ModBlocks.BLOCK_STEEL)), + new MultiblockHelper.ShapePart(new BlockPos(-1,-1,0), s -> s.is(ModBlocks.BLOCK_STEEL)), new MultiblockHelper.ShapePart(new BlockPos(-1,-1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), - new MultiblockHelper.ShapePart(new BlockPos(-1,-1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,-1,2), s -> s.is(ModBlocks.BLOCK_STEEL)), new MultiblockHelper.ShapePart(new BlockPos(0,-1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), new MultiblockHelper.ShapePart(new BlockPos(0,-1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), new MultiblockHelper.ShapePart(new BlockPos(0,-1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), @@ -441,12 +467,12 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men //Layer 1 - new MultiblockHelper.ShapePart(new BlockPos(1,1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,1,0), s -> s.is(ModBlocks.BLOCK_STEEL)), new MultiblockHelper.ShapePart(new BlockPos(1,1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), - new MultiblockHelper.ShapePart(new BlockPos(1,1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), - new MultiblockHelper.ShapePart(new BlockPos(-1,1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,1,2), s -> s.is(ModBlocks.BLOCK_STEEL)), + new MultiblockHelper.ShapePart(new BlockPos(-1,1,0), s -> s.is(ModBlocks.BLOCK_STEEL)), new MultiblockHelper.ShapePart(new BlockPos(-1,1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), - new MultiblockHelper.ShapePart(new BlockPos(-1,1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,1,2), s -> s.is(ModBlocks.BLOCK_STEEL)), new MultiblockHelper.ShapePart(new BlockPos(0,1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), new MultiblockHelper.ShapePart(new BlockPos(0,1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), new MultiblockHelper.ShapePart(new BlockPos(0,1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), @@ -493,15 +519,12 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men this.formed = formed; invalidateCapabilities(); if (!formed) { - drops(); +// drops(); } } - private void setFormedState(boolean value) { - BlockState state = getBlockState(); - if (state.hasProperty(ElectricArcFurnace.FORMED) && state.getValue(ElectricArcFurnace.FORMED) != value) { - level.setBlock(worldPosition, state.setValue(ElectricArcFurnace.FORMED, value), 3); - } + @Override + public void markDirty() { + dirty = true; } - } diff --git a/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java b/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java index 77df5c6..537ebcf 100644 --- a/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java +++ b/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java @@ -4,6 +4,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredRegister; import net.xevianlight.aphelion.Aphelion; +import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity; import net.xevianlight.aphelion.block.entity.custom.*; import java.util.function.Supplier; @@ -35,4 +36,9 @@ public class ModBlockEntities { BLOCK_ENTITIES.register("vacuum_arc_furnace_controller_entity", () -> BlockEntityType.Builder.of( VacuumArcFurnaceControllerEntity::new, ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get()).build(null) ); + + public static final Supplier> VAF_MULTIBLOCK_DUMMY_ENTITY = + BLOCK_ENTITIES.register("vaf_multiblock_dummy_entity", () -> BlockEntityType.Builder.of( + VAFMultiblockDummyBlockEntity::new, ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get()).build(null) + ); } diff --git a/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java b/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java index f8b30f4..82f43d0 100644 --- a/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java +++ b/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java @@ -5,6 +5,7 @@ import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.block.custom.*; +import net.xevianlight.aphelion.block.dummy.VAFMultiblockDummyBlock; public class ModBlocks { public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Aphelion.MOD_ID); @@ -15,4 +16,5 @@ public class ModBlocks { public static final DeferredBlock ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties())); public static final DeferredBlock ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties())); public static final DeferredBlock VACUUM_ARC_FURNACE_CONTROLLER = BLOCKS.register("vacuum_arc_furnace_controller", () -> new VacuumArcFurnaceController(VacuumArcFurnaceController.getProperties())); + public static final DeferredBlock VAF_MULTIBLOCK_DUMMY_BLOCK = BLOCKS.register("vaf_dummy_block", () -> new VAFMultiblockDummyBlock(VAFMultiblockDummyBlock.getProperties())); } diff --git a/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java b/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java index 3753663..c11f8cf 100644 --- a/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java +++ b/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java @@ -7,6 +7,7 @@ import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.block.custom.*; +import net.xevianlight.aphelion.block.dummy.VAFMultiblockDummyBlock; import net.xevianlight.aphelion.item.*; public class ModItems { @@ -34,4 +35,5 @@ public static final DeferredItem MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu public static final DeferredItem BLOCK_STEEL = ITEMS.register("block_steel", () -> new BlockItem(ModBlocks.BLOCK_STEEL.get(), BlockSteel.getItemProperties())); public static final DeferredItem ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties())); public static final DeferredItem VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties())); +// public static final DeferredItem VAF_MULTIBLOCK_DUMMY_BLOCK = ITEMS.register("vaf_multiblock_dummy_block", () -> new BlockItem(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), VAFMultiblockDummyBlock.getItemProperties())); } diff --git a/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java b/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java index 41e9bfd..a699219 100644 --- a/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java +++ b/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java @@ -9,6 +9,8 @@ import net.neoforged.neoforge.network.handlers.ClientPayloadHandler; import net.neoforged.neoforge.network.registration.HandlerThread; import net.neoforged.neoforge.network.registration.PayloadRegistrar; import net.xevianlight.aphelion.Aphelion; +import net.xevianlight.aphelion.block.dummy.entity.BaseMultiblockDummyBlockEntity; +import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity; import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity; import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; @@ -25,6 +27,9 @@ public class ModBusEvents { event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getEnergyStorage); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getItemHandler); event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getEnergyStorage); +// event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), VAFMultiblockDummyBlockEntity::getItemHandler); + event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getItemHandler); + event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getEnergyStorage); } @SubscribeEvent diff --git a/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java b/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java index 122dc5c..82afb93 100644 --- a/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java +++ b/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java @@ -1,9 +1,27 @@ package net.xevianlight.aphelion.util; import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.energy.IEnergyStorage; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage; import org.jetbrains.annotations.Nullable; public interface IMultiblockController { boolean isFormed(); void setFormed(boolean formed); + + void markDirty(); + + BlockPos getBlockPos(); + + BlockState getBlockState(); + + MultiblockHelper.ShapePart[] getMultiblockShape(); + + String getMenuTitle(); + + ItemStackHandler getInventory(); + + ModEnergyStorage getEnergy(); } diff --git a/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java b/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java index 98112f6..5eba6a2 100644 --- a/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java +++ b/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java @@ -1,10 +1,17 @@ package net.xevianlight.aphelion.util; import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; public interface IMultiblockPart { @Nullable BlockPos getControllerPos(); void setControllerPos(@Nullable BlockPos pos); -} + + BlockState getMimicing(); + + void setMimicing(BlockState newState); + + void onDummyBroken(); +} \ No newline at end of file diff --git a/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java b/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java index 9528fa0..53134d8 100644 --- a/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java +++ b/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java @@ -3,11 +3,14 @@ package net.xevianlight.aphelion.util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.xevianlight.aphelion.Aphelion; +import net.xevianlight.aphelion.core.init.ModBlocks; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -43,18 +46,29 @@ public class MultiblockHelper { }; } - private static boolean structureMatches(Level level, BlockState state, BlockPos pos, ShapePart[] shape) { + private static BlockState effectiveState(Level level, BlockPos pos) { + BlockState state = level.getBlockState(pos); + + if (state.is(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get())) { + BlockEntity be = level.getBlockEntity(pos); + if (be instanceof IMultiblockPart part) { + BlockState mimic = part.getMimicing(); + if (mimic != null) return mimic; + } + } + return state; + } + + private static boolean structureMatches(Level level, BlockState controllerState, BlockPos controllerPos, ShapePart[] shape) { if (level == null || level.isClientSide) return false; - Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + Direction facing = controllerState.getValue(BlockStateProperties.HORIZONTAL_FACING); for (ShapePart part : shape) { - BlockPos testPos = pos.offset(rotateY(part.offset(), facing)); - BlockState check = level.getBlockState(testPos); + BlockPos testPos = controllerPos.offset(rotateY(part.offset(), facing)); + BlockState check = effectiveState(level, testPos); - if (!part.rule().test(check)) { - return false; - } + if (!part.rule().test(check)) return false; } return true; } @@ -70,14 +84,13 @@ public class MultiblockHelper { boolean valid = structureMatches(level, state, pos, shape); if (valid && !controller.isFormed()) { - controller.setFormed(true); linkParts(level, state, pos, shape, formedProp); - if (state.hasProperty(AphelionBlockStateProperties.FORMED) && !state.getValue(AphelionBlockStateProperties.FORMED)) { - level.setBlock(pos, state.setValue(AphelionBlockStateProperties.FORMED, true), 3); - } + controller.setFormed(true); + setControllerFormedProp(level, pos, true, formedProp); } else if (!valid && controller.isFormed()) { unform(level, state, pos, shape, formedProp); } + } public static void unform(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { @@ -87,11 +100,9 @@ public class MultiblockHelper { if (!(be instanceof IMultiblockController controller)) return; controller.setFormed(false); - if (state.hasProperty(AphelionBlockStateProperties.FORMED) && state.getValue(AphelionBlockStateProperties.FORMED)) { - level.setBlock(pos, state.setValue(AphelionBlockStateProperties.FORMED, false), 3); - } + setControllerFormedProp(level, pos, false, formedProp); - unlinkParts(level, state, pos, shape, formedProp); + unlinkParts(level, level.getBlockState(pos), pos, shape, formedProp); } @@ -106,58 +117,64 @@ public class MultiblockHelper { } - private static void unlinkParts(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + private static void unlinkParts(Level level, BlockState controllerState, BlockPos controllerPos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { if (level == null || level.isClientSide) return; - Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + Direction facing = controllerState.getValue(BlockStateProperties.HORIZONTAL_FACING); for (ShapePart part : shape) { - BlockPos partPos = pos.offset(rotateY(part.offset(), facing)); - BlockState st = level.getBlockState(partPos); - - if (formedProp != null && st.hasProperty(formedProp) && st.getValue(formedProp)) { - level.setBlock(partPos, st.setValue(formedProp, false), 3); - } + BlockPos partPos = controllerPos.offset(rotateY(part.offset(), facing)); BlockEntity be = level.getBlockEntity(partPos); - if (be instanceof IMultiblockPart mbPart) { - if (pos.equals(mbPart.getControllerPos())) { - mbPart.setControllerPos(null); - be.setChanged(); - } + if (!(be instanceof IMultiblockPart dummy)) { + // Don't return — just skip this position + continue; } - } + // Only undo if this dummy belongs to this controller + if (!controllerPos.equals(dummy.getControllerPos())) continue; + + BlockState restore = dummy.getMimicing(); + if (restore == null) restore = Blocks.AIR.defaultBlockState(); + + // Restore the original block + level.setBlock(partPos, restore, Block.UPDATE_ALL); + + // Clear ownership (not strictly necessary since BE is being removed by setBlock) + dummy.setControllerPos(null); + be.setChanged(); + } } - private static void linkParts(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + private static void linkParts(Level level, BlockState controllerState, BlockPos controllerPos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { if (level == null || level.isClientSide) return; - Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + Direction facing = controllerState.getValue(BlockStateProperties.HORIZONTAL_FACING); for (ShapePart part : shape) { - BlockPos partPos = pos.offset(rotateY(part.offset(), facing)); - BlockState st = level.getBlockState(partPos); + BlockPos partPos = controllerPos.offset(rotateY(part.offset(), facing)); - Aphelion.LOGGER.debug("[Multiblock] partPos={} block={} hasFormed={} formedVal={}", - partPos, - st.getBlock(), - (formedProp != null && st.hasProperty(formedProp)), - (formedProp != null && st.hasProperty(formedProp)) ? st.getValue(formedProp) : "n/a" - ); + // 1) Capture the original state BEFORE replacing + BlockState original = level.getBlockState(partPos); - if (formedProp != null && st.hasProperty(formedProp) && !st.getValue(formedProp)) { - level.setBlock(partPos, st.setValue(formedProp, true), 3); - st = level.getBlockState(partPos); - } + // Optional: don't replace the controller itself + if (partPos.equals(controllerPos)) continue; + // 2) Replace with dummy block unconditionally + level.setBlock(partPos, ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get().defaultBlockState(), Block.UPDATE_ALL); + + // 3) Now fetch the dummy BE (might be null in edge cases) BlockEntity be = level.getBlockEntity(partPos); - if (be instanceof IMultiblockPart mbPart) { - if (!pos.equals(mbPart.getControllerPos())) { - mbPart.setControllerPos(pos); - be.setChanged(); - } + if (!(be instanceof IMultiblockPart dummy)) { + // If this happens, your dummy block probably isn't creating its BE + Aphelion.LOGGER.warn("[Multiblock] Dummy BE missing at {}", partPos); + continue; } + + // 4) Set ownership + mimic state + dummy.setControllerPos(controllerPos); + dummy.setMimicing(original); + be.setChanged(); } } @@ -167,20 +184,23 @@ public class MultiblockHelper { BlockPos controllerPos, ShapePart[] shape ) { + if (level == null || level.isClientSide) return false; + Direction facing = controllerState.getValue(BlockStateProperties.HORIZONTAL_FACING); for (ShapePart part : shape) { BlockPos rotated = rotateY(part.offset(), facing); BlockPos worldPos = controllerPos.offset(rotated); + BlockState check = effectiveState(level, worldPos); BlockState found = level.getBlockState(worldPos); - if (!part.rule().test(found)) { + if (!part.rule().test(check)) { Aphelion.LOGGER.debug("[Multiblock] FAIL at offset=" + part.offset() + " facing=" + facing + " rotated=" + rotated + " worldPos=" + worldPos - + " found=" + found.getBlock()); + + " found=" + check.getBlock()); return false; } } @@ -189,4 +209,23 @@ public class MultiblockHelper { return true; } + private static void setControllerFormedProp(Level level, BlockPos pos, boolean formed, @Nullable BooleanProperty formedProp) { + if (formedProp == null) return; + + BlockState cur = level.getBlockState(pos); + if (!cur.hasProperty(formedProp)) { return; } + if (cur.getValue(formedProp) == formed) { return; } + + BlockState before = level.getBlockState(pos); + level.setBlock(pos, cur.setValue(formedProp, formed), Block.UPDATE_ALL); + level.sendBlockUpdated(pos, before, level.getBlockState(pos), 3); + + BlockEntity be = level.getBlockEntity(pos); + if (be instanceof IMultiblockController controller) { + controller.setFormed(formed); + be.setChanged(); + } + + } + } diff --git a/src/main/resources/assets/aphelion/lang/en_us.json b/src/main/resources/assets/aphelion/lang/en_us.json index ab41d97..29e4c51 100644 --- a/src/main/resources/assets/aphelion/lang/en_us.json +++ b/src/main/resources/assets/aphelion/lang/en_us.json @@ -2,6 +2,8 @@ "item.aphelion.test_item": "Test Item", "block.aphelion.test_block": "Test Block", "block.aphelion.electric_arc_furnace": "Electric Arc Furnace", + "block.aphelion.vacuum_arc_furnace_controller": "Vacuum Arc Furnace Controller", + "block.aphelion.vaf_dummy_block": "Vacuum Arc Furnace", "item.aphelion.ingot_steel": "Steel Ingot", "block.aphelion.block_steel": "Steel Block",