From d79b3669f9473a404e9269c48776ab8d6ef91ea7 Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:34:41 +0400 Subject: [PATCH 1/7] update --- gradle/gradle-daemon-jvm.properties | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index 991a69c72..58206c6ed 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,12 +1,12 @@ #This file is generated by updateDaemonJvm toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect -toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect -toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect -toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect -toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect -toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect -toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect toolchainVersion=25 From ba07ce3bda18b4e71e4f209dd49bd80e4e9da5f0 Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:35:13 +0400 Subject: [PATCH 2/7] Add auto-equip on grave interaction Players can now retrieve items from graves directly into equipment slots. Right-click shows death message, Shift+right-click or left-click auto-equips armor, baubles, and backpacks into their correct slots. Remaining items go to inventory. Grave is unbreakable by hand while it contains items. Soft dependencies: Baubles Expanded, AdventureBackpack2, Minecraft Backpack Mod, Tinkers' Construct. --- dependencies.gradle | 15 +- .../openblocks/common/GraveAutoEquip.java | 178 ++++++++++++++++++ .../openblocks/common/block/BlockGrave.java | 23 ++- .../common/tileentity/TileEntityGrave.java | 58 +++++- .../assets/openblocks/lang/en_US.lang | 3 + 5 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 src/main/java/openblocks/common/GraveAutoEquip.java diff --git a/dependencies.gradle b/dependencies.gradle index f1681b102..f2847d3ff 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,11 +1,20 @@ // Add your dependencies here dependencies { - api('com.github.GTNewHorizons:OpenModsLib:0.10.13:dev') + api('com.github.GTNewHorizons:OpenModsLib:0.10.14:dev') - devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev') + devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.93-GTNH:dev') compileOnly('openperipheral:OpenPeripheralCore-API:3.4.1') compileOnly('curse.maven:computercraft-67504:2269339') - compileOnly('com.github.GTNewHorizons:Mobs-Info:0.5.10-GTNH:dev') + compileOnly('com.github.GTNewHorizons:Mobs-Info:0.5.12-GTNH:dev') compileOnly("com.github.GTNewHorizons:Backhand:1.8.8:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Baubles-Expanded:2.2.13-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:AdventureBackpack2:1.4.19-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Minecraft-Backpack-Mod:2.6.11-GTNH:dev") { transitive = false } + devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.13-GTNH:dev") { transitive = false } + devOnlyNonPublishable("com.github.GTNewHorizons:AdventureBackpack2:1.4.19-GTNH:dev") { transitive = false } + devOnlyNonPublishable("com.github.GTNewHorizons:Minecraft-Backpack-Mod:2.6.11-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.59-GTNH-pre:dev") { transitive = false } + devOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.59-GTNH-pre:dev") { transitive = true } + devOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.12-GTNH:dev") { transitive = true } } diff --git a/src/main/java/openblocks/common/GraveAutoEquip.java b/src/main/java/openblocks/common/GraveAutoEquip.java new file mode 100644 index 000000000..e88509444 --- /dev/null +++ b/src/main/java/openblocks/common/GraveAutoEquip.java @@ -0,0 +1,178 @@ +package openblocks.common; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; + +import openmods.Log; + +public class GraveAutoEquip { + + /** + * Tries to equip the stack into the appropriate slot on the player. Returns null if equipped, or the original stack + * if it could not be equipped. Order: vanilla armor → Baubles → Adventure Backpack → Minecraft Backpack. + */ + public static ItemStack tryEquipOrDrop(EntityPlayer player, ItemStack stack) { + if (stack == null) return null; + + try { + if (tryEquipTConstructAccessory(player, stack)) return null; + } catch (Exception e) { + Log.warn("GraveAutoEquip: error equipping tconstruct accessory %s: %s", stack.getDisplayName(), e); + } + + try { + if (tryEquipVanillaArmor(player, stack)) return null; + } catch (Exception e) { + Log.warn("GraveAutoEquip: error equipping vanilla armor %s: %s", stack.getDisplayName(), e); + } + + try { + if (tryEquipBauble(player, stack)) return null; + } catch (Exception e) { + Log.warn("GraveAutoEquip: error equipping bauble %s: %s", stack.getDisplayName(), e); + } + + try { + if (tryEquipAdventureBackpack(player, stack)) return null; + } catch (Exception e) { + Log.warn("GraveAutoEquip: error equipping adventure backpack %s: %s", stack.getDisplayName(), e); + } + + try { + if (tryEquipMcBackpack(player, stack)) return null; + } catch (Exception e) { + Log.warn("GraveAutoEquip: error equipping mc backpack %s: %s", stack.getDisplayName(), e); + } + + return stack; + } + + // ------------------------------------------------------------------------- + // Tinkers' Construct accessories (soft dependency) — must run BEFORE vanilla armor + // ------------------------------------------------------------------------- + + private static boolean tryEquipTConstructAccessory(EntityPlayer player, ItemStack stack) { + try { + Class.forName("tconstruct.library.accessory.IAccessory"); + } catch (ClassNotFoundException ignored) { + return false; + } + return TConstructAccessoryHelper.equip(player, stack); + } + + private static final class TConstructAccessoryHelper { + + static boolean equip(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof tconstruct.library.accessory.IAccessory)) return false; + tconstruct.library.accessory.IAccessory accessory = (tconstruct.library.accessory.IAccessory) stack.getItem(); + tconstruct.armor.player.TPlayerStats stats = tconstruct.armor.player.TPlayerStats.get(player); + if (stats == null) return false; + tconstruct.armor.player.ArmorExtended armor = stats.armor; + for (int i = 0; i < armor.getSizeInventory(); i++) { + if (armor.getStackInSlot(i) == null && accessory.canEquipAccessory(stack, i)) { + armor.setInventorySlotContents(i, stack.copy()); + return true; + } + } + return false; + } + } + + // ------------------------------------------------------------------------- + // Vanilla armor + // ------------------------------------------------------------------------- + + private static boolean tryEquipVanillaArmor(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof ItemArmor)) return false; + // armorType: 0=helmet,1=chest,2=legs,3=boots; armorInventory: 0=boots,1=legs,2=chest,3=helmet + int slot = 3 - ((ItemArmor) stack.getItem()).armorType; + if (player.inventory.armorInventory[slot] == null) { + player.inventory.armorInventory[slot] = stack.copy(); + return true; + } + return false; + } + + // ------------------------------------------------------------------------- + // Baubles Expanded (soft dependency) + // ------------------------------------------------------------------------- + + private static boolean tryEquipBauble(EntityPlayer player, ItemStack stack) { + try { + Class.forName("baubles.api.IBauble"); + } catch (ClassNotFoundException ignored) { + return false; + } + return BaubleEquipHelper.equip(player, stack); + } + + private static final class BaubleEquipHelper { + + static boolean equip(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof baubles.api.IBauble)) return false; + IInventory inv = baubles.api.BaublesApi.getBaubles(player); + if (inv == null) return false; + for (int i = 0; i < inv.getSizeInventory(); i++) { + if (inv.getStackInSlot(i) == null && inv.isItemValidForSlot(i, stack)) { + inv.setInventorySlotContents(i, stack.copy()); + return true; + } + } + return false; + } + } + + // ------------------------------------------------------------------------- + // Adventure Backpack 2 (soft dependency) + // ------------------------------------------------------------------------- + + private static boolean tryEquipAdventureBackpack(EntityPlayer player, ItemStack stack) { + try { + Class.forName("com.darkona.adventurebackpack.item.IBackWearableItem"); + } catch (ClassNotFoundException ignored) { + return false; + } + return AdventureBackpackEquipHelper.equip(player, stack); + } + + private static final class AdventureBackpackEquipHelper { + + static boolean equip(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof com.darkona.adventurebackpack.item.IBackWearableItem)) return false; + com.darkona.adventurebackpack.playerProperties.BackpackProperty prop = com.darkona.adventurebackpack.playerProperties.BackpackProperty + .get(player); + if (prop == null || prop.getWearable() != null) return false; + prop.setWearable(stack.copy()); + ((com.darkona.adventurebackpack.item.IBackWearableItem) stack.getItem()) + .onEquipped(player.worldObj, player, stack); + com.darkona.adventurebackpack.playerProperties.BackpackProperty.sync(player); + return true; + } + } + + // ------------------------------------------------------------------------- + // Minecraft Backpack Mod (soft dependency) + // ------------------------------------------------------------------------- + + private static boolean tryEquipMcBackpack(EntityPlayer player, ItemStack stack) { + try { + Class.forName("de.eydamos.backpack.item.ItemBackpackBase"); + } catch (ClassNotFoundException ignored) { + return false; + } + return McBackpackEquipHelper.equip(player, stack); + } + + private static final class McBackpackEquipHelper { + + static boolean equip(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof de.eydamos.backpack.item.ItemBackpackBase)) return false; + de.eydamos.backpack.saves.PlayerSave save = new de.eydamos.backpack.saves.PlayerSave(player); + if (save.hasPersonalBackpack()) return false; + save.setPersonalBackpack(stack.copy()); + return true; + } + } +} diff --git a/src/main/java/openblocks/common/block/BlockGrave.java b/src/main/java/openblocks/common/block/BlockGrave.java index 74846ab22..d6b866cb1 100644 --- a/src/main/java/openblocks/common/block/BlockGrave.java +++ b/src/main/java/openblocks/common/block/BlockGrave.java @@ -8,6 +8,7 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; +import net.minecraft.item.ItemStack; import net.minecraft.world.Explosion; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; @@ -86,14 +87,28 @@ public void onBlockDestroyedByExplosion(World world, int x, int y, int z, Explos } @Override - public float getPlayerRelativeBlockHardness(EntityPlayer player, World world, int x, int y, int z) { + public void onBlockClicked(World world, int x, int y, int z, EntityPlayer player) { + if (world.isRemote) return; TileEntity tile = getTileEntity(world, x, y, z); - if (tile instanceof TileEntityGrave) { - TileEntityGrave graveStone = (TileEntityGrave) tile; - if (Objects.equals(graveStone.getUsername(), player.getGameProfile().getName())) return 2.0F; + TileEntityGrave grave = (TileEntityGrave) tile; + if (!grave.isInventoryEmpty()) { + grave.autoEquipAll(player); + } } + } + @Override + public float getPlayerRelativeBlockHardness(EntityPlayer player, World world, int x, int y, int z) { + TileEntity tile = getTileEntity(world, x, y, z); + if (tile instanceof TileEntityGrave) { + TileEntityGrave grave = (TileEntityGrave) tile; + if (!grave.isInventoryEmpty()) { + ItemStack held = player.getHeldItem(); + boolean hasShovel = held != null && held.getItem().getToolClasses(held).contains("shovel"); + if (!hasShovel) return 0; + } + } return super.getPlayerRelativeBlockHardness(player, world, x, y, z); } } diff --git a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java index 72128b829..0db56af21 100644 --- a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java +++ b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java @@ -9,11 +9,13 @@ import net.minecraft.entity.monster.IMob; import net.minecraft.entity.passive.EntityBat; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; import net.minecraft.world.EnumDifficulty; import net.minecraft.world.storage.WorldInfo; @@ -24,6 +26,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import openblocks.Config; +import openblocks.common.GraveAutoEquip; import openmods.api.IActivateAwareTile; import openmods.api.IAddAwareTile; import openmods.api.INeighbourAwareTile; @@ -173,19 +176,72 @@ public boolean onBlockActivated(EntityPlayer player, int side, float hitX, float return true; } + if (held == null && player.isSneaking()) { + autoEquipAll(player); + return true; + } + if (deathMessage != null) { player.addChatMessage(deathMessage.createCopy()); } return true; } + public boolean isInventoryEmpty() { + for (int i = 0; i < inventory.getSizeInventory(); i++) { + if (inventory.getStackInSlot(i) != null) return false; + } + return true; + } + + public void autoEquipAll(EntityPlayer player) { + int equipped = 0; + int toInventory = 0; + int leftover = 0; + for (int i = 0; i < inventory.getSizeInventory(); i++) { + final ItemStack stack = inventory.getStackInSlot(i); + if (stack != null) { + ItemStack remainder = GraveAutoEquip.tryEquipOrDrop(player, stack); + if (remainder == null) { + inventory.setInventorySlotContents(i, null); + equipped++; + } else if (player.inventory.addItemStackToInventory(remainder)) { + inventory.setInventorySlotContents(i, null); + toInventory++; + } else { + leftover++; + } + } + } + if (equipped > 0 || toInventory > 0) { + markDirty(); + sync(); + player.inventory.markDirty(); + if (player instanceof EntityPlayerMP) { + player.inventoryContainer.detectAndSendChanges(); + } + } + if (equipped > 0) { + player.addChatMessage(new ChatComponentTranslation("openblocks.misc.grave_equipped", equipped)); + } + if (toInventory > 0) { + player.addChatMessage(new ChatComponentTranslation("openblocks.misc.grave_to_inventory", toInventory)); + } + if (leftover > 0) { + player.addChatMessage(new ChatComponentTranslation("openblocks.misc.grave_skipped", leftover)); + } + } + protected void robGrave(EntityPlayer player, ItemStack held) { boolean dropped = false; for (int i = 0; i < inventory.getSizeInventory(); i++) { final ItemStack stack = inventory.getStackInSlot(i); if (stack != null) { dropped = true; - BlockUtils.dropItemStackInWorld(worldObj, xCoord, yCoord, zCoord, stack); + ItemStack remainder = GraveAutoEquip.tryEquipOrDrop(player, stack); + if (remainder != null) { + BlockUtils.dropItemStackInWorld(worldObj, xCoord, yCoord, zCoord, remainder); + } } } diff --git a/src/main/resources/assets/openblocks/lang/en_US.lang b/src/main/resources/assets/openblocks/lang/en_US.lang index afe019511..724638b82 100644 --- a/src/main/resources/assets/openblocks/lang/en_US.lang +++ b/src/main/resources/assets/openblocks/lang/en_US.lang @@ -99,6 +99,9 @@ openblocks.misc.sleeping_bag_broken=Item inactive due to failed initialization openblocks.misc.inverted=Inverted openblocks.misc.grave_msg=%s (day: %.1f) +openblocks.misc.grave_equipped=Equipped %d item(s) from the grave. +openblocks.misc.grave_to_inventory=Moved %d item(s) to your inventory. +openblocks.misc.grave_skipped=%d item(s) could not be retrieved (inventory full). openblocks.misc.cant_restore_player=Can't restore inventory for player %s openblocks.misc.cant_restore_inventory=Can't restore inventory From 307196b1ae2c87ccccc7a9faf1ad6be376037c9f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:43:59 +0400 Subject: [PATCH 3/7] Spotless apply for branch feature/grave-auto-equip for #48 (#49) Co-authored-by: GitHub GTNH Actions <> --- src/main/java/openblocks/common/GraveAutoEquip.java | 3 ++- src/main/java/openblocks/common/block/BlockGrave.java | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/openblocks/common/GraveAutoEquip.java b/src/main/java/openblocks/common/GraveAutoEquip.java index e88509444..fe2edf09c 100644 --- a/src/main/java/openblocks/common/GraveAutoEquip.java +++ b/src/main/java/openblocks/common/GraveAutoEquip.java @@ -66,7 +66,8 @@ private static final class TConstructAccessoryHelper { static boolean equip(EntityPlayer player, ItemStack stack) { if (!(stack.getItem() instanceof tconstruct.library.accessory.IAccessory)) return false; - tconstruct.library.accessory.IAccessory accessory = (tconstruct.library.accessory.IAccessory) stack.getItem(); + tconstruct.library.accessory.IAccessory accessory = (tconstruct.library.accessory.IAccessory) stack + .getItem(); tconstruct.armor.player.TPlayerStats stats = tconstruct.armor.player.TPlayerStats.get(player); if (stats == null) return false; tconstruct.armor.player.ArmorExtended armor = stats.armor; diff --git a/src/main/java/openblocks/common/block/BlockGrave.java b/src/main/java/openblocks/common/block/BlockGrave.java index d6b866cb1..dbf194daa 100644 --- a/src/main/java/openblocks/common/block/BlockGrave.java +++ b/src/main/java/openblocks/common/block/BlockGrave.java @@ -1,14 +1,13 @@ package openblocks.common.block; -import java.util.Objects; import java.util.Random; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.tileentity.TileEntity; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.world.Explosion; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; From 275927789e4627a02d30a5d50ba1e3f1428c056a Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:50:23 +0400 Subject: [PATCH 4/7] Fix dupe bug in grave auto-equip by working with item copies --- .../openblocks/common/GraveAutoEquip.java | 3 ++- .../openblocks/common/block/BlockGrave.java | 3 +-- .../common/tileentity/TileEntityGrave.java | 24 ++++++++++--------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/openblocks/common/GraveAutoEquip.java b/src/main/java/openblocks/common/GraveAutoEquip.java index e88509444..fe2edf09c 100644 --- a/src/main/java/openblocks/common/GraveAutoEquip.java +++ b/src/main/java/openblocks/common/GraveAutoEquip.java @@ -66,7 +66,8 @@ private static final class TConstructAccessoryHelper { static boolean equip(EntityPlayer player, ItemStack stack) { if (!(stack.getItem() instanceof tconstruct.library.accessory.IAccessory)) return false; - tconstruct.library.accessory.IAccessory accessory = (tconstruct.library.accessory.IAccessory) stack.getItem(); + tconstruct.library.accessory.IAccessory accessory = (tconstruct.library.accessory.IAccessory) stack + .getItem(); tconstruct.armor.player.TPlayerStats stats = tconstruct.armor.player.TPlayerStats.get(player); if (stats == null) return false; tconstruct.armor.player.ArmorExtended armor = stats.armor; diff --git a/src/main/java/openblocks/common/block/BlockGrave.java b/src/main/java/openblocks/common/block/BlockGrave.java index d6b866cb1..dbf194daa 100644 --- a/src/main/java/openblocks/common/block/BlockGrave.java +++ b/src/main/java/openblocks/common/block/BlockGrave.java @@ -1,14 +1,13 @@ package openblocks.common.block; -import java.util.Objects; import java.util.Random; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.tileentity.TileEntity; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.world.Explosion; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; diff --git a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java index 0db56af21..45dfbf032 100644 --- a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java +++ b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java @@ -200,17 +200,19 @@ public void autoEquipAll(EntityPlayer player) { int leftover = 0; for (int i = 0; i < inventory.getSizeInventory(); i++) { final ItemStack stack = inventory.getStackInSlot(i); - if (stack != null) { - ItemStack remainder = GraveAutoEquip.tryEquipOrDrop(player, stack); - if (remainder == null) { - inventory.setInventorySlotContents(i, null); - equipped++; - } else if (player.inventory.addItemStackToInventory(remainder)) { - inventory.setInventorySlotContents(i, null); - toInventory++; - } else { - leftover++; - } + if (stack == null) continue; + ItemStack copy = stack.copy(); + ItemStack remainder = GraveAutoEquip.tryEquipOrDrop(player, copy); + if (remainder == null) { + inventory.setInventorySlotContents(i, null); + equipped++; + } else if (player.inventory.addItemStackToInventory(copy)) { + inventory.setInventorySlotContents(i, null); + toInventory++; + } else { + // copy may have been partially consumed — write back the actual remainder + inventory.setInventorySlotContents(i, copy.stackSize > 0 ? copy : null); + leftover++; } } if (equipped > 0 || toInventory > 0) { From a73b900f5d09ba03d0c03bccf6bad7aeee53dc61 Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:55:18 +0400 Subject: [PATCH 5/7] Update dependencies.gradle --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index f2847d3ff..c8c9e4ef2 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -14,7 +14,7 @@ dependencies { devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.13-GTNH:dev") { transitive = false } devOnlyNonPublishable("com.github.GTNewHorizons:AdventureBackpack2:1.4.19-GTNH:dev") { transitive = false } devOnlyNonPublishable("com.github.GTNewHorizons:Minecraft-Backpack-Mod:2.6.11-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.59-GTNH-pre:dev") { transitive = false } - devOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.59-GTNH-pre:dev") { transitive = true } + compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.58-GTNH:dev") { transitive = false } + devOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.58-GTNH:dev") { transitive = true } devOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.12-GTNH:dev") { transitive = true } } From aeafda727943672205ae818ea148465b47fd0a99 Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:11:37 +0400 Subject: [PATCH 6/7] Add grave auto-equip with slot origin tracking and owner-only collection --- dependencies.gradle | 5 +- .../openblocks/common/GraveAutoEquip.java | 134 +++++++++++ .../common/GraveInventorySnapshot.java | 208 ++++++++++++++++++ .../openblocks/common/GraveSlotOrigin.java | 32 +++ .../openblocks/common/PlayerDeathHandler.java | 42 +++- .../openblocks/common/block/BlockGrave.java | 9 +- .../common/tileentity/TileEntityGrave.java | 66 +++++- .../assets/openblocks/lang/en_US.lang | 1 + .../assets/openblocks/lang/ru_RU.lang | 4 + 9 files changed, 487 insertions(+), 14 deletions(-) create mode 100644 src/main/java/openblocks/common/GraveInventorySnapshot.java create mode 100644 src/main/java/openblocks/common/GraveSlotOrigin.java diff --git a/dependencies.gradle b/dependencies.gradle index c8c9e4ef2..24ca7ba59 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,15 +6,16 @@ dependencies { devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.93-GTNH:dev') compileOnly('openperipheral:OpenPeripheralCore-API:3.4.1') compileOnly('curse.maven:computercraft-67504:2269339') - compileOnly('com.github.GTNewHorizons:Mobs-Info:0.5.12-GTNH:dev') + compileOnly('com.github.GTNewHorizons:Mobs-Info:0.5.13-GTNH:dev') compileOnly("com.github.GTNewHorizons:Backhand:1.8.8:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Baubles-Expanded:2.2.13-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:AdventureBackpack2:1.4.19-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Minecraft-Backpack-Mod:2.6.11-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.58-GTNH:dev") { transitive = false } devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.13-GTNH:dev") { transitive = false } devOnlyNonPublishable("com.github.GTNewHorizons:AdventureBackpack2:1.4.19-GTNH:dev") { transitive = false } devOnlyNonPublishable("com.github.GTNewHorizons:Minecraft-Backpack-Mod:2.6.11-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.58-GTNH:dev") { transitive = false } + devOnlyNonPublishable("com.github.GTNewHorizons:waila:1.19.23:dev") { transitive = true } devOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.58-GTNH:dev") { transitive = true } devOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.12-GTNH:dev") { transitive = true } } diff --git a/src/main/java/openblocks/common/GraveAutoEquip.java b/src/main/java/openblocks/common/GraveAutoEquip.java index fe2edf09c..230f96702 100644 --- a/src/main/java/openblocks/common/GraveAutoEquip.java +++ b/src/main/java/openblocks/common/GraveAutoEquip.java @@ -9,6 +9,140 @@ public class GraveAutoEquip { + /** + * Tries to restore the stack to the exact slot it came from at death. Returns true if restored. + */ + public static boolean tryRestoreToOrigin(EntityPlayer player, ItemStack stack, GraveSlotOrigin origin) { + if (stack == null || origin == null) return false; + try { + switch (origin.inventoryType) { + case GraveSlotOrigin.INV_MAIN: + return restoreToMain(player, stack, origin.slot); + case GraveSlotOrigin.INV_ARMOR: + return restoreToArmor(player, stack, origin.slot); + case GraveSlotOrigin.INV_TCONSTRUCT: + return restoreToTConstruct(player, stack, origin.slot); + case GraveSlotOrigin.INV_BAUBLES: + return restoreToBaubles(player, stack, origin.slot); + case GraveSlotOrigin.INV_ADVENTURE_BACKPACK: + return restoreToAdventureBackpack(player, stack); + case GraveSlotOrigin.INV_MC_BACKPACK: + return restoreToMcBackpack(player, stack); + default: + return false; + } + } catch (Exception e) { + Log.warn( + "GraveAutoEquip: error restoring %s to origin %s/%d: %s", + stack.getDisplayName(), + origin.inventoryType, + origin.slot, + e); + return false; + } + } + + private static boolean restoreToMain(EntityPlayer player, ItemStack stack, int slot) { + if (slot < 0 || slot >= player.inventory.mainInventory.length) return false; + if (player.inventory.mainInventory[slot] != null) return false; + player.inventory.mainInventory[slot] = stack.copy(); + return true; + } + + private static boolean restoreToArmor(EntityPlayer player, ItemStack stack, int slot) { + if (slot < 0 || slot >= player.inventory.armorInventory.length) return false; + if (player.inventory.armorInventory[slot] != null) return false; + player.inventory.armorInventory[slot] = stack.copy(); + return true; + } + + private static boolean restoreToTConstruct(EntityPlayer player, ItemStack stack, int slot) { + try { + Class.forName("tconstruct.armor.player.TPlayerStats"); + } catch (ClassNotFoundException ignored) { + return false; + } + return TConstructRestoreHelper.restore(player, stack, slot); + } + + private static final class TConstructRestoreHelper { + + static boolean restore(EntityPlayer player, ItemStack stack, int slot) { + tconstruct.armor.player.TPlayerStats stats = tconstruct.armor.player.TPlayerStats.get(player); + if (stats == null) return false; + tconstruct.armor.player.ArmorExtended armor = stats.armor; + if (slot < 0 || slot >= armor.getSizeInventory()) return false; + if (armor.getStackInSlot(slot) != null) return false; + armor.setInventorySlotContents(slot, stack.copy()); + return true; + } + } + + private static boolean restoreToBaubles(EntityPlayer player, ItemStack stack, int slot) { + try { + Class.forName("baubles.api.IBauble"); + } catch (ClassNotFoundException ignored) { + return false; + } + return BaublesRestoreHelper.restore(player, stack, slot); + } + + private static final class BaublesRestoreHelper { + + static boolean restore(EntityPlayer player, ItemStack stack, int slot) { + IInventory inv = baubles.api.BaublesApi.getBaubles(player); + if (inv == null) return false; + if (slot < 0 || slot >= inv.getSizeInventory()) return false; + if (inv.getStackInSlot(slot) != null) return false; + inv.setInventorySlotContents(slot, stack.copy()); + return true; + } + } + + private static boolean restoreToAdventureBackpack(EntityPlayer player, ItemStack stack) { + try { + Class.forName("com.darkona.adventurebackpack.item.IBackWearableItem"); + } catch (ClassNotFoundException ignored) { + return false; + } + return AdventureBackpackRestoreHelper.restore(player, stack); + } + + private static final class AdventureBackpackRestoreHelper { + + static boolean restore(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof com.darkona.adventurebackpack.item.IBackWearableItem)) return false; + com.darkona.adventurebackpack.playerProperties.BackpackProperty prop = com.darkona.adventurebackpack.playerProperties.BackpackProperty + .get(player); + if (prop == null || prop.getWearable() != null) return false; + prop.setWearable(stack.copy()); + ((com.darkona.adventurebackpack.item.IBackWearableItem) stack.getItem()) + .onEquipped(player.worldObj, player, stack); + com.darkona.adventurebackpack.playerProperties.BackpackProperty.sync(player); + return true; + } + } + + private static boolean restoreToMcBackpack(EntityPlayer player, ItemStack stack) { + try { + Class.forName("de.eydamos.backpack.item.ItemBackpackBase"); + } catch (ClassNotFoundException ignored) { + return false; + } + return McBackpackRestoreHelper.restore(player, stack); + } + + private static final class McBackpackRestoreHelper { + + static boolean restore(EntityPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof de.eydamos.backpack.item.ItemBackpackBase)) return false; + de.eydamos.backpack.saves.PlayerSave save = new de.eydamos.backpack.saves.PlayerSave(player); + if (save.hasPersonalBackpack()) return false; + save.setPersonalBackpack(stack.copy()); + return true; + } + } + /** * Tries to equip the stack into the appropriate slot on the player. Returns null if equipped, or the original stack * if it could not be equipped. Order: vanilla armor → Baubles → Adventure Backpack → Minecraft Backpack. diff --git a/src/main/java/openblocks/common/GraveInventorySnapshot.java b/src/main/java/openblocks/common/GraveInventorySnapshot.java new file mode 100644 index 000000000..3c30de0ab --- /dev/null +++ b/src/main/java/openblocks/common/GraveInventorySnapshot.java @@ -0,0 +1,208 @@ +package openblocks.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; + +import openmods.Log; +import openmods.inventory.GenericInventory; + +public class GraveInventorySnapshot { + + public static final class OriginatedStack { + + public final GraveSlotOrigin origin; + public final ItemStack stack; + + public OriginatedStack(GraveSlotOrigin origin, ItemStack stack) { + this.origin = origin; + this.stack = stack; + } + } + + private final List entries = new ArrayList(); + + public GraveInventorySnapshot(EntityPlayer player) { + captureMain(player); + captureArmor(player); + captureTConstruct(player); + captureBaubles(player); + captureAdventureBackpack(player); + captureMcBackpack(player); + } + + private void captureMain(EntityPlayer player) { + for (int i = 0; i < player.inventory.mainInventory.length; i++) { + ItemStack stack = player.inventory.mainInventory[i]; + if (stack != null) + entries.add(new OriginatedStack(new GraveSlotOrigin(GraveSlotOrigin.INV_MAIN, i), stack)); + } + } + + private void captureArmor(EntityPlayer player) { + for (int i = 0; i < player.inventory.armorInventory.length; i++) { + ItemStack stack = player.inventory.armorInventory[i]; + if (stack != null) + entries.add(new OriginatedStack(new GraveSlotOrigin(GraveSlotOrigin.INV_ARMOR, i), stack)); + } + } + + private void captureTConstruct(EntityPlayer player) { + try { + Class.forName("tconstruct.armor.player.TPlayerStats"); + } catch (ClassNotFoundException ignored) { + return; + } + try { + TConstructCaptureHelper.capture(player, entries); + } catch (Exception e) { + Log.warn("GraveInventorySnapshot: failed to capture TConstruct slots: %s", e); + } + } + + private static final class TConstructCaptureHelper { + + static void capture(EntityPlayer player, List out) { + tconstruct.armor.player.TPlayerStats stats = tconstruct.armor.player.TPlayerStats.get(player); + if (stats == null) return; + tconstruct.armor.player.ArmorExtended armor = stats.armor; + for (int i = 0; i < armor.getSizeInventory(); i++) { + ItemStack stack = armor.getStackInSlot(i); + if (stack != null) + out.add(new OriginatedStack(new GraveSlotOrigin(GraveSlotOrigin.INV_TCONSTRUCT, i), stack)); + } + } + } + + private void captureBaubles(EntityPlayer player) { + try { + Class.forName("baubles.api.IBauble"); + } catch (ClassNotFoundException ignored) { + return; + } + try { + BaublesCaptureHelper.capture(player, entries); + } catch (Exception e) { + Log.warn("GraveInventorySnapshot: failed to capture Baubles slots: %s", e); + } + } + + private static final class BaublesCaptureHelper { + + static void capture(EntityPlayer player, List out) { + IInventory inv = baubles.api.BaublesApi.getBaubles(player); + if (inv == null) return; + for (int i = 0; i < inv.getSizeInventory(); i++) { + ItemStack stack = inv.getStackInSlot(i); + if (stack != null) + out.add(new OriginatedStack(new GraveSlotOrigin(GraveSlotOrigin.INV_BAUBLES, i), stack)); + } + } + } + + private void captureAdventureBackpack(EntityPlayer player) { + try { + Class.forName("com.darkona.adventurebackpack.playerProperties.BackpackProperty"); + } catch (ClassNotFoundException ignored) { + return; + } + try { + AdventureBackpackCaptureHelper.capture(player, entries); + } catch (Exception e) { + Log.warn("GraveInventorySnapshot: failed to capture AdventureBackpack slot: %s", e); + } + } + + private static final class AdventureBackpackCaptureHelper { + + static void capture(EntityPlayer player, List out) { + com.darkona.adventurebackpack.playerProperties.BackpackProperty prop = com.darkona.adventurebackpack.playerProperties.BackpackProperty + .get(player); + if (prop == null) return; + ItemStack stack = prop.getWearable(); + if (stack != null) + out.add(new OriginatedStack(new GraveSlotOrigin(GraveSlotOrigin.INV_ADVENTURE_BACKPACK, 0), stack)); + } + } + + private void captureMcBackpack(EntityPlayer player) { + try { + Class.forName("de.eydamos.backpack.saves.PlayerSave"); + } catch (ClassNotFoundException ignored) { + return; + } + try { + McBackpackCaptureHelper.capture(player, entries); + } catch (Exception e) { + Log.warn("GraveInventorySnapshot: failed to capture McBackpack slot: %s", e); + } + } + + private static final class McBackpackCaptureHelper { + + static void capture(EntityPlayer player, List out) { + de.eydamos.backpack.saves.PlayerSave save = new de.eydamos.backpack.saves.PlayerSave(player); + if (!save.hasPersonalBackpack()) return; + ItemStack stack = save.getPersonalBackpack(); + if (stack != null) + out.add(new OriginatedStack(new GraveSlotOrigin(GraveSlotOrigin.INV_MC_BACKPACK, 0), stack)); + } + } + + /** + * Builds grave inventory and slot origins from the snapshot. Only items that appear in graveLoot (matched by + * item/damage/NBT) are included. + * + * @param graveLoot items that will go into the grave (from PlayerDropsEvent, post-filtering) + * @param originsOut populated with grave-slot-index → GraveSlotOrigin + * @return the grave inventory + */ + public IInventory buildLoot(List graveLoot, Map originsOut) { + // Build a consumable pool of drops for matching + List pool = new ArrayList(graveLoot.size()); + for (EntityItem ei : graveLoot) { + ItemStack s = ei.getEntityItem(); + if (s != null) pool.add(s); + } + + List matched = new ArrayList(); + for (OriginatedStack entry : entries) { + int idx = findAndConsume(pool, entry.stack); + if (idx >= 0) matched.add(entry); + } + + // Items in graveLoot that had no snapshot match (e.g. spawned by other mods) + // are added without origin info. + List unmatched = new ArrayList(pool); + + GenericInventory inv = new GenericInventory("tmpplayer", false, matched.size() + unmatched.size()); + int graveSlot = 0; + for (OriginatedStack os : matched) { + inv.setInventorySlotContents(graveSlot, os.stack.copy()); + originsOut.put(graveSlot, os.origin); + graveSlot++; + } + for (ItemStack s : unmatched) { + inv.setInventorySlotContents(graveSlot++, s.copy()); + } + return inv; + } + + /** Finds first stack in pool matching item+damage+NBT, removes it and returns its index; or -1. */ + private static int findAndConsume(List pool, ItemStack target) { + for (int i = 0; i < pool.size(); i++) { + ItemStack candidate = pool.get(i); + if (ItemStack.areItemStackTagsEqual(candidate, target) && candidate.getItem() == target.getItem() + && candidate.getItemDamage() == target.getItemDamage()) { + pool.remove(i); + return i; + } + } + return -1; + } +} diff --git a/src/main/java/openblocks/common/GraveSlotOrigin.java b/src/main/java/openblocks/common/GraveSlotOrigin.java new file mode 100644 index 000000000..9a36ea704 --- /dev/null +++ b/src/main/java/openblocks/common/GraveSlotOrigin.java @@ -0,0 +1,32 @@ +package openblocks.common; + +import net.minecraft.nbt.NBTTagCompound; + +public class GraveSlotOrigin { + + public static final String INV_MAIN = "main"; + public static final String INV_ARMOR = "armor"; + public static final String INV_TCONSTRUCT = "tconstruct"; + public static final String INV_BAUBLES = "baubles"; + public static final String INV_ADVENTURE_BACKPACK = "adventurebackpack"; + public static final String INV_MC_BACKPACK = "mcbackpack"; + + public final String inventoryType; + public final int slot; + + public GraveSlotOrigin(String inventoryType, int slot) { + this.inventoryType = inventoryType; + this.slot = slot; + } + + public NBTTagCompound toNBT() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setString("inv", inventoryType); + tag.setInteger("slot", slot); + return tag; + } + + public static GraveSlotOrigin fromNBT(NBTTagCompound tag) { + return new GraveSlotOrigin(tag.getString("inv"), tag.getInteger("slot")); + } +} diff --git a/src/main/java/openblocks/common/PlayerDeathHandler.java b/src/main/java/openblocks/common/PlayerDeathHandler.java index 02e7c25c3..174f99a8e 100644 --- a/src/main/java/openblocks/common/PlayerDeathHandler.java +++ b/src/main/java/openblocks/common/PlayerDeathHandler.java @@ -4,8 +4,11 @@ import java.lang.ref.WeakReference; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.UUID; import net.minecraft.block.Block; import net.minecraft.entity.item.EntityItem; @@ -25,6 +28,7 @@ import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.player.PlayerDropsEvent; import org.apache.logging.log4j.Level; @@ -57,6 +61,8 @@ public class PlayerDeathHandler { + private static final Map pendingSnapshots = new HashMap(); + private static final Comparator SEARCH_COMPARATOR = new Comparator() { private int coordSum(Coord c) { @@ -153,7 +159,10 @@ private static class GraveCallable implements Runnable { private final WeakReference exPlayer; - public GraveCallable(World world, EntityPlayer exPlayer, List loot) { + private final GraveInventorySnapshot snapshot; + + public GraveCallable(World world, EntityPlayer exPlayer, List loot, + GraveInventorySnapshot snapshot) { this.posX = MathHelper.floor_double(exPlayer.posX); this.posY = MathHelper.floor_double(exPlayer.posY); this.posZ = MathHelper.floor_double(exPlayer.posZ); @@ -168,6 +177,7 @@ public GraveCallable(World world, EntityPlayer exPlayer, List loot) this.cause = new ChatComponentTranslation("openblocks.misc.grave_msg", deathCause, day); this.loot = ImmutableList.copyOf(loot); + this.snapshot = snapshot; } private static IChatComponent formatDate(World world) { @@ -202,7 +212,8 @@ private boolean tryPlaceGrave(World world, final int x, final int y, final int z TileEntityGrave grave = (TileEntityGrave) tile; - IInventory loot = getLoot(); + Map origins = new HashMap(); + IInventory loot = getLootWithOrigins(origins); if (Config.backupGraves) backupGrave(world, loot, new ExtrasFiller() { @@ -218,17 +229,25 @@ public void addExtras(NBTTagCompound meta) { grave.setUsername(gravestoneText); grave.setLoot(loot); + grave.setSlotOrigins(origins); grave.setDeathMessage(deathMessage); return true; } protected IInventory getLoot() { - IInventory loot = new GenericInventory("tmpplayer", false, this.loot.size()); + return getLootWithOrigins(null); + } + + protected IInventory getLootWithOrigins(Map originsOut) { + if (snapshot != null && originsOut != null) { + return snapshot.buildLoot(loot, originsOut); + } + IInventory inv = new GenericInventory("tmpplayer", false, this.loot.size()); for (EntityItem entityItem : this.loot) { ItemStack stack = entityItem.getEntityItem(); - if (stack != null) ItemDistribution.insertItemIntoInventory(loot, stack.copy()); + if (stack != null) ItemDistribution.insertItemIntoInventory(inv, stack.copy()); } - return loot; + return inv; } private boolean trySpawnGrave(EntityPlayer player, World world) { @@ -338,6 +357,16 @@ private static Level debugLevel() { return Config.debugGraves ? Level.INFO : Level.DEBUG; } + @SubscribeEvent(priority = EventPriority.HIGH) + public void onPlayerDeath(LivingDeathEvent event) { + if (!(event.entity instanceof EntityPlayer)) return; + EntityPlayer player = (EntityPlayer) event.entity; + if (player.worldObj.isRemote) return; + if (player instanceof FakePlayer) return; + UUID uuid = player.getGameProfile().getId(); + pendingSnapshots.put(uuid, new GraveInventorySnapshot(player)); + } + @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) public void onPlayerDrops(PlayerDropsEvent event) { World world = event.entityPlayer.worldObj; @@ -422,7 +451,8 @@ public void onPlayerDrops(PlayerDropsEvent event) { graveLoot.size(), drops.size()); - DelayedActionTickHandler.INSTANCE.addTickCallback(world, new GraveCallable(world, player, graveLoot)); + GraveInventorySnapshot snapshot = pendingSnapshots.remove(player.getGameProfile().getId()); + DelayedActionTickHandler.INSTANCE.addTickCallback(world, new GraveCallable(world, player, graveLoot, snapshot)); } // TODO: candidate for scripting diff --git a/src/main/java/openblocks/common/block/BlockGrave.java b/src/main/java/openblocks/common/block/BlockGrave.java index dbf194daa..ac80bc5b7 100644 --- a/src/main/java/openblocks/common/block/BlockGrave.java +++ b/src/main/java/openblocks/common/block/BlockGrave.java @@ -26,7 +26,7 @@ public BlockGrave() { setRotationMode(BlockRotationMode.FOUR_DIRECTIONS); setBlockBounds(0, 0, 0, 1f, 0.2f, 1f); setResistance(2000.0F); - setHardness(25.0F); + setHardness(5.0F); setRenderMode(RenderMode.TESR_ONLY); } @@ -92,7 +92,12 @@ public void onBlockClicked(World world, int x, int y, int z, EntityPlayer player if (tile instanceof TileEntityGrave) { TileEntityGrave grave = (TileEntityGrave) tile; if (!grave.isInventoryEmpty()) { - grave.autoEquipAll(player); + if (grave.isOwner(player)) { + grave.autoEquipAll(player); + } else { + player.addChatMessage( + new net.minecraft.util.ChatComponentTranslation("openblocks.misc.grave_not_owner")); + } } } } diff --git a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java index 45dfbf032..07c6a750e 100644 --- a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java +++ b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java @@ -1,6 +1,8 @@ package openblocks.common.tileentity; +import java.util.HashMap; import java.util.List; +import java.util.Map; import net.minecraft.block.Block; import net.minecraft.entity.EntityLiving; @@ -14,6 +16,7 @@ import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; @@ -27,6 +30,7 @@ import cpw.mods.fml.relauncher.SideOnly; import openblocks.Config; import openblocks.common.GraveAutoEquip; +import openblocks.common.GraveSlotOrigin; import openmods.api.IActivateAwareTile; import openmods.api.IAddAwareTile; import openmods.api.INeighbourAwareTile; @@ -42,11 +46,16 @@ public class TileEntityGrave extends SyncedTileEntity implements IPlacerAwareTile, IInventoryProvider, INeighbourAwareTile, IActivateAwareTile, IAddAwareTile { private static final String TAG_MESSAGE = "Message"; + private static final String TAG_ORIGINS = "SlotOrigins"; private SyncableString perishedUsername; public SyncableBoolean onSoil; + private SyncableBoolean inventoryEmpty; private IChatComponent deathMessage; + /** grave slot index → origin slot at death; may be empty if grave predates this feature */ + private final Map slotOrigins = new HashMap(); + private final GenericInventory inventory = registerInventoryCallback(new GenericInventory("grave", false, 1)); public TileEntityGrave() {} @@ -55,6 +64,7 @@ public TileEntityGrave() {} protected void createSyncedFields() { perishedUsername = new SyncableString(); onSoil = new SyncableBoolean(true); + inventoryEmpty = new SyncableBoolean(false); } @Override @@ -94,9 +104,15 @@ public void setUsername(String username) { this.perishedUsername.setValue(username); } + public void setSlotOrigins(Map origins) { + slotOrigins.clear(); + slotOrigins.putAll(origins); + } + public void setLoot(IInventory invent) { inventory.clearAndSetSlotCount(invent.getSizeInventory()); inventory.copyFrom(invent); + refreshEmptyFlag(); } public boolean isOnSoil() { @@ -126,6 +142,16 @@ public void writeToNBT(NBTTagCompound tag) { String serialized = IChatComponent.Serializer.func_150696_a(deathMessage); tag.setString(TAG_MESSAGE, serialized); } + + if (!slotOrigins.isEmpty()) { + NBTTagList list = new NBTTagList(); + for (Map.Entry entry : slotOrigins.entrySet()) { + NBTTagCompound entry_tag = entry.getValue().toNBT(); + entry_tag.setInteger("graveSlot", entry.getKey()); + list.appendTag(entry_tag); + } + tag.setTag(TAG_ORIGINS, list); + } } @Override @@ -133,6 +159,16 @@ public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); inventory.readFromNBT(tag); + slotOrigins.clear(); + if (tag.hasKey(TAG_ORIGINS)) { + NBTTagList list = tag.getTagList(TAG_ORIGINS, 10); // 10 = TAG_Compound + for (int i = 0; i < list.tagCount(); i++) { + NBTTagCompound entry_tag = list.getCompoundTagAt(i); + int graveSlot = entry_tag.getInteger("graveSlot"); + slotOrigins.put(graveSlot, GraveSlotOrigin.fromNBT(entry_tag)); + } + } + String serializedMsg = tag.getString(TAG_MESSAGE); if (!Strings.isNullOrEmpty(serializedMsg)) { @@ -167,6 +203,10 @@ public AxisAlignedBB getRenderBoundingBox() { return AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1); } + public boolean isOwner(EntityPlayer player) { + return player.getGameProfile().getName().equals(perishedUsername.getValue()); + } + @Override public boolean onBlockActivated(EntityPlayer player, int side, float hitX, float hitY, float hitZ) { if (player.worldObj.isRemote) return false; @@ -177,7 +217,11 @@ public boolean onBlockActivated(EntityPlayer player, int side, float hitX, float } if (held == null && player.isSneaking()) { - autoEquipAll(player); + if (isOwner(player)) { + autoEquipAll(player); + } else { + player.addChatMessage(new ChatComponentTranslation("openblocks.misc.grave_not_owner")); + } return true; } @@ -188,10 +232,17 @@ public boolean onBlockActivated(EntityPlayer player, int side, float hitX, float } public boolean isInventoryEmpty() { + return inventoryEmpty.get(); + } + + private void refreshEmptyFlag() { for (int i = 0; i < inventory.getSizeInventory(); i++) { - if (inventory.getStackInSlot(i) != null) return false; + if (inventory.getStackInSlot(i) != null) { + inventoryEmpty.set(false); + return; + } } - return true; + inventoryEmpty.set(true); } public void autoEquipAll(EntityPlayer player) { @@ -202,7 +253,9 @@ public void autoEquipAll(EntityPlayer player) { final ItemStack stack = inventory.getStackInSlot(i); if (stack == null) continue; ItemStack copy = stack.copy(); - ItemStack remainder = GraveAutoEquip.tryEquipOrDrop(player, copy); + GraveSlotOrigin origin = slotOrigins.get(i); + boolean restoredToOrigin = origin != null && GraveAutoEquip.tryRestoreToOrigin(player, copy, origin); + ItemStack remainder = restoredToOrigin ? null : GraveAutoEquip.tryEquipOrDrop(player, copy); if (remainder == null) { inventory.setInventorySlotContents(i, null); equipped++; @@ -215,6 +268,7 @@ public void autoEquipAll(EntityPlayer player) { leftover++; } } + refreshEmptyFlag(); if (equipped > 0 || toInventory > 0) { markDirty(); sync(); @@ -232,6 +286,9 @@ public void autoEquipAll(EntityPlayer player) { if (leftover > 0) { player.addChatMessage(new ChatComponentTranslation("openblocks.misc.grave_skipped", leftover)); } + if (leftover == 0 && (equipped > 0 || toInventory > 0)) { + worldObj.setBlockToAir(xCoord, yCoord, zCoord); + } } protected void robGrave(EntityPlayer player, ItemStack held) { @@ -253,6 +310,7 @@ protected void robGrave(EntityPlayer player, ItemStack held) { worldObj.playAuxSFXAtEntity(null, 2001, xCoord, yCoord, zCoord, Block.getIdFromBlock(Blocks.dirt)); if (worldObj.rand.nextDouble() < Config.graveSpecialAction) ohNoes(player); held.damageItem(2, player); + worldObj.setBlockToAir(xCoord, yCoord, zCoord); } } diff --git a/src/main/resources/assets/openblocks/lang/en_US.lang b/src/main/resources/assets/openblocks/lang/en_US.lang index 724638b82..abb634f23 100644 --- a/src/main/resources/assets/openblocks/lang/en_US.lang +++ b/src/main/resources/assets/openblocks/lang/en_US.lang @@ -102,6 +102,7 @@ openblocks.misc.grave_msg=%s (day: %.1f) openblocks.misc.grave_equipped=Equipped %d item(s) from the grave. openblocks.misc.grave_to_inventory=Moved %d item(s) to your inventory. openblocks.misc.grave_skipped=%d item(s) could not be retrieved (inventory full). +openblocks.misc.grave_not_owner=This is not your grave. openblocks.misc.cant_restore_player=Can't restore inventory for player %s openblocks.misc.cant_restore_inventory=Can't restore inventory diff --git a/src/main/resources/assets/openblocks/lang/ru_RU.lang b/src/main/resources/assets/openblocks/lang/ru_RU.lang index a8a7025c8..95b7e3e16 100644 --- a/src/main/resources/assets/openblocks/lang/ru_RU.lang +++ b/src/main/resources/assets/openblocks/lang/ru_RU.lang @@ -96,6 +96,10 @@ openblocks.misc.sleeping_bag_broken=Предмет недоступен из-з #openblocks.misc.inverted=Inverted ## NEEDS TRANSLATION ## openblocks.misc.grave_msg=%s (день: %.1f) +openblocks.misc.grave_equipped=Экипировано %d предмет(ов) из могилы. +openblocks.misc.grave_to_inventory=Перемещено %d предмет(ов) в инвентарь. +openblocks.misc.grave_skipped=%d предмет(ов) не удалось вернуть (инвентарь заполнен). +openblocks.misc.grave_not_owner=Это не ваша могила. openblocks.misc.cant_restore_player=Не удалось восстановить инвентарь для игрока %s openblocks.misc.cant_restore_inventory=Не удалось восстановить инвентарь From c818c8f8f0b3dd7d616cb764291afd4cc1b18f7a Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Sun, 10 May 2026 16:09:20 +0400 Subject: [PATCH 7/7] Fix inventoryEmpty flag not restored on chunk reload, fix snapshot leak on early returns --- src/main/java/openblocks/common/PlayerDeathHandler.java | 3 ++- .../java/openblocks/common/tileentity/TileEntityGrave.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/openblocks/common/PlayerDeathHandler.java b/src/main/java/openblocks/common/PlayerDeathHandler.java index 174f99a8e..c7d519a2c 100644 --- a/src/main/java/openblocks/common/PlayerDeathHandler.java +++ b/src/main/java/openblocks/common/PlayerDeathHandler.java @@ -371,6 +371,8 @@ public void onPlayerDeath(LivingDeathEvent event) { public void onPlayerDrops(PlayerDropsEvent event) { World world = event.entityPlayer.worldObj; if (world.isRemote) return; + // Remove snapshot eagerly so it is never leaked on early returns + final GraveInventorySnapshot snapshot = pendingSnapshots.remove(event.entityPlayer.getGameProfile().getId()); if (Config.debugGraves) dumpDebugInfo(event); @@ -451,7 +453,6 @@ public void onPlayerDrops(PlayerDropsEvent event) { graveLoot.size(), drops.size()); - GraveInventorySnapshot snapshot = pendingSnapshots.remove(player.getGameProfile().getId()); DelayedActionTickHandler.INSTANCE.addTickCallback(world, new GraveCallable(world, player, graveLoot, snapshot)); } diff --git a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java index 07c6a750e..fa80163aa 100644 --- a/src/main/java/openblocks/common/tileentity/TileEntityGrave.java +++ b/src/main/java/openblocks/common/tileentity/TileEntityGrave.java @@ -169,6 +169,8 @@ public void readFromNBT(NBTTagCompound tag) { } } + refreshEmptyFlag(); + String serializedMsg = tag.getString(TAG_MESSAGE); if (!Strings.isNullOrEmpty(serializedMsg)) {