From 9504b0c3fc987963b00ab4600095380aa74f1356 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sat, 6 Sep 2025 21:47:16 +0300 Subject: [PATCH 001/123] spoilage lols (idk maybe works) --- .../gtceu/api/item/ComponentItem.java | 19 ++- .../gtceu/api/item/ISpoilableItemStack.java | 12 ++ .../api/item/component/ISpoilableItem.java | 24 +++ .../trait/NotifiableItemStackHandler.java | 4 + .../gtceu/common/data/GTItems.java | 3 +- .../gtceu/common/item/SpoilableBehaviour.java | 27 +++ .../gtceu/core/mixins/ItemStackMixin.java | 106 ++++++++++++ src/main/resources/gtceu.mixins.json | 161 +++++++++--------- 8 files changed, 274 insertions(+), 82 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java create mode 100644 src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java create mode 100644 src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java create mode 100644 src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java index b45213e753e..a1c69d30a94 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java @@ -49,7 +49,8 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public class ComponentItem extends Item - implements HeldItemUIFactory.IHeldItemUIHolder, IItemRendererProvider, IComponentItem { + implements HeldItemUIFactory.IHeldItemUIHolder, IItemRendererProvider, IComponentItem, + ISpoilableItem { protected int burnTime = -1; @@ -443,4 +444,20 @@ public ItemStack getInfiniteChargedStack() { electricItem.setInfiniteCharge(true); return itemStack; } + + @Override + public long getSpoilTicks(ItemStack stack) { + for (IItemComponent component : getComponents()) { + if (component instanceof ISpoilableItem spoilable) return spoilable.getSpoilTicks(stack); + } + return -1; + } + + @Override + public ItemStack spoilResult(ItemStack stack) { + for (IItemComponent component : getComponents()) { + if (component instanceof ISpoilableItem spoilable) return spoilable.spoilResult(stack); + } + return ItemStack.EMPTY; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java new file mode 100644 index 00000000000..63ba7183c4b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java @@ -0,0 +1,12 @@ +package com.gregtechceu.gtceu.api.item; + +import net.minecraft.world.level.Level; + +import org.jetbrains.annotations.Nullable; + +public interface ISpoilableItemStack { + + long gtceu$getCreationTick(@Nullable Level level); + + void gtceu$updateFreshness(@Nullable Level level); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java new file mode 100644 index 00000000000..285cd00c578 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -0,0 +1,24 @@ +package com.gregtechceu.gtceu.api.item.component; + +import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +public interface ISpoilableItem extends IItemComponent { + + static void update(ItemStack stack, Level level) { + ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level); + } + + /** + * Should return the amount of ticks that this item can stay fresh + * The result of this method shouldn't be based on the freshness of the provided stack + */ + long getSpoilTicks(ItemStack stack); + + /** + * Should return the stack to replace the provided stack with when it spoils + */ + ItemStack spoilResult(ItemStack stack); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index d0d97264cc0..fd44b73d630 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.recipe.DummyCraftingContainer; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -300,12 +301,14 @@ public ItemStack getStackInSlot(int slot) { @Override public void setStackInSlot(int index, @NotNull ItemStack stack) { + ISpoilableItem.update(stack, getMachine().getLevel()); storage.setStackInSlot(index, stack); } @NotNull @Override public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { + ISpoilableItem.update(stack, getMachine().getLevel()); if (canCapInput()) { return storage.insertItem(slot, stack, simulate); } @@ -313,6 +316,7 @@ public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate } public ItemStack insertItemInternal(int slot, @NotNull ItemStack stack, boolean simulate) { + ISpoilableItem.update(stack, getMachine().getLevel()); return storage.insertItem(slot, stack, simulate); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java index 759188ce52b..129046e93d1 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java @@ -524,7 +524,8 @@ public static ItemEntry createFluidCell(Material mat, int capacit .lang("Medium Sodium Battery") .model(overrideModel(GTCEu.id("battery"), 8)) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) - .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV))) + .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV), + new SpoilableBehaviour(10 * 20, Items.DIRT))) .tag(CustomTags.MV_BATTERIES).register(); public static ItemEntry BATTERY_HV_SODIUM = REGISTRATE .item("hv_sodium_battery", ComponentItem::create) diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java new file mode 100644 index 00000000000..c1352353e13 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -0,0 +1,27 @@ +package com.gregtechceu.gtceu.common.item; + +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; + +public class SpoilableBehaviour implements ISpoilableItem { + + private final long ticks; + private final ItemLike spoilResult; + + public SpoilableBehaviour(long ticks, ItemLike spoilResult) { + this.ticks = ticks; + this.spoilResult = spoilResult; + } + + @Override + public long getSpoilTicks(ItemStack stack) { + return ticks; + } + + @Override + public ItemStack spoilResult(ItemStack stack) { + return new ItemStack(spoilResult, stack.getCount()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java new file mode 100644 index 00000000000..6975168c9df --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -0,0 +1,106 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; + +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraftforge.registries.ForgeRegistries; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import javax.annotation.Nullable; + +@Mixin(ItemStack.class) +public abstract class ItemStackMixin implements ISpoilableItemStack { + + @Unique + private boolean isUpdating = false; + + @Shadow + public abstract CompoundTag getOrCreateTagElement(String key); + + @Shadow + public abstract Item getItem(); + + @Shadow + @Deprecated + @Nullable + private Item item; + + @Shadow + @Nullable + private Holder.Reference delegate; + + @Shadow + private int count; + + @Shadow + @Nullable + private CompoundTag tag; + + @Shadow + public abstract boolean isEmpty(); + + @Unique + @Override + public void gtceu$updateFreshness(Level level) { + if (isUpdating) return; + isUpdating = true; + if (getItem() instanceof ISpoilableItem spoilable) { + if (spoilable.getSpoilTicks((ItemStack) (Object) this) < 0) { + isUpdating = false; + return; + } + CompoundTag tag = getOrCreateTagElement("GTCEu_spoilable"); + if (!tag.contains("creation_tick")) { + if (level == null) { + isUpdating = false; + return; + } + tag.putLong("creation_tick", level.getGameTime()); + } + long spoilTicks = spoilable.getSpoilTicks((ItemStack) (Object) this); + if (spoilTicks >= tag.getLong("creation_tick")) { + @SuppressWarnings("DataFlowIssue") + ItemStack newStack = spoilable.spoilResult((ItemStack) (Object) this); + item = newStack.getItem(); + delegate = ForgeRegistries.ITEMS.getDelegateOrThrow(item); + count = newStack.getCount(); + this.tag = newStack.getTag(); + } + } + isUpdating = false; + } + + @Inject(at = @At("HEAD"), + method = { "getItem", "getCount", "getTag", "getOrCreateTag", "getTagElement", "getOrCreateTagElement", + "getItemHolder" }) + private void injectedFreshnessUpdate(CallbackInfoReturnable cir) { + gtceu$updateFreshness(null); + } + + @Inject(at = @At("HEAD"), method = "use") + private void useFreshnessUpdate(Level level, Player player, InteractionHand usedHand, + CallbackInfoReturnable> cir) { + gtceu$updateFreshness(level); + } + + @Override + @Unique + public long gtceu$getCreationTick(@Nullable Level level) { + gtceu$updateFreshness(level); + return getOrCreateTagElement("GTCEu_spoilable").getLong("creation_tick"); + } +} diff --git a/src/main/resources/gtceu.mixins.json b/src/main/resources/gtceu.mixins.json index 831cc4b1a4c..db505100924 100644 --- a/src/main/resources/gtceu.mixins.json +++ b/src/main/resources/gtceu.mixins.json @@ -1,82 +1,83 @@ { - "required": true, - "minVersion": "0.8", - "refmap": "gtceu.refmap.json", - "package": "com.gregtechceu.gtceu.core.mixins", - "compatibilityLevel": "JAVA_17", - "plugin": "com.gregtechceu.gtceu.core.mixins.GTMixinPlugin", - "client": [ - "client.AbstractClientPlayerAccessor", - "client.BakedQuadMixin", - "client.BiomeColorsMixin", - "client.BlockModelMixin", - "client.ClientLevelAccessor", - "client.FaceBakeryMixin", - "client.GuiGraphicsAccessor", - "client.GuiGraphicsMixin", - "client.GuiHeartTypeMixin", - "client.HumanoidArmorLayerMixin", - "client.LevelRendererMixin", - "client.ModelManagerMixin", - "client.MultiPlayerGameModeMixin", - "client.PlayerInfoAccessor", - "client.VariantDeserializerMixin", - "ftbchunks.FTBChunksClientMixin", - "ftbchunks.LargeMapScreenMixin", - "ftbchunks.RegionMapPanelMixin", - "ldlib.ModularWrapperWidgetMixin", - "rei.FluidEntryRendererMixin", - "xaerominimap.HighlighterRegistryMixin", - "xaerominimap.MinimapFBORendererMixin", - "xaeroworldmap.GuiMapMixin", - "xaeroworldmap.MapElementRenderHandlerBuilderMixin", - "xaeroworldmap.WorldMapSessionMixin" - ], - "mixins": [ - "AnvilMenuMixin", - "BlockBehaviourAccessor", - "BlockMixin", - "BlockPropertiesAccessor", - "ChunkGeneratorMixin", - "EntityMixin", - "IngredientAccessor", - "InventoryMixin", - "ItemValueAccessor", - "LevelMixin", - "LootDataManagerMixin", - "LootPoolAccessor", - "OreConfigurationMixin", - "OreVeinifierMixin", - "PotionBrewingAccessor", - "PrimedTntAccessor", - "RecipeManagerMixin", - "RepairItemRecipeMixin", - "ServerChunkProviderMixin", - "ServerGamePacketListenerImplAccessor", - "ShapedRecipeAccessor", - "SidedRedstoneConnectivityMixin", - "TagLoaderMixin", - "TagManagerMixin", - "TagValueAccessor", - "client.ItemEntityMixin", - "emi.EmiRecipeFillerMixin", - "emi.FillRecipePacketMixin", - "emi.FluidEmiStackMixin", - "forge.ConfiguredModelBuilderAccessor", - "forge.ConfiguredModelListAccessor", - "forge.IntersectionIngredientAccessor", - "forge.PartialNBTIngredientAccessor", - "forge.StrictNBTIngredientAccessor", - "jei.FluidHelperMixin", - "ldlib.SyncUtilsMixin", - "registrate.AbstractRegistrateAccessor", - "registrate.RegistrateDataProviderAccessor", - "rei.InputSlotCrafterMixin", - "rei.RecipeFinderMixin", - "top.ConfigMixin" - ], - "injectors": { - "defaultRequire": 1, - "maxShiftBy": 5 - } + "required": true, + "minVersion": "0.8", + "refmap": "gtceu.refmap.json", + "package": "com.gregtechceu.gtceu.core.mixins", + "compatibilityLevel": "JAVA_17", + "plugin": "com.gregtechceu.gtceu.core.mixins.GTMixinPlugin", + "client": [ + "client.AbstractClientPlayerAccessor", + "client.BakedQuadMixin", + "client.BiomeColorsMixin", + "client.BlockModelMixin", + "client.ClientLevelAccessor", + "client.FaceBakeryMixin", + "client.GuiGraphicsAccessor", + "client.GuiGraphicsMixin", + "client.GuiHeartTypeMixin", + "client.HumanoidArmorLayerMixin", + "client.LevelRendererMixin", + "client.ModelManagerMixin", + "client.MultiPlayerGameModeMixin", + "client.PlayerInfoAccessor", + "client.VariantDeserializerMixin", + "ftbchunks.FTBChunksClientMixin", + "ftbchunks.LargeMapScreenMixin", + "ftbchunks.RegionMapPanelMixin", + "ldlib.ModularWrapperWidgetMixin", + "rei.FluidEntryRendererMixin", + "xaerominimap.HighlighterRegistryMixin", + "xaerominimap.MinimapFBORendererMixin", + "xaeroworldmap.GuiMapMixin", + "xaeroworldmap.MapElementRenderHandlerBuilderMixin", + "xaeroworldmap.WorldMapSessionMixin" + ], + "mixins": [ + "AnvilMenuMixin", + "BlockBehaviourAccessor", + "BlockMixin", + "BlockPropertiesAccessor", + "ChunkGeneratorMixin", + "EntityMixin", + "IngredientAccessor", + "InventoryMixin", + "ItemStackMixin", + "ItemValueAccessor", + "LevelMixin", + "LootDataManagerMixin", + "LootPoolAccessor", + "OreConfigurationMixin", + "OreVeinifierMixin", + "PotionBrewingAccessor", + "PrimedTntAccessor", + "RecipeManagerMixin", + "RepairItemRecipeMixin", + "ServerChunkProviderMixin", + "ServerGamePacketListenerImplAccessor", + "ShapedRecipeAccessor", + "SidedRedstoneConnectivityMixin", + "TagLoaderMixin", + "TagManagerMixin", + "TagValueAccessor", + "client.ItemEntityMixin", + "emi.EmiRecipeFillerMixin", + "emi.FillRecipePacketMixin", + "emi.FluidEmiStackMixin", + "forge.ConfiguredModelBuilderAccessor", + "forge.ConfiguredModelListAccessor", + "forge.IntersectionIngredientAccessor", + "forge.PartialNBTIngredientAccessor", + "forge.StrictNBTIngredientAccessor", + "jei.FluidHelperMixin", + "ldlib.SyncUtilsMixin", + "registrate.AbstractRegistrateAccessor", + "registrate.RegistrateDataProviderAccessor", + "rei.InputSlotCrafterMixin", + "rei.RecipeFinderMixin", + "top.ConfigMixin" + ], + "injectors": { + "defaultRequire": 1, + "maxShiftBy": 5 + } } From ec7e1af0fb7b9451482a114d365e1834e6d04813 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 08:39:03 +0300 Subject: [PATCH 002/123] yay it works --- .../gtceu/api/item/ISpoilableItemStack.java | 2 +- .../api/item/component/ISpoilableItem.java | 2 +- .../trait/NotifiableItemStackHandler.java | 4 -- .../transfer/item/CustomItemStackHandler.java | 3 + .../gtceu/core/mixins/ItemStackMixin.java | 63 +++++++++++-------- 5 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java index 63ba7183c4b..701d4d1d598 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java @@ -8,5 +8,5 @@ public interface ISpoilableItemStack { long gtceu$getCreationTick(@Nullable Level level); - void gtceu$updateFreshness(@Nullable Level level); + void gtceu$updateFreshness(@Nullable Level level, boolean createTag); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 285cd00c578..024a1f6bb82 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -8,7 +8,7 @@ public interface ISpoilableItem extends IItemComponent { static void update(ItemStack stack, Level level) { - ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level); + ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level, true); } /** diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index fd44b73d630..d0d97264cc0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -4,7 +4,6 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; -import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.recipe.DummyCraftingContainer; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -301,14 +300,12 @@ public ItemStack getStackInSlot(int slot) { @Override public void setStackInSlot(int index, @NotNull ItemStack stack) { - ISpoilableItem.update(stack, getMachine().getLevel()); storage.setStackInSlot(index, stack); } @NotNull @Override public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { - ISpoilableItem.update(stack, getMachine().getLevel()); if (canCapInput()) { return storage.insertItem(slot, stack, simulate); } @@ -316,7 +313,6 @@ public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate } public ItemStack insertItemInternal(int slot, @NotNull ItemStack stack, boolean simulate) { - ISpoilableItem.update(stack, getMachine().getLevel()); return storage.insertItem(slot, stack, simulate); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/transfer/item/CustomItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/transfer/item/CustomItemStackHandler.java index 2bf028384c8..c71d40c6719 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/transfer/item/CustomItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/transfer/item/CustomItemStackHandler.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.api.transfer.item; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; + import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; @@ -48,6 +50,7 @@ public boolean isItemValid(int slot, @NotNull ItemStack stack) { @Override public void onContentsChanged(int slot) { onContentsChanged.run(); + ISpoilableItem.update(getStackInSlot(slot), null); } public void clear() { diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 6975168c9df..d185640c2a0 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -5,19 +5,18 @@ import net.minecraft.core.Holder; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.entity.player.Player; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.Entity; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.server.ServerLifecycleHooks; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import javax.annotation.Nullable; @@ -26,7 +25,7 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { @Unique - private boolean isUpdating = false; + private boolean gtceu$isUpdating = false; @Shadow public abstract CompoundTag getOrCreateTagElement(String key); @@ -35,11 +34,15 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { public abstract Item getItem(); @Shadow + @Mutable + @Final @Deprecated @Nullable private Item item; @Shadow + @Mutable + @Final @Nullable private Holder.Reference delegate; @@ -53,26 +56,36 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { @Shadow public abstract boolean isEmpty(); + @Shadow + @Nullable + public abstract CompoundTag getTagElement(String key); + @Unique @Override - public void gtceu$updateFreshness(Level level) { - if (isUpdating) return; - isUpdating = true; + public void gtceu$updateFreshness(Level level, boolean createTag) { + if (gtceu$isUpdating) return; + gtceu$isUpdating = true; + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + if (level == null && server != null) level = server.overworld(); if (getItem() instanceof ISpoilableItem spoilable) { if (spoilable.getSpoilTicks((ItemStack) (Object) this) < 0) { - isUpdating = false; + gtceu$isUpdating = false; + return; + } + CompoundTag tag = createTag ? getOrCreateTagElement("GTCEu_spoilable") : getTagElement("GTCEu_spoilable"); + if (tag == null) { + gtceu$isUpdating = false; + return; + } + if (level == null) { + gtceu$isUpdating = false; return; } - CompoundTag tag = getOrCreateTagElement("GTCEu_spoilable"); if (!tag.contains("creation_tick")) { - if (level == null) { - isUpdating = false; - return; - } tag.putLong("creation_tick", level.getGameTime()); } long spoilTicks = spoilable.getSpoilTicks((ItemStack) (Object) this); - if (spoilTicks >= tag.getLong("creation_tick")) { + if (spoilTicks <= level.getGameTime() - tag.getLong("creation_tick")) { @SuppressWarnings("DataFlowIssue") ItemStack newStack = spoilable.spoilResult((ItemStack) (Object) this); item = newStack.getItem(); @@ -81,26 +94,26 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { this.tag = newStack.getTag(); } } - isUpdating = false; + gtceu$isUpdating = false; } @Inject(at = @At("HEAD"), method = { "getItem", "getCount", "getTag", "getOrCreateTag", "getTagElement", "getOrCreateTagElement", "getItemHolder" }) private void injectedFreshnessUpdate(CallbackInfoReturnable cir) { - gtceu$updateFreshness(null); + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + gtceu$updateFreshness(server == null ? null : server.overworld(), false); } - @Inject(at = @At("HEAD"), method = "use") - private void useFreshnessUpdate(Level level, Player player, InteractionHand usedHand, - CallbackInfoReturnable> cir) { - gtceu$updateFreshness(level); + @Inject(at = @At("HEAD"), method = "inventoryTick") + private void tickFreshness(Level level, Entity entity, int inventorySlot, boolean isCurrentItem, CallbackInfo ci) { + gtceu$updateFreshness(null, true); } @Override @Unique public long gtceu$getCreationTick(@Nullable Level level) { - gtceu$updateFreshness(level); - return getOrCreateTagElement("GTCEu_spoilable").getLong("creation_tick"); + if (getTagElement("GTCEu_spoilable") == null) return 0; + return getTagElement("GTCEu_spoilable").getLong("creation_tick"); } } From 7c0c12927dabc4f898b978bea2405ba2250e3992 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 13:21:22 +0300 Subject: [PATCH 003/123] tooltip and bar --- .../resources/assets/gtceu/lang/en_ud.json | 4 ++ .../resources/assets/gtceu/lang/en_us.json | 4 ++ .../gtceu/api/item/ISpoilableItemStack.java | 2 + .../gtceu/common/item/SpoilableBehaviour.java | 62 ++++++++++++++++++- .../gtceu/core/mixins/ItemStackMixin.java | 20 +++++- .../gtceu/data/lang/LangHandler.java | 4 ++ .../gtceu/utils/FormattingUtil.java | 10 +++ 7 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/generated/resources/assets/gtceu/lang/en_ud.json b/src/generated/resources/assets/gtceu/lang/en_ud.json index 857f60eb740..488e7e45144 100644 --- a/src/generated/resources/assets/gtceu/lang/en_ud.json +++ b/src/generated/resources/assets/gtceu/lang/en_ud.json @@ -3797,6 +3797,7 @@ "gtceu.tool_action.wrench.set_facing": "buıɔɐℲ ʇǝs oʇ ɥɔuǝɹM ǝs∩8§", "gtceu.tooltip.computer_monitor_config": "ɐʇɐp uoıʇɐɹnbıɟuoɔ ɹǝʌoɔ ɹoʇıuoɯ ɹǝʇndɯoɔ buıɹoʇS", "gtceu.tooltip.computer_monitor_data": "%s :ɐʇɐp buıɹoʇS", + "gtceu.tooltip.creation_tick": "%d ʞɔıʇ pןɹoʍɹǝʌo uo pǝʇɐǝɹƆ", "gtceu.tooltip.fluid_pipe_hold_shift": "oɟuI ʇuǝɯuıɐʇuoƆ pınןℲ ʍoɥs oʇ ⟘ℲIHS pןoHㄥ§", "gtceu.tooltip.hold_ctrl": "oɟuı ǝɹoɯ ɹoɟ Ꞁᴚ⟘Ɔ pןoHㄥ§", "gtceu.tooltip.hold_shift": "oɟuı ǝɹoɯ ɹoɟ ⟘ℲIHS pןoHㄥ§", @@ -3804,6 +3805,9 @@ "gtceu.tooltip.potion.each": "ɹ§buıuǝddɐɥ ɟo ǝɔuɐɥɔㄥ§ %s%% ɹ§ɐ ɥʇıʍ sʞɔıʇㄥ§ %s ɹ§ɹoɟㄥ§ %s %s", "gtceu.tooltip.potion.header": ":sʇɔǝɟɟǝ suıɐʇuoƆ9§", "gtceu.tooltip.proxy_bind": "%s %s %s ʇɐ ɹǝɟɟnᗺ uɹǝʇʇɐԀ ɐ oʇ buıpuıᗺɟ§", + "gtceu.tooltip.spoil_time_remaining": "%s :sןıods ןıʇun ǝɯı⟘", + "gtceu.tooltip.spoil_time_total": "%s :ǝɯıʇ ןıods ןɐʇo⟘", + "gtceu.tooltip.spoils_into": "%s :oʇuı sןıodS", "gtceu.tooltip.status.trinary.false": "ǝsןɐℲ", "gtceu.tooltip.status.trinary.true": "ǝnɹ⟘", "gtceu.tooltip.status.trinary.unknown": "uʍouʞu∩", diff --git a/src/generated/resources/assets/gtceu/lang/en_us.json b/src/generated/resources/assets/gtceu/lang/en_us.json index bfb70559730..dbb0eda8763 100644 --- a/src/generated/resources/assets/gtceu/lang/en_us.json +++ b/src/generated/resources/assets/gtceu/lang/en_us.json @@ -3797,6 +3797,7 @@ "gtceu.tool_action.wrench.set_facing": "§8Use Wrench to set Facing", "gtceu.tooltip.computer_monitor_config": "Storing computer monitor cover configuration data", "gtceu.tooltip.computer_monitor_data": "Storing data: %s", + "gtceu.tooltip.creation_tick": "Created on overworld tick %d", "gtceu.tooltip.fluid_pipe_hold_shift": "§7Hold SHIFT to show Fluid Containment Info", "gtceu.tooltip.hold_ctrl": "§7Hold CTRL for more info", "gtceu.tooltip.hold_shift": "§7Hold SHIFT for more info", @@ -3804,6 +3805,9 @@ "gtceu.tooltip.potion.each": "%s %s §7for§r %s §7ticks with a§r %s%% §7chance of happening§r", "gtceu.tooltip.potion.header": "§6Contains effects:", "gtceu.tooltip.proxy_bind": "§fBinding to a Pattern Buffer at %s %s %s", + "gtceu.tooltip.spoil_time_remaining": "Time until spoils: %s", + "gtceu.tooltip.spoil_time_total": "Total spoil time: %s", + "gtceu.tooltip.spoils_into": "Spoils into: %s", "gtceu.tooltip.status.trinary.false": "False", "gtceu.tooltip.status.trinary.true": "True", "gtceu.tooltip.status.trinary.unknown": "Unknown", diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java index 701d4d1d598..9f2418007dc 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java @@ -8,5 +8,7 @@ public interface ISpoilableItemStack { long gtceu$getCreationTick(@Nullable Level level); + long gtceu$getRemainingTicks(@Nullable Level level); + void gtceu$updateFreshness(@Nullable Level level, boolean createTag); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index c1352353e13..092604e1b9b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -1,11 +1,24 @@ package com.gregtechceu.gtceu.common.item; +import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.item.component.IAddInformation; +import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.Level; -public class SpoilableBehaviour implements ISpoilableItem { +import it.unimi.dsi.fastutil.ints.IntIntPair; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class SpoilableBehaviour implements ISpoilableItem, IAddInformation, IDurabilityBar { private final long ticks; private final ItemLike spoilResult; @@ -24,4 +37,51 @@ public long getSpoilTicks(ItemStack stack) { public ItemStack spoilResult(ItemStack stack) { return new ItemStack(spoilResult, stack.getCount()); } + + public long getTicksUntilSpoiled(ItemStack stack) { + return ((ISpoilableItemStack) (Object) stack).gtceu$getRemainingTicks(null); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, + TooltipFlag isAdvanced) { + ISpoilableItemStack spoilable = (ISpoilableItemStack) (Object) stack; + if (spoilable == null) return; + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.spoil_time_remaining", + Component.literal(FormattingUtil.formatTime(getTicksUntilSpoiled(stack))) + .withStyle(ChatFormatting.DARK_AQUA))); + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.spoils_into", + spoilResult(stack).getDisplayName())); + if (isAdvanced.isAdvanced()) { + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.spoil_time_total", + Component.literal(FormattingUtil.formatTime(getSpoilTicks(stack))) + .withStyle(ChatFormatting.GREEN))); + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.creation_tick", + spoilable.gtceu$getCreationTick(null))); + } + } + + @Override + public int getBarColor(ItemStack stack) { + return 0xFFFFFF; + } + + @Override + public boolean doDamagedStateColors(ItemStack itemStack) { + return false; + } + + @Override + public @Nullable IntIntPair getDurabilityColorsForDisplay(ItemStack itemStack) { + return IntIntPair.of(getBarColor(itemStack), getBarColor(itemStack)); + } + + @Override + public float getDurabilityForDisplay(ItemStack stack) { + return (float) getTicksUntilSpoiled(stack) / getSpoilTicks(stack); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index d185640c2a0..6a7b8fd86ca 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -36,7 +36,6 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { @Shadow @Mutable @Final - @Deprecated @Nullable private Item item; @@ -84,6 +83,7 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { if (!tag.contains("creation_tick")) { tag.putLong("creation_tick", level.getGameTime()); } + @SuppressWarnings("DataFlowIssue") long spoilTicks = spoilable.getSpoilTicks((ItemStack) (Object) this); if (spoilTicks <= level.getGameTime() - tag.getLong("creation_tick")) { @SuppressWarnings("DataFlowIssue") @@ -113,7 +113,21 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea @Override @Unique public long gtceu$getCreationTick(@Nullable Level level) { - if (getTagElement("GTCEu_spoilable") == null) return 0; - return getTagElement("GTCEu_spoilable").getLong("creation_tick"); + CompoundTag tag = getTagElement("GTCEu_spoilable"); + if (tag == null) return 0; + return tag.getLong("creation_tick"); + } + + @Override + @Unique + public long gtceu$getRemainingTicks(@Nullable Level level) { + gtceu$updateFreshness(level, false); + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + if (level == null && server != null) level = server.overworld(); + if (level != null && getTagElement("GTCEu_spoilable") != null && getItem() instanceof ISpoilableItem spoilable) + return spoilable.getSpoilTicks((ItemStack) (Object) this) - level.getGameTime() + + gtceu$getCreationTick(level); + if (getItem() instanceof ISpoilableItem spoilable) return spoilable.getSpoilTicks((ItemStack) (Object) this); + return 0; } } diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java index 5deaf965d1a..0b88aee96e3 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java @@ -1653,6 +1653,10 @@ public static void init(RegistrateLangProvider provider) { "Place the cover on the target block, right-click it with a data stick and put that data stick into a data access hatch in the multiblock.", "Then select the data access hatch as the target, and set the slot index of your data stick in the number field that appeared."); provider.add("gtceu.tooltip.player_bind", "Bound to player: %s"); + provider.add("gtceu.tooltip.spoil_time_remaining", "Time until spoils: %s"); + provider.add("gtceu.tooltip.spoil_time_total", "Total spoil time: %s"); + provider.add("gtceu.tooltip.spoils_into", "Spoils into: %s"); + provider.add("gtceu.tooltip.creation_tick", "Created on overworld tick %d"); } /** diff --git a/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java b/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java index 09edf06c3f3..b2bb1682d87 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java @@ -252,6 +252,16 @@ public static String formatBuckets(long mB) { return formatNumberReadable(mB, true, DECIMAL_FORMAT_2F, "B"); } + public static String formatTime(long ticks) { + long sec = ticks / 20; + String out = ""; + out = (sec % 60) + "s" + out; + if (sec > 60) out = ((sec / 60) % 60) + "m " + out; + if (sec > 3600) out = ((sec / 3600) % 24) + "h " + out; + if (sec > 24 * 3600) out = (sec / (3600 * 24)) + "d " + out; + return out; + } + @NotNull public static String formatNumber2Places(float number) { return DECIMAL_FORMAT_2F.format(number); From d0d8903452c485d60c86dcb59bd05991b214c1cf Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 14:20:29 +0300 Subject: [PATCH 004/123] bar color and different spoilage timer stacking --- .../api/item/component/ISpoilableItem.java | 12 +++++++++-- .../gtceu/common/item/SpoilableBehaviour.java | 3 ++- .../gtceu/core/mixins/ItemStackMixin.java | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 024a1f6bb82..5c9bb814054 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -5,14 +5,22 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import javax.annotation.Nullable; + public interface ISpoilableItem extends IItemComponent { - static void update(ItemStack stack, Level level) { + /** + * Initializes this ItemStack's spoilage timer if it wasn't initialized before. + * Should be called when it finishes crafting, for example. + * + * @param level may be {@code null}, maybe even should be lol + */ + static void update(ItemStack stack, @Nullable Level level) { ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level, true); } /** - * Should return the amount of ticks that this item can stay fresh + * Should return the amount of ticks that this item can stay fresh. * The result of this method shouldn't be based on the freshness of the provided stack */ long getSpoilTicks(ItemStack stack); diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 092604e1b9b..182be2cb4bf 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -8,6 +8,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; +import net.minecraft.util.FastColor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; @@ -67,7 +68,7 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List cir) { + CompoundTag tag1 = stack.getTagElement("GTCEu_spoilable"); + CompoundTag tag2 = other.getTagElement("GTCEu_spoilable"); + boolean isSameItem = ItemStack.isSameItem(stack, other) && stack.areCapsCompatible(other); + if (tag1 != null && tag2 != null) { + long tick1 = tag1.getLong("creation_tick"); + long tick2 = tag2.getLong("creation_tick"); + if (tick1 != tick2) { + long avg = (tick1 * stack.getCount() + tick2 * other.getCount()) / + (stack.getCount() + other.getCount()); + tag1.putLong("creation_tick", avg); + tag2.putLong("creation_tick", avg); + } + } + cir.setReturnValue(isSameItem && Objects.equals(tag1, tag2)); + cir.cancel(); + } } From 8b1a92793091c7885fbc75c092f4a785df397873 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 14:30:15 +0300 Subject: [PATCH 005/123] made mv sodium battery not spoil into dirt lols --- src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java index 129046e93d1..759188ce52b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java @@ -524,8 +524,7 @@ public static ItemEntry createFluidCell(Material mat, int capacit .lang("Medium Sodium Battery") .model(overrideModel(GTCEu.id("battery"), 8)) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) - .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV), - new SpoilableBehaviour(10 * 20, Items.DIRT))) + .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV))) .tag(CustomTags.MV_BATTERIES).register(); public static ItemEntry BATTERY_HV_SODIUM = REGISTRATE .item("hv_sodium_battery", ComponentItem::create) From 5971ae631c20d18af1667df8e2474bd4286ddced Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 15:01:15 +0300 Subject: [PATCH 006/123] idk --- .../com/gregtechceu/gtceu/api/item/ComponentItem.java | 9 +++++++++ .../gtceu/api/item/component/ISpoilableItem.java | 6 ++++++ .../gtceu/common/item/SpoilableBehaviour.java | 5 +++++ .../gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 3 ++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java index a1c69d30a94..824179db6d0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java @@ -460,4 +460,13 @@ public ItemStack spoilResult(ItemStack stack) { } return ItemStack.EMPTY; } + + @Override + public boolean shouldSpoil(ItemStack stack) { + boolean out = false; + for (IItemComponent component : getComponents()) { + if (component instanceof ISpoilableItem spoilable) out = out || spoilable.shouldSpoil(stack); + } + return out; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 5c9bb814054..72310538775 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -29,4 +29,10 @@ static void update(ItemStack stack, @Nullable Level level) { * Should return the stack to replace the provided stack with when it spoils */ ItemStack spoilResult(ItemStack stack); + + /** + * Note: returning {@code false} in this method won't stop the item from spoiling if the spoiling NBT has already + * been initialized + */ + boolean shouldSpoil(ItemStack stack); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 182be2cb4bf..57d1d73819f 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -39,6 +39,11 @@ public ItemStack spoilResult(ItemStack stack) { return new ItemStack(spoilResult, stack.getCount()); } + @Override + public boolean shouldSpoil(ItemStack stack) { + return true; + } + public long getTicksUntilSpoiled(ItemStack stack) { return ((ISpoilableItemStack) (Object) stack).gtceu$getRemainingTicks(null); } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index af0e016ccee..86323d41938 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -68,7 +68,8 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { gtceu$isUpdating = true; MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - if (getItem() instanceof ISpoilableItem spoilable) { + // noinspection ConstantValue + if (getItem() instanceof ISpoilableItem spoilable && spoilable.shouldSpoil((ItemStack) (Object) this)) { if (spoilable.getSpoilTicks((ItemStack) (Object) this) < 0) { gtceu$isUpdating = false; return; From 98e51a14d8306a1435ac1c146a8209f50d690a2b Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 16:14:31 +0300 Subject: [PATCH 007/123] im so dumb i wrote the wrong thing in the isSameItemSameTags function lols --- .../java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 86323d41938..3a0e538fc71 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -149,7 +149,7 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn tag2.putLong("creation_tick", avg); } } - cir.setReturnValue(isSameItem && Objects.equals(tag1, tag2)); + cir.setReturnValue(isSameItem && Objects.equals(stack.getTag(), other.getTag())); cir.cancel(); } } From 40dc1e22e7e48f9e116d48e7f2aef58b3aad818b Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 17:07:04 +0300 Subject: [PATCH 008/123] formatting --- src/main/resources/gtceu.mixins.json | 162 +++++++++++++-------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/src/main/resources/gtceu.mixins.json b/src/main/resources/gtceu.mixins.json index db505100924..8a525ca04a9 100644 --- a/src/main/resources/gtceu.mixins.json +++ b/src/main/resources/gtceu.mixins.json @@ -1,83 +1,83 @@ { - "required": true, - "minVersion": "0.8", - "refmap": "gtceu.refmap.json", - "package": "com.gregtechceu.gtceu.core.mixins", - "compatibilityLevel": "JAVA_17", - "plugin": "com.gregtechceu.gtceu.core.mixins.GTMixinPlugin", - "client": [ - "client.AbstractClientPlayerAccessor", - "client.BakedQuadMixin", - "client.BiomeColorsMixin", - "client.BlockModelMixin", - "client.ClientLevelAccessor", - "client.FaceBakeryMixin", - "client.GuiGraphicsAccessor", - "client.GuiGraphicsMixin", - "client.GuiHeartTypeMixin", - "client.HumanoidArmorLayerMixin", - "client.LevelRendererMixin", - "client.ModelManagerMixin", - "client.MultiPlayerGameModeMixin", - "client.PlayerInfoAccessor", - "client.VariantDeserializerMixin", - "ftbchunks.FTBChunksClientMixin", - "ftbchunks.LargeMapScreenMixin", - "ftbchunks.RegionMapPanelMixin", - "ldlib.ModularWrapperWidgetMixin", - "rei.FluidEntryRendererMixin", - "xaerominimap.HighlighterRegistryMixin", - "xaerominimap.MinimapFBORendererMixin", - "xaeroworldmap.GuiMapMixin", - "xaeroworldmap.MapElementRenderHandlerBuilderMixin", - "xaeroworldmap.WorldMapSessionMixin" - ], - "mixins": [ - "AnvilMenuMixin", - "BlockBehaviourAccessor", - "BlockMixin", - "BlockPropertiesAccessor", - "ChunkGeneratorMixin", - "EntityMixin", - "IngredientAccessor", - "InventoryMixin", - "ItemStackMixin", - "ItemValueAccessor", - "LevelMixin", - "LootDataManagerMixin", - "LootPoolAccessor", - "OreConfigurationMixin", - "OreVeinifierMixin", - "PotionBrewingAccessor", - "PrimedTntAccessor", - "RecipeManagerMixin", - "RepairItemRecipeMixin", - "ServerChunkProviderMixin", - "ServerGamePacketListenerImplAccessor", - "ShapedRecipeAccessor", - "SidedRedstoneConnectivityMixin", - "TagLoaderMixin", - "TagManagerMixin", - "TagValueAccessor", - "client.ItemEntityMixin", - "emi.EmiRecipeFillerMixin", - "emi.FillRecipePacketMixin", - "emi.FluidEmiStackMixin", - "forge.ConfiguredModelBuilderAccessor", - "forge.ConfiguredModelListAccessor", - "forge.IntersectionIngredientAccessor", - "forge.PartialNBTIngredientAccessor", - "forge.StrictNBTIngredientAccessor", - "jei.FluidHelperMixin", - "ldlib.SyncUtilsMixin", - "registrate.AbstractRegistrateAccessor", - "registrate.RegistrateDataProviderAccessor", - "rei.InputSlotCrafterMixin", - "rei.RecipeFinderMixin", - "top.ConfigMixin" - ], - "injectors": { - "defaultRequire": 1, - "maxShiftBy": 5 - } + "required": true, + "minVersion": "0.8", + "refmap": "gtceu.refmap.json", + "package": "com.gregtechceu.gtceu.core.mixins", + "compatibilityLevel": "JAVA_17", + "plugin": "com.gregtechceu.gtceu.core.mixins.GTMixinPlugin", + "client": [ + "client.AbstractClientPlayerAccessor", + "client.BakedQuadMixin", + "client.BiomeColorsMixin", + "client.BlockModelMixin", + "client.ClientLevelAccessor", + "client.FaceBakeryMixin", + "client.GuiGraphicsAccessor", + "client.GuiGraphicsMixin", + "client.GuiHeartTypeMixin", + "client.HumanoidArmorLayerMixin", + "client.LevelRendererMixin", + "client.ModelManagerMixin", + "client.MultiPlayerGameModeMixin", + "client.PlayerInfoAccessor", + "client.VariantDeserializerMixin", + "ftbchunks.FTBChunksClientMixin", + "ftbchunks.LargeMapScreenMixin", + "ftbchunks.RegionMapPanelMixin", + "ldlib.ModularWrapperWidgetMixin", + "rei.FluidEntryRendererMixin", + "xaerominimap.HighlighterRegistryMixin", + "xaerominimap.MinimapFBORendererMixin", + "xaeroworldmap.GuiMapMixin", + "xaeroworldmap.MapElementRenderHandlerBuilderMixin", + "xaeroworldmap.WorldMapSessionMixin" + ], + "mixins": [ + "AnvilMenuMixin", + "BlockBehaviourAccessor", + "BlockMixin", + "BlockPropertiesAccessor", + "ChunkGeneratorMixin", + "EntityMixin", + "IngredientAccessor", + "InventoryMixin", + "ItemStackMixin", + "ItemValueAccessor", + "LevelMixin", + "LootDataManagerMixin", + "LootPoolAccessor", + "OreConfigurationMixin", + "OreVeinifierMixin", + "PotionBrewingAccessor", + "PrimedTntAccessor", + "RecipeManagerMixin", + "RepairItemRecipeMixin", + "ServerChunkProviderMixin", + "ServerGamePacketListenerImplAccessor", + "ShapedRecipeAccessor", + "SidedRedstoneConnectivityMixin", + "TagLoaderMixin", + "TagManagerMixin", + "TagValueAccessor", + "client.ItemEntityMixin", + "emi.EmiRecipeFillerMixin", + "emi.FillRecipePacketMixin", + "emi.FluidEmiStackMixin", + "forge.ConfiguredModelBuilderAccessor", + "forge.ConfiguredModelListAccessor", + "forge.IntersectionIngredientAccessor", + "forge.PartialNBTIngredientAccessor", + "forge.StrictNBTIngredientAccessor", + "jei.FluidHelperMixin", + "ldlib.SyncUtilsMixin", + "registrate.AbstractRegistrateAccessor", + "registrate.RegistrateDataProviderAccessor", + "rei.InputSlotCrafterMixin", + "rei.RecipeFinderMixin", + "top.ConfigMixin" + ], + "injectors": { + "defaultRequire": 1, + "maxShiftBy": 5 + } } From 7b3b983044b03668d9cffeb45d91946b80727084 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 17:32:26 +0300 Subject: [PATCH 009/123] additional check before averaging --- .../com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 3a0e538fc71..9501de051b0 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -139,7 +139,12 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn CompoundTag tag1 = stack.getTagElement("GTCEu_spoilable"); CompoundTag tag2 = other.getTagElement("GTCEu_spoilable"); boolean isSameItem = ItemStack.isSameItem(stack, other) && stack.areCapsCompatible(other); - if (tag1 != null && tag2 != null) { + CompoundTag modifiedTag1 = stack.getTag() == null ? null : stack.getTag().copy(); + CompoundTag modifiedTag2 = other.getTag() == null ? null : other.getTag().copy(); + if (modifiedTag1 != null) modifiedTag1.remove("GTCEu_spoilable"); + if (modifiedTag2 != null) modifiedTag2.remove("GTCEu_spoilable"); + isSameItem = isSameItem && Objects.equals(modifiedTag1, modifiedTag2); + if (isSameItem && tag1 != null && tag2 != null) { long tick1 = tag1.getLong("creation_tick"); long tick2 = tag2.getLong("creation_tick"); if (tick1 != tick2) { From a6f8257a180dbbf49cb8c01ff2e823edcdf0662f Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 19:32:20 +0300 Subject: [PATCH 010/123] keep spoiling progress between recipe inputs and outputs --- .../gtceu/api/item/ISpoilableItemStack.java | 6 ++++++ .../api/item/component/ISpoilableItem.java | 8 ++++++++ .../trait/NotifiableItemStackHandler.java | 11 +++++++++++ .../gtceu/api/recipe/GTRecipe.java | 2 ++ .../gtceu/common/item/SpoilableBehaviour.java | 4 ---- .../gtceu/core/mixins/ItemStackMixin.java | 19 +++++++++++++++++++ 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java index 9f2418007dc..6a0a4777c51 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java @@ -3,12 +3,18 @@ import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Unique; public interface ISpoilableItemStack { long gtceu$getCreationTick(@Nullable Level level); + void gtceu$setCreationTick(@Nullable Level level, long value); + long gtceu$getRemainingTicks(@Nullable Level level); void gtceu$updateFreshness(@Nullable Level level, boolean createTag); + + @Unique + void gtceu$setRemainingTicks(@javax.annotation.Nullable Level level, long value); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 72310538775..360757552a8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -25,6 +25,14 @@ static void update(ItemStack stack, @Nullable Level level) { */ long getSpoilTicks(ItemStack stack); + default long getTicksUntilSpoiled(ItemStack stack) { + return ((ISpoilableItemStack) (Object) stack).gtceu$getRemainingTicks(null); + } + + default void setTicksUntilSpoiled(ItemStack stack, long value) { + ((ISpoilableItemStack) (Object) stack).gtceu$setRemainingTicks(null, value); + } + /** * Should return the stack to replace the provided stack with when it spoils */ diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index d0d97264cc0..20d0e9642d4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.recipe.DummyCraftingContainer; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -169,11 +170,21 @@ public static List handleRecipe(IO io, GTRecipe recipe, List 0 && output.getItem() instanceof ISpoilableItem item && + item.shouldSpoil(output)) { + double spoilProgress = 1 - recipe.spoilProgress / recipe.spoilableIngredientsAmount; + item.setTicksUntilSpoiled(output, (long) (spoilProgress * item.getSpoilTicks(output))); + } // Only try this slot if not visited or if visited with the same type of item if (visited[slot] == null || ItemStack.isSameItemSameTags(visited[slot], output)) { if (count < output.getMaxStackSize() && count < storage.getSlotLimit(slot)) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java index ce79fbe0edd..f7be07d2a47 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java @@ -53,6 +53,8 @@ public class GTRecipe implements net.minecraft.world.item.crafting.Recipe tooltipComponents, TooltipFlag isAdvanced) { diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 9501de051b0..d1c50ad3813 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -121,6 +121,14 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea return tag.getLong("creation_tick"); } + @Override + @Unique + public void gtceu$setCreationTick(@Nullable Level level, long value) { + CompoundTag tag = getTagElement("GTCEu_spoilable"); + if (tag == null) return; + tag.putLong("creation_tick", value); + } + @Override @Unique public long gtceu$getRemainingTicks(@Nullable Level level) { @@ -134,6 +142,17 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea return 0; } + @Unique + @Override + public void gtceu$setRemainingTicks(@Nullable Level level, long value) { + gtceu$updateFreshness(level, false); + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + if (level == null && server != null) level = server.overworld(); + if (level != null && getTagElement("GTCEu_spoilable") != null && getItem() instanceof ISpoilableItem spoilable) + gtceu$setCreationTick(level, + level.getGameTime() - spoilable.getSpoilTicks((ItemStack) (Object) this) + value); + } + @Inject(at = @At("HEAD"), method = "isSameItemSameTags", cancellable = true) private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackInfoReturnable cir) { CompoundTag tag1 = stack.getTagElement("GTCEu_spoilable"); From 004a07cf5ba042c6c11b067e6844e645ba1c6f3e Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 19:43:59 +0300 Subject: [PATCH 011/123] added an option to make a recipe output a fully fresh product regardless of inputs --- .../machine/trait/NotifiableItemStackHandler.java | 3 ++- .../com/gregtechceu/gtceu/api/recipe/GTRecipe.java | 12 ++++++++---- .../gtceu/api/recipe/GTRecipeSerializer.java | 14 +++++++++----- .../api/recipe/modifier/ModifierFunction.java | 2 +- .../electric/DistillationTowerMachine.java | 2 +- .../gtceu/data/recipe/builder/GTRecipeBuilder.java | 4 +++- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index 20d0e9642d4..92b48b7b55b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -180,7 +180,8 @@ public static List handleRecipe(IO io, GTRecipe recipe, List 0 && output.getItem() instanceof ISpoilableItem item && + if (recipe.transferSpoilingProgress && recipe.spoilableIngredientsAmount > 0 && + output.getItem() instanceof ISpoilableItem item && item.shouldSpoil(output)) { double spoilProgress = 1 - recipe.spoilProgress / recipe.spoilableIngredientsAmount; item.setTicksUntilSpoiled(output, (long) (spoilProgress * item.getSpoilTicks(output))); diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java index f7be07d2a47..266e97916f8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java @@ -44,6 +44,7 @@ public class GTRecipe implements net.minecraft.world.item.crafting.Recipe, ChanceLogic> tickOutputChanceLogics; public final List conditions; + public final boolean transferSpoilingProgress; // for KubeJS. actual type is List. // Must be List to not cause crashes without KubeJS. public final List ingredientActions; @@ -75,10 +76,11 @@ public GTRecipe(GTRecipeType recipeType, List ingredientActions, @NotNull CompoundTag data, int duration, - @NotNull GTRecipeCategory recipeCategory) { + @NotNull GTRecipeCategory recipeCategory, + boolean keepSpoilingProgress) { this(recipeType, null, inputs, outputs, tickInputs, tickOutputs, inputChanceLogics, outputChanceLogics, tickInputChanceLogics, tickOutputChanceLogics, - conditions, ingredientActions, data, duration, recipeCategory); + conditions, ingredientActions, data, duration, recipeCategory, keepSpoilingProgress); } public GTRecipe(GTRecipeType recipeType, @@ -95,7 +97,8 @@ public GTRecipe(GTRecipeType recipeType, List ingredientActions, @NotNull CompoundTag data, int duration, - @NotNull GTRecipeCategory recipeCategory) { + @NotNull GTRecipeCategory recipeCategory, + boolean keepSpoilingProgress) { this.recipeType = recipeType; this.id = id; @@ -114,6 +117,7 @@ public GTRecipe(GTRecipeType recipeType, this.data = data; this.duration = duration; this.recipeCategory = (recipeCategory != GTRecipeCategory.DEFAULT) ? recipeCategory : recipeType.getCategory(); + this.transferSpoilingProgress = keepSpoilingProgress; } public GTRecipe copy() { @@ -131,7 +135,7 @@ public GTRecipe copy(ContentModifier modifier, boolean modifyDuration) { new HashMap<>(inputChanceLogics), new HashMap<>(outputChanceLogics), new HashMap<>(tickInputChanceLogics), new HashMap<>(tickOutputChanceLogics), new ArrayList<>(conditions), - new ArrayList<>(ingredientActions), data, duration, recipeCategory); + new ArrayList<>(ingredientActions), data, duration, recipeCategory, transferSpoilingProgress); if (modifyDuration) { copied.duration = modifier.apply(this.duration); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeSerializer.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeSerializer.java index 457bfd0c435..516ed8646e1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeSerializer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeSerializer.java @@ -139,10 +139,12 @@ public GTRecipe fromNetwork(@NotNull ResourceLocation id, @NotNull FriendlyByteB GTRecipeType type = (GTRecipeType) BuiltInRegistries.RECIPE_TYPE.get(recipeType); GTRecipeCategory category = GTRegistries.RECIPE_CATEGORIES.get(categoryLoc); + boolean keepSpoilingProgress = buf.readBoolean(); + GTRecipe recipe = new GTRecipe(type, id, inputs, outputs, tickInputs, tickOutputs, inputChanceLogics, outputChanceLogics, tickInputChanceLogics, tickOutputChanceLogics, - conditions, ingredientActions, data, duration, category); + conditions, ingredientActions, data, duration, category, keepSpoilingProgress); recipe.recipeCategory.addRecipe(recipe); @@ -208,14 +210,15 @@ private static Codec makeCodec(boolean isKubeLoaded) { RecipeCondition.CODEC.listOf().optionalFieldOf("recipeConditions", List.of()).forGetter(val -> val.conditions), CompoundTag.CODEC.optionalFieldOf("data", new CompoundTag()).forGetter(val -> val.data), ExtraCodecs.NON_NEGATIVE_INT.fieldOf("duration").forGetter(val -> val.duration), - GTRegistries.RECIPE_CATEGORIES.codec().optionalFieldOf("category", GTRecipeCategory.DEFAULT).forGetter(val -> val.recipeCategory)) + GTRegistries.RECIPE_CATEGORIES.codec().optionalFieldOf("category", GTRecipeCategory.DEFAULT).forGetter(val -> val.recipeCategory), + Codec.BOOL.optionalFieldOf("keepSpoilingProgress", true).forGetter(val -> val.transferSpoilingProgress)) .apply(instance, (type, inputs, outputs, tickInputs, tickOutputs, inputChanceLogics, outputChanceLogics, tickInputChanceLogics, tickOutputChanceLogics, - conditions, data, duration, recipeCategory) -> + conditions, data, duration, recipeCategory, keepSpoilingProgress) -> new GTRecipe(type, inputs, outputs, tickInputs, tickOutputs, inputChanceLogics, outputChanceLogics, tickInputChanceLogics, tickOutputChanceLogics, - conditions, List.of(), data, duration, recipeCategory))); + conditions, List.of(), data, duration, recipeCategory, keepSpoilingProgress))); } else { return RecordCodecBuilder.create(instance -> instance.group( GTRegistries.RECIPE_TYPES.codec().fieldOf("type").forGetter(val -> val.recipeType), @@ -235,7 +238,8 @@ private static Codec makeCodec(boolean isKubeLoaded) { KJSCallWrapper.INGREDIENT_ACTION_CODEC.optionalFieldOf("kubejs:actions", List.of()).forGetter(val -> (List) val.ingredientActions), CompoundTag.CODEC.optionalFieldOf("data", new CompoundTag()).forGetter(val -> val.data), ExtraCodecs.NON_NEGATIVE_INT.fieldOf("duration").forGetter(val -> val.duration), - GTRegistries.RECIPE_CATEGORIES.codec().optionalFieldOf("category", GTRecipeCategory.DEFAULT).forGetter(val -> val.recipeCategory)) + GTRegistries.RECIPE_CATEGORIES.codec().optionalFieldOf("category", GTRecipeCategory.DEFAULT).forGetter(val -> val.recipeCategory), + Codec.BOOL.optionalFieldOf("keepSpoilingProgress", true).forGetter(val -> val.transferSpoilingProgress)) .apply(instance, GTRecipe::new)); } // spotless:on diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java index 7400f8727be..5f68f5814bf 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java @@ -158,7 +158,7 @@ public ModifierFunction build() { new HashMap<>(recipe.inputChanceLogics), new HashMap<>(recipe.outputChanceLogics), new HashMap<>(recipe.tickInputChanceLogics), new HashMap<>(recipe.tickOutputChanceLogics), newConditions, new ArrayList<>(recipe.ingredientActions), - recipe.data, recipe.duration, recipe.recipeCategory); + recipe.data, recipe.duration, recipe.recipeCategory, recipe.transferSpoilingProgress); copied.parallels = recipe.parallels * parallels; copied.ocLevel = recipe.ocLevel + addOCs; copied.batchParallels = recipe.batchParallels * batchParallels; diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/DistillationTowerMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/DistillationTowerMachine.java index b2da6931fcd..b31f998f933 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/DistillationTowerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/DistillationTowerMachine.java @@ -166,7 +166,7 @@ private static GTRecipe modifyOutputs(GTRecipe recipe, ContentModifier cm) { recipe.outputChanceLogics, recipe.tickInputChanceLogics, recipe.tickOutputChanceLogics, recipe.conditions, recipe.ingredientActions, - recipe.data, recipe.duration, recipe.recipeCategory); + recipe.data, recipe.duration, recipe.recipeCategory, recipe.transferSpoilingProgress); } public static class DistillationTowerLogic extends RecipeLogic { diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java index 78229097f23..34599fd11e4 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java @@ -104,6 +104,8 @@ public class GTRecipeBuilder { private boolean itemMaterialInfo = false; private boolean fluidMaterialInfo = false; private boolean removePreviousMatInfo = false; + @Setter + public boolean keepSpoilingProgress = true; public GTRecipeCategory recipeCategory; @Setter public @Nullable BiConsumer> onSave; @@ -1589,7 +1591,7 @@ public GTRecipe buildRawRecipe() { return new GTRecipe(recipeType, id.withPrefix(recipeType.registryName.getPath() + "/"), input, output, tickInput, tickOutput, inputChanceLogic, outputChanceLogic, tickInputChanceLogic, tickOutputChanceLogic, - conditions, List.of(), data, duration, recipeCategory); + conditions, List.of(), data, duration, recipeCategory, keepSpoilingProgress); } protected void warnTooManyIngredients(RecipeCapability capability, From ad443b0b1d41000a8a655d5b49457f72db5b2672 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 20:01:40 +0300 Subject: [PATCH 012/123] fixes --- .../gtceu/api/machine/trait/NotifiableItemStackHandler.java | 3 ++- src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java | 3 ++- .../com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index 92b48b7b55b..0b22b2882a3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -180,10 +180,11 @@ public static List handleRecipe(IO io, GTRecipe recipe, List 0 && output.getItem() instanceof ISpoilableItem item && item.shouldSpoil(output)) { - double spoilProgress = 1 - recipe.spoilProgress / recipe.spoilableIngredientsAmount; + double spoilProgress = recipe.spoilProgress / recipe.spoilableIngredientsAmount; item.setTicksUntilSpoiled(output, (long) (spoilProgress * item.getSpoilTicks(output))); } // Only try this slot if not visited or if visited with the same type of item diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java index 759188ce52b..bc923a03895 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java @@ -524,7 +524,8 @@ public static ItemEntry createFluidCell(Material mat, int capacit .lang("Medium Sodium Battery") .model(overrideModel(GTCEu.id("battery"), 8)) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) - .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV))) + .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV), + new SpoilableBehaviour(400, Items.DIRT))) .tag(CustomTags.MV_BATTERIES).register(); public static ItemEntry BATTERY_HV_SODIUM = REGISTRATE .item("hv_sodium_battery", ComponentItem::create) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java index 51e12270cea..1d613d14c38 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java @@ -230,7 +230,7 @@ private static void standardBatteries(Consumer provider) { EXTRACTOR_RECIPES.recipeBuilder("unpackage_mv_lithium_battery").inputItems(BATTERY_MV_LITHIUM) .outputItems(BATTERY_HULL_MV).save(provider); EXTRACTOR_RECIPES.recipeBuilder("unpackage_mv_sodium_battery").inputItems(BATTERY_MV_SODIUM) - .outputItems(BATTERY_HULL_MV).save(provider); + .outputItems(BATTERY_MV_SODIUM).save(provider); EXTRACTOR_RECIPES.recipeBuilder("unpackage_hv_cadmium_battery").inputItems(BATTERY_HV_CADMIUM) .outputItems(BATTERY_HULL_HV).save(provider); From 408cb2b366b8a094b42fac2ce1c23eecf31e9ea4 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 20:07:01 +0300 Subject: [PATCH 013/123] oops left mv battery spoilable --- src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java | 3 +-- .../com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java index bc923a03895..759188ce52b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java @@ -524,8 +524,7 @@ public static ItemEntry createFluidCell(Material mat, int capacit .lang("Medium Sodium Battery") .model(overrideModel(GTCEu.id("battery"), 8)) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) - .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV), - new SpoilableBehaviour(400, Items.DIRT))) + .onRegister(attach(ElectricStats.createRechargeableBattery(360000, GTValues.MV))) .tag(CustomTags.MV_BATTERIES).register(); public static ItemEntry BATTERY_HV_SODIUM = REGISTRATE .item("hv_sodium_battery", ComponentItem::create) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java index 1d613d14c38..51e12270cea 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/BatteryRecipes.java @@ -230,7 +230,7 @@ private static void standardBatteries(Consumer provider) { EXTRACTOR_RECIPES.recipeBuilder("unpackage_mv_lithium_battery").inputItems(BATTERY_MV_LITHIUM) .outputItems(BATTERY_HULL_MV).save(provider); EXTRACTOR_RECIPES.recipeBuilder("unpackage_mv_sodium_battery").inputItems(BATTERY_MV_SODIUM) - .outputItems(BATTERY_MV_SODIUM).save(provider); + .outputItems(BATTERY_HULL_MV).save(provider); EXTRACTOR_RECIPES.recipeBuilder("unpackage_hv_cadmium_battery").inputItems(BATTERY_HV_CADMIUM) .outputItems(BATTERY_HULL_HV).save(provider); From 14a282fd8b87ea78e7b37df0da1c9bce9d85265b Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 7 Sep 2025 20:45:16 +0300 Subject: [PATCH 014/123] april fools :) --- .../com/gregtechceu/gtceu/api/GTValues.java | 3 + .../gtceu/config/ConfigHolder.java | 2 + .../gtceu/core/mixins/ItemStackMixin.java | 60 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 66100aa5a6c..5c0696d9f1b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.api; +import com.gregtechceu.gtceu.config.ConfigHolder; + import net.minecraft.util.RandomSource; import java.time.LocalDate; @@ -300,6 +302,7 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { public static boolean HT = false; public static BooleanSupplier FOOLS = () -> { + if (ConfigHolder.INSTANCE != null && ConfigHolder.INSTANCE.client.aprilFoolsMode) return true; var now = LocalDate.now(); return now.getMonth() == Month.APRIL && now.getDayOfMonth() == 1; }; diff --git a/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java b/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java index b8a7cf8e4cf..3f857e65009 100644 --- a/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java @@ -743,6 +743,8 @@ public static class ClientConfigs { "Disable if experiencing performance issues.", "Default: true" }) public boolean machinesHaveBERsByDefault = true; @Configurable + public boolean aprilFoolsMode = false; + @Configurable @Configurable.Comment({ "Whether or not sounds should be played when using tools outside of crafting.", "Default: true" }) public boolean toolUseSounds = true; diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index d1c50ad3813..db2b001090d 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -1,14 +1,23 @@ package com.gregtechceu.gtceu.core.mixins; +import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import net.minecraft.ChatFormatting; import net.minecraft.core.Holder; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.FastColor; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.server.ServerLifecycleHooks; @@ -18,7 +27,9 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import java.util.List; import java.util.Objects; import javax.annotation.Nullable; @@ -29,6 +40,15 @@ public abstract class ItemStackMixin implements ISpoilableItemStack { @Unique private boolean gtceu$isUpdating = false; + @Unique + private boolean gtceu$fakeTooltip; + + @Inject(at = @At("RETURN"), + method = "(Lnet/minecraft/world/level/ItemLike;ILnet/minecraft/nbt/CompoundTag;)V") + private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606_, CallbackInfo ci) { + gtceu$fakeTooltip = GTValues.FOOLS.getAsBoolean() && Math.random() < .1; + } + @Shadow public abstract CompoundTag getOrCreateTagElement(String key); @@ -176,4 +196,44 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn cir.setReturnValue(isSameItem && Objects.equals(stack.getTag(), other.getTag())); cir.cancel(); } + + @Inject(at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/item/Item;appendHoverText(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/Level;Ljava/util/List;Lnet/minecraft/world/item/TooltipFlag;)V"), + method = "getTooltipLines", + locals = LocalCapture.CAPTURE_FAILSOFT) + private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackInfoReturnable> cir, + List list) { + if (gtceu$fakeTooltip) { + list.add(Component.translatable( + "gtceu.tooltip.spoil_time_remaining", + Component.literal(FormattingUtil.formatTime(100)).withStyle(ChatFormatting.DARK_AQUA))); + list.add(Component.translatable( + "gtceu.tooltip.spoils_into", + Items.DIRT.getDefaultInstance().getDisplayName())); + } + } + + @Inject(at = @At("HEAD"), method = "isBarVisible", cancellable = true) + private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { + if (gtceu$fakeTooltip) { + cir.setReturnValue(true); + cir.cancel(); + } + } + + @Inject(at = @At("HEAD"), method = "getBarColor", cancellable = true) + private void aprilFoolsBarColor(CallbackInfoReturnable cir) { + if (gtceu$fakeTooltip) { + cir.setReturnValue(FastColor.ARGB32.color(255, 255, 255, 255)); + cir.cancel(); + } + } + + @Inject(at = @At("HEAD"), method = "getBarWidth", cancellable = true) + private void aprilFoolsBarWidth(CallbackInfoReturnable cir) { + if (gtceu$fakeTooltip) { + cir.setReturnValue(13); + cir.cancel(); + } + } } From 0eb0a6c9c2b89bb303e1406b7f13f476b230e52e Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 8 Sep 2025 11:02:50 +0300 Subject: [PATCH 015/123] made SpoilableBehaviour more flexible --- .../gtceu/common/item/SpoilableBehaviour.java | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 493100b384f..dbc9849225c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -18,25 +18,44 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.Function; public class SpoilableBehaviour implements ISpoilableItem, IAddInformation, IDurabilityBar { - private final long ticks; - private final ItemLike spoilResult; + private final Function ticks; + private final Function spoilResult; + private final Function spoilsInto; + + public SpoilableBehaviour( + Function tickProvider, + Function spoilResultProvider, + Function spoilsIntoTooltip) { + this.ticks = tickProvider; + this.spoilResult = spoilResultProvider; + this.spoilsInto = spoilsIntoTooltip; + } + + public SpoilableBehaviour(Function tickProvider, + Function spoilResultProvider) { + this(tickProvider, spoilResultProvider, stack -> spoilResultProvider.apply(stack).getDisplayName()); + } + + public SpoilableBehaviour(long ticks, ItemStack spoilResult) { + this(stack -> ticks, stack -> spoilResult.copyWithCount(stack.getCount())); + } public SpoilableBehaviour(long ticks, ItemLike spoilResult) { - this.ticks = ticks; - this.spoilResult = spoilResult; + this(stack -> ticks, stack -> spoilResult.asItem().getDefaultInstance().copyWithCount(stack.getCount())); } @Override public long getSpoilTicks(ItemStack stack) { - return ticks; + return ticks.apply(stack); } @Override public ItemStack spoilResult(ItemStack stack) { - return new ItemStack(spoilResult, stack.getCount()); + return spoilResult.apply(stack); } @Override @@ -55,7 +74,7 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List Date: Mon, 8 Sep 2025 12:48:12 +0300 Subject: [PATCH 016/123] added the ability to attach SpoilableBehaviour to any item --- .../gtceu/common/item/SpoilableBehaviour.java | 20 +++++++++++++++++++ .../gtceu/core/mixins/ItemStackMixin.java | 13 +++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index dbc9849225c..19469361359 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -9,6 +9,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.util.FastColor; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; @@ -17,11 +18,15 @@ import it.unimi.dsi.fastutil.ints.IntIntPair; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Function; public class SpoilableBehaviour implements ISpoilableItem, IAddInformation, IDurabilityBar { + private static final Map ATTACHED_COMPONENTS = new HashMap<>(); + private final Function ticks; private final Function spoilResult; private final Function spoilsInto; @@ -105,4 +110,19 @@ public boolean doDamagedStateColors(ItemStack itemStack) { public float getDurabilityForDisplay(ItemStack stack) { return (float) getTicksUntilSpoiled(stack) / getSpoilTicks(stack); } + + public static @Nullable ISpoilableItem getSpoilable(ItemStack stack) { + Item item = stack.getItem(); + if (item instanceof ISpoilableItem spoilable) return spoilable; + if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); + return null; + } + + public static void unspoil(ItemLike item) { + ATTACHED_COMPONENTS.remove(item.asItem()); + } + + public void attachTo(ItemLike item) { + ATTACHED_COMPONENTS.put(item.asItem(), this); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index db2b001090d..8cc7e7163e0 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.utils.FormattingUtil; import net.minecraft.ChatFormatting; @@ -88,8 +89,8 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 gtceu$isUpdating = true; MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - // noinspection ConstantValue - if (getItem() instanceof ISpoilableItem spoilable && spoilable.shouldSpoil((ItemStack) (Object) this)) { + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (spoilable != null && spoilable.shouldSpoil((ItemStack) (Object) this)) { if (spoilable.getSpoilTicks((ItemStack) (Object) this) < 0) { gtceu$isUpdating = false; return; @@ -155,10 +156,11 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea gtceu$updateFreshness(level, false); MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - if (level != null && getTagElement("GTCEu_spoilable") != null && getItem() instanceof ISpoilableItem spoilable) + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (level != null && getTagElement("GTCEu_spoilable") != null && spoilable != null) return spoilable.getSpoilTicks((ItemStack) (Object) this) - level.getGameTime() + gtceu$getCreationTick(level); - if (getItem() instanceof ISpoilableItem spoilable) return spoilable.getSpoilTicks((ItemStack) (Object) this); + if (spoilable != null) return spoilable.getSpoilTicks((ItemStack) (Object) this); return 0; } @@ -168,7 +170,8 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea gtceu$updateFreshness(level, false); MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - if (level != null && getTagElement("GTCEu_spoilable") != null && getItem() instanceof ISpoilableItem spoilable) + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (level != null && getTagElement("GTCEu_spoilable") != null && spoilable != null) gtceu$setCreationTick(level, level.getGameTime() - spoilable.getSpoilTicks((ItemStack) (Object) this) + value); } From 0729962c11a8889dd356f54f03d42f96b68f66cf Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 8 Sep 2025 21:31:45 +0300 Subject: [PATCH 017/123] trollololol --- .../com/gregtechceu/gtceu/api/GTValues.java | 7 ++++ .../gtceu/common/item/SpoilableBehaviour.java | 4 ++- .../gtceu/core/mixins/ItemStackMixin.java | 35 +++++++++++++++---- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 5c0696d9f1b..9e77ffad3f8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -312,5 +312,12 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { return now.getMonth() == Month.DECEMBER && (now.getDayOfMonth() == 24 || now.getDayOfMonth() == 25); }; + /** + * Makes EVERY {@code ItemStack} spoilable. EVERY SINGLE ONE, even in recipe ingredients and results, creative + * inventory, JEI, EMI, + * statistics menu, EVERYTHING. + */ + public static final boolean BREAK_EVERYTHING_LOL = true; + public static final String CUSTOM_TAG_SOURCE = "GTCEu Custom Tags"; } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 19469361359..a9cfa252619 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.item; +import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; @@ -11,6 +12,7 @@ import net.minecraft.util.FastColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; @@ -115,7 +117,7 @@ public float getDurabilityForDisplay(ItemStack stack) { Item item = stack.getItem(); if (item instanceof ISpoilableItem spoilable) return spoilable; if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); - return null; + return GTValues.BREAK_EVERYTHING_LOL ? new SpoilableBehaviour(20 * 10, Items.DIRT) : null; } public static void unspoil(ItemLike item) { diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 8cc7e7163e0..d231ab39cec 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -2,6 +2,8 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.item.component.IAddInformation; +import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.utils.FormattingUtil; @@ -126,7 +128,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 "getItemHolder" }) private void injectedFreshnessUpdate(CallbackInfoReturnable cir) { MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - gtceu$updateFreshness(server == null ? null : server.overworld(), false); + gtceu$updateFreshness(server == null ? null : server.overworld(), GTValues.BREAK_EVERYTHING_LOL); } @Inject(at = @At("HEAD"), method = "inventoryTick") @@ -190,8 +192,11 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn long tick1 = tag1.getLong("creation_tick"); long tick2 = tag2.getLong("creation_tick"); if (tick1 != tick2) { - long avg = (tick1 * stack.getCount() + tick2 * other.getCount()) / - (stack.getCount() + other.getCount()); + long avg; + if (stack.getCount() + other.getCount() > 0) + avg = (tick1 * stack.getCount() + tick2 * other.getCount()) / + (stack.getCount() + other.getCount()); + else avg = tick1; tag1.putLong("creation_tick", avg); tag2.putLong("creation_tick", avg); } @@ -206,7 +211,11 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn locals = LocalCapture.CAPTURE_FAILSOFT) private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackInfoReturnable> cir, List list) { - if (gtceu$fakeTooltip) { + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IAddInformation addInformation) { + addInformation.appendHoverText((ItemStack) (Object) this, player == null ? null : player.level(), list, + isAdvanced); + } else if (gtceu$fakeTooltip) { list.add(Component.translatable( "gtceu.tooltip.spoil_time_remaining", Component.literal(FormattingUtil.formatTime(100)).withStyle(ChatFormatting.DARK_AQUA))); @@ -218,7 +227,11 @@ private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackIn @Inject(at = @At("HEAD"), method = "isBarVisible", cancellable = true) private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { - if (gtceu$fakeTooltip) { + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { + cir.setReturnValue(durabilityBar.isBarVisible((ItemStack) (Object) this)); + cir.cancel(); + } else if (gtceu$fakeTooltip) { cir.setReturnValue(true); cir.cancel(); } @@ -226,7 +239,11 @@ private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarColor", cancellable = true) private void aprilFoolsBarColor(CallbackInfoReturnable cir) { - if (gtceu$fakeTooltip) { + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { + cir.setReturnValue(durabilityBar.getBarColor((ItemStack) (Object) this)); + cir.cancel(); + } else if (gtceu$fakeTooltip) { cir.setReturnValue(FastColor.ARGB32.color(255, 255, 255, 255)); cir.cancel(); } @@ -234,7 +251,11 @@ private void aprilFoolsBarColor(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarWidth", cancellable = true) private void aprilFoolsBarWidth(CallbackInfoReturnable cir) { - if (gtceu$fakeTooltip) { + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { + cir.setReturnValue(durabilityBar.getBarWidth((ItemStack) (Object) this)); + cir.cancel(); + } else if (gtceu$fakeTooltip) { cir.setReturnValue(13); cir.cancel(); } From 522aac6ad3905fc7a4855b0815b4b7bbf02e31a4 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 8 Sep 2025 21:32:10 +0300 Subject: [PATCH 018/123] oops forgot to disable trollololol --- src/main/java/com/gregtechceu/gtceu/api/GTValues.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 9e77ffad3f8..20d47bc6f38 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -317,7 +317,7 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { * inventory, JEI, EMI, * statistics menu, EVERYTHING. */ - public static final boolean BREAK_EVERYTHING_LOL = true; + public static final boolean BREAK_EVERYTHING_LOL = false; public static final String CUSTOM_TAG_SOURCE = "GTCEu Custom Tags"; } From c386643e1653a4110dbcddeb8b7c0a4116bb882c Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 08:03:25 +0300 Subject: [PATCH 019/123] more functionality for pack devs ig :) --- .../java/com/gregtechceu/gtceu/api/GTValues.java | 12 +++++++++++- .../gtceu/common/item/SpoilableBehaviour.java | 5 +++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 20d47bc6f38..931a692eb1e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -1,14 +1,18 @@ package com.gregtechceu.gtceu.api; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; import net.minecraft.util.RandomSource; +import org.jetbrains.annotations.NotNull; + import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.function.BooleanSupplier; import java.util.function.IntFunction; +import java.util.function.Supplier; import static net.minecraft.ChatFormatting.*; @@ -317,7 +321,13 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { * inventory, JEI, EMI, * statistics menu, EVERYTHING. */ - public static final boolean BREAK_EVERYTHING_LOL = false; + public static boolean BREAK_EVERYTHING_LOL = false; + + /** + * Supplier to get the {@link SpoilableBehaviour} of an {@code Item} that doesn't have one attached + * Called once for every {@code Item}. Return {@code null} to not attach any {@link SpoilableBehaviour} (default). + */ + public static @NotNull Supplier DEFAULT_SPOIL_BEHAVIOR = () -> null; public static final String CUSTOM_TAG_SOURCE = "GTCEu Custom Tags"; } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index a9cfa252619..c333b936d3c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -12,7 +12,6 @@ import net.minecraft.util.FastColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; @@ -117,7 +116,9 @@ public float getDurabilityForDisplay(ItemStack stack) { Item item = stack.getItem(); if (item instanceof ISpoilableItem spoilable) return spoilable; if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); - return GTValues.BREAK_EVERYTHING_LOL ? new SpoilableBehaviour(20 * 10, Items.DIRT) : null; + SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.get(); + if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); + return behaviour; } public static void unspoil(ItemLike item) { From 613e25b6929fc3cc48cb8b47d175ddd5ec8ecd0e Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 08:38:30 +0300 Subject: [PATCH 020/123] pass item into DEFAULT_SPOIL_BEHAVIOR function --- src/main/java/com/gregtechceu/gtceu/api/GTValues.java | 4 +++- .../com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 931a692eb1e..1747fe87f43 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.config.ConfigHolder; import net.minecraft.util.RandomSource; +import net.minecraft.world.item.Item; import org.jetbrains.annotations.NotNull; @@ -11,6 +12,7 @@ import java.time.Month; import java.util.Arrays; import java.util.function.BooleanSupplier; +import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; @@ -327,7 +329,7 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { * Supplier to get the {@link SpoilableBehaviour} of an {@code Item} that doesn't have one attached * Called once for every {@code Item}. Return {@code null} to not attach any {@link SpoilableBehaviour} (default). */ - public static @NotNull Supplier DEFAULT_SPOIL_BEHAVIOR = () -> null; + public static @NotNull Function DEFAULT_SPOIL_BEHAVIOR = item -> null; public static final String CUSTOM_TAG_SOURCE = "GTCEu Custom Tags"; } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index c333b936d3c..f5a3960415c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -116,7 +116,7 @@ public float getDurabilityForDisplay(ItemStack stack) { Item item = stack.getItem(); if (item instanceof ISpoilableItem spoilable) return spoilable; if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); - SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.get(); + SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.apply(item); if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); return behaviour; } From d5fe81620714968374dd3f417c20d516f569c030 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 08:54:14 +0300 Subject: [PATCH 021/123] added API to allow things like spoilage to be made with addons --- .../java/com/gregtechceu/gtceu/api/GTValues.java | 2 +- .../trait/NotifiableItemStackHandler.java | 2 ++ .../gregtechceu/gtceu/api/recipe/GTRecipe.java | 16 ++++++++++++++++ .../api/recipe/modifier/ModifierFunction.java | 12 +++++++++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 1747fe87f43..18857872c8b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -14,7 +14,6 @@ import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.IntFunction; -import java.util.function.Supplier; import static net.minecraft.ChatFormatting.*; @@ -272,6 +271,7 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { 0x7EC3C4, 0x7EB07E, 0xBF74C0, 0x0B5CFE, 0x914E91, 0x488748, 0x8C0000, 0x2828F5 }; // Main colour for each tier + @SuppressWarnings("DataFlowIssue") public static final int[] VCM = new int[] { DARK_GRAY.getColor(), GRAY.getColor(), diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index 0b22b2882a3..ffe9d24c32e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -170,6 +170,7 @@ public static List handleRecipe(IO io, GTRecipe recipe, List handleRecipe(IO io, GTRecipe recipe, List 0 && output.getItem() instanceof ISpoilableItem item && diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java index 266e97916f8..7a41c683ee6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java @@ -6,6 +6,7 @@ import com.gregtechceu.gtceu.api.recipe.content.Content; import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; import com.gregtechceu.gtceu.api.recipe.ingredient.EnergyStack; +import com.gregtechceu.gtceu.api.recipe.modifier.RecipeModifier; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.RegistryAccess; @@ -22,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.BiConsumer; import javax.annotation.ParametersAreNonnullByDefault; @@ -56,6 +58,16 @@ public class GTRecipe implements net.minecraft.world.item.crafting.Recipe itemInputs = new ArrayList<>(); + /** + * Called for each {@code ItemStack} output before it is inserted into the output container. + * Does nothing by default, to be modified with {@link BiConsumer#andThen(BiConsumer)} in {@link RecipeModifier} + */ + public BiConsumer itemOutputModifier = (recipe, stack) -> {}; public final GTRecipeCategory recipeCategory; // Lazy fields, since we need the recipe EUt very often @Getter(lazy = true) @@ -249,4 +261,8 @@ public int hashCode() { public String toString() { return id.toString(); } + + public void mutateItemOutput(ItemStack stack) { + if (this.itemOutputModifier != null) itemOutputModifier.accept(this, stack); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java index 5f68f5814bf..940776c4fc5 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java @@ -9,6 +9,8 @@ import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; import com.gregtechceu.gtceu.api.recipe.ingredient.EnergyStack; +import net.minecraft.world.item.ItemStack; + import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.Contract; @@ -20,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; /** * Represents a function that accepts a GTRecipe and returns a modified version of the GTRecipe, or null. @@ -42,7 +45,7 @@ public interface ModifierFunction { /** * Use this static to denote that the recipe doesn't get modified */ - ModifierFunction IDENTITY = recipe -> recipe; + ModifierFunction IDENTITY = GTRecipe::copy; /** * Applies this modifier to the passed recipe @@ -107,6 +110,7 @@ final class FunctionBuilder { private ContentModifier outputModifier = ContentModifier.IDENTITY; private ContentModifier tickInputModifier = ContentModifier.IDENTITY; private ContentModifier tickOutputModifier = ContentModifier.IDENTITY; + private BiConsumer itemOutputModifier = (recipe, stack) -> {}; private final List addedConditions = new ArrayList<>(); public FunctionBuilder() {} @@ -134,6 +138,11 @@ public FunctionBuilder durationMultiplier(double multiplier) { return this; } + public FunctionBuilder modifyItemOutputs(BiConsumer func) { + itemOutputModifier = itemOutputModifier.andThen(func); + return this; + } + /** * Builds the ModifierFunction from this builder. *

@@ -172,6 +181,7 @@ public ModifierFunction build() { EnergyStack eut = EURecipeCapability.CAP.copyWithModifier(preEUt.stack(), eutModifier); EURecipeCapability.putEUContent(preEUt.isInput() ? copied.tickInputs : copied.tickOutputs, eut); } + copied.itemOutputModifier = itemOutputModifier; return copied; }; } From 20792982ca6658759f847859391d5b70b4ea406d Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 09:54:08 +0300 Subject: [PATCH 022/123] added event when registering a machine --- .../gtceu/api/cosmetics/CapeRegistry.java | 2 +- .../RegisterGTCapesEvent.java | 2 +- .../api/events/RegisterGTMachineEvent.java | 18 +++++++++++++++++ .../registry/registrate/MachineBuilder.java | 13 ++++++++++++ .../gtceu/common/cosmetics/GTCapes.java | 2 +- .../gtceu/forge/ForgeCommonEventListener.java | 2 +- .../integration/kjs/GTCEuStartupEvents.java | 2 ++ .../kjs/events/RegisterCapesEventJS.java | 2 +- .../kjs/events/RegisterGTMachineEventJS.java | 20 +++++++++++++++++++ 9 files changed, 58 insertions(+), 5 deletions(-) rename src/main/java/com/gregtechceu/gtceu/api/{cosmetics/event => events}/RegisterGTCapesEvent.java (97%) create mode 100644 src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/cosmetics/CapeRegistry.java b/src/main/java/com/gregtechceu/gtceu/api/cosmetics/CapeRegistry.java index f22f708f052..3764410bf5e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cosmetics/CapeRegistry.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cosmetics/CapeRegistry.java @@ -1,7 +1,7 @@ package com.gregtechceu.gtceu.api.cosmetics; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.cosmetics.event.RegisterGTCapesEvent; +import com.gregtechceu.gtceu.api.events.RegisterGTCapesEvent; import com.gregtechceu.gtceu.common.network.GTNetwork; import com.gregtechceu.gtceu.common.network.packets.SPacketNotifyCapeChange; import com.gregtechceu.gtceu.integration.kjs.GTCEuServerEvents; diff --git a/src/main/java/com/gregtechceu/gtceu/api/cosmetics/event/RegisterGTCapesEvent.java b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTCapesEvent.java similarity index 97% rename from src/main/java/com/gregtechceu/gtceu/api/cosmetics/event/RegisterGTCapesEvent.java rename to src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTCapesEvent.java index bdb805b56c4..ac6fdecffe3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cosmetics/event/RegisterGTCapesEvent.java +++ b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTCapesEvent.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.api.cosmetics.event; +package com.gregtechceu.gtceu.api.events; import com.gregtechceu.gtceu.api.cosmetics.CapeRegistry; diff --git a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java new file mode 100644 index 00000000000..7d75f3010ad --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.api.events; + +import com.gregtechceu.gtceu.api.machine.MachineDefinition; +import com.gregtechceu.gtceu.api.registry.registrate.MachineBuilder; + +import net.minecraftforge.eventbus.api.Event; + +import lombok.Getter; + +public class RegisterGTMachineEvent extends Event { + + @Getter + private final MachineBuilder builder; + + public RegisterGTMachineEvent(MachineBuilder builder) { + this.builder = builder; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 4a3d4136b8f..d3d5ef5a0b3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -5,6 +5,7 @@ import com.gregtechceu.gtceu.api.block.IMachineBlock; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; import com.gregtechceu.gtceu.api.data.RotationState; +import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; import com.gregtechceu.gtceu.api.gui.editor.EditableMachineUI; import com.gregtechceu.gtceu.api.item.MetaMachineItem; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; @@ -27,6 +28,8 @@ import com.gregtechceu.gtceu.common.data.models.GTMachineModels; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.model.builder.MachineModelBuilder; +import com.gregtechceu.gtceu.integration.kjs.GTCEuStartupEvents; +import com.gregtechceu.gtceu.integration.kjs.events.RegisterGTMachineEventJS; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.RenderType; @@ -46,6 +49,7 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.client.model.generators.BlockModelBuilder; +import net.minecraftforge.common.MinecraftForge; import com.tterrag.registrate.AbstractRegistrate; import com.tterrag.registrate.builders.BlockBuilder; @@ -519,6 +523,11 @@ protected void setupStateDefinition(MachineDefinition definition) { @HideFromJS public DEFINITION register() { + RegisterGTMachineEvent event = new RegisterGTMachineEvent<>(this); + MinecraftForge.EVENT_BUS.post(event); + if (GTCEu.Mods.isKubeJSLoaded()) { + KJSCallWrapper.fireKJSEvent(event); + } this.registrate.object(name); var definition = createDefinition(); @@ -693,5 +702,9 @@ public static void generateAssetJsons(@Nullable As generator.itemModel(id, gen -> gen.parent(id.withPrefix("block/machine/").toString())); } } + + public static void fireKJSEvent(RegisterGTMachineEvent event) { + GTCEuStartupEvents.REGISTER_GT_MACHINE.post(new RegisterGTMachineEventJS(event)); + } } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cosmetics/GTCapes.java b/src/main/java/com/gregtechceu/gtceu/common/cosmetics/GTCapes.java index b7018839ff7..2fc4d1de26a 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cosmetics/GTCapes.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cosmetics/GTCapes.java @@ -1,7 +1,7 @@ package com.gregtechceu.gtceu.common.cosmetics; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.cosmetics.event.RegisterGTCapesEvent; +import com.gregtechceu.gtceu.api.events.RegisterGTCapesEvent; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java index 80c0a314326..f01dd59abaa 100644 --- a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java +++ b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java @@ -10,12 +10,12 @@ import com.gregtechceu.gtceu.api.capability.compat.EUToFEProvider; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.cosmetics.CapeRegistry; -import com.gregtechceu.gtceu.api.cosmetics.event.RegisterGTCapesEvent; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.chemical.material.properties.HazardProperty; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.events.RegisterGTCapesEvent; import com.gregtechceu.gtceu.api.item.armor.ArmorComponentItem; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.machine.MetaMachine; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java b/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java index 59764783f61..3cadc64e759 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java @@ -5,6 +5,7 @@ import com.gregtechceu.gtceu.integration.kjs.events.CraftingComponentsEventJS; import com.gregtechceu.gtceu.integration.kjs.events.GTRegistryEventJS; import com.gregtechceu.gtceu.integration.kjs.events.MaterialModificationEventJS; +import com.gregtechceu.gtceu.integration.kjs.events.RegisterGTMachineEventJS; import dev.latvian.mods.kubejs.event.EventGroup; import dev.latvian.mods.kubejs.event.EventHandler; @@ -28,4 +29,5 @@ private static boolean validateRegistry(Object o) { EventHandler REGISTRY = GROUP.startup("registry", () -> GTRegistryEventJS.class).extra(REGISTRY_EXTRA); EventHandler MATERIAL_MODIFICATION = GROUP.startup("materialModification", () -> MaterialModificationEventJS.class); EventHandler CRAFTING_COMPONENTS = GROUP.startup("craftingComponents", () -> CraftingComponentsEventJS.class); + EventHandler REGISTER_GT_MACHINE = GROUP.startup("registerGTMachine", () -> RegisterGTMachineEventJS.class); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterCapesEventJS.java b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterCapesEventJS.java index b81bc089148..0ab4fb5ccb0 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterCapesEventJS.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterCapesEventJS.java @@ -1,6 +1,6 @@ package com.gregtechceu.gtceu.integration.kjs.events; -import com.gregtechceu.gtceu.api.cosmetics.event.RegisterGTCapesEvent; +import com.gregtechceu.gtceu.api.events.RegisterGTCapesEvent; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java new file mode 100644 index 00000000000..e0344e2a72d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java @@ -0,0 +1,20 @@ +package com.gregtechceu.gtceu.integration.kjs.events; + +import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; +import com.gregtechceu.gtceu.api.machine.MachineDefinition; +import com.gregtechceu.gtceu.api.registry.registrate.MachineBuilder; + +import dev.latvian.mods.kubejs.event.EventJS; + +public class RegisterGTMachineEventJS extends EventJS { + + private final RegisterGTMachineEvent event; + + public RegisterGTMachineEventJS(RegisterGTMachineEvent event) { + this.event = event; + } + + public MachineBuilder getBuilder() { + return this.event.getBuilder(); + } +} From da8354f078c1c5bacc61845bf4f520fd6e770c08 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 12:53:57 +0300 Subject: [PATCH 023/123] made spoilage transfer use the newly created API --- .../trait/NotifiableItemStackHandler.java | 12 ---------- .../gtceu/common/data/GTRecipeModifiers.java | 23 +++++++++++++++++++ .../gtceu/forge/ForgeCommonEventListener.java | 6 +++++ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index ffe9d24c32e..726729bfb63 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -170,12 +170,6 @@ public static List handleRecipe(IO io, GTRecipe recipe, List handleRecipe(IO io, GTRecipe recipe, List 0 && - output.getItem() instanceof ISpoilableItem item && - item.shouldSpoil(output)) { - double spoilProgress = recipe.spoilProgress / recipe.spoilableIngredientsAmount; - item.setTicksUntilSpoiled(output, (long) (spoilProgress * item.getSpoilTicks(output))); - } // Only try this slot if not visited or if visited with the same type of item if (visited[slot] == null || ItemStack.isSameItemSameTags(visited[slot], output)) { if (count < output.getMaxStackSize() && count < storage.getSlotLimit(slot)) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java index ba1fa7bba54..c0900592449 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.recipe.EURecipeCapability; import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.IOverclockMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; @@ -16,11 +17,13 @@ import com.gregtechceu.gtceu.api.recipe.modifier.ParallelLogic; import com.gregtechceu.gtceu.api.recipe.modifier.RecipeModifier; import com.gregtechceu.gtceu.common.capability.EnvironmentalHazardSavedData; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -48,6 +51,26 @@ public class GTRecipeModifiers { public static final RecipeModifier OC_NON_PERFECT = ELECTRIC_OVERCLOCK.apply(NON_PERFECT_OVERCLOCK); public static final RecipeModifier OC_PERFECT_SUBTICK = ELECTRIC_OVERCLOCK.apply(PERFECT_OVERCLOCK_SUBTICK); public static final RecipeModifier OC_NON_PERFECT_SUBTICK = ELECTRIC_OVERCLOCK.apply(NON_PERFECT_OVERCLOCK_SUBTICK); + public static final RecipeModifier SPOILAGE_TRANSFER = (machine, recipe) -> ModifierFunction.builder() + .modifyItemOutputs((r, stack) -> { + ISpoilableItem.update(stack, null); + if (!r.transferSpoilingProgress) return; + double spoilProgress = 0; + int spoilableCount = 0; + for (ItemStack in : r.itemInputs) { + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(in); + if (spoilable != null && spoilable.shouldSpoil(in)) { + spoilableCount += in.getCount(); + spoilProgress += in.getCount() * (double) spoilable.getTicksUntilSpoiled(in) / + spoilable.getSpoilTicks(in); + } + } + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + if (spoilable != null && spoilable.shouldSpoil(stack) && spoilableCount > 0) { + double spoiled = spoilProgress / spoilableCount; + spoilable.setTicksUntilSpoiled(stack, (long) (spoiled * spoilable.getSpoilTicks(stack))); + } + }).build(); public static final BiFunction ENVIRONMENT_REQUIREMENT = Util .memoize((condition, maxAllowedStrength) -> (machine, recipe) -> { diff --git a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java index f01dd59abaa..c7392eca777 100644 --- a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java +++ b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java @@ -16,6 +16,7 @@ import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.events.RegisterGTCapesEvent; +import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; import com.gregtechceu.gtceu.api.item.armor.ArmorComponentItem; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.machine.MetaMachine; @@ -159,6 +160,11 @@ public static void registerCapes(RegisterGTCapesEvent event) { GTCapes.giveDevCapes(event); } + @SubscribeEvent + public static void addSpoilTransferModifier(RegisterGTMachineEvent event) { + event.getBuilder().recipeModifier(GTRecipeModifiers.SPOILAGE_TRANSFER); + } + @SubscribeEvent public static void tickPlayerInventoryHazards(TickEvent.PlayerTickEvent event) { if (event.side == LogicalSide.CLIENT || event.phase != TickEvent.Phase.END) { From e46f6aef27972c9ade81f087a2fe3c0bc7387fac Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 12:55:53 +0300 Subject: [PATCH 024/123] oops --- .../gtceu/api/machine/trait/NotifiableItemStackHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index 726729bfb63..fb1abc2183c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -4,7 +4,6 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; -import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.recipe.DummyCraftingContainer; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -170,13 +169,13 @@ public static List handleRecipe(IO io, GTRecipe recipe, List Date: Tue, 9 Sep 2025 13:51:41 +0300 Subject: [PATCH 025/123] update item when it's dropped --- .../com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index d231ab39cec..f6dc7625831 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -84,6 +84,10 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 @Nullable public abstract CompoundTag getTagElement(String key); + @Shadow + @Nullable + private Entity entityRepresentation; + @Unique @Override public void gtceu$updateFreshness(Level level, boolean createTag) { @@ -128,7 +132,8 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 "getItemHolder" }) private void injectedFreshnessUpdate(CallbackInfoReturnable cir) { MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - gtceu$updateFreshness(server == null ? null : server.overworld(), GTValues.BREAK_EVERYTHING_LOL); + gtceu$updateFreshness(server == null ? null : server.overworld(), + GTValues.BREAK_EVERYTHING_LOL || entityRepresentation != null); } @Inject(at = @At("HEAD"), method = "inventoryTick") From 8ce3db211dfc93364ca0b7faba6de7284f8ddca7 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 13:54:35 +0300 Subject: [PATCH 026/123] update when crafted --- .../com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index f6dc7625831..4f220629cda 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -141,6 +141,11 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea gtceu$updateFreshness(null, true); } + @Inject(at = @At("HEAD"), method = "onCraftedBy") + private void onCraftedUpdateFreshness(Level level, Player player, int amount, CallbackInfo ci) { + gtceu$updateFreshness(null, true); + } + @Override @Unique public long gtceu$getCreationTick(@Nullable Level level) { From dcca6310771c7a4ca677f7a3a52d0dd82cbc4215 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 14:08:01 +0300 Subject: [PATCH 027/123] corrected handling of recursive spoiling --- .../gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 4f220629cda..0dadcd04505 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -115,13 +115,22 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 } @SuppressWarnings("DataFlowIssue") long spoilTicks = spoilable.getSpoilTicks((ItemStack) (Object) this); - if (spoilTicks <= level.getGameTime() - tag.getLong("creation_tick")) { + long timeDifference = level.getGameTime() - tag.getLong("creation_tick") - spoilTicks; + if (timeDifference >= 0) { @SuppressWarnings("DataFlowIssue") ItemStack newStack = spoilable.spoilResult((ItemStack) (Object) this); item = newStack.getItem(); delegate = ForgeRegistries.ITEMS.getDelegateOrThrow(item); count = newStack.getCount(); this.tag = newStack.getTag(); + ISpoilableItem newSpoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + if (newSpoilable != null && (this.tag == null || !this.tag.contains("GTCEu_spoilable"))) { + getOrCreateTagElement("GTCEu_spoilable").putLong("creation_tick", + level.getGameTime() - timeDifference); + gtceu$isUpdating = false; + gtceu$updateFreshness(null, false); + gtceu$isUpdating = true; + } } } gtceu$isUpdating = false; From 35b553f07741a0b036ab655a91b1aa66fd6f7c3b Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 14:11:02 +0300 Subject: [PATCH 028/123] but what if dirt spoils into dirt --- .../gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 0dadcd04505..7d197fbe461 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -127,9 +127,12 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 if (newSpoilable != null && (this.tag == null || !this.tag.contains("GTCEu_spoilable"))) { getOrCreateTagElement("GTCEu_spoilable").putLong("creation_tick", level.getGameTime() - timeDifference); - gtceu$isUpdating = false; - gtceu$updateFreshness(null, false); - gtceu$isUpdating = true; + try { + gtceu$isUpdating = false; + gtceu$updateFreshness(null, false); + gtceu$isUpdating = true; + } catch (StackOverflowError ignored) {} // if some crazy pack dev makes an item spoil into a + // spoilable that spoils into a spoilable 1000 times } } } From 584afb2849f262ef74652297e404aa356e2cb206 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 20:29:29 +0300 Subject: [PATCH 029/123] fixes and TESTS --- .../api/events/RegisterGTMachineEvent.java | 7 +- .../gtceu/api/item/ISpoilableItemStack.java | 2 + .../trait/NotifiableItemStackHandler.java | 5 +- .../registry/registrate/MachineBuilder.java | 14 +- .../gtceu/core/mixins/ItemStackMixin.java | 29 ++- .../gtceu/forge/ForgeCommonEventListener.java | 4 +- .../kjs/events/RegisterGTMachineEventJS.java | 9 +- .../common/item/SpoilableBehaviourTest.java | 183 ++++++++++++++++++ .../gtceu/gametest/util/TestUtils.java | 10 + 9 files changed, 246 insertions(+), 17 deletions(-) create mode 100644 src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java index 7d75f3010ad..b4557f2610b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java +++ b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java @@ -1,18 +1,17 @@ package com.gregtechceu.gtceu.api.events; -import com.gregtechceu.gtceu.api.machine.MachineDefinition; import com.gregtechceu.gtceu.api.registry.registrate.MachineBuilder; import net.minecraftforge.eventbus.api.Event; import lombok.Getter; -public class RegisterGTMachineEvent extends Event { +public class RegisterGTMachineEvent extends Event { @Getter - private final MachineBuilder builder; + private final MachineBuilder builder; - public RegisterGTMachineEvent(MachineBuilder builder) { + public RegisterGTMachineEvent(MachineBuilder builder) { this.builder = builder; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java index 6a0a4777c51..e6ada9c8272 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java @@ -13,6 +13,8 @@ public interface ISpoilableItemStack { long gtceu$getRemainingTicks(@Nullable Level level); + void gtceu$setFreezeSpoiling(boolean freezeUpdates); + void gtceu$updateFreshness(@Nullable Level level, boolean createTag); @Unique diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index fb1abc2183c..fe8434999a0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.recipe.DummyCraftingContainer; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -169,7 +170,9 @@ public static List handleRecipe(IO io, GTRecipe recipe, List recipeModifier(RecipeModifier recipeModifier) return this; } + public MachineBuilder addRecipeModifier(RecipeModifier recipeModifier) { + this.recipeModifier(new RecipeModifierList(this.recipeModifier, recipeModifier)); + return this; + } + public MachineBuilder recipeModifier(RecipeModifier recipeModifier, boolean alwaysTryModifyRecipe) { this.alwaysTryModifyRecipe = alwaysTryModifyRecipe; return this.recipeModifier(recipeModifier); @@ -523,7 +529,9 @@ protected void setupStateDefinition(MachineDefinition definition) { @HideFromJS public DEFINITION register() { - RegisterGTMachineEvent event = new RegisterGTMachineEvent<>(this); + RegisterGTMachineEvent event = new RegisterGTMachineEvent(this); + ForgeCommonEventListener.addSpoilTransferModifier(event); // this is a terrible way to do it, but I couldn't get + // the event to work MinecraftForge.EVENT_BUS.post(event); if (GTCEu.Mods.isKubeJSLoaded()) { KJSCallWrapper.fireKJSEvent(event); @@ -703,8 +711,8 @@ public static void generateAssetJsons(@Nullable As } } - public static void fireKJSEvent(RegisterGTMachineEvent event) { - GTCEuStartupEvents.REGISTER_GT_MACHINE.post(new RegisterGTMachineEventJS(event)); + public static void fireKJSEvent(RegisterGTMachineEvent event) { + GTCEuStartupEvents.REGISTER_GT_MACHINE.post(new RegisterGTMachineEventJS(event)); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 7d197fbe461..d468dea817d 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -88,6 +88,9 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 @Nullable private Entity entityRepresentation; + @Shadow + public abstract void removeTagKey(String key); + @Unique @Override public void gtceu$updateFreshness(Level level, boolean createTag) { @@ -102,7 +105,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 return; } CompoundTag tag = createTag ? getOrCreateTagElement("GTCEu_spoilable") : getTagElement("GTCEu_spoilable"); - if (tag == null) { + if (tag == null || tag.contains("frozenRemainingTicks")) { gtceu$isUpdating = false; return; } @@ -181,9 +184,12 @@ private void onCraftedUpdateFreshness(Level level, Player player, int amount, Ca MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); - if (level != null && getTagElement("GTCEu_spoilable") != null && spoilable != null) + CompoundTag spoilTag = getTagElement("GTCEu_spoilable"); + if (level != null && spoilTag != null && spoilable != null) { + if (spoilTag.contains("frozenRemainingTicks")) return spoilTag.getLong("frozenRemainingTicks"); return spoilable.getSpoilTicks((ItemStack) (Object) this) - level.getGameTime() + gtceu$getCreationTick(level); + } if (spoilable != null) return spoilable.getSpoilTicks((ItemStack) (Object) this); return 0; } @@ -200,6 +206,25 @@ private void onCraftedUpdateFreshness(Level level, Player player, int amount, Ca level.getGameTime() - spoilable.getSpoilTicks((ItemStack) (Object) this) + value); } + @Unique + @Override + public void gtceu$setFreezeSpoiling(boolean freezeUpdates) { + if (SpoilableBehaviour.getSpoilable((ItemStack) (Object) this) == null) return; + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + Level level = server == null ? null : server.overworld(); + if (level == null) return; + if (freezeUpdates) { + gtceu$updateFreshness(level, true); + getOrCreateTagElement("GTCEu_spoilable").putLong("frozenRemainingTicks", gtceu$getRemainingTicks(null)); + } else { + CompoundTag spoilTag = getTagElement("GTCEu_spoilable"); + if (spoilTag != null && spoilTag.contains("frozenRemainingTicks")) { + gtceu$setRemainingTicks(null, spoilTag.getLong("frozenRemainingTicks")); + spoilTag.remove("frozenRemainingTicks"); + } + } + } + @Inject(at = @At("HEAD"), method = "isSameItemSameTags", cancellable = true) private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackInfoReturnable cir) { CompoundTag tag1 = stack.getTagElement("GTCEu_spoilable"); diff --git a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java index c7392eca777..f756559d29d 100644 --- a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java +++ b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java @@ -161,8 +161,8 @@ public static void registerCapes(RegisterGTCapesEvent event) { } @SubscribeEvent - public static void addSpoilTransferModifier(RegisterGTMachineEvent event) { - event.getBuilder().recipeModifier(GTRecipeModifiers.SPOILAGE_TRANSFER); + public static void addSpoilTransferModifier(RegisterGTMachineEvent event) { + event.getBuilder().addRecipeModifier(GTRecipeModifiers.SPOILAGE_TRANSFER); } @SubscribeEvent diff --git a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java index e0344e2a72d..516f50ff522 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java @@ -1,20 +1,19 @@ package com.gregtechceu.gtceu.integration.kjs.events; import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; -import com.gregtechceu.gtceu.api.machine.MachineDefinition; import com.gregtechceu.gtceu.api.registry.registrate.MachineBuilder; import dev.latvian.mods.kubejs.event.EventJS; -public class RegisterGTMachineEventJS extends EventJS { +public class RegisterGTMachineEventJS extends EventJS { - private final RegisterGTMachineEvent event; + private final RegisterGTMachineEvent event; - public RegisterGTMachineEventJS(RegisterGTMachineEvent event) { + public RegisterGTMachineEventJS(RegisterGTMachineEvent event) { this.event = event; } - public MachineBuilder getBuilder() { + public MachineBuilder getBuilder() { return this.event.getBuilder(); } } diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java new file mode 100644 index 00000000000..b1ef9e40c09 --- /dev/null +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -0,0 +1,183 @@ +package com.gregtechceu.gtceu.common.item; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; +import com.gregtechceu.gtceu.common.data.GTMachines; +import com.gregtechceu.gtceu.common.machine.multiblock.part.FluidHatchPartMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.part.ItemBusPartMachine; +import com.gregtechceu.gtceu.common.machine.storage.CrateMachine; +import com.gregtechceu.gtceu.gametest.util.TestUtils; + +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.BeforeBatch; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.gametest.GameTestHolder; +import net.minecraftforge.gametest.PrefixGameTestTemplate; +import net.minecraftforge.items.IItemHandler; + +import java.util.Objects; + +import static com.gregtechceu.gtceu.gametest.util.TestUtils.getMetaMachine; + +@PrefixGameTestTemplate(false) +@GameTestHolder(GTCEu.MOD_ID) +public class SpoilableBehaviourTest { + + private static GTRecipeType LCR_RECIPE_TYPE; + + @BeforeBatch(batch = "spoilageTests") + public static void prepare(Level level) { + LCR_RECIPE_TYPE = TestUtils.createRecipeType("spoilage_lcr_tests"); + LCR_RECIPE_TYPE.getLookup().addRecipe(LCR_RECIPE_TYPE + .recipeBuilder(GTCEu.id("test_overclock_logic")) + .inputItems(new ItemStack(Items.JIGSAW)) + .outputItems(new ItemStack(Items.STRUCTURE_BLOCK)) + .EUt(GTValues.V[GTValues.HV]) + .duration(20) + .buildRawRecipe()); + LCR_RECIPE_TYPE.getLookup().addRecipe(LCR_RECIPE_TYPE + .recipeBuilder(GTCEu.id("test_overclock_logic")) + .inputItems(new ItemStack(Items.APPLE)) + .outputItems(new ItemStack(Items.STRUCTURE_BLOCK)) + .EUt(GTValues.V[GTValues.HV]) + .duration(20) + .keepSpoilingProgress(false) + .buildRawRecipe()); + } + + private static void makeSpoilables(GameTestHelper helper) { + new SpoilableBehaviour(10, Items.DIRT).attachTo(Items.JIGSAW); + new SpoilableBehaviour(10, Items.STRUCTURE_BLOCK).attachTo(Items.APPLE); + new SpoilableBehaviour(40, Items.STRUCTURE_VOID).attachTo(Items.STRUCTURE_BLOCK); + new SpoilableBehaviour(10, Items.JIGSAW).attachTo(Items.STRUCTURE_VOID); + helper.runAtTickTime(100, () -> { + SpoilableBehaviour.unspoil(Items.JIGSAW); + SpoilableBehaviour.unspoil(Items.STRUCTURE_VOID); + SpoilableBehaviour.unspoil(Items.APPLE); + SpoilableBehaviour.unspoil(Items.STRUCTURE_BLOCK); + helper.succeed(); + }); + } + + private static BusHolder getBussesAndForm(GameTestHelper helper) { + WorkableMultiblockMachine controller = (WorkableMultiblockMachine) getMetaMachine( + helper.getBlockEntity(new BlockPos(1, 2, 0))); + TestUtils.formMultiblock(controller); + controller.setRecipeType(LCR_RECIPE_TYPE); + ItemBusPartMachine inputBus1 = (ItemBusPartMachine) getMetaMachine( + helper.getBlockEntity(new BlockPos(2, 1, 0))); + ItemBusPartMachine inputBus2 = (ItemBusPartMachine) getMetaMachine( + helper.getBlockEntity(new BlockPos(2, 2, 0))); + ItemBusPartMachine outputBus1 = (ItemBusPartMachine) getMetaMachine( + helper.getBlockEntity(new BlockPos(0, 1, 0))); + FluidHatchPartMachine outputHatch1 = (FluidHatchPartMachine) getMetaMachine( + helper.getBlockEntity(new BlockPos(0, 2, 0))); + return new BusHolder(inputBus1, inputBus2, outputBus1, outputHatch1, controller); + } + + private record BusHolder(ItemBusPartMachine inputBus1, ItemBusPartMachine inputBus2, ItemBusPartMachine outputBus1, + FluidHatchPartMachine outputHatch1, WorkableMultiblockMachine controller) {} + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void itemDoesntSpoilInChest(GameTestHelper helper) { + makeSpoilables(helper); + helper.setBlock(1, 1, 1, Blocks.CHEST); + IItemHandler itemHandler = TestUtils.getItemHandler(helper, new BlockPos(1, 1, 1)); + itemHandler.insertItem(0, Items.JIGSAW.getDefaultInstance(), false); + helper.failIfEver(() -> helper.assertTrue(TestUtils.isItemStackEqual( + Items.JIGSAW.getDefaultInstance(), + itemHandler.getStackInSlot(0)), "jigsaw spoiled when shouldn't have")); + } + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void itemSpoilsRecursively(GameTestHelper helper) { + makeSpoilables(helper); + helper.setBlock(1, 1, 1, Blocks.CHEST); + IItemHandler itemHandler = TestUtils.getItemHandler(helper, new BlockPos(1, 1, 1)); + ItemStack in = Items.APPLE.getDefaultInstance().copyWithCount(41); + ISpoilableItem.update(in, null); + itemHandler.insertItem(0, in, false); + helper.runAtTickTime(70, () -> helper.assertTrue(TestUtils.isItemStackEqual( + Items.DIRT.getDefaultInstance().copyWithCount(41), + itemHandler.getStackInSlot(0)), + "apple didn't spoil recursively (apple -> structure block -> structure void -> jigsaw -> dirt)")); + } + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void itemSpoilsInCrate(GameTestHelper helper) { + makeSpoilables(helper); + CrateMachine crate = (CrateMachine) TestUtils.setMachine(helper, new BlockPos(1, 1, 1), GTMachines.STEEL_CRATE); + crate.inventory.insertItem(0, Items.JIGSAW.getDefaultInstance().copyWithCount(23), false); + helper.runAtTickTime(9, () -> { + ItemStack stack = crate.inventory.getStackInSlot(0); + helper.assertTrue(TestUtils.isItemStackEqual( + Items.JIGSAW.getDefaultInstance().copyWithCount(23), + stack), "jigsaw spoiled 1 tick earlier"); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + helper.assertTrue(spoilable != null, "spoilable was null when shouldn't have"); + assert spoilable != null; + helper.assertTrue(spoilable.shouldSpoil(stack), "shouldSpoil returned false on spoilable item"); + helper.assertTrue(spoilable.getTicksUntilSpoiled(stack) == 1, + "spoilable didn't return correct ticks until spoiled amount"); + helper.assertTrue(spoilable.getSpoilTicks(stack) == 10, + "spoilable didn't return correct total tick amount"); + }); + helper.runAtTickTime(10, () -> helper.assertTrue(TestUtils.isItemStackEqual( + Items.DIRT.getDefaultInstance().copyWithCount(23), + crate.inventory.getStackInSlot(0)), "jigsaw didn't spoil when should have")); + } + + @GameTest(template = "lcr_input_separation", batch = "spoilageTests") + public static void spoilageTransfersInRecipe(GameTestHelper helper) { + makeSpoilables(helper); + BusHolder busHolder = getBussesAndForm(helper); + ItemStack input = new ItemStack(Items.JIGSAW); + ISpoilableItem.update(input, null); + Objects.requireNonNull(SpoilableBehaviour.getSpoilable(input)).setTicksUntilSpoiled(input, 8); + busHolder.inputBus1.getInventory().setStackInSlot(0, input); + helper.runAtTickTime(21, () -> { + ItemStack stack = busHolder.outputBus1.getInventory().getStackInSlot(0); + helper.assertTrue(TestUtils.isItemStackEqual( + stack, + new ItemStack(Items.STRUCTURE_BLOCK)), + "incorrect recipe output (%s != %s)".formatted(stack.toString(), + new ItemStack(Items.STRUCTURE_BLOCK).toString())); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + helper.assertTrue(spoilable != null, "recipe output was not spoilable"); + assert spoilable != null; + TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(stack), 27, + "recipe output didn't have correct ticks until spoiled"); + }); + } + + @GameTest(template = "lcr_input_separation", batch = "spoilageTests") + public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { + makeSpoilables(helper); + BusHolder busHolder = getBussesAndForm(helper); + ItemStack input = new ItemStack(Items.APPLE); + ISpoilableItem.update(input, null); + Objects.requireNonNull(SpoilableBehaviour.getSpoilable(input)).setTicksUntilSpoiled(input, 8); + busHolder.inputBus1.getInventory().setStackInSlot(0, input); + helper.runAtTickTime(21, () -> { + ItemStack stack = busHolder.outputBus1.getInventory().getStackInSlot(0); + helper.assertTrue(TestUtils.isItemStackEqual( + stack, + new ItemStack(Items.STRUCTURE_BLOCK)), + "incorrect recipe output (%s != %s)".formatted(stack.toString(), + new ItemStack(Items.STRUCTURE_BLOCK).toString())); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + helper.assertTrue(spoilable != null, "recipe output was not spoilable"); + assert spoilable != null; + TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(stack), 40, + "recipe output didn't have correct ticks until spoiled"); + }); + } +} diff --git a/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java b/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java index b418a6400ab..0403cb0bbec 100644 --- a/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java +++ b/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; @@ -28,6 +29,7 @@ import net.minecraft.world.level.block.RedstoneLampBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.items.IItemHandler; import java.util.List; import java.util.Objects; @@ -179,6 +181,10 @@ public static void assertEqual(GameTestHelper helper, List tex "strings not equal: \"%s\" != \"%s\"".formatted(component.toString(), s)); } + public static void assertEqual(GameTestHelper helper, long a, long b, String message) { + helper.assertTrue(a == b, "%s (%d != %d)".formatted(message, a, b)); + } + public static void assertLampOn(GameTestHelper helper, BlockPos pos) { helper.assertBlockProperty(pos, RedstoneLampBlock.LIT, true); } @@ -215,4 +221,8 @@ public static void succeedAfterTest(GameTestHelper helper) { public static void succeedAfterTest(GameTestHelper helper, long timeout) { helper.runAtTickTime(timeout, helper::succeed); } + + public static IItemHandler getItemHandler(GameTestHelper helper, BlockPos pos) { + return GTCapabilityHelper.getItemHandler(helper.getLevel(), helper.absolutePos(pos), null); + } } From 8689cda74f2e8b8c36c4061f841996286c8322b9 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 20:41:32 +0300 Subject: [PATCH 030/123] even more tests --- .../common/item/SpoilableBehaviourTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index b1ef9e40c09..23372b60436 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -15,6 +15,8 @@ import net.minecraft.gametest.framework.BeforeBatch; import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; @@ -180,4 +182,26 @@ public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { "recipe output didn't have correct ticks until spoiled"); }); } + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void droppedItemSpoils(GameTestHelper helper) { + makeSpoilables(helper); + ItemEntity item = helper.spawnItem(Items.JIGSAW, new BlockPos(1, 1, 1)); + helper.runAtTickTime(10, () -> helper.assertTrue(TestUtils.isItemStackEqual( + item.getItem(), + Items.DIRT.getDefaultInstance()), "item didn't spoil when dropped")); + } + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void itemSpoilsInInventory(GameTestHelper helper) { + makeSpoilables(helper); + Player player = helper.makeMockPlayer(); + player.getInventory().setItem(0, Items.JIGSAW.getDefaultInstance()); + player.tick(); + helper.runAtTickTime(10, () -> helper.assertTrue(TestUtils.isItemStackEqual( + player.getInventory().getItem(0), + Items.DIRT.getDefaultInstance()), + "item didn't spoil in a player inventory (%s != %s)".formatted(player.getInventory().getItem(0), + Items.DIRT.getDefaultInstance()))); + } } From 2eb133169634c4852f307fb89af533414b9c26f8 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Tue, 9 Sep 2025 21:06:49 +0300 Subject: [PATCH 031/123] moved unsafe cast from NotifiableItemStackHandler to ISpoilableItem --- .../gtceu/api/item/component/ISpoilableItem.java | 8 ++++++++ .../api/machine/trait/NotifiableItemStackHandler.java | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 360757552a8..9ff69b30b45 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -33,6 +33,14 @@ default void setTicksUntilSpoiled(ItemStack stack, long value) { ((ISpoilableItemStack) (Object) stack).gtceu$setRemainingTicks(null, value); } + default void freezeSpoiling(ItemStack stack) { + ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(true); + } + + default void unfreezeSpoiling(ItemStack stack) { + ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(false); + } + /** * Should return the stack to replace the provided stack with when it spoils */ diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index fe8434999a0..7757c313023 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -4,13 +4,14 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; -import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.recipe.DummyCraftingContainer; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; @@ -171,7 +172,8 @@ public static List handleRecipe(IO io, GTRecipe recipe, List Date: Thu, 11 Sep 2025 13:12:20 +0300 Subject: [PATCH 032/123] filtering test --- .../gtceu/api/cover/filter/FilterHandler.java | 2 +- .../api/cover/filter/FilterHandlers.java | 4 +-- .../api/cover/filter/SimpleItemFilter.java | 12 +++++++ .../common/item/SpoilableBehaviourTest.java | 32 +++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java index 5855d4c1be6..3f44eba7f6a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java @@ -51,7 +51,7 @@ public FilterHandler(IEnhancedManaged container) { this.container = container; } - protected abstract F loadFilter(ItemStack filterItem); + public abstract F loadFilter(ItemStack filterItem); protected abstract F getEmptyFilter(); diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java index d2ccc7aa085..28ea2175c36 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java @@ -11,7 +11,7 @@ static FilterHandler item(IEnhancedManaged container) { return new FilterHandler<>(container) { @Override - protected ItemFilter loadFilter(ItemStack filterItem) { + public ItemFilter loadFilter(ItemStack filterItem) { return ItemFilter.loadFilter(filterItem); } @@ -31,7 +31,7 @@ static FilterHandler fluid(IEnhancedManaged container) return new FilterHandler<>(container) { @Override - protected FluidFilter loadFilter(ItemStack filterItem) { + public FluidFilter loadFilter(ItemStack filterItem) { return FluidFilter.loadFilter(filterItem); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java index 2d9bba66e45..ebf439b10a0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java @@ -46,6 +46,18 @@ public static SimpleItemFilter loadFilter(ItemStack itemStack) { return loadFilter(itemStack.getOrCreateTag(), filter -> itemStack.setTag(filter.saveFilter())); } + public static SimpleItemFilter forItems(ItemStack... items) { + SimpleItemFilter filter = new SimpleItemFilter(); + filter.ignoreNbt = true; + filter.isBlackList = false; + int i = 0; + for (ItemStack item : items) { + filter.matches[i] = item.copy(); + i++; + } + return filter; + } + private static SimpleItemFilter loadFilter(CompoundTag tag, Consumer itemWriter) { var handler = new SimpleItemFilter(); handler.itemWriter = itemWriter; diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index 23372b60436..c66829611f4 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -2,9 +2,12 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.cover.filter.SimpleItemFilter; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipeType; +import com.gregtechceu.gtceu.common.cover.ConveyorCover; +import com.gregtechceu.gtceu.common.data.GTItems; import com.gregtechceu.gtceu.common.data.GTMachines; import com.gregtechceu.gtceu.common.machine.multiblock.part.FluidHatchPartMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.ItemBusPartMachine; @@ -12,9 +15,11 @@ import com.gregtechceu.gtceu.gametest.util.TestUtils; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.gametest.framework.BeforeBatch; import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -204,4 +209,31 @@ public static void itemSpoilsInInventory(GameTestHelper helper) { "item didn't spoil in a player inventory (%s != %s)".formatted(player.getInventory().getItem(0), Items.DIRT.getDefaultInstance()))); } + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void spoilableFilteringTest(GameTestHelper helper) { + makeSpoilables(helper); + CrateMachine crate1 = (CrateMachine) TestUtils.setMachine(helper, new BlockPos(1, 1, 1), + GTMachines.STEEL_CRATE); + CrateMachine crate2 = (CrateMachine) TestUtils.setMachine(helper, new BlockPos(1, 2, 1), + GTMachines.STEEL_CRATE); + ConveyorCover cover = (ConveyorCover) TestUtils.placeCover(helper, crate1, GTItems.CONVEYOR_MODULE_HV.asStack(), + Direction.UP); + CompoundTag filterTag = SimpleItemFilter.forItems(Items.STRUCTURE_BLOCK.getDefaultInstance()).saveFilter(); + ItemStack filter = GTItems.ITEM_FILTER.asStack(); + filter.setTag(filterTag); + cover.getFilterHandler().loadFilter(filter); + cover.setWorkingEnabled(false); + crate1.inventory.setStackInSlot(0, Items.STRUCTURE_BLOCK.getDefaultInstance()); + helper.runAtTickTime(10, () -> cover.setWorkingEnabled(true)); + helper.runAtTickTime(20, () -> { + ItemStack stack = crate2.inventory.getStackInSlot(0); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + helper.assertTrue(TestUtils.isItemStackEqual(stack, Items.STRUCTURE_BLOCK.getDefaultInstance()), + "wrong item"); + helper.assertTrue(spoilable != null, "spoilable was null"); + assert spoilable != null; + TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(stack), "wrong ticks until spoiled"); + }); + } } From ffa3c852ec44f2f7193eb52d48e367201075e99c Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Thu, 11 Sep 2025 13:47:50 +0300 Subject: [PATCH 033/123] another test (also made items not spoil in phantom slots) --- .../api/cover/filter/SimpleItemFilter.java | 4 +++ .../api/gui/widget/PhantomSlotWidget.java | 9 +++++- .../gtceu/core/mixins/ItemStackMixin.java | 3 +- .../common/item/SpoilableBehaviourTest.java | 32 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java index ebf439b10a0..cff6d340fea 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java @@ -3,7 +3,9 @@ import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.PhantomSlotWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -53,6 +55,8 @@ public static SimpleItemFilter forItems(ItemStack... items) { int i = 0; for (ItemStack item : items) { filter.matches[i] = item.copy(); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(filter.matches[i]); + if (spoilable != null) spoilable.freezeSpoiling(filter.matches[i]); i++; } return filter; diff --git a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java index 190de3930ff..57b11c9b853 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java @@ -1,6 +1,8 @@ package com.gregtechceu.gtceu.api.gui.widget; import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.lowdragmc.lowdraglib.gui.editor.annotation.ConfigSetter; import com.lowdragmc.lowdraglib.gui.editor.annotation.Configurable; @@ -85,7 +87,10 @@ public PhantomSlotWidget setMaxStackSize(int stackSize) { public boolean mouseClicked(double mouseX, double mouseY, int button) { if (slotReference != null && isMouseOverElement(mouseX, mouseY) && gui != null) { if (isClientSideWidget && !gui.getModularUIContainer().getCarried().isEmpty()) { - slotReference.set(gui.getModularUIContainer().getCarried()); + ItemStack carried = gui.getModularUIContainer().getCarried().copy(); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(carried); + if (spoilable != null) spoilable.freezeSpoiling(carried); + slotReference.set(carried); } else if (button == 1 && clearSlotOnRightClick && !slotReference.getItem().isEmpty()) { slotReference.set(ItemStack.EMPTY); writeClientAction(2, buf -> {}); @@ -248,6 +253,8 @@ private void fillPhantomSlot(Slot slot, ItemStack stackHeld, int mouseButton) { stackSize = slot.getMaxStackSize(); } ItemStack phantomStack = stackHeld.copy(); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(phantomStack); + if (spoilable != null) spoilable.freezeSpoiling(phantomStack); phantomStack.setCount(Math.min(maxStackSize, stackSize)); if (validator.test(phantomStack)) slot.set(phantomStack); } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index d468dea817d..dcd36d70290 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -235,7 +235,8 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn if (modifiedTag1 != null) modifiedTag1.remove("GTCEu_spoilable"); if (modifiedTag2 != null) modifiedTag2.remove("GTCEu_spoilable"); isSameItem = isSameItem && Objects.equals(modifiedTag1, modifiedTag2); - if (isSameItem && tag1 != null && tag2 != null) { + if (isSameItem && tag1 != null && tag2 != null && + !(tag1.contains("frozenRemainingTicks") || tag2.contains("frozenRemainingTicks"))) { long tick1 = tag1.getLong("creation_tick"); long tick2 = tag2.getLong("creation_tick"); if (tick1 != tick2) { diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index c66829611f4..7597ef548ed 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -236,4 +236,36 @@ public static void spoilableFilteringTest(GameTestHelper helper) { TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(stack), "wrong ticks until spoiled"); }); } + + @GameTest(template = "empty_5x5", batch = "spoilageTests") + public static void spoilableFilteringWithSpoilableTest(GameTestHelper helper) { + makeSpoilables(helper); + CrateMachine crate1 = (CrateMachine) TestUtils.setMachine(helper, new BlockPos(1, 1, 1), + GTMachines.STEEL_CRATE); + CrateMachine crate2 = (CrateMachine) TestUtils.setMachine(helper, new BlockPos(1, 2, 1), + GTMachines.STEEL_CRATE); + ConveyorCover cover = (ConveyorCover) TestUtils.placeCover(helper, crate1, GTItems.CONVEYOR_MODULE_HV.asStack(), + Direction.UP); + ItemStack itemForFilter = Items.STRUCTURE_BLOCK.getDefaultInstance(); + ISpoilableItem filterSpoilable = SpoilableBehaviour.getSpoilable(itemForFilter); + assert filterSpoilable != null; + ISpoilableItem.update(itemForFilter, null); + filterSpoilable.setTicksUntilSpoiled(itemForFilter, 5); + CompoundTag filterTag = SimpleItemFilter.forItems(itemForFilter).saveFilter(); + ItemStack filter = GTItems.ITEM_FILTER.asStack(); + filter.setTag(filterTag); + cover.getFilterHandler().loadFilter(filter); + cover.setWorkingEnabled(false); + crate1.inventory.setStackInSlot(0, Items.STRUCTURE_BLOCK.getDefaultInstance()); + helper.runAtTickTime(10, () -> cover.setWorkingEnabled(true)); + helper.runAtTickTime(20, () -> { + ItemStack stack = crate2.inventory.getStackInSlot(0); + ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + helper.assertTrue(TestUtils.isItemStackEqual(stack, Items.STRUCTURE_BLOCK.getDefaultInstance()), + "wrong item"); + helper.assertTrue(spoilable != null, "spoilable was null"); + assert spoilable != null; + TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(stack), "wrong ticks until spoiled"); + }); + } } From ef7b6a3215e478ada1601c870d6e21546b7bb7e0 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 12 Sep 2025 10:52:44 +0300 Subject: [PATCH 034/123] formatting --- src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java b/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java index 0ceac535feb..4468a421855 100644 --- a/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java +++ b/src/test/java/com/gregtechceu/gtceu/gametest/util/TestUtils.java @@ -186,6 +186,7 @@ public static void assertEqual(GameTestHelper helper, List tex public static void assertEqual(GameTestHelper helper, long a, long b, String message) { helper.assertTrue(a == b, "%s (%d != %d)".formatted(message, a, b)); } + public static void assertEqual(GameTestHelper helper, ItemStack stack1, ItemStack stack2) { helper.assertTrue(isItemStackEqual(stack1, stack2), "Item stacks not equal: \"%s\" != \"%s\"".formatted(stack1.toString(), stack2.toString())); From aed4046aef556274fd94b95a572188f0b4dc1ce5 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 12 Sep 2025 13:53:57 +0300 Subject: [PATCH 035/123] made filtering work correctly when comparing by nbt --- .../gtceu/core/mixins/ItemStackMixin.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index dcd36d70290..4406913d5a1 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -235,18 +235,27 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn if (modifiedTag1 != null) modifiedTag1.remove("GTCEu_spoilable"); if (modifiedTag2 != null) modifiedTag2.remove("GTCEu_spoilable"); isSameItem = isSameItem && Objects.equals(modifiedTag1, modifiedTag2); - if (isSameItem && tag1 != null && tag2 != null && - !(tag1.contains("frozenRemainingTicks") || tag2.contains("frozenRemainingTicks"))) { - long tick1 = tag1.getLong("creation_tick"); - long tick2 = tag2.getLong("creation_tick"); - if (tick1 != tick2) { - long avg; - if (stack.getCount() + other.getCount() > 0) - avg = (tick1 * stack.getCount() + tick2 * other.getCount()) / - (stack.getCount() + other.getCount()); - else avg = tick1; - tag1.putLong("creation_tick", avg); - tag2.putLong("creation_tick", avg); + if (isSameItem && tag1 != null && tag2 != null) { + if (tag1.contains("frozenRemainingTicks") || tag2.contains("frozenRemainingTicks")) { + ISpoilableItem spoilable1 = SpoilableBehaviour.getSpoilable(stack), + spoilable2 = SpoilableBehaviour.getSpoilable(other); + if (spoilable1 != null && spoilable2 != null) { + cir.setReturnValue( + spoilable1.getTicksUntilSpoiled(stack) == spoilable2.getTicksUntilSpoiled(other)); + cir.cancel(); + } + } else { + long tick1 = tag1.getLong("creation_tick"); + long tick2 = tag2.getLong("creation_tick"); + if (tick1 != tick2) { + long avg; + if (stack.getCount() + other.getCount() > 0) + avg = (tick1 * stack.getCount() + tick2 * other.getCount()) / + (stack.getCount() + other.getCount()); + else avg = tick1; + tag1.putLong("creation_tick", avg); + tag2.putLong("creation_tick", avg); + } } } cir.setReturnValue(isSameItem && Objects.equals(stack.getTag(), other.getTag())); From 1dff6f2aee9a8286579e3d82fe4c71b898a2f81f Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 12 Sep 2025 13:56:26 +0300 Subject: [PATCH 036/123] moved getSpoilable to ISpoilableItem --- .../api/cover/filter/SimpleItemFilter.java | 3 +-- .../api/gui/widget/PhantomSlotWidget.java | 5 ++-- .../api/item/component/ISpoilableItem.java | 22 +++++++++++++++++ .../trait/NotifiableItemStackHandler.java | 3 +-- .../gtceu/common/data/GTRecipeModifiers.java | 5 ++-- .../gtceu/common/item/SpoilableBehaviour.java | 19 --------------- .../gtceu/core/mixins/ItemStackMixin.java | 23 +++++++++--------- .../common/item/SpoilableBehaviourTest.java | 24 +++++++++---------- 8 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java index cff6d340fea..74c553f8355 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java @@ -5,7 +5,6 @@ import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -55,7 +54,7 @@ public static SimpleItemFilter forItems(ItemStack... items) { int i = 0; for (ItemStack item : items) { filter.matches[i] = item.copy(); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(filter.matches[i]); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(filter.matches[i]); if (spoilable != null) spoilable.freezeSpoiling(filter.matches[i]); i++; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java index 57b11c9b853..c41992faeeb 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java @@ -2,7 +2,6 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; -import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.lowdragmc.lowdraglib.gui.editor.annotation.ConfigSetter; import com.lowdragmc.lowdraglib.gui.editor.annotation.Configurable; @@ -88,7 +87,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { if (slotReference != null && isMouseOverElement(mouseX, mouseY) && gui != null) { if (isClientSideWidget && !gui.getModularUIContainer().getCarried().isEmpty()) { ItemStack carried = gui.getModularUIContainer().getCarried().copy(); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(carried); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(carried); if (spoilable != null) spoilable.freezeSpoiling(carried); slotReference.set(carried); } else if (button == 1 && clearSlotOnRightClick && !slotReference.getItem().isEmpty()) { @@ -253,7 +252,7 @@ private void fillPhantomSlot(Slot slot, ItemStack stackHeld, int mouseButton) { stackSize = slot.getMaxStackSize(); } ItemStack phantomStack = stackHeld.copy(); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(phantomStack); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(phantomStack); if (spoilable != null) spoilable.freezeSpoiling(phantomStack); phantomStack.setCount(Math.min(maxStackSize, stackSize)); if (validator.test(phantomStack)) slot.set(phantomStack); diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 9ff69b30b45..0b6636598a3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -1,14 +1,23 @@ package com.gregtechceu.gtceu.api.item.component; +import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; +import java.util.HashMap; +import java.util.Map; + import javax.annotation.Nullable; public interface ISpoilableItem extends IItemComponent { + Map ATTACHED_COMPONENTS = new HashMap<>(); + /** * Initializes this ItemStack's spoilage timer if it wasn't initialized before. * Should be called when it finishes crafting, for example. @@ -19,6 +28,19 @@ static void update(ItemStack stack, @Nullable Level level) { ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level, true); } + static @org.jetbrains.annotations.Nullable ISpoilableItem getSpoilable(ItemStack stack) { + Item item = stack.getItem(); + if (item instanceof ISpoilableItem spoilable) return spoilable; + if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); + SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.apply(item); + if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); + return behaviour; + } + + static void unspoil(ItemLike item) { + ATTACHED_COMPONENTS.remove(item.asItem()); + } + /** * Should return the amount of ticks that this item can stay fresh. * The result of this method shouldn't be based on the freshness of the provided stack diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index 7757c313023..299e0740796 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -11,7 +11,6 @@ import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; @@ -172,7 +171,7 @@ public static List handleRecipe(IO io, GTRecipe recipe, List 0) { double spoiled = spoilProgress / spoilableCount; spoilable.setTicksUntilSpoiled(stack, (long) (spoiled * spoilable.getSpoilTicks(stack))); diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index f5a3960415c..995935dbbc7 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -1,6 +1,5 @@ package com.gregtechceu.gtceu.common.item; -import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; @@ -10,7 +9,6 @@ import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.util.FastColor; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; @@ -19,15 +17,11 @@ import it.unimi.dsi.fastutil.ints.IntIntPair; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.Function; public class SpoilableBehaviour implements ISpoilableItem, IAddInformation, IDurabilityBar { - private static final Map ATTACHED_COMPONENTS = new HashMap<>(); - private final Function ticks; private final Function spoilResult; private final Function spoilsInto; @@ -112,19 +106,6 @@ public float getDurabilityForDisplay(ItemStack stack) { return (float) getTicksUntilSpoiled(stack) / getSpoilTicks(stack); } - public static @Nullable ISpoilableItem getSpoilable(ItemStack stack) { - Item item = stack.getItem(); - if (item instanceof ISpoilableItem spoilable) return spoilable; - if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); - SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.apply(item); - if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); - return behaviour; - } - - public static void unspoil(ItemLike item) { - ATTACHED_COMPONENTS.remove(item.asItem()); - } - public void attachTo(ItemLike item) { ATTACHED_COMPONENTS.put(item.asItem(), this); } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 4406913d5a1..b6824109722 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -5,7 +5,6 @@ import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; -import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.utils.FormattingUtil; import net.minecraft.ChatFormatting; @@ -98,7 +97,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 gtceu$isUpdating = true; MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (spoilable != null && spoilable.shouldSpoil((ItemStack) (Object) this)) { if (spoilable.getSpoilTicks((ItemStack) (Object) this) < 0) { gtceu$isUpdating = false; @@ -126,7 +125,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 delegate = ForgeRegistries.ITEMS.getDelegateOrThrow(item); count = newStack.getCount(); this.tag = newStack.getTag(); - ISpoilableItem newSpoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem newSpoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (newSpoilable != null && (this.tag == null || !this.tag.contains("GTCEu_spoilable"))) { getOrCreateTagElement("GTCEu_spoilable").putLong("creation_tick", level.getGameTime() - timeDifference); @@ -183,7 +182,7 @@ private void onCraftedUpdateFreshness(Level level, Player player, int amount, Ca gtceu$updateFreshness(level, false); MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); CompoundTag spoilTag = getTagElement("GTCEu_spoilable"); if (level != null && spoilTag != null && spoilable != null) { if (spoilTag.contains("frozenRemainingTicks")) return spoilTag.getLong("frozenRemainingTicks"); @@ -200,7 +199,7 @@ private void onCraftedUpdateFreshness(Level level, Player player, int amount, Ca gtceu$updateFreshness(level, false); MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (level != null && getTagElement("GTCEu_spoilable") != null && spoilable != null) gtceu$setCreationTick(level, level.getGameTime() - spoilable.getSpoilTicks((ItemStack) (Object) this) + value); @@ -209,7 +208,7 @@ private void onCraftedUpdateFreshness(Level level, Player player, int amount, Ca @Unique @Override public void gtceu$setFreezeSpoiling(boolean freezeUpdates) { - if (SpoilableBehaviour.getSpoilable((ItemStack) (Object) this) == null) return; + if (ISpoilableItem.getSpoilable((ItemStack) (Object) this) == null) return; MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); Level level = server == null ? null : server.overworld(); if (level == null) return; @@ -237,8 +236,8 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn isSameItem = isSameItem && Objects.equals(modifiedTag1, modifiedTag2); if (isSameItem && tag1 != null && tag2 != null) { if (tag1.contains("frozenRemainingTicks") || tag2.contains("frozenRemainingTicks")) { - ISpoilableItem spoilable1 = SpoilableBehaviour.getSpoilable(stack), - spoilable2 = SpoilableBehaviour.getSpoilable(other); + ISpoilableItem spoilable1 = ISpoilableItem.getSpoilable(stack), + spoilable2 = ISpoilableItem.getSpoilable(other); if (spoilable1 != null && spoilable2 != null) { cir.setReturnValue( spoilable1.getTicksUntilSpoiled(stack) == spoilable2.getTicksUntilSpoiled(other)); @@ -268,7 +267,7 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn locals = LocalCapture.CAPTURE_FAILSOFT) private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackInfoReturnable> cir, List list) { - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IAddInformation addInformation) { addInformation.appendHoverText((ItemStack) (Object) this, player == null ? null : player.level(), list, isAdvanced); @@ -284,7 +283,7 @@ private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackIn @Inject(at = @At("HEAD"), method = "isBarVisible", cancellable = true) private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.isBarVisible((ItemStack) (Object) this)); cir.cancel(); @@ -296,7 +295,7 @@ private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarColor", cancellable = true) private void aprilFoolsBarColor(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.getBarColor((ItemStack) (Object) this)); cir.cancel(); @@ -308,7 +307,7 @@ private void aprilFoolsBarColor(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarWidth", cancellable = true) private void aprilFoolsBarWidth(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.getBarWidth((ItemStack) (Object) this)); cir.cancel(); diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index 7597ef548ed..f156013a45b 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -66,10 +66,10 @@ private static void makeSpoilables(GameTestHelper helper) { new SpoilableBehaviour(40, Items.STRUCTURE_VOID).attachTo(Items.STRUCTURE_BLOCK); new SpoilableBehaviour(10, Items.JIGSAW).attachTo(Items.STRUCTURE_VOID); helper.runAtTickTime(100, () -> { - SpoilableBehaviour.unspoil(Items.JIGSAW); - SpoilableBehaviour.unspoil(Items.STRUCTURE_VOID); - SpoilableBehaviour.unspoil(Items.APPLE); - SpoilableBehaviour.unspoil(Items.STRUCTURE_BLOCK); + ISpoilableItem.unspoil(Items.JIGSAW); + ISpoilableItem.unspoil(Items.STRUCTURE_VOID); + ISpoilableItem.unspoil(Items.APPLE); + ISpoilableItem.unspoil(Items.STRUCTURE_BLOCK); helper.succeed(); }); } @@ -128,7 +128,7 @@ public static void itemSpoilsInCrate(GameTestHelper helper) { helper.assertTrue(TestUtils.isItemStackEqual( Items.JIGSAW.getDefaultInstance().copyWithCount(23), stack), "jigsaw spoiled 1 tick earlier"); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); helper.assertTrue(spoilable != null, "spoilable was null when shouldn't have"); assert spoilable != null; helper.assertTrue(spoilable.shouldSpoil(stack), "shouldSpoil returned false on spoilable item"); @@ -148,7 +148,7 @@ public static void spoilageTransfersInRecipe(GameTestHelper helper) { BusHolder busHolder = getBussesAndForm(helper); ItemStack input = new ItemStack(Items.JIGSAW); ISpoilableItem.update(input, null); - Objects.requireNonNull(SpoilableBehaviour.getSpoilable(input)).setTicksUntilSpoiled(input, 8); + Objects.requireNonNull(ISpoilableItem.getSpoilable(input)).setTicksUntilSpoiled(input, 8); busHolder.inputBus1.getInventory().setStackInSlot(0, input); helper.runAtTickTime(21, () -> { ItemStack stack = busHolder.outputBus1.getInventory().getStackInSlot(0); @@ -157,7 +157,7 @@ public static void spoilageTransfersInRecipe(GameTestHelper helper) { new ItemStack(Items.STRUCTURE_BLOCK)), "incorrect recipe output (%s != %s)".formatted(stack.toString(), new ItemStack(Items.STRUCTURE_BLOCK).toString())); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); helper.assertTrue(spoilable != null, "recipe output was not spoilable"); assert spoilable != null; TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(stack), 27, @@ -171,7 +171,7 @@ public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { BusHolder busHolder = getBussesAndForm(helper); ItemStack input = new ItemStack(Items.APPLE); ISpoilableItem.update(input, null); - Objects.requireNonNull(SpoilableBehaviour.getSpoilable(input)).setTicksUntilSpoiled(input, 8); + Objects.requireNonNull(ISpoilableItem.getSpoilable(input)).setTicksUntilSpoiled(input, 8); busHolder.inputBus1.getInventory().setStackInSlot(0, input); helper.runAtTickTime(21, () -> { ItemStack stack = busHolder.outputBus1.getInventory().getStackInSlot(0); @@ -180,7 +180,7 @@ public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { new ItemStack(Items.STRUCTURE_BLOCK)), "incorrect recipe output (%s != %s)".formatted(stack.toString(), new ItemStack(Items.STRUCTURE_BLOCK).toString())); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); helper.assertTrue(spoilable != null, "recipe output was not spoilable"); assert spoilable != null; TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(stack), 40, @@ -228,7 +228,7 @@ public static void spoilableFilteringTest(GameTestHelper helper) { helper.runAtTickTime(10, () -> cover.setWorkingEnabled(true)); helper.runAtTickTime(20, () -> { ItemStack stack = crate2.inventory.getStackInSlot(0); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); helper.assertTrue(TestUtils.isItemStackEqual(stack, Items.STRUCTURE_BLOCK.getDefaultInstance()), "wrong item"); helper.assertTrue(spoilable != null, "spoilable was null"); @@ -247,7 +247,7 @@ public static void spoilableFilteringWithSpoilableTest(GameTestHelper helper) { ConveyorCover cover = (ConveyorCover) TestUtils.placeCover(helper, crate1, GTItems.CONVEYOR_MODULE_HV.asStack(), Direction.UP); ItemStack itemForFilter = Items.STRUCTURE_BLOCK.getDefaultInstance(); - ISpoilableItem filterSpoilable = SpoilableBehaviour.getSpoilable(itemForFilter); + ISpoilableItem filterSpoilable = ISpoilableItem.getSpoilable(itemForFilter); assert filterSpoilable != null; ISpoilableItem.update(itemForFilter, null); filterSpoilable.setTicksUntilSpoiled(itemForFilter, 5); @@ -260,7 +260,7 @@ public static void spoilableFilteringWithSpoilableTest(GameTestHelper helper) { helper.runAtTickTime(10, () -> cover.setWorkingEnabled(true)); helper.runAtTickTime(20, () -> { ItemStack stack = crate2.inventory.getStackInSlot(0); - ISpoilableItem spoilable = SpoilableBehaviour.getSpoilable(stack); + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); helper.assertTrue(TestUtils.isItemStackEqual(stack, Items.STRUCTURE_BLOCK.getDefaultInstance()), "wrong item"); helper.assertTrue(spoilable != null, "spoilable was null"); From a8b95bfc6e83a8014f2d5e1b19d60f271797a4c8 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 12 Sep 2025 13:57:44 +0300 Subject: [PATCH 037/123] technically gotta call onAttached when attaching --- .../com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 995935dbbc7..2570e382a4e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -107,6 +107,7 @@ public float getDurabilityForDisplay(ItemStack stack) { } public void attachTo(ItemLike item) { + this.onAttached(item.asItem()); ATTACHED_COMPONENTS.put(item.asItem(), this); } } From 980e9a2b93d0989b71a1045cf659ec2ad1d827e7 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 12 Sep 2025 22:04:54 +0300 Subject: [PATCH 038/123] docs --- .../api/item/component/ISpoilableItem.java | 153 +++++++++++++++++- .../gtceu/common/item/SpoilableBehaviour.java | 2 +- 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 0b6636598a3..3a37005620d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -9,15 +9,87 @@ import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; +import dev.latvian.mods.rhino.util.HideFromJS; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import javax.annotation.Nullable; +/** + * The interface items that spoil should implement. + *

+ * If you are developing an addon, and want to make an {@link Item} + * that does not extend this interface spoilable, use {@link ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike)}. + *

+ *

+ * If you want to make an item that spoils not spoil, use {@link ISpoilableItem#unspoil(ItemLike)}. + *

+ *

+ * Spoilable items will, as the name implies, spoil (who could've thought). + * Due to Minecraft's limitations, items will only start spoiling only if: + *

    + *
  • It is an output of a {@link com.gregtechceu.gtceu.api.recipe.GTRecipe}
  • + *
  • It is crafted in a crafting table
  • + *
  • It is put into a {@link com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler} (almost any GregTech + * container)
  • + *
  • It enters a player's inventory and gets ticked at least once
  • + *
  • It is dropped (exists as an entity)
  • + *
  • Any other mod calls {@link ISpoilableItem#update(ItemStack, Level)} on the item
  • + *
+ * If you are a developer of a mod that adds any other way to obtain items, that doesn't involve + * any of the conditions above being true at any tick, consider adding compatibility with this feature :) + *
+ * Items that don't start spoiling will simply not have spoilable NBT, and as a result, won't spoil, + * until any of the above conditions become true. + *

+ * Also due to Minecraft's limitations, merging stacks with different freshness requires overriding + * {@link ItemStack#isSameItemSameTags(ItemStack, ItemStack)} to make the stacks have the same freshness. + * The only exception is if one of the stacks is frozen, in which case it works as normal, except that + * frozen stacks will be equal to non-frozen stacks if all else is equal. This is done to make filtering work correctly. + *

+ *

+ * Spoilable stacks will be frozen if they enter a {@link com.gregtechceu.gtceu.api.gui.widget.PhantomSlotWidget}, + * to prevent stacks spoiling in filters. If you are a developer of a mod that adds filters, consider calling + * {@link ISpoilableItem#freezeSpoiling(ItemStack)} on stacks entering these filters for compatibility :) + *

+ *

+ * Note that if an item is spoilable, it does not mean that it spoils in all cases, as you can override + * {@link ISpoilableItem#shouldSpoil(ItemStack)} + * in your own implementation of the {@link ISpoilableItem} interface. + *

+ *

+ * You do not have to make your own implementation of {@link ISpoilableItem} to use this mechanic.
+ * Instead, you can use {@link SpoilableBehaviour} and {@link SpoilableBehaviour#attachTo(ItemLike)} to + * make any item from any mod spoil quite easily. This is especially useful for KubeJS devs. + *

+ */ public interface ISpoilableItem extends IItemComponent { + /** + * Should NOT be used outside of {@link ISpoilableItem}. + * + * @see ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike) + * @see ISpoilableItem#unspoil(ItemLike) + */ + @ApiStatus.Internal + @HideFromJS Map ATTACHED_COMPONENTS = new HashMap<>(); + /** + * Should NOT be used outside of {@link ISpoilableItem} + * + * @see ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike) + * @see ISpoilableItem#unspoil(ItemLike) + */ + @ApiStatus.Internal + @HideFromJS + Set UNSPOILED_ITEMS = new HashSet<>(); + /** * Initializes this ItemStack's spoilage timer if it wasn't initialized before. * Should be called when it finishes crafting, for example. @@ -28,17 +100,55 @@ static void update(ItemStack stack, @Nullable Level level) { ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level, true); } - static @org.jetbrains.annotations.Nullable ISpoilableItem getSpoilable(ItemStack stack) { + /** + * Makes an item spoilable by attaching an {@link ISpoilableItem} to it. + * The newly attached spoilable will override any other spoilables that this item may + * have had attached (this includes the {@link Item} itself implementing {@link ISpoilableItem}). + *
+ * Note that this will NOT magically make the {@link Item} implement {@link ISpoilableItem}. + *
+ * This method may be called during gameplay. I have no idea what for, but you can :) + * + * @param spoilable the new spoiling behaviour to assign to this item + * @param item the item to make spoilable + * @see ISpoilableItem#unspoil(ItemLike) + */ + static void attachSpoilable(@NotNull ISpoilableItem spoilable, ItemLike item) { + UNSPOILED_ITEMS.remove(item.asItem()); + ATTACHED_COMPONENTS.put(item.asItem(), spoilable); + } + + /** + * While an {@link ISpoilableItem} may be obtained by simply casting an {@link Item} + * to it (or attempting to), it is not recommended, as an item may be spoilable + * even if its {@link Item} doesn't implement {@link ISpoilableItem}. + * + * @return the {@link ISpoilableItem} of the provided {@link ItemStack} ({@code null} if the item can't spoil) + */ + static @Nullable ISpoilableItem getSpoilable(ItemStack stack) { Item item = stack.getItem(); - if (item instanceof ISpoilableItem spoilable) return spoilable; + if (UNSPOILED_ITEMS.contains(item)) return null; if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); + if (item instanceof ISpoilableItem spoilable) return spoilable; SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.apply(item); if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); return behaviour; } + /** + * Removes any {@link ISpoilableItem} previously attached to the provided {@link Item}. + *
+ * This includes the {@link Item} itself implementing {@link ISpoilableItem} and + * behaviours attached using {@link ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike)}. + *
+ * This method may be called during gameplay. I have no idea what for, but you can :) + * + * @param item the item to remove any spoiling behaviour from + * @see ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike) + */ static void unspoil(ItemLike item) { ATTACHED_COMPONENTS.remove(item.asItem()); + if (item.asItem() instanceof ISpoilableItem) UNSPOILED_ITEMS.add(item.asItem()); } /** @@ -47,18 +157,57 @@ static void unspoil(ItemLike item) { */ long getSpoilTicks(ItemStack stack); + /** + * Please refrain from overriding this method unless absolutely necessary (I have no idea what will happen) + * + * @return the amount of ticks left until the provided {@link ItemStack} spoils. + * The ticks still reduce even when the item is unloaded, and only pause if the + * overworld time pauses, as all tick calculations are done with overworld tick time + * @see ISpoilableItem#setTicksUntilSpoiled(ItemStack, long) + */ default long getTicksUntilSpoiled(ItemStack stack) { return ((ISpoilableItemStack) (Object) stack).gtceu$getRemainingTicks(null); } + /** + * Please refrain from overriding this method unless absolutely necessary (I have no idea what will happen). + *
+ * Sets the amount of ticks left until the provided {@link ItemStack} spoils. + * This modifies the provided stack's NBT data. + * The provided value may be more than {@link ISpoilableItem#getSpoilTicks(ItemStack)} + * + * @see ISpoilableItem#getTicksUntilSpoiled(ItemStack) + */ default void setTicksUntilSpoiled(ItemStack stack, long value) { ((ISpoilableItemStack) (Object) stack).gtceu$setRemainingTicks(null, value); } + /** + * Freezes the provided stack's spoiling progress until it is unfrozen by + * {@link ISpoilableItem#unfreezeSpoiling(ItemStack)}. + * Frozen stacks will NOT spoil, even if {@link ISpoilableItem#getTicksUntilSpoiled(ItemStack)} is {@code <= 0}. + * This method modifies the provided stack's NBT data. + * Calls to {@link ItemStack#isSameItemSameTags(ItemStack, ItemStack)} with a frozen stack as one of the arguments + * will check equality of both stacks' {@link ISpoilableItem#getTicksUntilSpoiled(ItemStack)} values, as well as all + * non-spoilage + * related tags and the equality of the item itself. + * + * @see ISpoilableItem#unfreezeSpoiling(ItemStack) + */ default void freezeSpoiling(ItemStack stack) { ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(true); } + /** + * Please refrain from overriding this method unless absolutely necessary (I have no idea what will happen). + *
+ * Unfreezes the provided stack's spoiling progress. If the stack's + * {@link ISpoilableItem#getTicksUntilSpoiled(ItemStack)} is {@code <= 0}, it will spoil + * immediately after this method call. + * This method modifies the provided stack's NBT data. + * + * @see ISpoilableItem#freezeSpoiling(ItemStack) + */ default void unfreezeSpoiling(ItemStack stack) { ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(false); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 2570e382a4e..627890fdc6a 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -108,6 +108,6 @@ public float getDurabilityForDisplay(ItemStack stack) { public void attachTo(ItemLike item) { this.onAttached(item.asItem()); - ATTACHED_COMPONENTS.put(item.asItem(), this); + ISpoilableItem.attachSpoilable(this, item); } } From b3329cce04f4f6b46cb9728fd83ffafa737f0d00 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sat, 13 Sep 2025 08:01:48 +0300 Subject: [PATCH 039/123] more docs --- .../content/Modpacks/Other-Topics/Spoilage.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/content/Modpacks/Other-Topics/Spoilage.md diff --git a/docs/content/Modpacks/Other-Topics/Spoilage.md b/docs/content/Modpacks/Other-Topics/Spoilage.md new file mode 100644 index 00000000000..2b2761a4451 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/Spoilage.md @@ -0,0 +1,38 @@ +--- +title: Spoilage +--- + +**Spoilage** is a mechanic that allows items to, well, *spoil*.\ +Spoilable items spoil based on the amount of ticks that passed from their creation, +or, more specifically, from one of these events (due to Minecraft's limitations): + - The item was crafted in a GregTech recipe + - The item was crafted in a crafting table + - The item was in a GregTech inventory at any point in time + - The item was in a player's inventory for at least 1 tick + - The item was dropped + - `ISpoilableItem.update(ItemStack, null)` was called + +If you want to make an item spoil, you can either make it implement the `ISpoilableItem` interface, or +attach an `ISpoilableItem` to it. Here's some examples: +```java +public class Example { + public void attachSpoilables() { + // make diamonds spoil into dirt in 100 seconds + new SpoilableBehaviour(Items.DIRT, 20*100).attachTo(Items.DIAMOND); + } + + public void removeSpoilables() { + // make diamonds not spoil anymore + ISpoilableItem.unspoil(Items.DIAMOND); + } + + public void everythingSpoilsRandomly() { + GTValues.DEFAULT_SPOIL_BEHAVIOUR = item -> { + List allItems = ForgeRegistries.ITEMS.getValues().stream().toList(); + Item randomItem = allItems.get(GTValues.RNG.nextIntBetweenInclusive(0, allItems.size() - 1)); + // make everything spoil every 50 seconds + return new SpoilableBehaviour(randomItem, 50*20); + }; + } +} +``` \ No newline at end of file From 64117bb9e089f4114e2c6a6b602cea3991680835 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sat, 13 Sep 2025 08:52:26 +0300 Subject: [PATCH 040/123] even more docs --- .../content/Modpacks/Other-Topics/Spoilage.md | 32 +++++++++++++++++++ .../Recipes/Adding-and-Removing-Recipes.md | 4 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/content/Modpacks/Other-Topics/Spoilage.md b/docs/content/Modpacks/Other-Topics/Spoilage.md index 2b2761a4451..d26f1e88ca2 100644 --- a/docs/content/Modpacks/Other-Topics/Spoilage.md +++ b/docs/content/Modpacks/Other-Topics/Spoilage.md @@ -26,6 +26,33 @@ public class Example { ISpoilableItem.unspoil(Items.DIAMOND); } + public void getAndSetValuesAndStuff(ItemStack stack) { + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + // if spoilable is null, it means the stack can not spoil + if (spoilable != null) { + // get amount of ticks until a completely fresh stack spoils + long totalTicks = spoilable.getSpoilTicks(stack); + // get amount of ticks until this stack spoils (may be more than the previous value in some cases) + long ticksRemaining = spoilable.getTicksUntilSpoiled(stack); + // get the stack this stack spoils into + ItemStack spoilResult = spoilable.spoilResult(stack); + // get whether this stack should START spoiling + boolean shouldStartSpoiling = spoilable.shouldSpoil(stack); + // set the amount of ticks until this stack spoils (may be more than spoilable.getSpoilTicks(stack)) + spoilable.setTicksUntilSpoiled(stack, 12345); + // freeze the spoiling progress of this stack + spoilable.freezeSpoiling(stack); + // unfreeze the spoiling progress of this stack + spoilable.unfreezeSpoiling(stack); + } + } + + public void makeStackStartSpoiling(ItemStack stack) { + // if for some reason the stack still hasn't started spoiling, you can start it using this + // that may happen if it is the result of a non-GT recipe and not a crafting result for example + ISpoilableItem.update(stack, null); + } + public void everythingSpoilsRandomly() { GTValues.DEFAULT_SPOIL_BEHAVIOUR = item -> { List allItems = ForgeRegistries.ITEMS.getValues().stream().toList(); @@ -34,5 +61,10 @@ public class Example { return new SpoilableBehaviour(randomItem, 50*20); }; } + + public void makeEverythingSpoilEverywhere() { + // please do not actually do this + GTValues.BREAK_EVERYTHING_LOL = true; + } } ``` \ No newline at end of file diff --git a/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md b/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md index 90aa03bd076..e6d644c90bd 100644 --- a/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md +++ b/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md @@ -139,7 +139,9 @@ to distinguish them from other recipes in the same machine with similar ingredie running or all at once at recipe start/end. Set to true with `.perTick(true)` to make the recipe builder consider any following input/output calls as per-tick. Remember to set the value to false with `.perTick(false)` after the calls you intend to be per-tick, to prevent behaviour you don't want! - + - `.keepSpoilingProgress()`:\ + If set to true, spoilable outputs' freshness will depend on the recipe's inputs' freshness (default)\ + If set to false, spoilable outputs of this recipe will always be crafted completely fresh ### The Research System From e768a8bca2c1f517ef8374ac716fada1d80c0040 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sat, 13 Sep 2025 11:40:59 +0300 Subject: [PATCH 041/123] docs fixes --- .../content/Modpacks/Other-Topics/Spoilage.md | 34 +++++++++---------- .../com/gregtechceu/gtceu/api/GTValues.java | 18 ---------- .../api/item/component/ISpoilableItem.java | 16 +++++++-- .../gtceu/core/mixins/ItemStackMixin.java | 2 +- 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/docs/content/Modpacks/Other-Topics/Spoilage.md b/docs/content/Modpacks/Other-Topics/Spoilage.md index d26f1e88ca2..c68a85c0fdc 100644 --- a/docs/content/Modpacks/Other-Topics/Spoilage.md +++ b/docs/content/Modpacks/Other-Topics/Spoilage.md @@ -2,7 +2,7 @@ title: Spoilage --- -**Spoilage** is a mechanic that allows items to, well, *spoil*.\ +**Spoilage** is a mechanic that allows items to *spoil*.\ Spoilable items spoil based on the amount of ticks that passed from their creation, or, more specifically, from one of these events (due to Minecraft's limitations): - The item was crafted in a GregTech recipe @@ -17,54 +17,54 @@ attach an `ISpoilableItem` to it. Here's some examples: ```java public class Example { public void attachSpoilables() { - // make diamonds spoil into dirt in 100 seconds + // Make diamonds spoil into dirt in 100 seconds new SpoilableBehaviour(Items.DIRT, 20*100).attachTo(Items.DIAMOND); } public void removeSpoilables() { - // make diamonds not spoil anymore + // Make diamonds not spoil anymore ISpoilableItem.unspoil(Items.DIAMOND); } public void getAndSetValuesAndStuff(ItemStack stack) { ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); - // if spoilable is null, it means the stack can not spoil + // If spoilable is null, it means the stack can not spoil if (spoilable != null) { - // get amount of ticks until a completely fresh stack spoils + // Get amount of ticks until a completely fresh stack spoils long totalTicks = spoilable.getSpoilTicks(stack); - // get amount of ticks until this stack spoils (may be more than the previous value in some cases) + // Get amount of ticks until this stack spoils (may be more than the previous value in some cases) long ticksRemaining = spoilable.getTicksUntilSpoiled(stack); - // get the stack this stack spoils into + // Get the stack this stack spoils into ItemStack spoilResult = spoilable.spoilResult(stack); - // get whether this stack should START spoiling + // Get whether this stack should START spoiling boolean shouldStartSpoiling = spoilable.shouldSpoil(stack); - // set the amount of ticks until this stack spoils (may be more than spoilable.getSpoilTicks(stack)) + // Get the amount of ticks until this stack spoils (may be more than spoilable.getSpoilTicks(stack)) spoilable.setTicksUntilSpoiled(stack, 12345); - // freeze the spoiling progress of this stack + // Freeze the spoiling progress of this stack spoilable.freezeSpoiling(stack); - // unfreeze the spoiling progress of this stack + // Unfreeze the spoiling progress of this stack spoilable.unfreezeSpoiling(stack); } } public void makeStackStartSpoiling(ItemStack stack) { - // if for some reason the stack still hasn't started spoiling, you can start it using this - // that may happen if it is the result of a non-GT recipe and not a crafting result for example + // If for some reason the stack still hasn't started spoiling, you can start the spoiling progress using this + // That may happen if it is the result of a non-GT recipe and not a crafting result for example ISpoilableItem.update(stack, null); } public void everythingSpoilsRandomly() { - GTValues.DEFAULT_SPOIL_BEHAVIOUR = item -> { + ISpoilableItem.DEFAULT_SPOIL_BEHAVIOUR = item -> { List allItems = ForgeRegistries.ITEMS.getValues().stream().toList(); Item randomItem = allItems.get(GTValues.RNG.nextIntBetweenInclusive(0, allItems.size() - 1)); - // make everything spoil every 50 seconds + // Make everything spoil every 50 seconds into a random item return new SpoilableBehaviour(randomItem, 50*20); }; } public void makeEverythingSpoilEverywhere() { - // please do not actually do this - GTValues.BREAK_EVERYTHING_LOL = true; + // Please do not actually do this + ISpoilableItem.BREAK_EVERYTHING = true; } } ``` \ No newline at end of file diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java index 18857872c8b..9cf2b5209ba 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTValues.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTValues.java @@ -1,18 +1,13 @@ package com.gregtechceu.gtceu.api; -import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; import net.minecraft.util.RandomSource; -import net.minecraft.world.item.Item; - -import org.jetbrains.annotations.NotNull; import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.function.BooleanSupplier; -import java.util.function.Function; import java.util.function.IntFunction; import static net.minecraft.ChatFormatting.*; @@ -318,18 +313,5 @@ public static int[] tiersBetween(int minInclusive, int maxInclusive) { return now.getMonth() == Month.DECEMBER && (now.getDayOfMonth() == 24 || now.getDayOfMonth() == 25); }; - /** - * Makes EVERY {@code ItemStack} spoilable. EVERY SINGLE ONE, even in recipe ingredients and results, creative - * inventory, JEI, EMI, - * statistics menu, EVERYTHING. - */ - public static boolean BREAK_EVERYTHING_LOL = false; - - /** - * Supplier to get the {@link SpoilableBehaviour} of an {@code Item} that doesn't have one attached - * Called once for every {@code Item}. Return {@code null} to not attach any {@link SpoilableBehaviour} (default). - */ - public static @NotNull Function DEFAULT_SPOIL_BEHAVIOR = item -> null; - public static final String CUSTOM_TAG_SOURCE = "GTCEu Custom Tags"; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 3a37005620d..726998d55cd 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -1,6 +1,5 @@ package com.gregtechceu.gtceu.api.item.component; -import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; @@ -17,6 +16,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.function.Function; import javax.annotation.Nullable; @@ -89,6 +89,18 @@ public interface ISpoilableItem extends IItemComponent { @ApiStatus.Internal @HideFromJS Set UNSPOILED_ITEMS = new HashSet<>(); + /** + * Makes EVERY {@code ItemStack} spoilable. EVERY SINGLE ONE, even in recipe ingredients and results, creative + * inventory, JEI, EMI, + * statistics menu, EVERYTHING. + */ + boolean BREAK_EVERYTHING = false; + /** + * Supplier to get the {@link SpoilableBehaviour} of an {@code Item} that doesn't have one attached + * Called once for every {@code Item}. Return {@code null} to not attach any {@link SpoilableBehaviour} (default). + */ + @NotNull + Function DEFAULT_SPOIL_BEHAVIOR = item -> null; /** * Initializes this ItemStack's spoilage timer if it wasn't initialized before. @@ -130,7 +142,7 @@ static void attachSpoilable(@NotNull ISpoilableItem spoilable, ItemLike item) { if (UNSPOILED_ITEMS.contains(item)) return null; if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); if (item instanceof ISpoilableItem spoilable) return spoilable; - SpoilableBehaviour behaviour = GTValues.DEFAULT_SPOIL_BEHAVIOR.apply(item); + SpoilableBehaviour behaviour = DEFAULT_SPOIL_BEHAVIOR.apply(item); if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); return behaviour; } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index b6824109722..1e929507e7a 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -147,7 +147,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 private void injectedFreshnessUpdate(CallbackInfoReturnable cir) { MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); gtceu$updateFreshness(server == null ? null : server.overworld(), - GTValues.BREAK_EVERYTHING_LOL || entityRepresentation != null); + ISpoilableItem.BREAK_EVERYTHING || entityRepresentation != null); } @Inject(at = @At("HEAD"), method = "inventoryTick") From b66c5dcab6782823b2a30796c2ba8001367ba671 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sat, 13 Sep 2025 16:37:36 +0300 Subject: [PATCH 042/123] indentation fixes --- docs/content/Modpacks/Other-Topics/Spoilage.md | 15 ++++++++------- .../Recipes/Adding-and-Removing-Recipes.md | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/content/Modpacks/Other-Topics/Spoilage.md b/docs/content/Modpacks/Other-Topics/Spoilage.md index c68a85c0fdc..8c2ace6f021 100644 --- a/docs/content/Modpacks/Other-Topics/Spoilage.md +++ b/docs/content/Modpacks/Other-Topics/Spoilage.md @@ -2,15 +2,16 @@ title: Spoilage --- -**Spoilage** is a mechanic that allows items to *spoil*.\ +**Spoilage** is a mechanic that allows items to *spoil*.
Spoilable items spoil based on the amount of ticks that passed from their creation, or, more specifically, from one of these events (due to Minecraft's limitations): - - The item was crafted in a GregTech recipe - - The item was crafted in a crafting table - - The item was in a GregTech inventory at any point in time - - The item was in a player's inventory for at least 1 tick - - The item was dropped - - `ISpoilableItem.update(ItemStack, null)` was called + +- The item was crafted in a GregTech recipe +- The item was crafted in a crafting table +- The item was in a GregTech inventory at any point in time +- The item was in a player's inventory for at least 1 tick +- The item was dropped +- `ISpoilableItem.update(ItemStack, null)` was called If you want to make an item spoil, you can either make it implement the `ISpoilableItem` interface, or attach an `ISpoilableItem` to it. Here's some examples: diff --git a/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md b/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md index e6d644c90bd..fc9ccb71c8c 100644 --- a/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md +++ b/docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md @@ -139,9 +139,9 @@ to distinguish them from other recipes in the same machine with similar ingredie running or all at once at recipe start/end. Set to true with `.perTick(true)` to make the recipe builder consider any following input/output calls as per-tick. Remember to set the value to false with `.perTick(false)` after the calls you intend to be per-tick, to prevent behaviour you don't want! - - `.keepSpoilingProgress()`:\ - If set to true, spoilable outputs' freshness will depend on the recipe's inputs' freshness (default)\ - If set to false, spoilable outputs of this recipe will always be crafted completely fresh + - `.keepSpoilingProgress()`: + If set to true, spoilable outputs' freshness will depend on the recipe's inputs' freshness (default).
+ If set to false, spoilable outputs of this recipe will always be crafted completely fresh. ### The Research System From 4a9b23f0e125dfa9e6082cfbb1425db66c401b3f Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sat, 13 Sep 2025 18:38:39 +0300 Subject: [PATCH 043/123] docs improvements --- .../content/Modpacks/Other-Topics/Spoilage.md | 146 ++++++++++++------ .../api/item/component/ISpoilableItem.java | 17 +- .../gtceu/api/recipe/GTRecipe.java | 2 - .../transfer/item/CustomItemStackHandler.java | 2 +- .../gtceu/common/data/GTRecipeModifiers.java | 2 +- .../gtceu/core/mixins/ItemStackMixin.java | 10 +- .../common/item/SpoilableBehaviourTest.java | 8 +- 7 files changed, 125 insertions(+), 62 deletions(-) diff --git a/docs/content/Modpacks/Other-Topics/Spoilage.md b/docs/content/Modpacks/Other-Topics/Spoilage.md index 8c2ace6f021..28f908a43ed 100644 --- a/docs/content/Modpacks/Other-Topics/Spoilage.md +++ b/docs/content/Modpacks/Other-Topics/Spoilage.md @@ -11,61 +11,115 @@ or, more specifically, from one of these events (due to Minecraft's limitations) - The item was in a GregTech inventory at any point in time - The item was in a player's inventory for at least 1 tick - The item was dropped -- `ISpoilableItem.update(ItemStack, null)` was called +- `ISpoilableItem.update(ItemStack)` was called + +!!! danger "Dangers of universal spoilage" + + You can make items spoil everywhere, but that comes at a cost. **_Everywhere_** includes the creative inventory, JEI and even + recipe ingredients and results. This can be done by setting `ISpoilableItem.BREAK_EVERYTHING` to `true`.
+ If you enable this feature, **prepare for unforeseen consequences...** If you want to make an item spoil, you can either make it implement the `ISpoilableItem` interface, or -attach an `ISpoilableItem` to it. Here's some examples: -```java -public class Example { - public void attachSpoilables() { - // Make diamonds spoil into dirt in 100 seconds - new SpoilableBehaviour(Items.DIRT, 20*100).attachTo(Items.DIAMOND); - } - - public void removeSpoilables() { - // Make diamonds not spoil anymore - ISpoilableItem.unspoil(Items.DIAMOND); - } - - public void getAndSetValuesAndStuff(ItemStack stack) { - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); - // If spoilable is null, it means the stack can not spoil - if (spoilable != null) { - // Get amount of ticks until a completely fresh stack spoils - long totalTicks = spoilable.getSpoilTicks(stack); - // Get amount of ticks until this stack spoils (may be more than the previous value in some cases) - long ticksRemaining = spoilable.getTicksUntilSpoiled(stack); - // Get the stack this stack spoils into - ItemStack spoilResult = spoilable.spoilResult(stack); - // Get whether this stack should START spoiling - boolean shouldStartSpoiling = spoilable.shouldSpoil(stack); - // Get the amount of ticks until this stack spoils (may be more than spoilable.getSpoilTicks(stack)) - spoilable.setTicksUntilSpoiled(stack, 12345); - // Freeze the spoiling progress of this stack - spoilable.freezeSpoiling(stack); - // Unfreeze the spoiling progress of this stack - spoilable.unfreezeSpoiling(stack); +attach an `ISpoilableItem` to it (most commonly `SpoilableBehaviour`). +Please note that the spoilage timer still decrements even if the stack is in an unloaded chunk. + +??? example "Example usages of (almost) all methods in the spoilage API (in Java)" + + ```java + public class Example { + public void attachSpoilables() { + // Make diamonds spoil into dirt in 100 seconds + new SpoilableBehaviour(Items.DIRT, 20*100).attachTo(Items.DIAMOND); + } + + public void removeSpoilables() { + // Make diamonds not spoil anymore + ISpoilableItem.unspoil(Items.DIAMOND); + } + + public void getAndSetValuesAndStuff(ItemStack stack) { + ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + // If spoilable is null, it means the stack can not spoil + if (spoilable != null) { + // Get amount of ticks until a completely fresh stack spoils + long totalTicks = spoilable.getSpoilTicks(stack); + // Get amount of ticks until this stack spoils (may be more than the previous value in some cases) + long ticksRemaining = spoilable.getTicksUntilSpoiled(stack); + // Get the stack this stack spoils into + ItemStack spoilResult = spoilable.spoilResult(stack); + // Get whether this stack should START spoiling + boolean shouldStartSpoiling = spoilable.shouldSpoil(stack); + // Get the amount of ticks until this stack spoils (may be more than spoilable.getSpoilTicks(stack)) + spoilable.setTicksUntilSpoiled(stack, 12345); + // Freeze the spoiling progress of this stack + spoilable.freezeSpoiling(stack); + // Unfreeze the spoiling progress of this stack + spoilable.unfreezeSpoiling(stack); + } + } + + public void makeStackStartSpoiling(ItemStack stack) { + // If for some reason the stack still hasn't started spoiling, you can start the spoiling progress using this + // That may happen if it is the result of a non-GT recipe and not a crafting result for example + ISpoilableItem.update(stack); + } + + public void disableFrozenAndNonFrozenEquality() { + // If you want the player to have frozen stacks in their inventory, do this + // A side effect of this is that filtering by ticks remaining until spoiled will no longer work + ISpoilableItem.FROZEN_EQUALITY = false; } } - - public void makeStackStartSpoiling(ItemStack stack) { - // If for some reason the stack still hasn't started spoiling, you can start the spoiling progress using this - // That may happen if it is the result of a non-GT recipe and not a crafting result for example - ISpoilableItem.update(stack, null); - } - - public void everythingSpoilsRandomly() { + ``` + +!!! warning "Items may spoil in other mods' filters (even if they are phantom slots)." + +### Frozen stacks + +To freeze a stack's spoiling progress, you can use `spoilable.freezeSpoiling(stack)`, and `spoilable.unfreezeSpoiling(stack)` +to unfreeze. A frozen stack's freshness will never be changed unless `spoilable.setTicksUntilSpoiled(stack, value)` is called. +Currently, a stack is frozen only if it is in a phantom slot (in a GregTech filter). +!!! warning "`ItemStack.isSameItemSameTag` behaviour is completely different for spoilables" + + `ItemStack.isSameItemSameTag` returns `true` if: + + - Both items are not frozen and: + - They are the same item + - They have the same NBT + - **Their ticks until spoiling are averaged before the equality check** + - One of the items is frozen and: + - They are the same item + - They have the same non-spoilage-related NBT + - **One of them MAY be not frozen, and they will still be equal if `ISpoilableItem.FROZEN_EQUALITY` is `true`** + - **Their ticks until spoiling are NOT modified in any way in this method** + + !!! info "The following only applies if `ISpoilableItem.FROZEN_EQUALITY` is `true`:" + That means that frozen and non-frozen spoilables may stack, this is done mostly to make filtering by remaining ticks possible. + **Please prevent the player from having direct access to frozen stacks, as they could use them to bypass the spoiling system entirely.** + +### Spoilables in recipes + +If a GT recipe that does not have spoilable ingredients outputs a spoilable, it is outputted at full freshness. +If a GT recipe that has spoilable ingredients outputs a spoilable, it outputs it at the freshness level equal to the average +freshness of the ingredients. This can be overridden by setting `keepSpoilingProgress` (a parameter of the GTRecipe) to `false`. +

+Results of crafts in a crafting table are always outputted fully fresh. + +!!! note + + Items will spoil in machine inputs, and there's no way to automatically remove items from inputs. + +??? danger "Example: making everything spoil everywhere" + + ```java + public void everythingSpoilsEverywhereRandomly() { ISpoilableItem.DEFAULT_SPOIL_BEHAVIOUR = item -> { List allItems = ForgeRegistries.ITEMS.getValues().stream().toList(); Item randomItem = allItems.get(GTValues.RNG.nextIntBetweenInclusive(0, allItems.size() - 1)); // Make everything spoil every 50 seconds into a random item return new SpoilableBehaviour(randomItem, 50*20); }; - } - - public void makeEverythingSpoilEverywhere() { - // Please do not actually do this + // Make items spoil everywhere ISpoilableItem.BREAK_EVERYTHING = true; } -} -``` \ No newline at end of file + ``` \ No newline at end of file diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 726998d55cd..8cef7d628e2 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -6,7 +6,6 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.Level; import dev.latvian.mods.rhino.util.HideFromJS; import org.jetbrains.annotations.ApiStatus; @@ -39,7 +38,7 @@ * container) *
  • It enters a player's inventory and gets ticked at least once
  • *
  • It is dropped (exists as an entity)
  • - *
  • Any other mod calls {@link ISpoilableItem#update(ItemStack, Level)} on the item
  • + *
  • Any other mod calls {@link ISpoilableItem#update(ItemStack)} on the item
  • * * If you are a developer of a mod that adds any other way to obtain items, that doesn't involve * any of the conditions above being true at any tick, consider adding compatibility with this feature :) @@ -95,6 +94,14 @@ public interface ISpoilableItem extends IItemComponent { * statistics menu, EVERYTHING. */ boolean BREAK_EVERYTHING = false; + /** + * Consider frozen and non-frozen spoilables equal. This is done to allow filtering by ticks remaining until + * spoiled.
    + * If you want the player to have frozen stacks in their inventory, set this to {@code false} to prevent players + * from + * entirely bypassing the spoilage system. + */ + boolean FROZEN_EQUALITY = true; /** * Supplier to get the {@link SpoilableBehaviour} of an {@code Item} that doesn't have one attached * Called once for every {@code Item}. Return {@code null} to not attach any {@link SpoilableBehaviour} (default). @@ -105,11 +112,9 @@ public interface ISpoilableItem extends IItemComponent { /** * Initializes this ItemStack's spoilage timer if it wasn't initialized before. * Should be called when it finishes crafting, for example. - * - * @param level may be {@code null}, maybe even should be lol */ - static void update(ItemStack stack, @Nullable Level level) { - ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(level, true); + static void update(ItemStack stack) { + ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(null, true); } /** diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java index 7a41c683ee6..871993e0737 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipe.java @@ -56,8 +56,6 @@ public class GTRecipe implements net.minecraft.world.item.crafting.Recipe ModifierFunction.builder() .modifyItemOutputs((r, stack) -> { - ISpoilableItem.update(stack, null); + ISpoilableItem.update(stack); if (!r.transferSpoilingProgress) return; double spoilProgress = 0; int spoilableCount = 0; diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 1e929507e7a..8a9897f0b72 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -236,8 +236,14 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn isSameItem = isSameItem && Objects.equals(modifiedTag1, modifiedTag2); if (isSameItem && tag1 != null && tag2 != null) { if (tag1.contains("frozenRemainingTicks") || tag2.contains("frozenRemainingTicks")) { - ISpoilableItem spoilable1 = ISpoilableItem.getSpoilable(stack), - spoilable2 = ISpoilableItem.getSpoilable(other); + if (!ISpoilableItem.FROZEN_EQUALITY && + (tag1.contains("frozenRemainingTicks") ^ tag2.contains("frozenRemainingTicks"))) { + cir.setReturnValue(false); + cir.cancel(); + return; + } + ISpoilableItem spoilable1 = ISpoilableItem.getSpoilable(stack); + ISpoilableItem spoilable2 = ISpoilableItem.getSpoilable(other); if (spoilable1 != null && spoilable2 != null) { cir.setReturnValue( spoilable1.getTicksUntilSpoiled(stack) == spoilable2.getTicksUntilSpoiled(other)); diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index f156013a45b..ff26977f26a 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -110,7 +110,7 @@ public static void itemSpoilsRecursively(GameTestHelper helper) { helper.setBlock(1, 1, 1, Blocks.CHEST); IItemHandler itemHandler = TestUtils.getItemHandler(helper, new BlockPos(1, 1, 1)); ItemStack in = Items.APPLE.getDefaultInstance().copyWithCount(41); - ISpoilableItem.update(in, null); + ISpoilableItem.update(in); itemHandler.insertItem(0, in, false); helper.runAtTickTime(70, () -> helper.assertTrue(TestUtils.isItemStackEqual( Items.DIRT.getDefaultInstance().copyWithCount(41), @@ -147,7 +147,7 @@ public static void spoilageTransfersInRecipe(GameTestHelper helper) { makeSpoilables(helper); BusHolder busHolder = getBussesAndForm(helper); ItemStack input = new ItemStack(Items.JIGSAW); - ISpoilableItem.update(input, null); + ISpoilableItem.update(input); Objects.requireNonNull(ISpoilableItem.getSpoilable(input)).setTicksUntilSpoiled(input, 8); busHolder.inputBus1.getInventory().setStackInSlot(0, input); helper.runAtTickTime(21, () -> { @@ -170,7 +170,7 @@ public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { makeSpoilables(helper); BusHolder busHolder = getBussesAndForm(helper); ItemStack input = new ItemStack(Items.APPLE); - ISpoilableItem.update(input, null); + ISpoilableItem.update(input); Objects.requireNonNull(ISpoilableItem.getSpoilable(input)).setTicksUntilSpoiled(input, 8); busHolder.inputBus1.getInventory().setStackInSlot(0, input); helper.runAtTickTime(21, () -> { @@ -249,7 +249,7 @@ public static void spoilableFilteringWithSpoilableTest(GameTestHelper helper) { ItemStack itemForFilter = Items.STRUCTURE_BLOCK.getDefaultInstance(); ISpoilableItem filterSpoilable = ISpoilableItem.getSpoilable(itemForFilter); assert filterSpoilable != null; - ISpoilableItem.update(itemForFilter, null); + ISpoilableItem.update(itemForFilter); filterSpoilable.setTicksUntilSpoiled(itemForFilter, 5); CompoundTag filterTag = SimpleItemFilter.forItems(itemForFilter).saveFilter(); ItemStack filter = GTItems.ITEM_FILTER.asStack(); From 8c5b2f75c4f7973fb9c3538ed64ab536f7422308 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 3 Oct 2025 16:52:03 +0300 Subject: [PATCH 044/123] make event work (i think) --- .../gtceu/api/registry/registrate/MachineBuilder.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 260e4a27754..984662a9def 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -28,7 +28,6 @@ import com.gregtechceu.gtceu.common.data.models.GTMachineModels; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.model.builder.MachineModelBuilder; -import com.gregtechceu.gtceu.forge.ForgeCommonEventListener; import com.gregtechceu.gtceu.integration.kjs.GTCEuStartupEvents; import com.gregtechceu.gtceu.integration.kjs.events.RegisterGTMachineEventJS; @@ -50,7 +49,7 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.client.model.generators.BlockModelBuilder; -import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.ModLoader; import com.tterrag.registrate.AbstractRegistrate; import com.tterrag.registrate.builders.BlockBuilder; @@ -530,9 +529,7 @@ protected void setupStateDefinition(MachineDefinition definition) { @HideFromJS public DEFINITION register() { RegisterGTMachineEvent event = new RegisterGTMachineEvent(this); - ForgeCommonEventListener.addSpoilTransferModifier(event); // this is a terrible way to do it, but I couldn't get - // the event to work - MinecraftForge.EVENT_BUS.post(event); + ModLoader.get().postEvent(event); if (GTCEu.Mods.isKubeJSLoaded()) { KJSCallWrapper.fireKJSEvent(event); } From b1528d750207ba2063dd2f1dac899d4848e6a056 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 3 Oct 2025 16:56:39 +0300 Subject: [PATCH 045/123] oops --- .../gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java index b4557f2610b..5d27cf838ab 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java +++ b/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java @@ -3,10 +3,11 @@ import com.gregtechceu.gtceu.api.registry.registrate.MachineBuilder; import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.IModBusEvent; import lombok.Getter; -public class RegisterGTMachineEvent extends Event { +public class RegisterGTMachineEvent extends Event implements IModBusEvent { @Getter private final MachineBuilder builder; From 146c44b79f90745469517c11f935405d4c76e527 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 3 Oct 2025 17:06:21 +0300 Subject: [PATCH 046/123] revert --- .../gtceu/api/registry/registrate/MachineBuilder.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 132fa60f3cc..690f7b43c20 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -28,6 +28,7 @@ import com.gregtechceu.gtceu.common.data.models.GTMachineModels; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.model.builder.MachineModelBuilder; +import com.gregtechceu.gtceu.forge.ForgeCommonEventListener; import com.gregtechceu.gtceu.integration.kjs.GTCEuStartupEvents; import com.gregtechceu.gtceu.integration.kjs.events.RegisterGTMachineEventJS; @@ -49,7 +50,7 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.client.model.generators.BlockModelBuilder; -import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.common.MinecraftForge; import com.tterrag.registrate.AbstractRegistrate; import com.tterrag.registrate.builders.BlockBuilder; @@ -541,7 +542,8 @@ protected void setupStateDefinition(MachineDefinition definition) { @HideFromJS public DEFINITION register() { RegisterGTMachineEvent event = new RegisterGTMachineEvent(this); - ModLoader.get().postEvent(event); + ForgeCommonEventListener.addSpoilTransferModifier(event); // ok im putting this back lol FIXME + MinecraftForge.EVENT_BUS.post(event); if (GTCEu.Mods.isKubeJSLoaded()) { KJSCallWrapper.fireKJSEvent(event); } From 591c3cdacc65109e7b380cd33fff209fb81ea593 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Thu, 23 Oct 2025 22:26:47 +0300 Subject: [PATCH 047/123] fix event listener and rename some things --- ...erGTMachineEvent.java => ModifyMachineEvent.java} | 4 ++-- .../gtceu/api/item/component/ISpoilableItem.java | 1 + .../api/registry/registrate/MachineBuilder.java | 12 +++++------- .../com/gregtechceu/gtceu/common/CommonProxy.java | 7 +++++++ .../gtceu/core/mixins/ItemStackMixin.java | 11 +---------- .../gtceu/forge/ForgeCommonEventListener.java | 6 ------ .../gtceu/integration/kjs/GTCEuStartupEvents.java | 4 ++-- ...MachineEventJS.java => ModifyMachineEventJS.java} | 8 ++++---- 8 files changed, 22 insertions(+), 31 deletions(-) rename src/main/java/com/gregtechceu/gtceu/api/events/{RegisterGTMachineEvent.java => ModifyMachineEvent.java} (70%) rename src/main/java/com/gregtechceu/gtceu/integration/kjs/events/{RegisterGTMachineEventJS.java => ModifyMachineEventJS.java} (56%) diff --git a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java b/src/main/java/com/gregtechceu/gtceu/api/events/ModifyMachineEvent.java similarity index 70% rename from src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java rename to src/main/java/com/gregtechceu/gtceu/api/events/ModifyMachineEvent.java index 5d27cf838ab..b006555ffcb 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/events/RegisterGTMachineEvent.java +++ b/src/main/java/com/gregtechceu/gtceu/api/events/ModifyMachineEvent.java @@ -7,12 +7,12 @@ import lombok.Getter; -public class RegisterGTMachineEvent extends Event implements IModBusEvent { +public class ModifyMachineEvent extends Event implements IModBusEvent { @Getter private final MachineBuilder builder; - public RegisterGTMachineEvent(MachineBuilder builder) { + public ModifyMachineEvent(MachineBuilder builder) { this.builder = builder; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 8cef7d628e2..357334157d8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -225,6 +225,7 @@ default void freezeSpoiling(ItemStack stack) { * * @see ISpoilableItem#freezeSpoiling(ItemStack) */ + @ApiStatus.NonExtendable default void unfreezeSpoiling(ItemStack stack) { ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(false); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 690f7b43c20..2ef0d2e5e6b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -5,7 +5,7 @@ import com.gregtechceu.gtceu.api.block.IMachineBlock; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; import com.gregtechceu.gtceu.api.data.RotationState; -import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; +import com.gregtechceu.gtceu.api.events.ModifyMachineEvent; import com.gregtechceu.gtceu.api.gui.editor.EditableMachineUI; import com.gregtechceu.gtceu.api.item.MetaMachineItem; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; @@ -28,9 +28,8 @@ import com.gregtechceu.gtceu.common.data.models.GTMachineModels; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.model.builder.MachineModelBuilder; -import com.gregtechceu.gtceu.forge.ForgeCommonEventListener; import com.gregtechceu.gtceu.integration.kjs.GTCEuStartupEvents; -import com.gregtechceu.gtceu.integration.kjs.events.RegisterGTMachineEventJS; +import com.gregtechceu.gtceu.integration.kjs.events.ModifyMachineEventJS; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.RenderType; @@ -541,8 +540,7 @@ protected void setupStateDefinition(MachineDefinition definition) { @HideFromJS public DEFINITION register() { - RegisterGTMachineEvent event = new RegisterGTMachineEvent(this); - ForgeCommonEventListener.addSpoilTransferModifier(event); // ok im putting this back lol FIXME + ModifyMachineEvent event = new ModifyMachineEvent(this); MinecraftForge.EVENT_BUS.post(event); if (GTCEu.Mods.isKubeJSLoaded()) { KJSCallWrapper.fireKJSEvent(event); @@ -722,8 +720,8 @@ public static void generateAssetJsons(@Nullable As } } - public static void fireKJSEvent(RegisterGTMachineEvent event) { - GTCEuStartupEvents.REGISTER_GT_MACHINE.post(new RegisterGTMachineEventJS(event)); + public static void fireKJSEvent(ModifyMachineEvent event) { + GTCEuStartupEvents.MACHINE_MODIFICATION.post(new ModifyMachineEventJS(event)); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java index 62d66185e5a..6e799f5d447 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java +++ b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java @@ -16,6 +16,7 @@ import com.gregtechceu.gtceu.api.data.worldgen.WorldGenLayers; import com.gregtechceu.gtceu.api.data.worldgen.generator.IndicatorGenerators; import com.gregtechceu.gtceu.api.data.worldgen.generator.VeinGenerators; +import com.gregtechceu.gtceu.api.events.ModifyMachineEvent; import com.gregtechceu.gtceu.api.gui.factory.CoverUIFactory; import com.gregtechceu.gtceu.api.gui.factory.GTUIEditorFactory; import com.gregtechceu.gtceu.api.gui.factory.MachineUIFactory; @@ -97,6 +98,7 @@ public CommonProxy() { IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus(); eventBus.register(this); eventBus.addListener(AlloyBlastPropertyAddition::addAlloyBlastProperties); + eventBus.addListener(CommonProxy::addSpoilTransferModifier); // must be set here because of KubeJS compat // trying to read this before the pre-init stage GTCEuAPI.materialManager = MaterialRegistryManager.getInstance(); @@ -241,6 +243,11 @@ private static void initMaterials() { /* End Material Registration */ } + @SubscribeEvent + public static void addSpoilTransferModifier(ModifyMachineEvent event) { + event.getBuilder().addRecipeModifier(GTRecipeModifiers.SPOILAGE_TRANSFER); + } + @SubscribeEvent public void register(RegisterEvent event) { if (event.getRegistryKey().equals(BuiltInRegistries.LOOT_FUNCTION_TYPE.key())) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 8a9897f0b72..c7bc970fd10 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -156,7 +156,7 @@ private void tickFreshness(Level level, Entity entity, int inventorySlot, boolea } @Inject(at = @At("HEAD"), method = "onCraftedBy") - private void onCraftedUpdateFreshness(Level level, Player player, int amount, CallbackInfo ci) { + private void updateFreshnessOnCraft(Level level, Player player, int amount, CallbackInfo ci) { gtceu$updateFreshness(null, true); } @@ -239,7 +239,6 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn if (!ISpoilableItem.FROZEN_EQUALITY && (tag1.contains("frozenRemainingTicks") ^ tag2.contains("frozenRemainingTicks"))) { cir.setReturnValue(false); - cir.cancel(); return; } ISpoilableItem spoilable1 = ISpoilableItem.getSpoilable(stack); @@ -247,7 +246,6 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn if (spoilable1 != null && spoilable2 != null) { cir.setReturnValue( spoilable1.getTicksUntilSpoiled(stack) == spoilable2.getTicksUntilSpoiled(other)); - cir.cancel(); } } else { long tick1 = tag1.getLong("creation_tick"); @@ -264,7 +262,6 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn } } cir.setReturnValue(isSameItem && Objects.equals(stack.getTag(), other.getTag())); - cir.cancel(); } @Inject(at = @At(value = "INVOKE", @@ -292,10 +289,8 @@ private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.isBarVisible((ItemStack) (Object) this)); - cir.cancel(); } else if (gtceu$fakeTooltip) { cir.setReturnValue(true); - cir.cancel(); } } @@ -304,10 +299,8 @@ private void aprilFoolsBarColor(CallbackInfoReturnable cir) { ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.getBarColor((ItemStack) (Object) this)); - cir.cancel(); } else if (gtceu$fakeTooltip) { cir.setReturnValue(FastColor.ARGB32.color(255, 255, 255, 255)); - cir.cancel(); } } @@ -316,10 +309,8 @@ private void aprilFoolsBarWidth(CallbackInfoReturnable cir) { ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.getBarWidth((ItemStack) (Object) this)); - cir.cancel(); } else if (gtceu$fakeTooltip) { cir.setReturnValue(13); - cir.cancel(); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java index f756559d29d..f01dd59abaa 100644 --- a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java +++ b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java @@ -16,7 +16,6 @@ import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.events.RegisterGTCapesEvent; -import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; import com.gregtechceu.gtceu.api.item.armor.ArmorComponentItem; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.machine.MetaMachine; @@ -160,11 +159,6 @@ public static void registerCapes(RegisterGTCapesEvent event) { GTCapes.giveDevCapes(event); } - @SubscribeEvent - public static void addSpoilTransferModifier(RegisterGTMachineEvent event) { - event.getBuilder().addRecipeModifier(GTRecipeModifiers.SPOILAGE_TRANSFER); - } - @SubscribeEvent public static void tickPlayerInventoryHazards(TickEvent.PlayerTickEvent event) { if (event.side == LogicalSide.CLIENT || event.phase != TickEvent.Phase.END) { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java b/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java index 3cadc64e759..4d59a63ca1e 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/kjs/GTCEuStartupEvents.java @@ -5,7 +5,7 @@ import com.gregtechceu.gtceu.integration.kjs.events.CraftingComponentsEventJS; import com.gregtechceu.gtceu.integration.kjs.events.GTRegistryEventJS; import com.gregtechceu.gtceu.integration.kjs.events.MaterialModificationEventJS; -import com.gregtechceu.gtceu.integration.kjs.events.RegisterGTMachineEventJS; +import com.gregtechceu.gtceu.integration.kjs.events.ModifyMachineEventJS; import dev.latvian.mods.kubejs.event.EventGroup; import dev.latvian.mods.kubejs.event.EventHandler; @@ -29,5 +29,5 @@ private static boolean validateRegistry(Object o) { EventHandler REGISTRY = GROUP.startup("registry", () -> GTRegistryEventJS.class).extra(REGISTRY_EXTRA); EventHandler MATERIAL_MODIFICATION = GROUP.startup("materialModification", () -> MaterialModificationEventJS.class); EventHandler CRAFTING_COMPONENTS = GROUP.startup("craftingComponents", () -> CraftingComponentsEventJS.class); - EventHandler REGISTER_GT_MACHINE = GROUP.startup("registerGTMachine", () -> RegisterGTMachineEventJS.class); + EventHandler MACHINE_MODIFICATION = GROUP.startup("registerGTMachine", () -> ModifyMachineEventJS.class); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/ModifyMachineEventJS.java similarity index 56% rename from src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java rename to src/main/java/com/gregtechceu/gtceu/integration/kjs/events/ModifyMachineEventJS.java index 516f50ff522..7d0d49b9f9d 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/RegisterGTMachineEventJS.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/kjs/events/ModifyMachineEventJS.java @@ -1,15 +1,15 @@ package com.gregtechceu.gtceu.integration.kjs.events; -import com.gregtechceu.gtceu.api.events.RegisterGTMachineEvent; +import com.gregtechceu.gtceu.api.events.ModifyMachineEvent; import com.gregtechceu.gtceu.api.registry.registrate.MachineBuilder; import dev.latvian.mods.kubejs.event.EventJS; -public class RegisterGTMachineEventJS extends EventJS { +public class ModifyMachineEventJS extends EventJS { - private final RegisterGTMachineEvent event; + private final ModifyMachineEvent event; - public RegisterGTMachineEventJS(RegisterGTMachineEvent event) { + public ModifyMachineEventJS(ModifyMachineEvent event) { this.event = event; } From 3f2d79a182fbc00ddc6af78632faca11e76642f6 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 24 Oct 2025 18:44:20 +0300 Subject: [PATCH 048/123] yay event works --- .../gtceu/api/registry/registrate/MachineBuilder.java | 4 ++-- src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 2ef0d2e5e6b..9c6da7a515a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -49,7 +49,7 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.client.model.generators.BlockModelBuilder; -import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import com.tterrag.registrate.AbstractRegistrate; import com.tterrag.registrate.builders.BlockBuilder; @@ -541,7 +541,7 @@ protected void setupStateDefinition(MachineDefinition definition) { @HideFromJS public DEFINITION register() { ModifyMachineEvent event = new ModifyMachineEvent(this); - MinecraftForge.EVENT_BUS.post(event); + FMLJavaModLoadingContext.get().getModEventBus().post(event); if (GTCEu.Mods.isKubeJSLoaded()) { KJSCallWrapper.fireKJSEvent(event); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java index 6e799f5d447..073b8d935ed 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java +++ b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java @@ -98,7 +98,6 @@ public CommonProxy() { IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus(); eventBus.register(this); eventBus.addListener(AlloyBlastPropertyAddition::addAlloyBlastProperties); - eventBus.addListener(CommonProxy::addSpoilTransferModifier); // must be set here because of KubeJS compat // trying to read this before the pre-init stage GTCEuAPI.materialManager = MaterialRegistryManager.getInstance(); @@ -244,7 +243,7 @@ private static void initMaterials() { } @SubscribeEvent - public static void addSpoilTransferModifier(ModifyMachineEvent event) { + public void addSpoilTransferModifier(ModifyMachineEvent event) { event.getBuilder().addRecipeModifier(GTRecipeModifiers.SPOILAGE_TRANSFER); } From 7f63650e45881cd264c2ffd1a8f0ec9a570ae4ff Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 24 Oct 2025 19:18:35 +0300 Subject: [PATCH 049/123] generalized itemOutputModifier to allow all types of outputs --- .../api/machine/trait/NotifiableFluidTank.java | 3 +++ .../machine/trait/NotifiableItemStackHandler.java | 4 ++-- .../com/gregtechceu/gtceu/api/recipe/GTRecipe.java | 14 +++++++------- .../api/recipe/modifier/ModifierFunction.java | 10 ++++------ .../gtceu/common/data/GTRecipeModifiers.java | 6 ++++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java index 85905c71f6e..f42387af315 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java @@ -179,11 +179,14 @@ public List handleRecipeInner(IO io, GTRecipe recipe, List handleRecipe(IO io, GTRecipe recipe, List itemInputs = new ArrayList<>(); + public List consumedInputs = new ArrayList<>(); /** - * Called for each {@code ItemStack} output before it is inserted into the output container. + * Called for each output before it is inserted into the output container. * Does nothing by default, to be modified with {@link BiConsumer#andThen(BiConsumer)} in {@link RecipeModifier} */ - public BiConsumer itemOutputModifier = (recipe, stack) -> {}; + public BiConsumer outputModifier = (recipe, object) -> {}; public final GTRecipeCategory recipeCategory; // Lazy fields, since we need the recipe EUt very often @Getter(lazy = true) @@ -266,7 +266,7 @@ public String toString() { return id.toString(); } - public void mutateItemOutput(ItemStack stack) { - if (this.itemOutputModifier != null) itemOutputModifier.accept(this, stack); + public void mutateOutput(Object stack) { + if (this.outputModifier != null) outputModifier.accept(this, stack); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java index 5abda34b363..e7e90bbe154 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java +++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/modifier/ModifierFunction.java @@ -9,8 +9,6 @@ import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; import com.gregtechceu.gtceu.api.recipe.ingredient.EnergyStack; -import net.minecraft.world.item.ItemStack; - import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.Contract; @@ -111,7 +109,7 @@ final class FunctionBuilder { private ContentModifier outputModifier = ContentModifier.IDENTITY; private ContentModifier tickInputModifier = ContentModifier.IDENTITY; private ContentModifier tickOutputModifier = ContentModifier.IDENTITY; - private BiConsumer itemOutputModifier = (recipe, stack) -> {}; + private BiConsumer actualOutputModifier = (recipe, object) -> {}; private final List addedConditions = new ArrayList<>(); public FunctionBuilder() {} @@ -139,8 +137,8 @@ public FunctionBuilder durationMultiplier(double multiplier) { return this; } - public FunctionBuilder modifyItemOutputs(BiConsumer func) { - itemOutputModifier = itemOutputModifier.andThen(func); + public FunctionBuilder modifyItemOutputs(BiConsumer func) { + actualOutputModifier = actualOutputModifier.andThen(func); return this; } @@ -183,7 +181,7 @@ public ModifierFunction build() { EnergyStack eut = EURecipeCapability.CAP.copyWithModifier(preEUt.stack(), eutModifier); EURecipeCapability.putEUContent(preEUt.isInput() ? copied.tickInputs : copied.tickOutputs, eut); } - copied.itemOutputModifier = itemOutputModifier; + copied.outputModifier = actualOutputModifier; return copied; }; } diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java index b24b7c6f1a9..009f259aa19 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeModifiers.java @@ -51,12 +51,14 @@ public class GTRecipeModifiers { public static final RecipeModifier OC_PERFECT_SUBTICK = ELECTRIC_OVERCLOCK.apply(PERFECT_OVERCLOCK_SUBTICK); public static final RecipeModifier OC_NON_PERFECT_SUBTICK = ELECTRIC_OVERCLOCK.apply(NON_PERFECT_OVERCLOCK_SUBTICK); public static final RecipeModifier SPOILAGE_TRANSFER = (machine, recipe) -> ModifierFunction.builder() - .modifyItemOutputs((r, stack) -> { + .modifyItemOutputs((r, stackObject) -> { + if (!(stackObject instanceof ItemStack stack)) return; ISpoilableItem.update(stack); if (!r.transferSpoilingProgress) return; double spoilProgress = 0; int spoilableCount = 0; - for (ItemStack in : r.itemInputs) { + for (Object inObject : r.consumedInputs) { + if (!(inObject instanceof ItemStack in)) continue; ISpoilableItem spoilable = ISpoilableItem.getSpoilable(in); if (spoilable != null && spoilable.shouldSpoil(in)) { spoilableCount += in.getCount(); From 93970c9bd4d7221517a21152407267d503546f7e Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 24 Oct 2025 19:20:45 +0300 Subject: [PATCH 050/123] added @NonExtendable --- .../gregtechceu/gtceu/api/item/component/ISpoilableItem.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 357334157d8..2edc9cc8bdc 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -182,6 +182,7 @@ static void unspoil(ItemLike item) { * overworld time pauses, as all tick calculations are done with overworld tick time * @see ISpoilableItem#setTicksUntilSpoiled(ItemStack, long) */ + @ApiStatus.NonExtendable default long getTicksUntilSpoiled(ItemStack stack) { return ((ISpoilableItemStack) (Object) stack).gtceu$getRemainingTicks(null); } @@ -195,6 +196,7 @@ default long getTicksUntilSpoiled(ItemStack stack) { * * @see ISpoilableItem#getTicksUntilSpoiled(ItemStack) */ + @ApiStatus.NonExtendable default void setTicksUntilSpoiled(ItemStack stack, long value) { ((ISpoilableItemStack) (Object) stack).gtceu$setRemainingTicks(null, value); } From 3756999bbe076683a355ac6b73843f4b9658bfff Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 24 Oct 2025 19:24:08 +0300 Subject: [PATCH 051/123] suggested change --- .../gtceu/api/registry/registrate/MachineBuilder.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 9c6da7a515a..e5df38dbbe1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -467,7 +467,11 @@ public MachineBuilder recipeModifier(RecipeModifier recipeModifier) } public MachineBuilder addRecipeModifier(RecipeModifier recipeModifier) { - this.recipeModifier(new RecipeModifierList(this.recipeModifier, recipeModifier)); + if (this.recipeModifier instanceof RecipeModifierList list) { + this.recipeModifier = new RecipeModifierList(ArrayUtils.add(list.getModifiers(), recipeModifier)); + } else { + this.recipeModifier = new RecipeModifierList(this.recipeModifier, recipeModifier); + } return this; } From 8a3af97f608d57e2f7bf93140a773884760c2b7e Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Fri, 24 Oct 2025 19:33:08 +0300 Subject: [PATCH 052/123] remove unused shadow --- .../java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index c7bc970fd10..b8e712de783 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -87,9 +87,6 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 @Nullable private Entity entityRepresentation; - @Shadow - public abstract void removeTagKey(String key); - @Unique @Override public void gtceu$updateFreshness(Level level, boolean createTag) { From 1b5537d3e998dc9a2fea4c35c9c9d90a4aeec507 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 26 Oct 2025 19:59:29 +0300 Subject: [PATCH 053/123] spoilage is a capability now (need to update docs later) --- .../api/capability/GTCapabilityHelper.java | 5 + .../api/capability/forge/GTCapability.java | 4 + .../api/cover/filter/SimpleItemFilter.java | 5 +- .../api/gui/widget/PhantomSlotWidget.java | 9 +- .../gtceu/api/item/ComponentItem.java | 28 +--- .../gtceu/api/item/ISpoilableItemStack.java | 22 --- .../api/item/ISpoilableItemStackMixin.java | 9 ++ .../api/item/component/ISpoilableItem.java | 153 ++++-------------- .../trait/NotifiableItemStackHandler.java | 5 +- .../gtceu/common/data/GTRecipeModifiers.java | 15 +- .../gtceu/common/item/SpoilableBehaviour.java | 64 +++++--- .../gtceu/common/item/SpoilableItemStack.java | 105 ++++++++++++ .../gtceu/core/mixins/ItemStackMixin.java | 119 +++----------- .../common/item/SpoilableBehaviourTest.java | 62 +++---- 14 files changed, 269 insertions(+), 336 deletions(-) delete mode 100644 src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java create mode 100644 src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java create mode 100644 src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java b/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java index 49ba637ac4d..424db343e5c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.api.capability; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; @@ -138,4 +139,8 @@ private static T getBlockEntityCapability(Capability capability, Level le public static IMedicalConditionTracker getMedicalConditionTracker(@NotNull Entity entity) { return entity.getCapability(GTCapability.CAPABILITY_MEDICAL_CONDITION_TRACKER, null).resolve().orElse(null); } + + public static @javax.annotation.Nullable ISpoilableItem getSpoilable(ItemStack stack) { + return stack.getCapability(GTCapability.CAPABILITY_SPOILABLE_ITEM).resolve().orElse(null); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java b/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java index 18e989bda1e..a87dc0826df 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.api.capability.forge; import com.gregtechceu.gtceu.api.capability.*; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; @@ -42,6 +43,8 @@ public class GTCapability { .get(new CapabilityToken<>() {}); public static final Capability CAPABILITY_CENTRAL_MONITOR = CapabilityManager .get(new CapabilityToken<>() {}); + public static final Capability CAPABILITY_SPOILABLE_ITEM = CapabilityManager + .get(new CapabilityToken<>() {}); public static final Capability CAPABILITY_MEDICAL_CONDITION_TRACKER = CapabilityManager .get(new CapabilityToken<>() {}); @@ -65,5 +68,6 @@ public static void register(RegisterCapabilitiesEvent event) { event.register(IHazardParticleContainer.class); event.register(IMonitorComponent.class); event.register(ICentralMonitor.class); + event.register(ISpoilableItem.class); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java index 74c553f8355..e14b23106af 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.api.cover.filter; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.PhantomSlotWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; @@ -54,8 +55,8 @@ public static SimpleItemFilter forItems(ItemStack... items) { int i = 0; for (ItemStack item : items) { filter.matches[i] = item.copy(); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(filter.matches[i]); - if (spoilable != null) spoilable.freezeSpoiling(filter.matches[i]); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(filter.matches[i]); + if (spoilable != null) spoilable.freezeSpoiling(); i++; } return filter; diff --git a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java index c41992faeeb..c607249e8d4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/PhantomSlotWidget.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.api.gui.widget; import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.lowdragmc.lowdraglib.gui.editor.annotation.ConfigSetter; @@ -87,8 +88,8 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { if (slotReference != null && isMouseOverElement(mouseX, mouseY) && gui != null) { if (isClientSideWidget && !gui.getModularUIContainer().getCarried().isEmpty()) { ItemStack carried = gui.getModularUIContainer().getCarried().copy(); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(carried); - if (spoilable != null) spoilable.freezeSpoiling(carried); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(carried); + if (spoilable != null) spoilable.freezeSpoiling(); slotReference.set(carried); } else if (button == 1 && clearSlotOnRightClick && !slotReference.getItem().isEmpty()) { slotReference.set(ItemStack.EMPTY); @@ -252,8 +253,8 @@ private void fillPhantomSlot(Slot slot, ItemStack stackHeld, int mouseButton) { stackSize = slot.getMaxStackSize(); } ItemStack phantomStack = stackHeld.copy(); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(phantomStack); - if (spoilable != null) spoilable.freezeSpoiling(phantomStack); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(phantomStack); + if (spoilable != null) spoilable.freezeSpoiling(); phantomStack.setCount(Math.min(maxStackSize, stackSize)); if (validator.test(phantomStack)) slot.set(phantomStack); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java index 824179db6d0..b45213e753e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ComponentItem.java @@ -49,8 +49,7 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public class ComponentItem extends Item - implements HeldItemUIFactory.IHeldItemUIHolder, IItemRendererProvider, IComponentItem, - ISpoilableItem { + implements HeldItemUIFactory.IHeldItemUIHolder, IItemRendererProvider, IComponentItem { protected int burnTime = -1; @@ -444,29 +443,4 @@ public ItemStack getInfiniteChargedStack() { electricItem.setInfiniteCharge(true); return itemStack; } - - @Override - public long getSpoilTicks(ItemStack stack) { - for (IItemComponent component : getComponents()) { - if (component instanceof ISpoilableItem spoilable) return spoilable.getSpoilTicks(stack); - } - return -1; - } - - @Override - public ItemStack spoilResult(ItemStack stack) { - for (IItemComponent component : getComponents()) { - if (component instanceof ISpoilableItem spoilable) return spoilable.spoilResult(stack); - } - return ItemStack.EMPTY; - } - - @Override - public boolean shouldSpoil(ItemStack stack) { - boolean out = false; - for (IItemComponent component : getComponents()) { - if (component instanceof ISpoilableItem spoilable) out = out || spoilable.shouldSpoil(stack); - } - return out; - } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java deleted file mode 100644 index e6ada9c8272..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStack.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gregtechceu.gtceu.api.item; - -import net.minecraft.world.level.Level; - -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Unique; - -public interface ISpoilableItemStack { - - long gtceu$getCreationTick(@Nullable Level level); - - void gtceu$setCreationTick(@Nullable Level level, long value); - - long gtceu$getRemainingTicks(@Nullable Level level); - - void gtceu$setFreezeSpoiling(boolean freezeUpdates); - - void gtceu$updateFreshness(@Nullable Level level, boolean createTag); - - @Unique - void gtceu$setRemainingTicks(@javax.annotation.Nullable Level level, long value); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java new file mode 100644 index 00000000000..e2ce576ae36 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java @@ -0,0 +1,9 @@ +package com.gregtechceu.gtceu.api.item; + +import net.minecraft.world.level.Level; + +import org.jetbrains.annotations.Nullable; + +public interface ISpoilableItemStackMixin { + void gtceu$updateFreshness(@Nullable Level level, boolean createTag); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 2edc9cc8bdc..45607d61689 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -1,24 +1,12 @@ package com.gregtechceu.gtceu.api.item.component; -import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.item.ISpoilableItemStackMixin; import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ItemLike; -import dev.latvian.mods.rhino.util.HideFromJS; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -import javax.annotation.Nullable; - /** * The interface items that spoil should implement. *

    @@ -54,11 +42,11 @@ *

    * Spoilable stacks will be frozen if they enter a {@link com.gregtechceu.gtceu.api.gui.widget.PhantomSlotWidget}, * to prevent stacks spoiling in filters. If you are a developer of a mod that adds filters, consider calling - * {@link ISpoilableItem#freezeSpoiling(ItemStack)} on stacks entering these filters for compatibility :) + * {@link ISpoilableItem#freezeSpoiling()} on stacks entering these filters for compatibility :) *

    *

    * Note that if an item is spoilable, it does not mean that it spoils in all cases, as you can override - * {@link ISpoilableItem#shouldSpoil(ItemStack)} + * {@link ISpoilableItem#shouldSpoil()} * in your own implementation of the {@link ISpoilableItem} interface. *

    *

    @@ -67,33 +55,8 @@ * make any item from any mod spoil quite easily. This is especially useful for KubeJS devs. *

    */ -public interface ISpoilableItem extends IItemComponent { - - /** - * Should NOT be used outside of {@link ISpoilableItem}. - * - * @see ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike) - * @see ISpoilableItem#unspoil(ItemLike) - */ - @ApiStatus.Internal - @HideFromJS - Map ATTACHED_COMPONENTS = new HashMap<>(); +public interface ISpoilableItem { - /** - * Should NOT be used outside of {@link ISpoilableItem} - * - * @see ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike) - * @see ISpoilableItem#unspoil(ItemLike) - */ - @ApiStatus.Internal - @HideFromJS - Set UNSPOILED_ITEMS = new HashSet<>(); - /** - * Makes EVERY {@code ItemStack} spoilable. EVERY SINGLE ONE, even in recipe ingredients and results, creative - * inventory, JEI, EMI, - * statistics menu, EVERYTHING. - */ - boolean BREAK_EVERYTHING = false; /** * Consider frozen and non-frozen spoilables equal. This is done to allow filtering by ticks remaining until * spoiled.
    @@ -102,77 +65,20 @@ public interface ISpoilableItem extends IItemComponent { * entirely bypassing the spoilage system. */ boolean FROZEN_EQUALITY = true; - /** - * Supplier to get the {@link SpoilableBehaviour} of an {@code Item} that doesn't have one attached - * Called once for every {@code Item}. Return {@code null} to not attach any {@link SpoilableBehaviour} (default). - */ - @NotNull - Function DEFAULT_SPOIL_BEHAVIOR = item -> null; /** * Initializes this ItemStack's spoilage timer if it wasn't initialized before. * Should be called when it finishes crafting, for example. */ static void update(ItemStack stack) { - ((ISpoilableItemStack) (Object) stack).gtceu$updateFreshness(null, true); - } - - /** - * Makes an item spoilable by attaching an {@link ISpoilableItem} to it. - * The newly attached spoilable will override any other spoilables that this item may - * have had attached (this includes the {@link Item} itself implementing {@link ISpoilableItem}). - *
    - * Note that this will NOT magically make the {@link Item} implement {@link ISpoilableItem}. - *
    - * This method may be called during gameplay. I have no idea what for, but you can :) - * - * @param spoilable the new spoiling behaviour to assign to this item - * @param item the item to make spoilable - * @see ISpoilableItem#unspoil(ItemLike) - */ - static void attachSpoilable(@NotNull ISpoilableItem spoilable, ItemLike item) { - UNSPOILED_ITEMS.remove(item.asItem()); - ATTACHED_COMPONENTS.put(item.asItem(), spoilable); - } - - /** - * While an {@link ISpoilableItem} may be obtained by simply casting an {@link Item} - * to it (or attempting to), it is not recommended, as an item may be spoilable - * even if its {@link Item} doesn't implement {@link ISpoilableItem}. - * - * @return the {@link ISpoilableItem} of the provided {@link ItemStack} ({@code null} if the item can't spoil) - */ - static @Nullable ISpoilableItem getSpoilable(ItemStack stack) { - Item item = stack.getItem(); - if (UNSPOILED_ITEMS.contains(item)) return null; - if (ATTACHED_COMPONENTS.containsKey(item)) return ATTACHED_COMPONENTS.get(item); - if (item instanceof ISpoilableItem spoilable) return spoilable; - SpoilableBehaviour behaviour = DEFAULT_SPOIL_BEHAVIOR.apply(item); - if (behaviour != null) ATTACHED_COMPONENTS.put(item, behaviour); - return behaviour; - } - - /** - * Removes any {@link ISpoilableItem} previously attached to the provided {@link Item}. - *
    - * This includes the {@link Item} itself implementing {@link ISpoilableItem} and - * behaviours attached using {@link ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike)}. - *
    - * This method may be called during gameplay. I have no idea what for, but you can :) - * - * @param item the item to remove any spoiling behaviour from - * @see ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike) - */ - static void unspoil(ItemLike item) { - ATTACHED_COMPONENTS.remove(item.asItem()); - if (item.asItem() instanceof ISpoilableItem) UNSPOILED_ITEMS.add(item.asItem()); + ((ISpoilableItemStackMixin) (Object) stack).gtceu$updateFreshness(null, true); } /** * Should return the amount of ticks that this item can stay fresh. * The result of this method shouldn't be based on the freshness of the provided stack */ - long getSpoilTicks(ItemStack stack); + long getSpoilTicks(); /** * Please refrain from overriding this method unless absolutely necessary (I have no idea what will happen) @@ -180,66 +86,61 @@ static void unspoil(ItemLike item) { * @return the amount of ticks left until the provided {@link ItemStack} spoils. * The ticks still reduce even when the item is unloaded, and only pause if the * overworld time pauses, as all tick calculations are done with overworld tick time - * @see ISpoilableItem#setTicksUntilSpoiled(ItemStack, long) + * @see ISpoilableItem#setTicksUntilSpoiled(long) */ - @ApiStatus.NonExtendable - default long getTicksUntilSpoiled(ItemStack stack) { - return ((ISpoilableItemStack) (Object) stack).gtceu$getRemainingTicks(null); - } + long getTicksUntilSpoiled(); /** * Please refrain from overriding this method unless absolutely necessary (I have no idea what will happen). *
    * Sets the amount of ticks left until the provided {@link ItemStack} spoils. * This modifies the provided stack's NBT data. - * The provided value may be more than {@link ISpoilableItem#getSpoilTicks(ItemStack)} + * The provided value may be more than {@link ISpoilableItem#getSpoilTicks()} * - * @see ISpoilableItem#getTicksUntilSpoiled(ItemStack) + * @see ISpoilableItem#getTicksUntilSpoiled() */ - @ApiStatus.NonExtendable - default void setTicksUntilSpoiled(ItemStack stack, long value) { - ((ISpoilableItemStack) (Object) stack).gtceu$setRemainingTicks(null, value); - } + void setTicksUntilSpoiled(long value); /** * Freezes the provided stack's spoiling progress until it is unfrozen by - * {@link ISpoilableItem#unfreezeSpoiling(ItemStack)}. - * Frozen stacks will NOT spoil, even if {@link ISpoilableItem#getTicksUntilSpoiled(ItemStack)} is {@code <= 0}. + * {@link ISpoilableItem#unfreezeSpoiling()}. + * Frozen stacks will NOT spoil, even if {@link ISpoilableItem#getTicksUntilSpoiled()} is {@code <= 0}. * This method modifies the provided stack's NBT data. * Calls to {@link ItemStack#isSameItemSameTags(ItemStack, ItemStack)} with a frozen stack as one of the arguments - * will check equality of both stacks' {@link ISpoilableItem#getTicksUntilSpoiled(ItemStack)} values, as well as all + * will check equality of both stacks' {@link ISpoilableItem#getTicksUntilSpoiled()} values, as well as all * non-spoilage * related tags and the equality of the item itself. * - * @see ISpoilableItem#unfreezeSpoiling(ItemStack) + * @see ISpoilableItem#unfreezeSpoiling() */ - default void freezeSpoiling(ItemStack stack) { - ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(true); - } + void freezeSpoiling(); /** * Please refrain from overriding this method unless absolutely necessary (I have no idea what will happen). *
    * Unfreezes the provided stack's spoiling progress. If the stack's - * {@link ISpoilableItem#getTicksUntilSpoiled(ItemStack)} is {@code <= 0}, it will spoil + * {@link ISpoilableItem#getTicksUntilSpoiled()} is {@code <= 0}, it will spoil * immediately after this method call. * This method modifies the provided stack's NBT data. * - * @see ISpoilableItem#freezeSpoiling(ItemStack) + * @see ISpoilableItem#freezeSpoiling() */ - @ApiStatus.NonExtendable - default void unfreezeSpoiling(ItemStack stack) { - ((ISpoilableItemStack) (Object) stack).gtceu$setFreezeSpoiling(false); - } + void unfreezeSpoiling(); + + boolean isFrozen(); /** * Should return the stack to replace the provided stack with when it spoils */ - ItemStack spoilResult(ItemStack stack); + ItemStack spoilResult(); /** * Note: returning {@code false} in this method won't stop the item from spoiling if the spoiling NBT has already * been initialized */ - boolean shouldSpoil(ItemStack stack); + boolean shouldSpoil(); + + long getCreationTick(); + + void setCreationTick(long tick); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index bac53809e3d..0e9e39fb6a7 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.api.machine.trait; import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; @@ -171,8 +172,8 @@ public static List handleRecipe(IO io, GTRecipe recipe, List 0) { + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); + if (spoilable != null && spoilable.shouldSpoil() && spoilableCount > 0) { double spoiled = spoilProgress / spoilableCount; - spoilable.setTicksUntilSpoiled(stack, (long) (spoiled * spoilable.getSpoilTicks(stack))); + spoilable.setTicksUntilSpoiled((long) (spoiled * spoilable.getSpoilTicks())); } }).build(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 627890fdc6a..374060fa211 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -1,12 +1,16 @@ package com.gregtechceu.gtceu.common.item; -import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; +import com.gregtechceu.gtceu.api.item.component.IItemComponent; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.api.item.component.forge.IComponentCapability; import com.gregtechceu.gtceu.utils.FormattingUtil; import net.minecraft.ChatFormatting; +import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.util.FastColor; import net.minecraft.world.item.ItemStack; @@ -15,12 +19,16 @@ import net.minecraft.world.level.Level; import it.unimi.dsi.fastutil.ints.IntIntPair; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.function.Function; -public class SpoilableBehaviour implements ISpoilableItem, IAddInformation, IDurabilityBar { +public class SpoilableBehaviour implements IAddInformation, IDurabilityBar, IItemComponent, IComponentCapability { private final Function ticks; private final Function spoilResult; @@ -48,29 +56,14 @@ public SpoilableBehaviour(long ticks, ItemLike spoilResult) { this(stack -> ticks, stack -> spoilResult.asItem().getDefaultInstance().copyWithCount(stack.getCount())); } - @Override - public long getSpoilTicks(ItemStack stack) { - return ticks.apply(stack); - } - - @Override - public ItemStack spoilResult(ItemStack stack) { - return spoilResult.apply(stack); - } - - @Override - public boolean shouldSpoil(ItemStack stack) { - return true; - } - @Override public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, TooltipFlag isAdvanced) { - ISpoilableItemStack spoilable = (ISpoilableItemStack) (Object) stack; + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); if (spoilable == null) return; tooltipComponents.add(Component.translatable( "gtceu.tooltip.spoil_time_remaining", - Component.literal(FormattingUtil.formatTime(getTicksUntilSpoiled(stack))) + Component.literal(FormattingUtil.formatTime(spoilable.getTicksUntilSpoiled())) .withStyle(ChatFormatting.DARK_AQUA))); tooltipComponents.add(Component.translatable( "gtceu.tooltip.spoils_into", @@ -78,11 +71,11 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List LazyOptional getCapability(ItemStack itemStack, @NotNull Capability cap) { + return GTCapability.CAPABILITY_SPOILABLE_ITEM.orEmpty(cap, LazyOptional.of(() -> new SpoilableItemStack(itemStack) { + @Override + public long getSpoilTicks() { + return ticks.apply(getStack()); + } + + @Override + public ItemStack spoilResult() { + return spoilResult.apply(getStack()); + } + })); } - public void attachTo(ItemLike item) { - this.onAttached(item.asItem()); - ISpoilableItem.attachSpoilable(this, item); + public ICapabilityProvider toCapProvider(ItemStack stack) { + return new ICapabilityProvider() { + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability capability, @Nullable Direction direction) { + return SpoilableBehaviour.this.getCapability(stack, capability); + } + }; } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java new file mode 100644 index 00000000000..3a8db2b8a4a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java @@ -0,0 +1,105 @@ +package com.gregtechceu.gtceu.common.item; + +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.item.ISpoilableItemStackMixin; +import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import lombok.Getter; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraftforge.server.ServerLifecycleHooks; + +public abstract class SpoilableItemStack implements ISpoilableItem { + @Getter + private final ItemStack stack; + + public SpoilableItemStack(ItemStack stack) { + this.stack = stack; + } + + private ISpoilableItemStackMixin mixin() { + return (ISpoilableItemStackMixin) (Object) stack; + } + + public void updateFreshness(boolean createTag) { + mixin().gtceu$updateFreshness(null, createTag); + } + + @Override + public long getCreationTick() { + CompoundTag tag = stack.getTagElement("GTCEu_spoilable"); + if (tag == null) return 0; + return tag.getLong("creation_tick"); + } + + @Override + public void setCreationTick(long tick) { + CompoundTag tag = stack.getTagElement("GTCEu_spoilable"); + if (tag == null) return; + tag.putLong("creation_tick", tick); + } + + @Override + public long getTicksUntilSpoiled() { + updateFreshness(false); + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + Level level = null; + if (server != null) level = server.overworld(); + CompoundTag spoilTag = stack.getTagElement("GTCEu_spoilable"); + if (level != null && spoilTag != null) { + if (spoilTag.contains("frozenRemainingTicks")) return spoilTag.getLong("frozenRemainingTicks"); + return this.getSpoilTicks() - level.getGameTime() + + this.getCreationTick(); + } + return this.getSpoilTicks(); + } + + @Override + public void setTicksUntilSpoiled(long value) { + updateFreshness(false); + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + Level level = null; + if (server != null) level = server.overworld(); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + if (level != null && stack.getTagElement("GTCEu_spoilable") != null && spoilable != null) + setCreationTick(level.getGameTime() - spoilable.getSpoilTicks() + value); + } + + private void setFreezeSpoiling(boolean freeze) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + Level level = server == null ? null : server.overworld(); + if (level == null) return; + if (freeze) { + updateFreshness(true); + stack.getOrCreateTagElement("GTCEu_spoilable").putLong("frozenRemainingTicks", getTicksUntilSpoiled()); + } else { + CompoundTag spoilTag = stack.getTagElement("GTCEu_spoilable"); + if (spoilTag != null && spoilTag.contains("frozenRemainingTicks")) { + setTicksUntilSpoiled(spoilTag.getLong("frozenRemainingTicks")); + spoilTag.remove("frozenRemainingTicks"); + } + } + } + + @Override + public void freezeSpoiling() { + setFreezeSpoiling(true); + } + + @Override + public void unfreezeSpoiling() { + setFreezeSpoiling(false); + } + + @Override + public boolean isFrozen() { + CompoundTag tag = stack.getTagElement("GTCEu_spoilable"); + return tag != null && tag.contains("frozenRemainingTicks"); + } + + @Override + public boolean shouldSpoil() { + return true; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index b8e712de783..63995649b07 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -1,7 +1,8 @@ package com.gregtechceu.gtceu.core.mixins; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.item.ISpoilableItemStack; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.item.ISpoilableItemStackMixin; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; @@ -37,7 +38,7 @@ import javax.annotation.Nullable; @Mixin(ItemStack.class) -public abstract class ItemStackMixin implements ISpoilableItemStack { +public abstract class ItemStackMixin implements ISpoilableItemStackMixin { @Unique private boolean gtceu$isUpdating = false; @@ -94,9 +95,9 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 gtceu$isUpdating = true; MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); - if (spoilable != null && spoilable.shouldSpoil((ItemStack) (Object) this)) { - if (spoilable.getSpoilTicks((ItemStack) (Object) this) < 0) { + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + if (spoilable != null && spoilable.shouldSpoil()) { + if (spoilable.getSpoilTicks() < 0) { gtceu$isUpdating = false; return; } @@ -112,17 +113,16 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 if (!tag.contains("creation_tick")) { tag.putLong("creation_tick", level.getGameTime()); } - @SuppressWarnings("DataFlowIssue") - long spoilTicks = spoilable.getSpoilTicks((ItemStack) (Object) this); + long spoilTicks = spoilable.getSpoilTicks(); long timeDifference = level.getGameTime() - tag.getLong("creation_tick") - spoilTicks; if (timeDifference >= 0) { @SuppressWarnings("DataFlowIssue") - ItemStack newStack = spoilable.spoilResult((ItemStack) (Object) this); + ItemStack newStack = spoilable.spoilResult(); item = newStack.getItem(); delegate = ForgeRegistries.ITEMS.getDelegateOrThrow(item); count = newStack.getCount(); this.tag = newStack.getTag(); - ISpoilableItem newSpoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); + ISpoilableItem newSpoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); if (newSpoilable != null && (this.tag == null || !this.tag.contains("GTCEu_spoilable"))) { getOrCreateTagElement("GTCEu_spoilable").putLong("creation_tick", level.getGameTime() - timeDifference); @@ -143,8 +143,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 "getItemHolder" }) private void injectedFreshnessUpdate(CallbackInfoReturnable cir) { MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - gtceu$updateFreshness(server == null ? null : server.overworld(), - ISpoilableItem.BREAK_EVERYTHING || entityRepresentation != null); + gtceu$updateFreshness(server == null ? null : server.overworld(), entityRepresentation != null); } @Inject(at = @At("HEAD"), method = "inventoryTick") @@ -157,104 +156,36 @@ private void updateFreshnessOnCraft(Level level, Player player, int amount, Call gtceu$updateFreshness(null, true); } - @Override - @Unique - public long gtceu$getCreationTick(@Nullable Level level) { - CompoundTag tag = getTagElement("GTCEu_spoilable"); - if (tag == null) return 0; - return tag.getLong("creation_tick"); - } - - @Override - @Unique - public void gtceu$setCreationTick(@Nullable Level level, long value) { - CompoundTag tag = getTagElement("GTCEu_spoilable"); - if (tag == null) return; - tag.putLong("creation_tick", value); - } - - @Override - @Unique - public long gtceu$getRemainingTicks(@Nullable Level level) { - gtceu$updateFreshness(level, false); - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); - CompoundTag spoilTag = getTagElement("GTCEu_spoilable"); - if (level != null && spoilTag != null && spoilable != null) { - if (spoilTag.contains("frozenRemainingTicks")) return spoilTag.getLong("frozenRemainingTicks"); - return spoilable.getSpoilTicks((ItemStack) (Object) this) - level.getGameTime() + - gtceu$getCreationTick(level); - } - if (spoilable != null) return spoilable.getSpoilTicks((ItemStack) (Object) this); - return 0; - } - - @Unique - @Override - public void gtceu$setRemainingTicks(@Nullable Level level, long value) { - gtceu$updateFreshness(level, false); - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); - if (level != null && getTagElement("GTCEu_spoilable") != null && spoilable != null) - gtceu$setCreationTick(level, - level.getGameTime() - spoilable.getSpoilTicks((ItemStack) (Object) this) + value); - } - - @Unique - @Override - public void gtceu$setFreezeSpoiling(boolean freezeUpdates) { - if (ISpoilableItem.getSpoilable((ItemStack) (Object) this) == null) return; - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - Level level = server == null ? null : server.overworld(); - if (level == null) return; - if (freezeUpdates) { - gtceu$updateFreshness(level, true); - getOrCreateTagElement("GTCEu_spoilable").putLong("frozenRemainingTicks", gtceu$getRemainingTicks(null)); - } else { - CompoundTag spoilTag = getTagElement("GTCEu_spoilable"); - if (spoilTag != null && spoilTag.contains("frozenRemainingTicks")) { - gtceu$setRemainingTicks(null, spoilTag.getLong("frozenRemainingTicks")); - spoilTag.remove("frozenRemainingTicks"); - } - } - } - @Inject(at = @At("HEAD"), method = "isSameItemSameTags", cancellable = true) private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackInfoReturnable cir) { - CompoundTag tag1 = stack.getTagElement("GTCEu_spoilable"); - CompoundTag tag2 = other.getTagElement("GTCEu_spoilable"); + ISpoilableItem spoilable1 = GTCapabilityHelper.getSpoilable(stack); + ISpoilableItem spoilable2 = GTCapabilityHelper.getSpoilable(stack); boolean isSameItem = ItemStack.isSameItem(stack, other) && stack.areCapsCompatible(other); CompoundTag modifiedTag1 = stack.getTag() == null ? null : stack.getTag().copy(); CompoundTag modifiedTag2 = other.getTag() == null ? null : other.getTag().copy(); if (modifiedTag1 != null) modifiedTag1.remove("GTCEu_spoilable"); if (modifiedTag2 != null) modifiedTag2.remove("GTCEu_spoilable"); isSameItem = isSameItem && Objects.equals(modifiedTag1, modifiedTag2); - if (isSameItem && tag1 != null && tag2 != null) { - if (tag1.contains("frozenRemainingTicks") || tag2.contains("frozenRemainingTicks")) { + if (isSameItem && spoilable1 != null && spoilable2 != null) { + if (spoilable1.isFrozen() || spoilable2.isFrozen()) { if (!ISpoilableItem.FROZEN_EQUALITY && - (tag1.contains("frozenRemainingTicks") ^ tag2.contains("frozenRemainingTicks"))) { + (spoilable1.isFrozen() ^ spoilable2.isFrozen())) { cir.setReturnValue(false); return; } - ISpoilableItem spoilable1 = ISpoilableItem.getSpoilable(stack); - ISpoilableItem spoilable2 = ISpoilableItem.getSpoilable(other); - if (spoilable1 != null && spoilable2 != null) { - cir.setReturnValue( - spoilable1.getTicksUntilSpoiled(stack) == spoilable2.getTicksUntilSpoiled(other)); - } + cir.setReturnValue(spoilable1.getTicksUntilSpoiled() == spoilable2.getTicksUntilSpoiled()); + return; } else { - long tick1 = tag1.getLong("creation_tick"); - long tick2 = tag2.getLong("creation_tick"); + long tick1 = spoilable1.getCreationTick(); + long tick2 = spoilable2.getCreationTick(); if (tick1 != tick2) { long avg; if (stack.getCount() + other.getCount() > 0) avg = (tick1 * stack.getCount() + tick2 * other.getCount()) / (stack.getCount() + other.getCount()); else avg = tick1; - tag1.putLong("creation_tick", avg); - tag2.putLong("creation_tick", avg); + spoilable1.setCreationTick(avg); + spoilable2.setCreationTick(avg); } } } @@ -267,7 +198,7 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn locals = LocalCapture.CAPTURE_FAILSOFT) private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackInfoReturnable> cir, List list) { - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IAddInformation addInformation) { addInformation.appendHoverText((ItemStack) (Object) this, player == null ? null : player.level(), list, isAdvanced); @@ -283,7 +214,7 @@ private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackIn @Inject(at = @At("HEAD"), method = "isBarVisible", cancellable = true) private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.isBarVisible((ItemStack) (Object) this)); } else if (gtceu$fakeTooltip) { @@ -293,7 +224,7 @@ private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarColor", cancellable = true) private void aprilFoolsBarColor(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.getBarColor((ItemStack) (Object) this)); } else if (gtceu$fakeTooltip) { @@ -303,7 +234,7 @@ private void aprilFoolsBarColor(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarWidth", cancellable = true) private void aprilFoolsBarWidth(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = ISpoilableItem.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { cir.setReturnValue(durabilityBar.getBarWidth((ItemStack) (Object) this)); } else if (gtceu$fakeTooltip) { diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index ff26977f26a..b0b4caf183a 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.cover.filter.SimpleItemFilter; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; @@ -20,12 +21,15 @@ import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.fml.common.Mod; import net.minecraftforge.gametest.GameTestHolder; import net.minecraftforge.gametest.PrefixGameTestTemplate; import net.minecraftforge.items.IItemHandler; @@ -58,20 +62,24 @@ public static void prepare(Level level) { .duration(20) .keepSpoilingProgress(false) .buildRawRecipe()); + Mod.EventBusSubscriber.Bus.FORGE.bus().get().addListener(SpoilableBehaviourTest::attachSpoilables); } private static void makeSpoilables(GameTestHelper helper) { - new SpoilableBehaviour(10, Items.DIRT).attachTo(Items.JIGSAW); - new SpoilableBehaviour(10, Items.STRUCTURE_BLOCK).attachTo(Items.APPLE); - new SpoilableBehaviour(40, Items.STRUCTURE_VOID).attachTo(Items.STRUCTURE_BLOCK); - new SpoilableBehaviour(10, Items.JIGSAW).attachTo(Items.STRUCTURE_VOID); - helper.runAtTickTime(100, () -> { - ISpoilableItem.unspoil(Items.JIGSAW); - ISpoilableItem.unspoil(Items.STRUCTURE_VOID); - ISpoilableItem.unspoil(Items.APPLE); - ISpoilableItem.unspoil(Items.STRUCTURE_BLOCK); - helper.succeed(); - }); + helper.runAtTickTime(100, helper::succeed); + } + + private static void attachSpoilables(AttachCapabilitiesEvent event) { + ResourceLocation id = GTCEu.id("spoilable"); + ItemStack stack = event.getObject(); + if (stack.getItem() == Items.JIGSAW) + event.addCapability(id, new SpoilableBehaviour(10, Items.DIRT).toCapProvider(stack)); + if (stack.getItem() == Items.APPLE) + event.addCapability(id, new SpoilableBehaviour(10, Items.STRUCTURE_BLOCK).toCapProvider(stack)); + if (stack.getItem() == Items.STRUCTURE_BLOCK) + event.addCapability(id, new SpoilableBehaviour(40, Items.STRUCTURE_VOID).toCapProvider(stack)); + if (stack.getItem() == Items.STRUCTURE_VOID) + event.addCapability(id, new SpoilableBehaviour(10, Items.JIGSAW).toCapProvider(stack)); } private static BusHolder getBussesAndForm(GameTestHelper helper) { @@ -128,13 +136,13 @@ public static void itemSpoilsInCrate(GameTestHelper helper) { helper.assertTrue(TestUtils.isItemStackEqual( Items.JIGSAW.getDefaultInstance().copyWithCount(23), stack), "jigsaw spoiled 1 tick earlier"); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); helper.assertTrue(spoilable != null, "spoilable was null when shouldn't have"); assert spoilable != null; - helper.assertTrue(spoilable.shouldSpoil(stack), "shouldSpoil returned false on spoilable item"); - helper.assertTrue(spoilable.getTicksUntilSpoiled(stack) == 1, + helper.assertTrue(spoilable.shouldSpoil(), "shouldSpoil returned false on spoilable item"); + helper.assertTrue(spoilable.getTicksUntilSpoiled() == 1, "spoilable didn't return correct ticks until spoiled amount"); - helper.assertTrue(spoilable.getSpoilTicks(stack) == 10, + helper.assertTrue(spoilable.getSpoilTicks() == 10, "spoilable didn't return correct total tick amount"); }); helper.runAtTickTime(10, () -> helper.assertTrue(TestUtils.isItemStackEqual( @@ -148,7 +156,7 @@ public static void spoilageTransfersInRecipe(GameTestHelper helper) { BusHolder busHolder = getBussesAndForm(helper); ItemStack input = new ItemStack(Items.JIGSAW); ISpoilableItem.update(input); - Objects.requireNonNull(ISpoilableItem.getSpoilable(input)).setTicksUntilSpoiled(input, 8); + Objects.requireNonNull(GTCapabilityHelper.getSpoilable(input)).setTicksUntilSpoiled(8); busHolder.inputBus1.getInventory().setStackInSlot(0, input); helper.runAtTickTime(21, () -> { ItemStack stack = busHolder.outputBus1.getInventory().getStackInSlot(0); @@ -157,10 +165,10 @@ public static void spoilageTransfersInRecipe(GameTestHelper helper) { new ItemStack(Items.STRUCTURE_BLOCK)), "incorrect recipe output (%s != %s)".formatted(stack.toString(), new ItemStack(Items.STRUCTURE_BLOCK).toString())); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); helper.assertTrue(spoilable != null, "recipe output was not spoilable"); assert spoilable != null; - TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(stack), 27, + TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(), 27, "recipe output didn't have correct ticks until spoiled"); }); } @@ -171,7 +179,7 @@ public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { BusHolder busHolder = getBussesAndForm(helper); ItemStack input = new ItemStack(Items.APPLE); ISpoilableItem.update(input); - Objects.requireNonNull(ISpoilableItem.getSpoilable(input)).setTicksUntilSpoiled(input, 8); + Objects.requireNonNull(GTCapabilityHelper.getSpoilable(input)).setTicksUntilSpoiled(8); busHolder.inputBus1.getInventory().setStackInSlot(0, input); helper.runAtTickTime(21, () -> { ItemStack stack = busHolder.outputBus1.getInventory().getStackInSlot(0); @@ -180,10 +188,10 @@ public static void spoilageDoesntTransferInRecipe(GameTestHelper helper) { new ItemStack(Items.STRUCTURE_BLOCK)), "incorrect recipe output (%s != %s)".formatted(stack.toString(), new ItemStack(Items.STRUCTURE_BLOCK).toString())); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); helper.assertTrue(spoilable != null, "recipe output was not spoilable"); assert spoilable != null; - TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(stack), 40, + TestUtils.assertEqual(helper, spoilable.getTicksUntilSpoiled(), 40, "recipe output didn't have correct ticks until spoiled"); }); } @@ -228,12 +236,12 @@ public static void spoilableFilteringTest(GameTestHelper helper) { helper.runAtTickTime(10, () -> cover.setWorkingEnabled(true)); helper.runAtTickTime(20, () -> { ItemStack stack = crate2.inventory.getStackInSlot(0); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); helper.assertTrue(TestUtils.isItemStackEqual(stack, Items.STRUCTURE_BLOCK.getDefaultInstance()), "wrong item"); helper.assertTrue(spoilable != null, "spoilable was null"); assert spoilable != null; - TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(stack), "wrong ticks until spoiled"); + TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(), "wrong ticks until spoiled"); }); } @@ -247,10 +255,10 @@ public static void spoilableFilteringWithSpoilableTest(GameTestHelper helper) { ConveyorCover cover = (ConveyorCover) TestUtils.placeCover(helper, crate1, GTItems.CONVEYOR_MODULE_HV.asStack(), Direction.UP); ItemStack itemForFilter = Items.STRUCTURE_BLOCK.getDefaultInstance(); - ISpoilableItem filterSpoilable = ISpoilableItem.getSpoilable(itemForFilter); + ISpoilableItem filterSpoilable = GTCapabilityHelper.getSpoilable(itemForFilter); assert filterSpoilable != null; ISpoilableItem.update(itemForFilter); - filterSpoilable.setTicksUntilSpoiled(itemForFilter, 5); + filterSpoilable.setTicksUntilSpoiled(5); CompoundTag filterTag = SimpleItemFilter.forItems(itemForFilter).saveFilter(); ItemStack filter = GTItems.ITEM_FILTER.asStack(); filter.setTag(filterTag); @@ -260,12 +268,12 @@ public static void spoilableFilteringWithSpoilableTest(GameTestHelper helper) { helper.runAtTickTime(10, () -> cover.setWorkingEnabled(true)); helper.runAtTickTime(20, () -> { ItemStack stack = crate2.inventory.getStackInSlot(0); - ISpoilableItem spoilable = ISpoilableItem.getSpoilable(stack); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); helper.assertTrue(TestUtils.isItemStackEqual(stack, Items.STRUCTURE_BLOCK.getDefaultInstance()), "wrong item"); helper.assertTrue(spoilable != null, "spoilable was null"); assert spoilable != null; - TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(stack), "wrong ticks until spoiled"); + TestUtils.assertEqual(helper, 20, spoilable.getTicksUntilSpoiled(), "wrong ticks until spoiled"); }); } } From b45a610794f0a7e1ea6cb86a67c69cfc6ed40e50 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 26 Oct 2025 20:03:38 +0300 Subject: [PATCH 054/123] addGenericListener --- .../gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index b0b4caf183a..0d3065fb381 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -62,7 +62,7 @@ public static void prepare(Level level) { .duration(20) .keepSpoilingProgress(false) .buildRawRecipe()); - Mod.EventBusSubscriber.Bus.FORGE.bus().get().addListener(SpoilableBehaviourTest::attachSpoilables); + Mod.EventBusSubscriber.Bus.FORGE.bus().get().addGenericListener(ItemStack.class, SpoilableBehaviourTest::attachSpoilables); } private static void makeSpoilables(GameTestHelper helper) { From 3d82800553a683bc410deb12197fcb17caee2585 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Sun, 26 Oct 2025 20:16:17 +0300 Subject: [PATCH 055/123] remove direct mixin call from ISpoilableItem --- .../gtceu/api/item/component/ISpoilableItem.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 45607d61689..d8c7bdcd736 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.api.item.component; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.item.ISpoilableItemStackMixin; import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; @@ -71,9 +72,12 @@ public interface ISpoilableItem { * Should be called when it finishes crafting, for example. */ static void update(ItemStack stack) { - ((ISpoilableItemStackMixin) (Object) stack).gtceu$updateFreshness(null, true); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); + if (spoilable != null) spoilable.updateFreshness(true); } + void updateFreshness(boolean createTag); + /** * Should return the amount of ticks that this item can stay fresh. * The result of this method shouldn't be based on the freshness of the provided stack From 4520b6ddf01bca84f8699506030995ed3ba53912 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 27 Oct 2025 08:47:52 +0300 Subject: [PATCH 056/123] move some methods to SpoilableItemStack --- .../gtceu/common/item/SpoilableBehaviour.java | 63 ++----------------- .../gtceu/common/item/SpoilableItemStack.java | 63 ++++++++++++++++++- .../gtceu/core/mixins/ItemStackMixin.java | 1 - 3 files changed, 67 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 374060fa211..995904c828e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -1,34 +1,23 @@ package com.gregtechceu.gtceu.common.item; -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.item.component.IAddInformation; -import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.IItemComponent; -import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.api.item.component.forge.IComponentCapability; -import com.gregtechceu.gtceu.utils.FormattingUtil; -import net.minecraft.ChatFormatting; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; -import net.minecraft.util.FastColor; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.Level; -import it.unimi.dsi.fastutil.ints.IntIntPair; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; import java.util.function.Function; -public class SpoilableBehaviour implements IAddInformation, IDurabilityBar, IItemComponent, IComponentCapability { +public class SpoilableBehaviour implements IItemComponent, IComponentCapability { private final Function ticks; private final Function spoilResult; @@ -56,51 +45,6 @@ public SpoilableBehaviour(long ticks, ItemLike spoilResult) { this(stack -> ticks, stack -> spoilResult.asItem().getDefaultInstance().copyWithCount(stack.getCount())); } - @Override - public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, - TooltipFlag isAdvanced) { - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); - if (spoilable == null) return; - tooltipComponents.add(Component.translatable( - "gtceu.tooltip.spoil_time_remaining", - Component.literal(FormattingUtil.formatTime(spoilable.getTicksUntilSpoiled())) - .withStyle(ChatFormatting.DARK_AQUA))); - tooltipComponents.add(Component.translatable( - "gtceu.tooltip.spoils_into", - spoilsInto.apply(stack))); - if (isAdvanced.isAdvanced()) { - tooltipComponents.add(Component.translatable( - "gtceu.tooltip.spoil_time_total", - Component.literal(FormattingUtil.formatTime(spoilable.getSpoilTicks())) - .withStyle(ChatFormatting.GREEN))); - tooltipComponents.add(Component.translatable( - "gtceu.tooltip.creation_tick", - spoilable.getCreationTick())); - } - } - - @Override - public int getBarColor(ItemStack stack) { - return FastColor.ARGB32.color(255, 255, 255, 255); - } - - @Override - public boolean doDamagedStateColors(ItemStack itemStack) { - return false; - } - - @Override - public @Nullable IntIntPair getDurabilityColorsForDisplay(ItemStack itemStack) { - return IntIntPair.of(getBarColor(itemStack), getBarColor(itemStack)); - } - - @Override - public float getDurabilityForDisplay(ItemStack stack) { - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); - if (spoilable == null) return 0; - return (float) spoilable.getTicksUntilSpoiled() / spoilable.getSpoilTicks(); - } - @Override public @NotNull LazyOptional getCapability(ItemStack itemStack, @NotNull Capability cap) { return GTCapability.CAPABILITY_SPOILABLE_ITEM.orEmpty(cap, LazyOptional.of(() -> new SpoilableItemStack(itemStack) { @@ -113,6 +57,11 @@ public long getSpoilTicks() { public ItemStack spoilResult() { return spoilResult.apply(getStack()); } + + @Override + protected Component getSpoilResultTooltip() { + return spoilsInto.apply(getStack()); + } })); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java index 3a8db2b8a4a..810b624d86b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java @@ -2,15 +2,26 @@ import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.item.ISpoilableItemStackMixin; +import com.gregtechceu.gtceu.api.item.component.IAddInformation; +import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import it.unimi.dsi.fastutil.ints.IntIntPair; import lombok.Getter; +import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.FastColor; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; import net.minecraftforge.server.ServerLifecycleHooks; +import org.jetbrains.annotations.Nullable; -public abstract class SpoilableItemStack implements ISpoilableItem { +import java.util.List; + +public abstract class SpoilableItemStack implements ISpoilableItem, IAddInformation, IDurabilityBar { @Getter private final ItemStack stack; @@ -61,7 +72,7 @@ public void setTicksUntilSpoiled(long value) { MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); Level level = null; if (server != null) level = server.overworld(); - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); if (level != null && stack.getTagElement("GTCEu_spoilable") != null && spoilable != null) setCreationTick(level.getGameTime() - spoilable.getSpoilTicks() + value); } @@ -102,4 +113,52 @@ public boolean isFrozen() { public boolean shouldSpoil() { return true; } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, + TooltipFlag isAdvanced) { + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); + if (spoilable == null) return; + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.spoil_time_remaining", + Component.literal(FormattingUtil.formatTime(spoilable.getTicksUntilSpoiled())) + .withStyle(ChatFormatting.DARK_AQUA))); + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.spoils_into", spoilResult())); + if (isAdvanced.isAdvanced()) { + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.spoil_time_total", + Component.literal(FormattingUtil.formatTime(spoilable.getSpoilTicks())) + .withStyle(ChatFormatting.GREEN))); + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.creation_tick", + spoilable.getCreationTick())); + } + } + + protected Component getSpoilResultTooltip() { + return spoilResult().getDisplayName(); + } + + @Override + public int getBarColor(ItemStack stack) { + return FastColor.ARGB32.color(255, 255, 255, 255); + } + + @Override + public boolean doDamagedStateColors(ItemStack itemStack) { + return false; + } + + @Override + public @Nullable IntIntPair getDurabilityColorsForDisplay(ItemStack itemStack) { + return IntIntPair.of(getBarColor(itemStack), getBarColor(itemStack)); + } + + @Override + public float getDurabilityForDisplay(ItemStack stack) { + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(stack); + if (spoilable == null) return 0; + return (float) spoilable.getTicksUntilSpoiled() / spoilable.getSpoilTicks(); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 63995649b07..f161d46bb42 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -116,7 +116,6 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 long spoilTicks = spoilable.getSpoilTicks(); long timeDifference = level.getGameTime() - tag.getLong("creation_tick") - spoilTicks; if (timeDifference >= 0) { - @SuppressWarnings("DataFlowIssue") ItemStack newStack = spoilable.spoilResult(); item = newStack.getItem(); delegate = ForgeRegistries.ITEMS.getDelegateOrThrow(item); From c589cc0904ddb91605d9950445772a73dc56e22f Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 27 Oct 2025 10:51:47 +0300 Subject: [PATCH 057/123] reinit capabilities when setting item --- .../com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index f161d46bb42..9b596af5f8e 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -88,6 +88,9 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 @Nullable private Entity entityRepresentation; + @Shadow + protected abstract void forgeInit(); + @Unique @Override public void gtceu$updateFreshness(Level level, boolean createTag) { @@ -121,6 +124,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 delegate = ForgeRegistries.ITEMS.getDelegateOrThrow(item); count = newStack.getCount(); this.tag = newStack.getTag(); + forgeInit(); ISpoilableItem newSpoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); if (newSpoilable != null && (this.tag == null || !this.tag.contains("GTCEu_spoilable"))) { getOrCreateTagElement("GTCEu_spoilable").putLong("creation_tick", From 4419ebf4acecb44e908915531331f2bbd8e085f6 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 27 Oct 2025 11:04:32 +0300 Subject: [PATCH 058/123] spotless --- .../api/item/ISpoilableItemStackMixin.java | 1 + .../api/item/component/ISpoilableItem.java | 1 - .../gtceu/common/item/SpoilableBehaviour.java | 36 ++++++++++--------- .../gtceu/common/item/SpoilableItemStack.java | 7 ++-- .../common/item/SpoilableBehaviourTest.java | 3 +- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java index e2ce576ae36..4ab5d3f3a15 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/ISpoilableItemStackMixin.java @@ -5,5 +5,6 @@ import org.jetbrains.annotations.Nullable; public interface ISpoilableItemStackMixin { + void gtceu$updateFreshness(@Nullable Level level, boolean createTag); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index d8c7bdcd736..94c57c932df 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -1,7 +1,6 @@ package com.gregtechceu.gtceu.api.item.component; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.item.ISpoilableItemStackMixin; import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; import net.minecraft.world.item.Item; diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java index 995904c828e..e1e2b384208 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviour.java @@ -8,10 +8,10 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ItemLike; - import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,28 +47,32 @@ public SpoilableBehaviour(long ticks, ItemLike spoilResult) { @Override public @NotNull LazyOptional getCapability(ItemStack itemStack, @NotNull Capability cap) { - return GTCapability.CAPABILITY_SPOILABLE_ITEM.orEmpty(cap, LazyOptional.of(() -> new SpoilableItemStack(itemStack) { - @Override - public long getSpoilTicks() { - return ticks.apply(getStack()); - } + return GTCapability.CAPABILITY_SPOILABLE_ITEM.orEmpty(cap, + LazyOptional.of(() -> new SpoilableItemStack(itemStack) { - @Override - public ItemStack spoilResult() { - return spoilResult.apply(getStack()); - } + @Override + public long getSpoilTicks() { + return ticks.apply(getStack()); + } - @Override - protected Component getSpoilResultTooltip() { - return spoilsInto.apply(getStack()); - } - })); + @Override + public ItemStack spoilResult() { + return spoilResult.apply(getStack()); + } + + @Override + protected Component getSpoilResultTooltip() { + return spoilsInto.apply(getStack()); + } + })); } public ICapabilityProvider toCapProvider(ItemStack stack) { return new ICapabilityProvider() { + @Override - public @NotNull LazyOptional getCapability(@NotNull Capability capability, @Nullable Direction direction) { + public @NotNull LazyOptional getCapability(@NotNull Capability capability, + @Nullable Direction direction) { return SpoilableBehaviour.this.getCapability(stack, capability); } }; diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java index 810b624d86b..68e6708e2df 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/SpoilableItemStack.java @@ -6,8 +6,7 @@ import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.ISpoilableItem; import com.gregtechceu.gtceu.utils.FormattingUtil; -import it.unimi.dsi.fastutil.ints.IntIntPair; -import lombok.Getter; + import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; @@ -17,11 +16,15 @@ import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; import net.minecraftforge.server.ServerLifecycleHooks; + +import it.unimi.dsi.fastutil.ints.IntIntPair; +import lombok.Getter; import org.jetbrains.annotations.Nullable; import java.util.List; public abstract class SpoilableItemStack implements ISpoilableItem, IAddInformation, IDurabilityBar { + @Getter private final ItemStack stack; diff --git a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java index 0d3065fb381..449f539c4f1 100644 --- a/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java +++ b/src/test/java/com/gregtechceu/gtceu/common/item/SpoilableBehaviourTest.java @@ -62,7 +62,8 @@ public static void prepare(Level level) { .duration(20) .keepSpoilingProgress(false) .buildRawRecipe()); - Mod.EventBusSubscriber.Bus.FORGE.bus().get().addGenericListener(ItemStack.class, SpoilableBehaviourTest::attachSpoilables); + Mod.EventBusSubscriber.Bus.FORGE.bus().get().addGenericListener(ItemStack.class, + SpoilableBehaviourTest::attachSpoilables); } private static void makeSpoilables(GameTestHelper helper) { From a1d5c3af298215f60c80a687963530d79fca38d5 Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 27 Oct 2025 11:07:01 +0300 Subject: [PATCH 059/123] fix warnings in mixin --- .../gtceu/core/mixins/ItemStackMixin.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java index 9b596af5f8e..b51b7b852d3 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ItemStackMixin.java @@ -91,6 +91,11 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 @Shadow protected abstract void forgeInit(); + @Unique + private ItemStack gtceu$self() { + return (ItemStack) (Object) this; + } + @Unique @Override public void gtceu$updateFreshness(Level level, boolean createTag) { @@ -98,7 +103,7 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 gtceu$isUpdating = true; MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); if (level == null && server != null) level = server.overworld(); - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(gtceu$self()); if (spoilable != null && spoilable.shouldSpoil()) { if (spoilable.getSpoilTicks() < 0) { gtceu$isUpdating = false; @@ -125,14 +130,13 @@ private void injectFakeTooltipInit(ItemLike item, int count, CompoundTag p_41606 count = newStack.getCount(); this.tag = newStack.getTag(); forgeInit(); - ISpoilableItem newSpoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem newSpoilable = GTCapabilityHelper.getSpoilable(gtceu$self()); if (newSpoilable != null && (this.tag == null || !this.tag.contains("GTCEu_spoilable"))) { getOrCreateTagElement("GTCEu_spoilable").putLong("creation_tick", level.getGameTime() - timeDifference); try { gtceu$isUpdating = false; gtceu$updateFreshness(null, false); - gtceu$isUpdating = true; } catch (StackOverflowError ignored) {} // if some crazy pack dev makes an item spoil into a // spoilable that spoils into a spoilable 1000 times } @@ -201,9 +205,9 @@ private static void mergeSpoilables(ItemStack stack, ItemStack other, CallbackIn locals = LocalCapture.CAPTURE_FAILSOFT) private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackInfoReturnable> cir, List list) { - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(gtceu$self()); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IAddInformation addInformation) { - addInformation.appendHoverText((ItemStack) (Object) this, player == null ? null : player.level(), list, + addInformation.appendHoverText(gtceu$self(), player == null ? null : player.level(), list, isAdvanced); } else if (gtceu$fakeTooltip) { list.add(Component.translatable( @@ -217,9 +221,9 @@ private void aprilFoolsTooltip(Player player, TooltipFlag isAdvanced, CallbackIn @Inject(at = @At("HEAD"), method = "isBarVisible", cancellable = true) private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(gtceu$self()); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { - cir.setReturnValue(durabilityBar.isBarVisible((ItemStack) (Object) this)); + cir.setReturnValue(durabilityBar.isBarVisible(gtceu$self())); } else if (gtceu$fakeTooltip) { cir.setReturnValue(true); } @@ -227,9 +231,9 @@ private void aprilFoolsBarVisible(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarColor", cancellable = true) private void aprilFoolsBarColor(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(gtceu$self()); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { - cir.setReturnValue(durabilityBar.getBarColor((ItemStack) (Object) this)); + cir.setReturnValue(durabilityBar.getBarColor(gtceu$self())); } else if (gtceu$fakeTooltip) { cir.setReturnValue(FastColor.ARGB32.color(255, 255, 255, 255)); } @@ -237,9 +241,9 @@ private void aprilFoolsBarColor(CallbackInfoReturnable cir) { @Inject(at = @At("HEAD"), method = "getBarWidth", cancellable = true) private void aprilFoolsBarWidth(CallbackInfoReturnable cir) { - ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable((ItemStack) (Object) this); + ISpoilableItem spoilable = GTCapabilityHelper.getSpoilable(gtceu$self()); if (!(getItem() instanceof ISpoilableItem) && spoilable instanceof IDurabilityBar durabilityBar) { - cir.setReturnValue(durabilityBar.getBarWidth((ItemStack) (Object) this)); + cir.setReturnValue(durabilityBar.getBarWidth(gtceu$self())); } else if (gtceu$fakeTooltip) { cir.setReturnValue(13); } From 2c8b23c00cfee4842a22a71bdf982bfbf509bb0b Mon Sep 17 00:00:00 2001 From: TarLaboratories Date: Mon, 27 Oct 2025 11:25:06 +0300 Subject: [PATCH 060/123] modify javadoc in ISpoilableItem to talk about it being a capability --- .../api/item/component/ISpoilableItem.java | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java index 94c57c932df..ca7c19ad7c6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ISpoilableItem.java @@ -3,20 +3,14 @@ import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.common.item.SpoilableBehaviour; +import com.gregtechceu.gtceu.common.item.SpoilableItemStack; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.ItemLike; /** - * The interface items that spoil should implement. - *

    - * If you are developing an addon, and want to make an {@link Item} - * that does not extend this interface spoilable, use {@link ISpoilableItem#attachSpoilable(ISpoilableItem, ItemLike)}. - *

    - *

    - * If you want to make an item that spoils not spoil, use {@link ISpoilableItem#unspoil(ItemLike)}. - *

    - *

    + * This is a capability! {@link Item} subclasses should not implement this directly! + *
    * Spoilable items will, as the name implies, spoil (who could've thought). * Due to Minecraft's limitations, items will only start spoiling only if: *