From 7afc1956bb5e150f908cde1ad30889026ca63178 Mon Sep 17 00:00:00 2001 From: micle Date: Sun, 18 Jan 2026 00:00:36 +0100 Subject: [PATCH] Reworked durability configs to be dynamically set during inventory tick update whenever a config change is detected. Added unbreakable text to totem tooltip. Fixed server world crashing when reviving player due to closing level (try-with-resources). --- .../component/TotemConfigHashData.java | 46 +++++++++++ .../item/totem/DiamondTotemItem.java | 2 +- .../item/totem/IronTotemItem.java | 2 +- .../item/totem/NetheriteTotemItem.java | 2 +- .../item/totem/StrawTotemItem.java | 2 +- .../item/totem/TotemItem.java | 81 +++++++++---------- .../micle/totem_of_reviving/setup/Config.java | 14 +--- .../setup/ModDataComponents.java | 10 ++- .../totem_of_reviving/util/LangAsset.java | 1 + .../assets/totem_of_reviving/lang/en_us.json | 1 + 10 files changed, 105 insertions(+), 56 deletions(-) create mode 100644 src/main/java/dev/micle/totem_of_reviving/component/TotemConfigHashData.java diff --git a/src/main/java/dev/micle/totem_of_reviving/component/TotemConfigHashData.java b/src/main/java/dev/micle/totem_of_reviving/component/TotemConfigHashData.java new file mode 100644 index 0000000..354936e --- /dev/null +++ b/src/main/java/dev/micle/totem_of_reviving/component/TotemConfigHashData.java @@ -0,0 +1,46 @@ +package dev.micle.totem_of_reviving.component; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +import java.util.Objects; + +public class TotemConfigHashData { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> + instance.group( + Codec.INT.fieldOf("configHash").forGetter(TotemConfigHashData::getConfigHash) + ).apply(instance, TotemConfigHashData::new) + ); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, TotemConfigHashData::getConfigHash, + TotemConfigHashData::new + ); + + private final int configHash; + + public TotemConfigHashData(int configHash) { + this.configHash = configHash; + } + + public int getConfigHash() { + return this.configHash; + } + + @Override + public int hashCode() { + return Objects.hash(this.configHash); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else { + return obj instanceof TotemConfigHashData tchd + && this.configHash == tchd.configHash; + } + } +} diff --git a/src/main/java/dev/micle/totem_of_reviving/item/totem/DiamondTotemItem.java b/src/main/java/dev/micle/totem_of_reviving/item/totem/DiamondTotemItem.java index fb37201..2f2aa0f 100644 --- a/src/main/java/dev/micle/totem_of_reviving/item/totem/DiamondTotemItem.java +++ b/src/main/java/dev/micle/totem_of_reviving/item/totem/DiamondTotemItem.java @@ -7,7 +7,7 @@ import net.minecraft.world.item.Rarity; public class DiamondTotemItem extends TotemItem { public DiamondTotemItem() { - super(createProperties(Rarity.RARE, Config.Server.getDiamondTotemConfig().getDurability(), false)); + super(createProperties(Rarity.RARE, false)); } public static String getName() { diff --git a/src/main/java/dev/micle/totem_of_reviving/item/totem/IronTotemItem.java b/src/main/java/dev/micle/totem_of_reviving/item/totem/IronTotemItem.java index 6d83384..96f14b4 100644 --- a/src/main/java/dev/micle/totem_of_reviving/item/totem/IronTotemItem.java +++ b/src/main/java/dev/micle/totem_of_reviving/item/totem/IronTotemItem.java @@ -7,7 +7,7 @@ import net.minecraft.world.item.Rarity; public class IronTotemItem extends TotemItem { public IronTotemItem() { - super(createProperties(Rarity.COMMON, Config.Server.getIronTotemConfig().getDurability(), false)); + super(createProperties(Rarity.COMMON, false)); } public static String getName() { diff --git a/src/main/java/dev/micle/totem_of_reviving/item/totem/NetheriteTotemItem.java b/src/main/java/dev/micle/totem_of_reviving/item/totem/NetheriteTotemItem.java index 19760eb..a81e0f6 100644 --- a/src/main/java/dev/micle/totem_of_reviving/item/totem/NetheriteTotemItem.java +++ b/src/main/java/dev/micle/totem_of_reviving/item/totem/NetheriteTotemItem.java @@ -7,7 +7,7 @@ import net.minecraft.world.item.Rarity; public class NetheriteTotemItem extends TotemItem { public NetheriteTotemItem() { - super(createProperties(Rarity.EPIC, Config.Server.getNetheriteTotemConfig().getDurability(), true)); + super(createProperties(Rarity.EPIC, true)); } public static String getName() { diff --git a/src/main/java/dev/micle/totem_of_reviving/item/totem/StrawTotemItem.java b/src/main/java/dev/micle/totem_of_reviving/item/totem/StrawTotemItem.java index c587a87..52f790b 100644 --- a/src/main/java/dev/micle/totem_of_reviving/item/totem/StrawTotemItem.java +++ b/src/main/java/dev/micle/totem_of_reviving/item/totem/StrawTotemItem.java @@ -7,7 +7,7 @@ import net.minecraft.world.item.Rarity; public class StrawTotemItem extends TotemItem { public StrawTotemItem() { - super(createProperties(Rarity.UNCOMMON, Config.Server.getStrawTotemConfig().getDurability(), false)); + super(createProperties(Rarity.UNCOMMON, false)); } public static String getName() { diff --git a/src/main/java/dev/micle/totem_of_reviving/item/totem/TotemItem.java b/src/main/java/dev/micle/totem_of_reviving/item/totem/TotemItem.java index 20f77a1..191bf6b 100644 --- a/src/main/java/dev/micle/totem_of_reviving/item/totem/TotemItem.java +++ b/src/main/java/dev/micle/totem_of_reviving/item/totem/TotemItem.java @@ -1,6 +1,7 @@ package dev.micle.totem_of_reviving.item.totem; import com.mojang.blaze3d.platform.InputConstants; +import dev.micle.totem_of_reviving.component.TotemConfigHashData; import dev.micle.totem_of_reviving.component.TotemData; import dev.micle.totem_of_reviving.setup.Config; import dev.micle.totem_of_reviving.setup.ModDataComponents; @@ -8,6 +9,7 @@ import dev.micle.totem_of_reviving.util.ComponentHelper; import dev.micle.totem_of_reviving.util.LangAsset; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; +import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; @@ -16,17 +18,18 @@ import net.minecraft.server.players.PlayerList; import net.minecraft.stats.Stats; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.Unbreakable; import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; import javax.annotation.ParametersAreNonnullByDefault; -import java.io.IOException; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -36,8 +39,8 @@ public abstract class TotemItem extends Item { super(properties); } - public static Properties createProperties(Rarity rarity, int durability, boolean isFireResistant) { - Properties properties = new Properties().stacksTo(1).rarity(rarity).durability(durability); + public static Properties createProperties(Rarity rarity, boolean isFireResistant) { + Properties properties = new Properties().stacksTo(1).rarity(rarity); if (isFireResistant) properties = properties.fireResistant(); @@ -160,28 +163,18 @@ public abstract class TotemItem extends Item { ).withStyle(ChatFormatting.GRAY); } - try (ServerLevel targetLevel = target.serverLevel()) { - if (!targetLevel.equals(user.serverLevel()) && !config.getCanReviveAcrossDimensions()) { - return LangAsset.MESSAGE_PLAYER_IS_IN_ANOTHER_DIMENSION.getComponent( - Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.WHITE) - ).withStyle(ChatFormatting.GRAY); - } - } catch (IOException e) { - return LangAsset.MESSAGE_UNABLE_TO_GET_PLAYER_WORLD.getComponent( - Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.RED) - ).withStyle(ChatFormatting.DARK_RED); + if (!target.serverLevel().equals(user.serverLevel()) && !config.getCanReviveAcrossDimensions()) { + return LangAsset.MESSAGE_PLAYER_IS_IN_ANOTHER_DIMENSION.getComponent( + Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.WHITE) + ).withStyle(ChatFormatting.GRAY); } if (!totemItem.canAffordTarget(totemData)) { return LangAsset.MESSAGE_NOT_ENOUGH_CHARGE.getComponent().withStyle(ChatFormatting.WHITE); } - try (ServerLevel userLevel = user.serverLevel()) { - target.teleportTo(userLevel, user.position().x, user.position().y, user.position().z, user.getYRot(), user.getXRot()); - target.setGameMode(userLevel.getServer().getDefaultGameType()); - } catch (IOException e) { - return LangAsset.MESSAGE_UNABLE_TO_GET_YOUR_WORLD.getComponent().withStyle(ChatFormatting.RED); - } + target.teleportTo(user.serverLevel(), user.position().x, user.position().y, user.position().z, user.getYRot(), user.getXRot()); + target.setGameMode(user.serverLevel().getServer().getDefaultGameType()); totemData = new TotemData( totemData.getTargetIndex(), @@ -234,6 +227,9 @@ public abstract class TotemItem extends Item { if (getConfig().getCanReviveAcrossDimensions()) { tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_CAN_REVIVE_ACROSS_DIMENSIONS.getComponent().withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC)); } + if (!stack.isDamageableItem()) { + tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_IS_UNBREAKABLE.getComponent().withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC)); + } tooltipComponents.add(Component.empty()); @@ -256,6 +252,31 @@ public abstract class TotemItem extends Item { super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); } + @Override + @ParametersAreNonnullByDefault + public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) { + if (level.isClientSide) { + super.inventoryTick(stack, level, entity, slotId, isSelected); + return; + } + + TotemConfigHashData totemConfigHashData = stack.get(ModDataComponents.TOTEM_CONFIG_HASH_DATA); + if (totemConfigHashData == null || totemConfigHashData.getConfigHash() != getConfig().getConfigHash()) { + if (getConfig().getDurability() == 0) { + stack.set(DataComponents.UNBREAKABLE, new Unbreakable(false)); + stack.remove(DataComponents.MAX_DAMAGE); + stack.remove(DataComponents.DAMAGE); + } else { + stack.remove(DataComponents.UNBREAKABLE); + stack.set(DataComponents.MAX_DAMAGE, getConfig().getDurability()); + stack.set(DataComponents.DAMAGE, 0); + } + stack.set(ModDataComponents.TOTEM_CONFIG_HASH_DATA, new TotemConfigHashData(getConfig().getConfigHash())); + } + + super.inventoryTick(stack, level, entity, slotId, isSelected); + } + @Override @ParametersAreNonnullByDefault public @NotNull InteractionResultHolder use(Level level, Player player, InteractionHand usedHand) { @@ -283,26 +304,4 @@ public abstract class TotemItem extends Item { return super.use(level, player, usedHand); } - - @Override - @ParametersAreNonnullByDefault - public int getMaxDamage(ItemStack stack) { - return getConfig().getDurability(); - } - - @Override - @ParametersAreNonnullByDefault - public boolean isDamageable(ItemStack stack) { - return getConfig().getDurability() != 0; - } - - @Override - @ParametersAreNonnullByDefault - public int getDamage(ItemStack stack) { - if (isDamageable(stack) && super.getDamage(stack) >= getMaxDamage(stack)) { - stack.setCount(0); - return getMaxDamage(stack); - } - return (isDamageable(stack)) ? super.getDamage(stack) : 0; - } } diff --git a/src/main/java/dev/micle/totem_of_reviving/setup/Config.java b/src/main/java/dev/micle/totem_of_reviving/setup/Config.java index 996ff2c..e83849c 100644 --- a/src/main/java/dev/micle/totem_of_reviving/setup/Config.java +++ b/src/main/java/dev/micle/totem_of_reviving/setup/Config.java @@ -8,6 +8,8 @@ import net.neoforged.fml.ModContainer; import net.neoforged.fml.config.ModConfig; import net.neoforged.neoforge.common.ModConfigSpec; +import java.util.Objects; + public final class Config { public static void init(ModContainer modContainer) { modContainer.registerConfig(ModConfig.Type.SERVER, Server.SPEC); @@ -24,8 +26,6 @@ public final class Config { static { ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); - builder.comment("WHEN MAKING CHANGES IT IS RECOMMENDED TO NOT BE IN A WORLD.\n" + - "CHANGES WILL MOST LIKELY REQUIRE A RESTART FOR EVERYTHING TO WORK PROPERLY."); STRAW_TOTEM_CONFIG = new TotemConfig(builder, StrawTotemItem.getName(), -1, 3, 1, false, false, 1); IRON_TOTEM_CONFIG = new TotemConfig(builder, IronTotemItem.getName(), -1, 5, @@ -85,13 +85,7 @@ public final class Config { public double getChargeCostMultiplier() { return CHARGE_COST_MULTIPLIER.get(); } public boolean getCanReviveMoreExpensiveTargets() { return CAN_REVIVE_MORE_EXPENSIVE_TARGETS.get(); } public boolean getCanReviveAcrossDimensions() { return CAN_REVIVE_ACROSS_DIMENSIONS.get(); } - public int getDurability() { - try { - return DURABILITY.get(); - } catch (IllegalStateException e) { - return 10; - } - - } + public int getDurability() { return DURABILITY.get(); } + public int getConfigHash() { return Objects.hash(getDurability()); } } } diff --git a/src/main/java/dev/micle/totem_of_reviving/setup/ModDataComponents.java b/src/main/java/dev/micle/totem_of_reviving/setup/ModDataComponents.java index a2324f4..16e1bd8 100644 --- a/src/main/java/dev/micle/totem_of_reviving/setup/ModDataComponents.java +++ b/src/main/java/dev/micle/totem_of_reviving/setup/ModDataComponents.java @@ -1,16 +1,24 @@ package dev.micle.totem_of_reviving.setup; +import dev.micle.totem_of_reviving.component.TotemConfigHashData; import dev.micle.totem_of_reviving.component.TotemData; import net.minecraft.core.component.DataComponentType; import net.neoforged.neoforge.registries.DeferredHolder; public class ModDataComponents { public static final DeferredHolder, DataComponentType> TOTEM_DATA = Registration.DATA_COMPONENTS.registerComponentType( - "basic", + "totem_data", builder -> builder .persistent(TotemData.CODEC) .networkSynchronized(TotemData.STREAM_CODEC) ); + public static final DeferredHolder, DataComponentType> TOTEM_CONFIG_HASH_DATA = Registration.DATA_COMPONENTS.registerComponentType( + "totem_config_hash_data", + builder -> builder + .persistent(TotemConfigHashData.CODEC) + .networkSynchronized(TotemConfigHashData.STREAM_CODEC) + ); + public static void register() {} } diff --git a/src/main/java/dev/micle/totem_of_reviving/util/LangAsset.java b/src/main/java/dev/micle/totem_of_reviving/util/LangAsset.java index a57a355..25f4d0f 100644 --- a/src/main/java/dev/micle/totem_of_reviving/util/LangAsset.java +++ b/src/main/java/dev/micle/totem_of_reviving/util/LangAsset.java @@ -21,6 +21,7 @@ public enum LangAsset { TOOLTIP_TOTEM_DYNAMIC_COST("tooltip", "totem_dynamic_cost"), TOOLTIP_TOTEM_CAN_REVIVE_MORE_EXPENSIVE_TARGETS("tooltip", "totem_can_revive_more_expensive_targets"), TOOLTIP_TOTEM_CAN_REVIVE_ACROSS_DIMENSIONS("tooltip", "totem_can_revive_across_dimensions"), + TOOLTIP_TOTEM_IS_UNBREAKABLE("tooltip", "totem_is_unbreakable"), TOOLTIP_TOTEM_REVIVE_INSTRUCTION("tooltip", "totem_revive_instruction"), TOOLTIP_TOTEM_CHARGE_INSTRUCTION("tooltip", "totem_charge_instruction"), TOOLTIP_TOTEM_CHANGE_TARGET_INSTRUCTION("tooltip", "totem_change_target_instructions"), diff --git a/src/main/resources/assets/totem_of_reviving/lang/en_us.json b/src/main/resources/assets/totem_of_reviving/lang/en_us.json index 9fa798d..5464aab 100644 --- a/src/main/resources/assets/totem_of_reviving/lang/en_us.json +++ b/src/main/resources/assets/totem_of_reviving/lang/en_us.json @@ -14,6 +14,7 @@ "tooltip.totem_of_reviving.totem_dynamic_cost": "Dynamic cost (Multiplier: %s).", "tooltip.totem_of_reviving.totem_can_revive_more_expensive_targets": "Can revive more expensive targets.", "tooltip.totem_of_reviving.totem_can_revive_across_dimensions": "Can revive across dimensions.", + "tooltip.totem_of_reviving.totem_is_unbreakable": "Unbreakable.", "tooltip.totem_of_reviving.totem_revive_instruction": "When second hand is empty: revive target.", "tooltip.totem_of_reviving.totem_charge_instruction": "When second hand has a reviving charge: charge totem.", "tooltip.totem_of_reviving.totem_change_target_instructions": "Change target.",