From 37cfc7cecda313225b226d0276f4eca02314afe7 Mon Sep 17 00:00:00 2001 From: Fff <141797114+WhereisFff@users.noreply.github.com> Date: Fri, 2 Oct 2026 00:06:38 -0700 Subject: [PATCH 1/3] =?UTF-8?q?fix(building):=20=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E5=8F=8C=E6=A0=BC=E6=A4=8D=E7=89=A9=E7=9A=84?= =?UTF-8?q?=E4=B8=8B=E8=90=BD=E6=96=B9=E5=9D=97=E6=92=A4=E9=94=80=20(#5124?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 记录落点双格植物的完整配对快照,恢复上下两部分并按实际资源结算。 - 撤销前检查历史植物配对完整性,避免混合时点的快照消耗材料后恢复失败。 --- .../building/BuildingRegionSnapshot.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java index 128b1ae717..2a55ba5e79 100644 --- a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java +++ b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java @@ -14,8 +14,10 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockEventData; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.DoublePlantBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.phys.AABB; import net.neoforged.neoforge.common.CommonHooks; @@ -74,9 +76,21 @@ boolean contains(BlockPos pos) { return this.bounds.isInside(pos) || this.addedPositions.contains(pos); } - /** 只补记落地覆盖的方块,避免重建已落地实体或回滚附近的无关实体。 */ + /** 只补记落地覆盖的方块及其双格植物配对方块,避免回滚附近的无关实体。 */ List captureBlocks(List positions) { - List added = positions.stream().filter(pos -> !this.contains(pos) && this.level.isInWorldBounds(pos)) + List expanded = new ArrayList<>(positions); + for (BlockPos pos : positions) { + if (!this.level.isInWorldBounds(pos)) continue; + BlockState state = this.level.getBlockState(pos); + if (!(state.getBlock() instanceof DoublePlantBlock)) continue; + BlockPos other = state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.LOWER ? pos.above() : pos.below(); + if (!this.level.isInWorldBounds(other)) continue; + BlockState otherState = this.level.getBlockState(other); + if (otherState.is(state.getBlock()) && otherState.getValue(DoublePlantBlock.HALF) != state.getValue(DoublePlantBlock.HALF)) { + expanded.add(other); + } + } + List added = expanded.stream().filter(pos -> !this.contains(pos) && this.level.isInWorldBounds(pos)) .map(BlockPos::immutable).distinct().toList(); if (added.isEmpty()) return added; List captured = new ArrayList<>(); @@ -111,9 +125,19 @@ boolean containsEntity(UUID uuid) { } boolean canRestore(ServerPlayer player) { + Map original = new LinkedHashMap<>(); + this.blocks.forEach(block -> original.put(block.pos(), block.state())); for (SavedBlock block : this.blocks) { if (!BuildingRodService.canModify(player, block.pos())) return false; BlockState current = this.level.getBlockState(block.pos()); + if (current != block.state() && block.state().getBlock() instanceof DoublePlantBlock) { + DoubleBlockHalf half = block.state().getValue(DoublePlantBlock.HALF); + BlockPos other = half == DoubleBlockHalf.LOWER ? block.pos().above() : block.pos().below(); + BlockState otherState = original.get(other); + if (otherState == null || !otherState.is(block.state().getBlock()) || otherState.getValue(DoublePlantBlock.HALF) == half) { + return false; + } + } if (current != block.state() && !current.isAir() && CommonHooks.fireBlockBreak(this.level, player.gameMode.getGameModeForPlayer(), player, block.pos(), current).isCanceled()) { return false; From bed450399100eb8957358ee24e944d1bdce29a5d Mon Sep 17 00:00:00 2001 From: Fff <141797114+WhereisFff@users.noreply.github.com> Date: Fri, 2 Oct 2026 00:23:49 -0700 Subject: [PATCH 2/3] =?UTF-8?q?fix(building):=20=E5=8C=BA=E5=88=86?= =?UTF-8?q?=E6=A4=8D=E7=89=A9=E5=BF=AB=E7=85=A7=E5=86=B2=E7=AA=81=E4=B8=8E?= =?UTF-8?q?=E6=92=A4=E9=94=80=E6=9D=83=E9=99=90=E9=94=99=E8=AF=AF=20(#5124?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在权限检查后、资源结算前独立校验双格植物快照,保留生存与创造模式的原子拒绝。 - 使用现有资源冲突提示说明不完整或混合时点的植物快照。 --- .../building/BuildingRegionSnapshot.java | 26 ++++++++++++------- .../anvilcraft/building/BuildingRodUndo.java | 4 +++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java index 2a55ba5e79..ba7c508322 100644 --- a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java +++ b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java @@ -125,19 +125,9 @@ boolean containsEntity(UUID uuid) { } boolean canRestore(ServerPlayer player) { - Map original = new LinkedHashMap<>(); - this.blocks.forEach(block -> original.put(block.pos(), block.state())); for (SavedBlock block : this.blocks) { if (!BuildingRodService.canModify(player, block.pos())) return false; BlockState current = this.level.getBlockState(block.pos()); - if (current != block.state() && block.state().getBlock() instanceof DoublePlantBlock) { - DoubleBlockHalf half = block.state().getValue(DoublePlantBlock.HALF); - BlockPos other = half == DoubleBlockHalf.LOWER ? block.pos().above() : block.pos().below(); - BlockState otherState = original.get(other); - if (otherState == null || !otherState.is(block.state().getBlock()) || otherState.getValue(DoublePlantBlock.HALF) == half) { - return false; - } - } if (current != block.state() && !current.isAir() && CommonHooks.fireBlockBreak(this.level, player.gameMode.getGameModeForPlayer(), player, block.pos(), current).isCanceled()) { return false; @@ -155,6 +145,22 @@ boolean canRestore(ServerPlayer player) { return true; } + boolean hasCompletePlants() { + Map original = new LinkedHashMap<>(); + this.blocks.forEach(block -> original.put(block.pos(), block.state())); + for (SavedBlock block : this.blocks) { + if (!(block.state().getBlock() instanceof DoublePlantBlock) + || this.level.getBlockState(block.pos()) == block.state()) continue; + DoubleBlockHalf half = block.state().getValue(DoublePlantBlock.HALF); + BlockPos other = half == DoubleBlockHalf.LOWER ? block.pos().above() : block.pos().below(); + BlockState otherState = original.get(other); + if (otherState == null || !otherState.is(block.state().getBlock()) || otherState.getValue(DoublePlantBlock.HALF) == half) { + return false; + } + } + return true; + } + void resources(BuildingUndoResources recovered, BuildingUndoResources required) { Map original = new LinkedHashMap<>(); this.blocks.forEach(block -> original.put(block.pos(), block.state())); diff --git a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodUndo.java b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodUndo.java index f9def589d5..e54fef07f5 100644 --- a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodUndo.java +++ b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodUndo.java @@ -334,6 +334,10 @@ public static void undo(ServerPlayer player) { BuildingRodService.message(player, "blocked"); return; } + if (!undo.region.hasCompletePlants()) { + BuildingRodService.message(player, "undo_conflict"); + return; + } BuildingUndoResources recovered = new BuildingUndoResources(); BuildingUndoResources required = new BuildingUndoResources(); BuildingMaterials recovery = new BuildingMaterials(player, false); From e10793f6d873be8bccafc26b68abe4eacc7791af Mon Sep 17 00:00:00 2001 From: Fff <141797114+WhereisFff@users.noreply.github.com> Date: Fri, 2 Oct 2026 00:50:34 -0700 Subject: [PATCH 3/3] =?UTF-8?q?fix(building):=20=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E7=8E=B0=E5=9C=BA=E6=A4=8D=E7=89=A9=E7=9A=84?= =?UTF-8?q?=E5=88=9B=E9=80=A0=E6=A8=A1=E5=BC=8F=E6=92=A4=E9=94=80=20(#5124?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 仅在配对方块没有历史快照时检查现场状态,允许创造模式安全恢复区域边界的完整植物。 - 保留已有快照的配对冲突校验,不覆盖区域外方块,也不放宽生存模式的资源检查。 --- .../dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java index ba7c508322..5c5b911e81 100644 --- a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java +++ b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRegionSnapshot.java @@ -145,6 +145,7 @@ boolean canRestore(ServerPlayer player) { return true; } + /** 只校验已变化的植物;未记录的配对方块不会被还原,可按现场状态判断。 */ boolean hasCompletePlants() { Map original = new LinkedHashMap<>(); this.blocks.forEach(block -> original.put(block.pos(), block.state())); @@ -154,6 +155,7 @@ boolean hasCompletePlants() { DoubleBlockHalf half = block.state().getValue(DoublePlantBlock.HALF); BlockPos other = half == DoubleBlockHalf.LOWER ? block.pos().above() : block.pos().below(); BlockState otherState = original.get(other); + if (otherState == null && this.level.isInWorldBounds(other)) otherState = this.level.getBlockState(other); if (otherState == null || !otherState.is(block.state().getBlock()) || otherState.getValue(DoublePlantBlock.HALF) == half) { return false; }