Skip to content

Add DragonRod and Devourer's devour priority judgement 吞噬额外判断#3383

Open
Chrise2024 wants to merge 2 commits intoAnvil-Dev:dev/1.21/1.6from
Chrise2024:misc
Open

Add DragonRod and Devourer's devour priority judgement 吞噬额外判断#3383
Chrise2024 wants to merge 2 commits intoAnvil-Dev:dev/1.21/1.6from
Chrise2024:misc

Conversation

@Chrise2024
Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses issue #3382 by adding an attachment-aware “devour ordering” so blocks that would otherwise pop off (torches, redstone dust, etc.) can be handled before their supporting blocks are removed.

Changes:

  • Added DevouringLevelReader (a wrapper LevelReader) to simulate “to-be-devoured blocks are already air” when evaluating BlockState#canSurvive.
  • Updated DragonRodItem devour logic to split blocks into “primary” vs “secondary” destruction passes using survivability checks.
  • Updated BlockDevourerBlock devour logic to use survivability checks (plus chain-devour handling) to influence devour order.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
src/main/java/dev/dubhe/anvilcraft/util/DevouringLevelReader.java New fake LevelReader used to evaluate survivability as if devoured blocks were already removed.
src/main/java/dev/dubhe/anvilcraft/item/DragonRodItem.java Uses the fake reader to reorder destruction and reduce attachment pop-off drops.
src/main/java/dev/dubhe/anvilcraft/block/BlockDevourerBlock.java Applies survivability-based ordering to block devourer (including chain devouring).
Comments suppressed due to low confidence (1)

src/main/java/dev/dubhe/anvilcraft/block/BlockDevourerBlock.java:33

  • import net.minecraft.world.level.block.Blocks; appears unused in this file (no Blocks. references). If your build enables unused-import checks, this will fail compilation; please remove the import.
import net.minecraft.world.level.BlockGetter;
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.DirectionalBlock;
import net.minecraft.world.level.block.DoublePlantBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.RenderShape;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


