Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.21.3] Implement support for preventing effects from being removed by milk or totems #1603

Open
wants to merge 3 commits into
base: 1.21.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions patches/net/minecraft/world/item/component/Consumables.java.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--- a/net/minecraft/world/item/component/Consumables.java
+++ b/net/minecraft/world/item/component/Consumables.java
@@ -62,7 +_,11 @@
public static final Consumable SPIDER_EYE = defaultFood()
.onConsume(new ApplyStatusEffectsConsumeEffect(new MobEffectInstance(MobEffects.POISON, 100, 0)))
.build();
- public static final Consumable MILK_BUCKET = defaultDrink().onConsume(ClearAllStatusEffectsConsumeEffect.INSTANCE).build();
+ public static final Consumable MILK_BUCKET = defaultDrink().onConsume(new RemoveStatusEffectsConsumeEffect(new net.neoforged.neoforge.registries.holdersets.NotHolderSet<>(
+ net.minecraft.core.registries.BuiltInRegistries.MOB_EFFECT,
+ net.minecraft.core.registries.BuiltInRegistries.acquireBootstrapRegistrationLookup(net.minecraft.core.registries.BuiltInRegistries.MOB_EFFECT)
+ .getOrThrow(net.neoforged.neoforge.common.Tags.MobEffects.NOT_MILK_CURABLE)
+ ))).build();
public static final Consumable CHORUS_FRUIT = defaultFood().onConsume(new TeleportRandomlyConsumeEffect()).build();

