Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/main/java/com/compactmachinespor/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,25 @@ public class Config {
.translation("config.compactmachinespor.unpack_permission_level")
.defineInRange("PermissionLevel", 2, 0, 4);

public static final ModConfigSpec.BooleanValue ENABLE_INVENTORY_AUDIT = BUILDER
.comment("Enable inventory baseline audit (conservation check)")
.translation("config.compactmachinespor.enable_inventory_audit")
.define("EnableInventoryAudit", true);

public static final ModConfigSpec.ConfigValue<java.util.List<? extends String>> SUSPICIOUS_MODS = BUILDER
.comment("Mod IDs whose blocks are untrusted (unscannable storage, e.g. AE2). Evaluation aborts if found.")
.translation("config.compactmachinespor.suspicious_mods")
.defineListAllowEmpty("SuspiciousMods", java.util.List.of("ae2", "refinedstorage"), o -> o instanceof String);

public static final ModConfigSpec.ConfigValue<java.util.List<? extends String>> SUSPICIOUS_BLOCKS = BUILDER
.comment("Specific block IDs (namespace:path) that are untrusted.")
.translation("config.compactmachinespor.suspicious_blocks")
.defineListAllowEmpty("SuspiciousBlocks", java.util.List.of(), o -> o instanceof String);

public static final ModConfigSpec.BooleanValue ENABLE_FACTORY_REVERT = BUILDER
.comment("Allow launcher stick to revert FactoryBlock back to bound machine. Default false to prevent item duplication.")
.translation("config.compactmachinespor.enable_factory_revert")
.define("EnableFactoryRevert", false);

static final ModConfigSpec SPEC = BUILDER.build();
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,21 @@ protected DataSetType getDataSetType() {
protected void handle(Holder<?> holder, int count) {
if (!checkAndDeactivate()) return;
if (getLevel() instanceof ServerLevel serverLevel) {
Core.setMachineData(roomCode, getDataSetType(), holder, count, Core.getTicks(serverLevel));
DataSetType type = getDataSetType();
long ticks = Core.getTicks(serverLevel);
Core.setMachineData(roomCode, type, holder, count, ticks);
// 累计 IO 总量(用于库存基线审计)
Core.getMachine(roomCode).addTotal(type, holder, count);
}
}

protected void handle(int energy) {
if (!checkAndDeactivate()) return;
if (getLevel() instanceof ServerLevel serverLevel) {
Core.getMachine(roomCode).addEnergyData(getDataSetType(), energy, Core.getTicks(serverLevel));
DataSetType type = getDataSetType();
Core.getMachine(roomCode).addEnergyData(type, energy, Core.getTicks(serverLevel));
// 累计 IO 总量(用于库存基线审计)
Core.getMachine(roomCode).addTotalEnergy(type, energy);
}
}

Expand Down
122 changes: 121 additions & 1 deletion src/main/java/com/compactmachinespor/block/EvaluatorBlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,138 @@

import com.compactmachinespor.Cyumocompactmachinespor;
import com.compactmachinespor.core.Core;
import com.compactmachinespor.core.Core.CreateResult;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;

public class EvaluatorBlockEntity extends RoomCodeBlockEntity {
// 原机器方块信息,用于评估失败时还原
private ResourceLocation originalMachineBlock;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

何意味?

private CompoundTag savedOriginalNbt; // 原 BE 的完整 NBT(含 data components)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

目前没有存完整nbt的意义,原BE里只有颜色和RoomCode两个数据,只要RoomCode对应存在,还原的方块就能正常使用,只是颜色会丢失。如果要存颜色,直接写存颜色的代码


public EvaluatorBlockEntity(BlockPos pos, BlockState blockState) {
super(Cyumocompactmachinespor.EVALUATOR_BLOCK_ENTITY.get(), pos, blockState);
}

public void saveOriginalMachine(ResourceLocation blockId, CompoundTag beNbt) {
this.originalMachineBlock = blockId;
this.savedOriginalNbt = beNbt;
setChanged();
}

/**
* 供 Core.createMachine 获取原始机器 NBT(用于提取附件数据)
*/
public CompoundTag getSavedOriginalNbt() {
return savedOriginalNbt;
}

public void trigger() {
if (roomCode != null && !roomCode.isEmpty() && getLevel() instanceof ServerLevel serverLevel) {
Core.createMachine(serverLevel, roomCode, getBlockPos());
CreateResult result = Core.createMachine(serverLevel, roomCode, getBlockPos());
if (result == CreateResult.ABORTED_SUSPICIOUS_BLOCKS) {
serverLevel.players().forEach(p ->
p.displayClientMessage(
Component.translatable("chat.compactmachinespor.suspicious_blocks"),
false
)
);
restoreOriginalMachine(serverLevel);
}
}
}

/**
* 还原为原来的紧缩空间机器方块
* 延后到 neighborChanged 执行完毕后再执行,避免冲突
*/
private void restoreOriginalMachine(ServerLevel level) {
if (originalMachineBlock == null || savedOriginalNbt == null) return;

final BlockPos pos = getBlockPos();
final Block machineBlock = BuiltInRegistries.BLOCK.get(originalMachineBlock);
final CompoundTag nbt = savedOriginalNbt.copy();
if (machineBlock == null || machineBlock == net.minecraft.world.level.block.Blocks.AIR) return;

Cyumocompactmachinespor.LOGGER.info("Will restore original machine at {} (delayed)", pos);

// 使用 server.execute 在邻居更新完成后执行还原
// 避免与 EvaluatorBlock.neighborChanged 中的 setBlockAndUpdate 冲突
level.getServer().execute(() -> {
if (!(level.getBlockEntity(pos) instanceof EvaluatorBlockEntity)) {
return;
}

// 1. 替换方块(会创建默认 BE)
level.removeBlockEntity(pos);
level.setBlockAndUpdate(pos, machineBlock.defaultBlockState());

// 2. 用 loadStatic 从保存的 NBT 创建完整的 BE(包含 neoforge:attachments)
// loadWithComponents 在已有 BE 上调用可能不覆盖全部数据,
// loadStatic 直接从 NBT 创建全新 BE,能正确恢复所有数据
BlockEntity loaded = BlockEntity.loadStatic(pos, level.getBlockState(pos), nbt, level.registryAccess());
if (loaded != null) {
level.setBlockEntity(loaded);
loaded.setChanged();
Cyumocompactmachinespor.LOGGER.info("Machine restored at {} (via loadStatic)", pos);
} else {
Cyumocompactmachinespor.LOGGER.warn("loadStatic returned null at {}", pos);
}

level.sendBlockUpdated(pos, level.getBlockState(pos), level.getBlockState(pos), Block.UPDATE_ALL);
});
}

@Override
protected void loadCommon(CompoundTag tag) {
super.loadCommon(tag);
this.originalMachineBlock = null;
this.savedOriginalNbt = null;
if (tag.contains("original_machine_block")) {
String id = tag.getString("original_machine_block");
this.originalMachineBlock = ResourceLocation.tryParse(id);
}
if (tag.contains("original_machine_nbt")) {
this.savedOriginalNbt = tag.getCompound("original_machine_nbt");
}
}

@Override
protected void saveCommon(CompoundTag tag) {
super.saveCommon(tag);
if (originalMachineBlock != null) {
tag.putString("original_machine_block", originalMachineBlock.toString());
}
if (savedOriginalNbt != null) {
tag.put("original_machine_nbt", savedOriginalNbt);
}
}

@Override
protected void applyImplicitComponents(DataComponentInput componentInput) {
super.applyImplicitComponents(componentInput);
CustomData customData = componentInput.get(DataComponents.CUSTOM_DATA);
if (customData != null) {
loadCommon(customData.copyTag());
}
}

@Override
protected void collectImplicitComponents(DataComponentMap.Builder builder) {
super.collectImplicitComponents(builder);
CompoundTag tag = new CompoundTag();
saveCommon(tag);
builder.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
}
}
39 changes: 39 additions & 0 deletions src/main/java/com/compactmachinespor/block/FactoryBlock.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
package com.compactmachinespor.block;

import com.compactmachinespor.Config;
import com.compactmachinespor.Cyumocompactmachinespor;
import com.compactmachinespor.core.Core;
import com.mojang.serialization.MapCodec;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.RenderShape;
Expand All @@ -12,6 +21,7 @@
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.MapColor;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.Nullable;

public class FactoryBlock extends BaseEntityBlock {
Expand All @@ -37,6 +47,35 @@ protected RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}

/**
* 启动棒右键工厂方块 → 还原为原来的紧凑空间机器方块

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个功能已经有了,不要重复实现

*/
@Override
protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) {
if (!stack.is(Cyumocompactmachinespor.LAUNCHER_STICK)) {
return super.useItemOn(stack, state, level, pos, player, hand, hitResult);
}
if (level.isClientSide) {
return ItemInteractionResult.SUCCESS;
}
if (!Config.ENABLE_FACTORY_REVERT.get()) {
player.displayClientMessage(
Component.literal("[CM] 工厂还原功能已被禁用").withStyle(ChatFormatting.GRAY),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

应使用翻译键

true);
return ItemInteractionResult.SUCCESS;
}
if (level.getBlockEntity(pos) instanceof FactoryBlockEntity factoryBe) {
String roomCode = factoryBe.getRoomCode();
if (roomCode != null && !roomCode.isEmpty() && level instanceof ServerLevel serverLevel) {
Core.revertToBoundMachine(serverLevel, pos, roomCode);
stack.shrink(1);
player.inventoryMenu.broadcastChanges();
return ItemInteractionResult.SUCCESS;
}
}
return super.useItemOn(stack, state, level, pos, player, hand, hitResult);
}

@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/compactmachinespor/block/FactoryBlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ protected void loadCommon(CompoundTag tag) {
loadFluidMap(tag, "output_fluids", outputFluids);
inputEnergy = loadEnergy(tag, "input_energy");
outputEnergy = loadEnergy(tag, "output_energy");
if (tag.contains("original_attachments")) {
originalAttachments = tag.getCompound("original_attachments");
}
updateLists();
}

Expand All @@ -339,6 +342,9 @@ protected void saveCommon(CompoundTag tag) {
saveFluidMap(tag, "output_fluids", outputFluids);
saveEnergy(tag, "input_energy", inputEnergy);
saveEnergy(tag, "output_energy", outputEnergy);
if (originalAttachments != null) {
tag.put("original_attachments", originalAttachments);
}
}

public IItemHandler getItemHandler() {
Expand Down Expand Up @@ -560,6 +566,18 @@ public static EnergyStorage loadEnergy(CompoundTag tag, String key) {
return null;
}

// 原始 machine_color 所在 neoforge:attachments,由 Core.finish() 设置
private CompoundTag originalAttachments;

public CompoundTag getOriginalAttachments() {
return originalAttachments;
}

public void setOriginalAttachments(CompoundTag attachments) {
this.originalAttachments = attachments;
setChanged();
}

public List<List<?>> getForShow() {
return List.of(inputItemList, inputFluidList, outputItemList, outputFluidList);
}
Expand Down
Loading
Loading