From b276dfd2c770e6296295e5da3db177c197e0c019 Mon Sep 17 00:00:00 2001 From: nicksitnikov Date: Sun, 12 Apr 2026 00:06:10 -0500 Subject: [PATCH] Make the Endest Pearl check whether the throwing player has permission to break the affected blocks. --- dependencies.gradle | 5 +- .../avaritia/entity/EntityEndestPearl.java | 2 +- .../avaritia/entity/EntityGapingVoid.java | 143 +++++++++++++++++- 3 files changed, 143 insertions(+), 7 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 26b5a599..f878886b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -17,5 +17,8 @@ dependencies { compileOnly("curse.maven:cofh-core-69162:2388751") {transitive = false} compileOnly("curse.maven:witchery-69673:2234410") {transitive = false} - runtimeOnly("com.github.GTNewHorizons:GTNHLib:0.7.7:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GTNHLib:0.7.7:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.20:dev") + + compileOnly('org.jetbrains:annotations:26.0.1') } diff --git a/src/main/java/fox/spiteful/avaritia/entity/EntityEndestPearl.java b/src/main/java/fox/spiteful/avaritia/entity/EntityEndestPearl.java index 4a2cb758..6c634bd7 100644 --- a/src/main/java/fox/spiteful/avaritia/entity/EntityEndestPearl.java +++ b/src/main/java/fox/spiteful/avaritia/entity/EntityEndestPearl.java @@ -42,7 +42,7 @@ protected void onImpact(MovingObjectPosition pos) { if (!this.worldObj.isRemote) { // this.worldObj.createExplosion(this, pos.hitVec.xCoord, pos.hitVec.yCoord, pos.hitVec.zCoord, 4.0f, true); - Entity ent = new EntityGapingVoid(this.worldObj); + Entity ent = new EntityGapingVoid(this.worldObj, this.getThrower()); // Entity ent = new EntityChicken(this.worldObj); ForgeDirection dir = ForgeDirection.getOrientation(pos.sideHit); ent.setLocationAndAngles( diff --git a/src/main/java/fox/spiteful/avaritia/entity/EntityGapingVoid.java b/src/main/java/fox/spiteful/avaritia/entity/EntityGapingVoid.java index 3bd4b5ee..38b94c07 100644 --- a/src/main/java/fox/spiteful/avaritia/entity/EntityGapingVoid.java +++ b/src/main/java/fox/spiteful/avaritia/entity/EntityGapingVoid.java @@ -1,26 +1,49 @@ package fox.spiteful.avaritia.entity; +import java.lang.ref.WeakReference; import java.util.List; +import java.util.UUID; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.MinecraftServer; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; import net.minecraft.util.Vec3; import net.minecraft.world.Explosion; import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.FakePlayerFactory; +import net.minecraftforge.event.world.BlockEvent; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.mojang.authlib.GameProfile; import fox.spiteful.avaritia.Config; public class EntityGapingVoid extends Entity { public static final int maxLifetime = 186; - public static double collapse = .95; - public static double suckrange = 20.0; + public static final double collapse = .95; + public static final double suckrange = 20.0; + + private static final GameProfile defaultProfile = new GameProfile( + UUID.fromString("139a261e-4a64-4d98-8321-e484cfe7c6af"), + "[Endest Pearl]"); + private static WeakReference defaultFakePlayer = new WeakReference<>(null); + private @Nullable GameProfile throwerInfo; + private WeakReference thrower; + + @SuppressWarnings("unused") public EntityGapingVoid(World world) { super(world); this.isImmuneToFire = true; @@ -29,6 +52,15 @@ public EntityGapingVoid(World world) { this.renderDistanceWeight = 100.0; } + public EntityGapingVoid(World world, EntityLivingBase thrower) { + this(world); + + if (thrower instanceof EntityPlayer player) { + this.thrower = new WeakReference<>(player); + this.throwerInfo = player.getGameProfile(); + } + } + @Override protected void entityInit() { dataWatcher.addObject(12, 0); @@ -43,7 +75,9 @@ public void onUpdate() { int age = this.getAge(); if (age >= maxLifetime) { - this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, 6.0f, true); + if (!this.worldObj.isRemote) { + this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, 6.0f, true); + } this.setDead(); } else { if (age == 0) { @@ -188,7 +222,7 @@ public void onUpdate() { this.posY, this.posZ); - if (resist <= 10.0) { + if (resist <= 10.0 && checkPermissions(this.worldObj, lx, ly, lz, b, meta)) { b.dropBlockAsItemWithChance(worldObj, lx, ly, lz, meta, 0.9f, 0); this.worldObj.setBlockToAir(lx, ly, lz); } @@ -210,11 +244,48 @@ public int getAge() { @Override protected void readEntityFromNBT(NBTTagCompound tag) { this.setAge(tag.getInteger("age")); + + if (tag.hasKey("owner", Constants.NBT.TAG_COMPOUND)) { + NBTTagCompound ownerTag = tag.getCompoundTag("owner"); + + String name = ownerTag.getString("name"); + if (name.isEmpty()) name = null; + + UUID uuid; + if (ownerTag.hasKey("uuidLower", Constants.NBT.TAG_LONG) + && ownerTag.hasKey("uuidUpper", Constants.NBT.TAG_LONG)) { + uuid = new UUID(tag.getLong("uuidUpper"), tag.getLong("uuidLower")); + } else { + uuid = null; + } + + if (name != null || uuid != null) { + this.throwerInfo = new GameProfile(uuid, name); + this.thrower = new WeakReference<>(null); + } + } } @Override protected void writeEntityToNBT(NBTTagCompound tag) { tag.setInteger("age", this.getAge()); + + if (this.throwerInfo != null) { + NBTTagCompound ownerTag = new NBTTagCompound(); + + final var ownerName = this.throwerInfo.getName(); + if (ownerName != null) { + ownerTag.setString("name", ownerName); + } + + final var ownerUUID = this.throwerInfo.getId(); + if (ownerUUID != null) { + ownerTag.setLong("uuidUpper", ownerUUID.getMostSignificantBits()); + ownerTag.setLong("uuidLower", ownerUUID.getLeastSignificantBits()); + } + + tag.setTag("owner", ownerTag); + } } public static double getVoidScale(double age) { @@ -243,6 +314,68 @@ public float getShadowSize() { public boolean func_145774_a(Explosion explosionIn, World worldIn, int x, int y, int z, Block blockIn, float unused) { // Can the final explosion break this block? - return Config.endestGriefing && (Config.endestTileGriefing || worldIn.getTileEntity(x, y, z) == null); + return Config.endestGriefing && (Config.endestTileGriefing || worldIn.getTileEntity(x, y, z) == null) + && checkPermissions(worldIn, x, y, z, blockIn, worldIn.getBlockMetadata(x, y, z)); + } + + private boolean checkPermissions(World worldIn, int x, int y, int z, Block block, int meta) { + final var event = new BlockEvent.BreakEvent(x, y, z, worldIn, block, meta, getOwningPlayer()); + return !MinecraftForge.EVENT_BUS.post(event); + } + + private @NotNull EntityPlayer getOwningPlayer() { + // If throwerInfo is null, this pearl intentionally has no player. + if (this.throwerInfo == null) return getDefaultFakePlayer(); + + // If we have the player entity cached, return it. + final EntityPlayer thrower = this.thrower.get(); + if (thrower != null) return thrower; + + // We have the player info, but no player - try to find the player. + final var onlinePlayer = findPlayerByProfile(this.throwerInfo); + if (onlinePlayer != null) { + this.thrower = new WeakReference<>(onlinePlayer); + return onlinePlayer; + } + + // If the player is offline, make a fake player that pretends to be that player (same UUID & name) + final var playerMimic = FakePlayerFactory.get((WorldServer) this.worldObj, this.throwerInfo); + playerMimic.setPosition(this.posX, this.posY, this.posZ); + this.thrower = new WeakReference<>(playerMimic); + return playerMimic; + } + + private @NotNull EntityPlayer getDefaultFakePlayer() { + EntityPlayer fakePlayer = defaultFakePlayer.get(); + if (fakePlayer == null) { + fakePlayer = FakePlayerFactory.get((WorldServer) this.worldObj, defaultProfile); + defaultFakePlayer = new WeakReference<>(fakePlayer); + } + + fakePlayer.setWorld(this.worldObj); + fakePlayer.setPosition(this.posX, this.posY, this.posZ); + return fakePlayer; + } + + private static @Nullable EntityPlayerMP findPlayerByProfile(GameProfile profile) { + final String throwerName = profile.getName(); + final UUID throwerUUID = profile.getId(); + + final var players = MinecraftServer.getServer().getConfigurationManager().playerEntityList; + if (throwerUUID != null) { + for (var player : players) { + if (throwerUUID.equals(player.getGameProfile().getId())) { + return player; + } + } + } else { + for (var player : players) { + if (throwerName.equals(player.getCommandSenderName())) { + return player; + } + } + } + + return null; } }