public static Consumable.Builder defaultFood() {
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--- a/net/minecraft/world/item/component/DeathProtection.java
+++ b/net/minecraft/world/item/component/DeathProtection.java
@@ -25,7 +_,11 @@
);
public static final DeathProtection TOTEM_OF_UNDYING = new DeathProtection(
List.of(
- new ClearAllStatusEffectsConsumeEffect(),
+ new net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect(new net.neoforged.neoforge.registries.holdersets.NotHolderSet<>(
+ net.minecraft.core.registries.BuiltInRegistries.MOB_EFFECT,
+ net.minecraft.core.registries.BuiltInRegistries.acquireBootstrapRegistrationLookup(net.minecraft.core.registries.BuiltInRegistries.MOB_EFFECT)
+ .getOrThrow(net.neoforged.neoforge.common.Tags.MobEffects.NOT_TOTEM_CURABLE)
+ )),
new ApplyStatusEffectsConsumeEffect(
List.of(
new MobEffectInstance(MobEffects.REGENERATION, 900, 1),
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": []
}
2 changes: 2 additions & 0 deletions src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
import net.neoforged.neoforge.common.data.internal.NeoForgeItemTagsProvider;
import net.neoforged.neoforge.common.data.internal.NeoForgeLanguageProvider;
import net.neoforged.neoforge.common.data.internal.NeoForgeLootTableProvider;
import net.neoforged.neoforge.common.data.internal.NeoForgeMobEffectsTagsProvider;
import net.neoforged.neoforge.common.data.internal.NeoForgeRecipeProvider;
import net.neoforged.neoforge.common.data.internal.NeoForgeRegistryOrderReportProvider;
import net.neoforged.neoforge.common.data.internal.NeoForgeSpriteSourceProvider;
Expand Down Expand Up @@ -631,6 +632,7 @@ public void gatherData(GatherDataEvent event) {
event.createProvider(includeServer, NeoForgeDamageTypeTagsProvider::new);
event.createProvider(includeServer, NeoForgeRegistryOrderReportProvider::new);
event.createProvider(includeServer, NeoForgeDataMapsProvider::new);
event.createProvider(includeServer, NeoForgeMobEffectsTagsProvider::new);

event.createProvider(includeClient, NeoForgeSpriteSourceProvider::new);
event.createProvider(includeClient, VanillaSoundDefinitionsProvider::new);
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/net/neoforged/neoforge/common/Tags.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
Expand Down Expand Up @@ -1162,6 +1163,16 @@ private static TagKey<DamageType> neoforgeTag(String name) {
}
}

public static class MobEffects {
public static final TagKey<MobEffect> NOT_MILK_CURABLE = neoforgeTag("not_milk_curable");

public static final TagKey<MobEffect> NOT_TOTEM_CURABLE = neoforgeTag("not_totem_curable");

private static TagKey<MobEffect> neoforgeTag(String name) {
return TagKey.create(Registries.MOB_EFFECT, ResourceLocation.fromNamespaceAndPath("neoforge", name));
}
}

/**
* Use this to get a TagKey's translation key safely on any side.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.common.data.internal;

import java.util.concurrent.CompletableFuture;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.world.effect.MobEffect;
import net.neoforged.neoforge.common.Tags;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion;
import org.jetbrains.annotations.Nullable;

public final class NeoForgeMobEffectsTagsProvider extends TagsProvider<MobEffect> {
public NeoForgeMobEffectsTagsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, @Nullable ExistingFileHelper fileHelper) {
super(output, Registries.MOB_EFFECT, lookupProvider, NeoForgeVersion.MOD_ID, fileHelper);
}

@Override
protected void addTags(HolderLookup.Provider lookupProvider) {
tag(Tags.MobEffects.NOT_MILK_CURABLE);
tag(Tags.MobEffects.NOT_TOTEM_CURABLE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"values": [
"neotests_effect_cures:test_effect"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,33 @@
* SPDX-License-Identifier: LGPL-2.1-only
*/

// FIXME: effect cures need to be implemented differently and the test adapted
/*
package net.neoforged.neoforge.debug.effect;

import java.util.concurrent.CompletableFuture;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.HolderSet;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.gametest.framework.GameTest;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.animal.Pig;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.Consumables;
import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect;
import net.neoforged.neoforge.common.Tags;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.data.event.GatherDataEvent;
import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion;
import net.neoforged.testframework.DynamicTest;
import net.neoforged.testframework.annotation.ForEachTest;
import net.neoforged.testframework.annotation.TestHolder;
import net.neoforged.testframework.gametest.EmptyTemplate;
import net.neoforged.testframework.registration.RegistrationHelper;

@ForEachTest(groups = MobEffectTests.GROUP)
public class MobEffectTests {
Expand All @@ -16,61 +39,44 @@ public class MobEffectTests {
@EmptyTemplate
@TestHolder(description = "Tests whether items and effects can properly specify what they cure and what they are cured by respectively")
static void effectCures(final DynamicTest test, final RegistrationHelper reg) {
final var testCure = EffectCure.get("test_cure");
final var testCureTwo = EffectCure.get("test_cure_two");
final var testEffect = reg.registrar(Registries.MOB_EFFECT).register("test_effect", () -> new MobEffect(MobEffectCategory.HARMFUL, 0xFF0000) {});

final var testEffect = reg.registrar(Registries.MOB_EFFECT).register("test_effect", () -> new MobEffect(
MobEffectCategory.HARMFUL, 0xFF0000) {
@Override
public void fillEffectCures(Set<EffectCure> cures, MobEffectInstance effectInstance) {
super.fillEffectCures(cures, effectInstance);
cures.remove(EffectCures.MILK);
cures.add(testCureTwo);
}
});
test.framework().modEventBus().addListener(GatherDataEvent.class, event -> {
PackOutput output = event.getGenerator().getPackOutput();
CompletableFuture<HolderLookup.Provider> lookupProvider = event.getLookupProvider();
ExistingFileHelper fileHelper = event.getExistingFileHelper();

test.eventListeners().forge().addListener((MobEffectEvent.Added event) -> {
if (event.getEffectInstance().getEffect() == MobEffects.NIGHT_VISION) {
event.getEffectInstance().getCures().add(testCure);
}
event.getGenerator().addProvider(event.includeServer(), new TagsProvider<MobEffect>(output, Registries.MOB_EFFECT, lookupProvider, NeoForgeVersion.MOD_ID, fileHelper) {
@Override
protected void addTags(HolderLookup.Provider registries) {
tag(Tags.MobEffects.NOT_MILK_CURABLE).add(testEffect.unwrapKey().orElseThrow());
}
});
});

test.onGameTest(helper -> {
Pig pig = helper.spawnWithNoFreeWill(EntityType.PIG, 1, 1, 1);

pig.addEffect(new MobEffectInstance(MobEffects.CONFUSION));
helper.assertMobEffectPresent(pig, MobEffects.CONFUSION, "'confusion was applied'");
pig.removeEffectsCuredBy(testCure);
helper.assertMobEffectPresent(pig, MobEffects.CONFUSION, "'confusion not removed by test cure'");
pig.removeEffectsCuredBy(EffectCures.MILK);
new RemoveStatusEffectsConsumeEffect(MobEffects.BLINDNESS).apply(pig.level(), ItemStack.EMPTY, pig);
helper.assertMobEffectPresent(pig, MobEffects.CONFUSION, "'confusion not removed by blindness cure'");
Consumables.MILK_BUCKET.onConsume(pig.level(), pig, ItemStack.EMPTY);
helper.assertMobEffectAbsent(pig, MobEffects.CONFUSION, "'confusion removed by milk'");

pig.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION));
helper.assertMobEffectPresent(pig, MobEffects.NIGHT_VISION, "'nightvision was applied'");
pig.removeEffectsCuredBy(testCure);
helper.assertMobEffectAbsent(pig, MobEffects.NIGHT_VISION, "'nightvision removed by test cure'");
new RemoveStatusEffectsConsumeEffect(HolderSet.direct(MobEffects.NIGHT_VISION)).apply(pig.level(), ItemStack.EMPTY, pig);
helper.assertMobEffectAbsent(pig, MobEffects.NIGHT_VISION, "'nightvision removed by nightvision cure'");

pig.addEffect(new MobEffectInstance(testEffect));
helper.assertMobEffectPresent(pig, testEffect, "'test effect was applied'");
pig.removeEffectsCuredBy(EffectCures.MILK);
Consumables.MILK_BUCKET.onConsume(pig.level(), pig, ItemStack.EMPTY);
helper.assertMobEffectPresent(pig, testEffect, "'test effect not removed by milk'");
pig.removeEffectsCuredBy(testCureTwo);
helper.assertMobEffectAbsent(pig, testEffect, "'test effect removed by test cure'");

MobEffectInstance srcInst = new MobEffectInstance(MobEffects.CONFUSION);
MobEffectInstance destInst = MobEffectInstance.load((CompoundTag) srcInst.save());
helper.assertTrue(srcInst.getCures().equals(destInst.getCures()), "'MobEffectInstance serialization roundtrip (standard cures)'");

srcInst.getCures().add(testCure);
destInst = MobEffectInstance.load((CompoundTag) srcInst.save());
helper.assertTrue(srcInst.getCures().equals(destInst.getCures()), "'MobEffectInstance serialization roundtrip (custom additional cure)'");

srcInst.getCures().clear();
destInst = MobEffectInstance.load((CompoundTag) srcInst.save());
helper.assertTrue(srcInst.getCures().equals(destInst.getCures()), "'MobEffectInstance serialization roundtrip (no cures)'");
new RemoveStatusEffectsConsumeEffect(testEffect).apply(pig.level(), ItemStack.EMPTY, pig);
helper.assertMobEffectAbsent(pig, testEffect, "'test effect removed by test effect cure'");

helper.succeed();
});
}
}
*/