@Override
public @Nullable ChunkAccess getChunk(int i, int i1, ChunkStatus chunkStatus, boolean b) {
return null;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

getChunk(...) currently always returns null. Some BlockState#canSurvive implementations call into LevelReader#getChunk/chunk access, which can cause NPEs or incorrect behavior when using this reader. Delegate to parentLevel.getChunk(...) (or implement a safe passthrough) instead of returning null.

Suggested change
return null;
return parentLevel.getChunk(i, i1, chunkStatus, b);

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +31
* 用来执行吞噬判断的假世界,仅修改了 {@link LevelReader#getBlockState(BlockPos) LevelReade.getBlockState} 的逻辑.
* 原则上来说应该重写所有方法以规避意料之外的问题(
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Class Javadoc has a typo in the link label (LevelReade) and an unclosed parenthesis in the second line, which makes the comment misleading and renders poorly in Javadoc. Please correct the reference text and close the sentence/parenthesis.

Suggested change
* 用来执行吞噬判断的假世界仅修改了 {@link LevelReader#getBlockState(BlockPos) LevelReade.getBlockState} 的逻辑.
* 原则上来说应该重写所有方法以规避意料之外的问题(
* 用来执行吞噬判断的假世界仅修改了 {@link LevelReader#getBlockState(BlockPos) LevelReader.getBlockState} 的逻辑.
* 原则上来说应该重写所有方法以规避意料之外的问题

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +138
public BlockState getBlockState(BlockPos blockPos) {
BlockState blockState = parentLevel.getBlockState(blockPos);
if (devouringList.contains(blockPos) && BlockDevourerBlock.canDevour(blockState)) {
return AIR_STATE;
}
return blockState;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

getBlockState uses devouringList.contains(blockPos) on a List, making lookups O(n). canSurvive checks can invoke many getBlockState calls, and the devour list can be dozens/hundreds of positions, so this can become a hot path. Consider storing devour positions in a Set (e.g., LongSet using BlockPos#asLong) for O(1) lookups.

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +138
List<BlockPos> devouringPosList = Streams.stream(devouringPoses).map(BlockPos::immutable).toList();
DevouringLevelReader devouringLevelReader = new DevouringLevelReader(level, devouringPosList);

for (BlockPos devouringPos : devouringPoses) {
List<BlockPos> secondaryDevouringPosList = new ArrayList<>(devouringPosList.size());
for (BlockPos devouringPos : devouringPosList) {
BlockState devouringState = level.getBlockState(devouringPos);
if (devouringState.isAir()) continue;
if (!BlockDevourerBlock.canDevour(devouringState)) continue;
if (devouringState.is(ModBlockTags.BLOCK_DEVOURER_PROBABILITY_DROPPING)
&& level.random.nextDouble() > 0.05) {
level.destroyBlock(devouringPos, false);
continue;
}

devouringPos = MultiPartBlockUtil.getChainableMainPartPos(level, devouringPos);
devouringState = level.getBlockState(devouringPos);
boolean shouldDrop = !devouringState.is(ModBlockTags.BLOCK_DEVOURER_PROBABILITY_DROPPING)
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

DevouringLevelReader is built from devouringPosList before positions are normalized via MultiPartBlockUtil.getChainableMainPartPos(...), but the later survivability check uses the normalized devouringPos. If the main-part position differs and isn’t in devouringPosList (e.g., bed foot/head, double blocks at edge of range), the fake world won’t treat it as devoured, which can defeat the attachment-order fix. Build the devour-position collection after normalizing to main parts (and de-duplicate), or ensure both original and normalized positions are included in the reader’s devour set.

Copilot uses AI. Check for mistakes.
Comment on lines 138 to +185
@@ -175,6 +176,16 @@ public static void devourBlock(
if (!(devouringState.getBlock() instanceof DoublePlantBlock)) {
devouringState.getBlock().playerWillDestroy(level, devouringPos, devouringState, player);
}

// 通过假世界判断需要先被破坏的方块
if (!devouringState.canSurvive(devouringLevelReader, devouringPos)) {
level.destroyBlock(devouringPos, false);
} else {
secondaryDevouringPosList.add(devouringPos);
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The probability-dropping path behavior changed: previously when the random check failed, the block was destroyed immediately and the loop continued (skipping playerWillDestroy and survivability ordering). Now playerWillDestroy is always called and the block is scheduled/destroyed later, even when shouldDrop is false. If those side effects (events/stats/block-specific logic) were intentionally skipped before, this is a behavior change; consider keeping the early-destroy/continue behavior for the no-drop case to preserve prior semantics.

Copilot uses AI. Check for mistakes.
Comment on lines +254 to 270
if (level.getBlockState(devourBlockPos).canSurvive(devouringLevelReader, devourBlockPos)) {
devourSingleBlockInternalLogic(level, anvil, devourBlockPos, filteredBlockPosList, itemHandlerList, center);
} else {
secondaryBlockPosList.add(devourBlockPos);
}
}
DevouringLevelReader chainDevouringLevelReader = new DevouringLevelReader(level, chainDevourBlockPosList);
for (BlockPos devourBlockPos : chainDevourBlockPosList) {
if (level.getBlockState(devourBlockPos).canSurvive(chainDevouringLevelReader, devourBlockPos)) {
devourSingleBlockInternalLogic(level, anvil, devourBlockPos, filteredBlockPosList, itemHandlerList, center);
} else {
secondaryBlockPosList.add(devourBlockPos);
}
}
for (BlockPos devourBlockPos : secondaryBlockPosList) {
devourSingleBlockInternalLogic(level, anvil, devourBlockPos, filteredBlockPosList, itemHandlerList, center);
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The survivability-based ordering appears inverted. Blocks that cannot survive when other devoured blocks are treated as air are the ones that need to be devoured first (to avoid attachment blocks popping/dropping after their support is removed). Here, canSurvive(...) blocks are processed first and non-survivors are deferred to secondaryBlockPosList, which likely recreates the original drop problem. Swap the branches so non-survivors are devoured before survivors (apply similarly to the chain loop).

Copilot uses AI. Check for mistakes.
Comment on lines 236 to +266
@@ -246,19 +251,31 @@ public void devourBlock(
chainDevourBlockPosList.add(chainDevourBlockPos.immutable());
}
}

devourSingleBlockInternalLogic(level, anvil, devourBlockPos, filteredBlockPosList, itemHandlerList, center);
if (level.getBlockState(devourBlockPos).canSurvive(devouringLevelReader, devourBlockPos)) {
devourSingleBlockInternalLogic(level, anvil, devourBlockPos, filteredBlockPosList, itemHandlerList, center);
} else {
secondaryBlockPosList.add(devourBlockPos);
}
}
DevouringLevelReader chainDevouringLevelReader = new DevouringLevelReader(level, chainDevourBlockPosList);
for (BlockPos devourBlockPos : chainDevourBlockPosList) {
if (level.getBlockState(devourBlockPos).canSurvive(chainDevouringLevelReader, devourBlockPos)) {
devourSingleBlockInternalLogic(level, anvil, devourBlockPos, filteredBlockPosList, itemHandlerList, center);
} else {
secondaryBlockPosList.add(devourBlockPos);
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Survivability checks are done on devourBlockPos/chainDevourBlockPos before devourSingleBlockInternalLogic normalizes positions via MultiPartBlockUtil.getChainableMainPartPos(...). If normalization changes the actual broken position, the ordering decision may be based on the wrong block, and the fake-world “devoured positions” set may not include the normalized position. Consider normalizing (and de-duplicating) positions before performing canSurvive ordering and before constructing the DevouringLevelReader.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants