From e6cbb6c9c1160d33bdf758572f4e325f051a7a03 Mon Sep 17 00:00:00 2001 From: Eli Orona Date: Sun, 1 Dec 2024 19:27:08 -0800 Subject: [PATCH] Backport the status effect changes (#387) --- .../src/main/java/qsl/internal/Versions.java | 2 +- library/entity/status_effect/build.gradle | 1 + .../impl/QuiltStatusEffectInternals.java | 2 + .../effect/mixin/EffectCommandMixin.java | 15 ++- .../effect/mixin/LivingEntityMixin.java | 103 ++++++++++++------ .../effect/mixin/MilkBucketItemMixin.java | 11 +- 6 files changed, 87 insertions(+), 47 deletions(-) diff --git a/build-logic/src/main/java/qsl/internal/Versions.java b/build-logic/src/main/java/qsl/internal/Versions.java index 4ed726df08..1655a67a40 100644 --- a/build-logic/src/main/java/qsl/internal/Versions.java +++ b/build-logic/src/main/java/qsl/internal/Versions.java @@ -23,7 +23,7 @@ public final class Versions { /** * The QSL version */ - public static final String QSL_VERSION = "6.2.0"; + public static final String QSL_VERSION = "6.3.0"; /** * The target Minecraft version. diff --git a/library/entity/status_effect/build.gradle b/library/entity/status_effect/build.gradle index d909cd82ae..f73a00e622 100644 --- a/library/entity/status_effect/build.gradle +++ b/library/entity/status_effect/build.gradle @@ -10,6 +10,7 @@ qslModule { moduleDependencies { core { api("qsl_base") + testmodOnly("resource_loader") } } injectedInterface("net/minecraft/class_1309") { diff --git a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/impl/QuiltStatusEffectInternals.java b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/impl/QuiltStatusEffectInternals.java index 25a5fa178e..becd044e6e 100644 --- a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/impl/QuiltStatusEffectInternals.java +++ b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/impl/QuiltStatusEffectInternals.java @@ -30,6 +30,8 @@ private QuiltStatusEffectInternals() { public static final String NAMESPACE = "quilt_status_effect"; + public static final int MIXIN_PRIORITY = 500; + public static Identifier id(String path) { return new Identifier(NAMESPACE, path); } diff --git a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/EffectCommandMixin.java b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/EffectCommandMixin.java index 02f02f3088..243b94aaeb 100644 --- a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/EffectCommandMixin.java +++ b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/EffectCommandMixin.java @@ -16,34 +16,37 @@ package org.quiltmc.qsl.entity.effect.mixin; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.effect.StatusEffect; import net.minecraft.server.command.EffectCommand; import org.quiltmc.qsl.entity.effect.api.StatusEffectRemovalReason; +import org.quiltmc.qsl.entity.effect.impl.QuiltStatusEffectInternals; -@Mixin(EffectCommand.class) +// See LivingEntityMixin +@Mixin(value = EffectCommand.class, priority = QuiltStatusEffectInternals.MIXIN_PRIORITY) public abstract class EffectCommandMixin { - @Redirect( + @WrapOperation( method = "executeClear(Lnet/minecraft/server/command/ServerCommandSource;Ljava/util/Collection;)I", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;clearStatusEffects()Z") ) - private static boolean quilt$addRemovalReason(LivingEntity instance) { + private static boolean quilt$addRemovalReason(LivingEntity instance, Operation original) { return instance.clearStatusEffects(StatusEffectRemovalReason.COMMAND_ALL) > 0; } - @Redirect( + @WrapOperation( method = "executeClear(Lnet/minecraft/server/command/ServerCommandSource;Ljava/util/Collection;Lnet/minecraft/registry/Holder;)I", at = @At( value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;removeStatusEffect(Lnet/minecraft/entity/effect/StatusEffect;)Z" ) ) - private static boolean quilt$addRemovalReason(LivingEntity instance, StatusEffect type) { + private static boolean quilt$addRemovalReason(LivingEntity instance, StatusEffect type, Operation original) { return instance.removeStatusEffect(type, StatusEffectRemovalReason.COMMAND_ONE); } } diff --git a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/LivingEntityMixin.java b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/LivingEntityMixin.java index 8a4a49e167..982a9f0f46 100644 --- a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/LivingEntityMixin.java +++ b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/LivingEntityMixin.java @@ -16,17 +16,20 @@ package org.quiltmc.qsl.entity.effect.mixin; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; +import com.google.common.collect.Iterators; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.entity.Entity; @@ -40,8 +43,11 @@ import org.quiltmc.qsl.entity.effect.api.StatusEffectRemovalReason; import org.quiltmc.qsl.entity.effect.api.StatusEffectUtils; import org.quiltmc.qsl.entity.effect.impl.QuiltStatusEffectInternals; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(LivingEntity.class) +// We want to make sure that our wrap operations are put before other mods, so that we wrap the vanilla call and not a mod's call. +// This is because we do not call the vanilla method, so any mod adding something will not be called. +@Mixin(value = LivingEntity.class, priority = QuiltStatusEffectInternals.MIXIN_PRIORITY) public abstract class LivingEntityMixin extends Entity implements QuiltLivingEntityStatusEffectExtensions { @SuppressWarnings("ConstantConditions") public LivingEntityMixin() { @@ -116,44 +122,79 @@ public void onStatusEffectRemoved(@NotNull StatusEffectInstance effect, @NotNull StatusEffectEvents.ON_APPLIED.invoker().onApplied((LivingEntity) (Object) this, effect, false); } - @Redirect( + @WrapOperation( method = "onStatusEffectRemoved", at = @At( value = "INVOKE", target = "Lnet/minecraft/entity/effect/StatusEffect;onRemoved(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/entity/attribute/AttributeContainer;I)V" ) ) - private void quilt$callOnRemovedWithReason(StatusEffect instance, LivingEntity entity, AttributeContainer attributes, int amplifier, - StatusEffectInstance effect) { - instance.onRemoved(entity, attributes, effect, this.quilt$lastRemovalReason); - StatusEffectEvents.ON_REMOVED.invoker().onRemoved(entity, effect, this.quilt$lastRemovalReason); + private void quilt$callOnRemovedWithReason(StatusEffect instance, LivingEntity entity, AttributeContainer attributes, int amplifier, Operation original, StatusEffectInstance effect) { + instance.onRemoved((LivingEntity) (Object) this, attributes, effect, this.quilt$lastRemovalReason); + StatusEffectEvents.ON_REMOVED.invoker().onRemoved((LivingEntity) (Object) this, effect, this.quilt$lastRemovalReason); } - /** - * @author The Quilt Project - * @reason Adding removal reason - */ - @Overwrite - public boolean removeStatusEffect(StatusEffect type) { - return this.removeStatusEffect(type, StatusEffectRemovalReason.GENERIC_ONE); + @Inject( + method = "removeStatusEffect", + at = @At( + value = "HEAD" + ), + cancellable = true + ) + public void quilt$shouldRemoveEffect(StatusEffect effect, CallbackInfoReturnable cir) { + StatusEffectInstance instance = this.activeStatusEffects.get(effect); + if (instance != null) { + if (!StatusEffectUtils.shouldRemove((LivingEntity) (Object) this, instance, StatusEffectRemovalReason.GENERIC_ONE)) { + cir.setReturnValue(false); + } + } + } + + @WrapOperation( + method = "removeStatusEffect", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/LivingEntity;onStatusEffectRemoved(Lnet/minecraft/entity/effect/StatusEffectInstance;)V" + ) + ) + public void quilt$addRemoveStatusEffectReason(LivingEntity instance, StatusEffectInstance effect, Operation original) { + this.quilt$lastRemovalReason = StatusEffectRemovalReason.GENERIC_ONE; + original.call(instance, effect); + this.quilt$lastRemovalReason = QuiltStatusEffectInternals.UNKNOWN_REASON; } - /** - * @author The Quilt Project - * @reason Adding removal reason - */ - @Overwrite - public boolean clearStatusEffects() { - return this.clearStatusEffects(StatusEffectRemovalReason.GENERIC_ALL) > 0; + @WrapOperation( + method = "clearStatusEffects", + at = @At( + value = "INVOKE", + target = "Ljava/util/Collection;iterator()Ljava/util/Iterator;" + ) + ) + private Iterator quilt$filterStatusEffects(Collection instance, Operation> original) { + return Iterators.filter(original.call(instance), effect -> StatusEffectUtils.shouldRemove( + (LivingEntity) (Object) this, effect, StatusEffectRemovalReason.GENERIC_ALL + )); } - @Redirect(method = "tickStatusEffects", at = @At( + @WrapOperation(method = "tickStatusEffects", at = @At( value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;onStatusEffectRemoved(Lnet/minecraft/entity/effect/StatusEffectInstance;)V") ) - private void quilt$removeWithExpiredReason(LivingEntity instance, StatusEffectInstance effect) { - instance.onStatusEffectRemoved(effect, StatusEffectRemovalReason.EXPIRED); - StatusEffectEvents.ON_REMOVED.invoker().onRemoved(instance, effect, StatusEffectRemovalReason.EXPIRED); + private void quilt$removeWithExpiredReason(LivingEntity instance, StatusEffectInstance effect, Operation original) { + this.quilt$lastRemovalReason = StatusEffectRemovalReason.EXPIRED; + original.call(instance, effect); + } + + @WrapOperation( + method = "onStatusEffectUpgraded", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/effect/StatusEffect;onRemoved(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/entity/attribute/AttributeContainer;I)V" + ) + ) + private void quilt$removeWithUpgradeApplyingReason(StatusEffect instance, LivingEntity entity, AttributeContainer attributes, int amplifier, Operation original, StatusEffectInstance statusEffectInstance) { + instance.onRemoved((LivingEntity) (Object) this, attributes, statusEffectInstance, StatusEffectRemovalReason.UPGRADE_REAPPLYING); + StatusEffectEvents.ON_REMOVED.invoker().onRemoved((LivingEntity) (Object) this, statusEffectInstance, StatusEffectRemovalReason.UPGRADE_REAPPLYING); } @SuppressWarnings("ConstantConditions") @@ -168,14 +209,4 @@ public boolean clearStatusEffects() { private void quilt$callOnAppliedEvent_upgradeReapplying(StatusEffectInstance effect, boolean reapplyEffect, Entity source, CallbackInfo ci) { StatusEffectEvents.ON_APPLIED.invoker().onApplied((LivingEntity) (Object) this, effect, true); } - - @Redirect(method = "onStatusEffectUpgraded", at = @At( - value = "INVOKE", - target = "Lnet/minecraft/entity/effect/StatusEffect;onRemoved(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/entity/attribute/AttributeContainer;I)V") - ) - private void quilt$removeWithUpgradeApplyingReason(StatusEffect instance, LivingEntity entity, AttributeContainer attributes, int amplifier, - StatusEffectInstance effect) { - instance.onRemoved(entity, attributes, effect, StatusEffectRemovalReason.UPGRADE_REAPPLYING); - StatusEffectEvents.ON_REMOVED.invoker().onRemoved(entity, effect, StatusEffectRemovalReason.UPGRADE_REAPPLYING); - } } diff --git a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/MilkBucketItemMixin.java b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/MilkBucketItemMixin.java index b2be001815..75f2afc98d 100644 --- a/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/MilkBucketItemMixin.java +++ b/library/entity/status_effect/src/main/java/org/quiltmc/qsl/entity/effect/mixin/MilkBucketItemMixin.java @@ -16,19 +16,22 @@ package org.quiltmc.qsl.entity.effect.mixin; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import net.minecraft.entity.LivingEntity; import net.minecraft.item.MilkBucketItem; import org.quiltmc.qsl.entity.effect.api.StatusEffectRemovalReason; +import org.quiltmc.qsl.entity.effect.impl.QuiltStatusEffectInternals; -@Mixin(MilkBucketItem.class) +// See LivingEntityMixin +@Mixin(value = MilkBucketItem.class, priority = QuiltStatusEffectInternals.MIXIN_PRIORITY) public abstract class MilkBucketItemMixin { - @Redirect(method = "finishUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;clearStatusEffects()Z")) - private boolean quilt$addRemovalReason(LivingEntity instance) { + @WrapOperation(method = "finishUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;clearStatusEffects()Z")) + private boolean quilt$addRemovalReason(LivingEntity instance, Operation original) { return instance.clearStatusEffects(StatusEffectRemovalReason.DRANK_MILK) > 0; } }