fix: 奇点吞方块前问保护层(BreakEvent);毁灭爆炸遵守 mobGriefing;暴露 getOwnerEntity
Build / build (push) Skipped
Build / publish (push) Skipped
Build / build (pull_request) Successful in 7m10s
Build / publish (pull_request) Skipped

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
layue13andClaude Sonnet 5.5 committed 2026-11-12 23:52:46 +08:00
1 parent b51852c15b
commit 770cdec7f9
3 files changed
+100 -2

No files matched your search

@@ -1,11 +1,13 @@
package com.pengu.thaumcraft.additions.entity;
import com.pengu.thaumcraft.additions.api.integration.ThaumicAdditionsDamage;
import com.pengu.thaumcraft.additions.misc.TerritoryGuard;
import java.util.List;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.boss.EntityDragon;
import net.minecraft.entity.boss.EntityWither;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
@@ -58,7 +60,7 @@ public class EntitySingularity extends EntityAlumentum {
++this.ticksExisted;
if(this.ticksExisted >= 2000 || this.suckedItems >= 2000.0F) {
for(p = 0; p < 5; ++p) {
this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, this.suckedItems / 200.0F, true);
this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, this.suckedItems / 200.0F, this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"));
}
this.setDead();
@@ -137,7 +139,7 @@ public class EntitySingularity extends EntityAlumentum {
i = (int)this.posX + this.worldObj.rand.nextInt(this.radius) - this.worldObj.rand.nextInt(this.radius);
var22 = (int)this.posY + this.worldObj.rand.nextInt(this.radius) - this.worldObj.rand.nextInt(this.radius);
var23 = (int)this.posZ + this.worldObj.rand.nextInt(this.radius) - this.worldObj.rand.nextInt(this.radius);
if(this.worldObj.getBlock(i, var22, var23) != Blocks.air && this.worldObj.getBlock(i, var22, var23).getBlockHardness(this.worldObj, i, var22, var23) > -1.0F && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")) {
if(this.worldObj.getBlock(i, var22, var23) != Blocks.air && this.worldObj.getBlock(i, var22, var23).getBlockHardness(this.worldObj, i, var22, var23) > -1.0F && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing") && this.canDestroy(i, var22, var23)) {
this.worldObj.getBlock(i, var22, var23).breakBlock(this.worldObj, i, var22, var23, this.worldObj.getBlock(i, var22, var23), 1);
this.worldObj.playSoundEffect((double)i, (double)var22, (double)var23, this.worldObj.getBlock(i, var22, var23).stepSound.getBreakSound(), this.worldObj.getBlock(i, var22, var23).stepSound.getVolume(), this.worldObj.getBlock(i, var22, var23).stepSound.getPitch());
if(!this.worldObj.isRemote) {
@@ -188,6 +190,17 @@ public class EntitySingularity extends EntityAlumentum {
}
/** The player who threw it, or null (dispenser, merged singularity). Protection plugins read this by name. */
public EntityPlayer getOwnerEntity() {
EntityLivingBase t = this.getThrower();
return t instanceof EntityPlayer ? (EntityPlayer)t : null;
}
/** Island/territory check: a BreakEvent as the thrower, or as a fake player when nobody threw it. */
private boolean canDestroy(int x, int y, int z) {
return TerritoryGuard.canRemoveBlock(this.worldObj, x, y, z, this.getOwnerEntity());
}
protected void onImpact(MovingObjectPosition m) {
this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "thaumicadditions:singularity", 16.0F, 1.0F);
this.inGround = true;
@@ -0,0 +1,57 @@
package com.pengu.thaumcraft.additions.misc;
import java.lang.ref.WeakReference;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.FakePlayerFactory;
import net.minecraftforge.event.world.BlockEvent;
/**
* Standard cancellable BlockEvent.BreakEvent gate for block removal that no player performs directly
* (void entity, hungry blocks, ...). Island and territory plugins receive the contract they already handle for player
* mining; the actor is the responsible player when there is one, otherwise a per-world fake player (which the
* protection layer judges as an unattributed machine). With no receiver nothing changes.
*/
public final class TerritoryGuard {
private static WeakReference<EntityPlayer> fallback = new WeakReference<>(null);
/** @param actor the player responsible for the removal, or null when there is none */
public static boolean canRemoveBlock(World world, int x, int y, int z, EntityPlayer actor) {
EntityPlayer who = resolveActor(world, actor);
if (who == null) {
// no world to attribute the event to (client world): nothing to protect
return !(world instanceof WorldServer);
}
BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(
x,
y,
z,
world,
world.getBlock(x, y, z),
world.getBlockMetadata(x, y, z),
who);
return !MinecraftForge.EVENT_BUS.post(event);
}
private static EntityPlayer resolveActor(World world, EntityPlayer actor) {
if (actor != null) return actor;
if (world instanceof WorldServer) {
EntityPlayer player = fallback.get();
if (player == null || player.worldObj != world) {
FakePlayer fake = FakePlayerFactory.getMinecraft((WorldServer) world);
player = fake;
fallback = new WeakReference<>(player);
}
return player;
}
return null;
}
private TerritoryGuard() {}
}
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""The singularity must ask the protection layer before removing a block and respect mobGriefing for its blast."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
SRC = (ROOT / "src/main/java/com/pengu/thaumcraft/additions/entity/EntitySingularity.java").read_text(encoding="utf-8")
GUARD = (ROOT / "src/main/java/com/pengu/thaumcraft/additions/misc/TerritoryGuard.java").read_text(encoding="utf-8")
def main():
assert "new BlockEvent.BreakEvent(" in GUARD and "EVENT_BUS.post(event)" in GUARD
assert "FakePlayerFactory.getMinecraft" in GUARD
# the block-eating branch is gated by the guard before any breakBlock / setBlockToAir
gate = SRC.index("this.canDestroy(i, var22, var23)")
assert gate < SRC.index(".breakBlock(this.worldObj, i, var22, var23"), "guard must precede breakBlock"
assert gate < SRC.index("this.worldObj.setBlockToAir(i, var22, var23)")
assert "TerritoryGuard.canRemoveBlock(this.worldObj, x, y, z, this.getOwnerEntity())" in SRC
# explosion attribution accessor, plus the end-of-life blast no longer ignores mobGriefing
assert "public EntityPlayer getOwnerEntity()" in SRC
assert 'this.suckedItems / 200.0F, true)' not in SRC
print("ok")
if __name__ == "__main__":
main()