Skip to content
Merged
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
19 changes: 19 additions & 0 deletions src/main/java/org/cyclops/evilcraft/block/BlockPurifierConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.cyclops.evilcraft.block;

import com.google.common.collect.Lists;
import net.minecraft.core.Holder;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType;
import org.cyclops.cyclopscore.config.ConfigurableProperty;
Expand All @@ -23,9 +25,26 @@ public class BlockPurifierConfig extends BlockConfig {
public static List<String> disenchantBlacklist = Lists.newArrayList(
"tetra:.*"
);
@ConfigurableProperty(category = "machine", comment = "Enchantments to be disqualified from disenchantment, or curses disallowed from purification. Regular expressions are allowed.", isCommandable = true)
public static List<String> enchantmentIdBlacklist = Lists.newArrayList();
@ConfigurableProperty(category = "machine", comment = "The duration limit in ticks for which potion effect can be collected. Set to a negative value to allow any duration.", isCommandable = true)
public static int maxPotionEffectDuration = MinecraftHelpers.SECOND_IN_TICKS * 60 * 5;

/**
* Check if the given enchantment is blacklisted.
* @param enchantment The enchantment to check.
* @return True if blacklisted.
*/
public static boolean isEnchantmentBlacklisted(Holder<Enchantment> enchantment) {
String enchantmentId = enchantment.getRegisteredName();
for (String pattern : enchantmentIdBlacklist) {
if (enchantmentId.matches(pattern)) {
return true;
}
}
return false;
}

public BlockPurifierConfig() {
super(
EvilCraft._instance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import org.cyclops.evilcraft.blockentity.BlockEntityPurifier;
import org.cyclops.evilcraft.core.algorithm.Wrapper;

import java.util.List;
import java.util.stream.Collectors;

/**
* Purifier action to remove enchantments from tools.
* @author Ruben Taelman
Expand Down Expand Up @@ -55,7 +58,9 @@ public boolean isItemValidForAdditionalSlot(ItemStack itemStack) {
public boolean canWork(BlockEntityPurifier tile) {
if(tile.getBucketsFloored() == tile.getMaxBuckets() && !tile.getPurifyItem().isEmpty() &&
!tile.getAdditionalItem().isEmpty() && tile.getAdditionalItem().getItem() == ALLOWED_BOOK.get()) {
return isAllowed(tile.getPurifyItem()) && tile.getPurifyItem().get(DataComponents.ENCHANTMENTS) != null;
if (!isAllowed(tile.getPurifyItem())) return false;
ItemEnchantments enchantments = tile.getPurifyItem().get(DataComponents.ENCHANTMENTS);
return enchantments != null && !getAvailableEnchantments(enchantments).isEmpty();
}
return false;
}
Expand All @@ -70,7 +75,7 @@ public boolean work(BlockEntityPurifier tile) {

// Try disenchanting.
ItemEnchantments enchantments = purifyItem.get(DataComponents.ENCHANTMENTS);
if (enchantments != null && !enchantments.isEmpty()) {
if (enchantments != null && !getAvailableEnchantments(enchantments).isEmpty()) {
if (tick >= PURIFY_DURATION) {
if (!world.isClientSide()) {
Holder<Enchantment> enchantment = getRandomEnchantment(world, enchantments);
Expand All @@ -90,9 +95,15 @@ public boolean work(BlockEntityPurifier tile) {
return done;
}

private List<Holder<Enchantment>> getAvailableEnchantments(ItemEnchantments enchantments) {
return Lists.newArrayList(enchantments.keySet()).stream()
.filter(e -> !BlockPurifierConfig.isEnchantmentBlacklisted(e))
.collect(Collectors.toList());
}

private Holder<Enchantment> getRandomEnchantment(Level world, ItemEnchantments enchantments) {
int enchantmentIndex = world.random.nextInt(enchantments.size());
return Lists.newArrayList(enchantments.keySet()).get(enchantmentIndex);
List<Holder<Enchantment>> available = getAvailableEnchantments(enchantments);
return available.get(world.random.nextInt(available.size()));
}

private void setResultingEnchantmentBook(BlockEntityPurifier tile, ItemEnchantments enchantments, Holder<Enchantment> enchantment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import net.minecraft.world.level.Level;
import org.cyclops.cyclopscore.helper.EnchantmentHelpers;
import org.cyclops.evilcraft.api.tileentity.purifier.IPurifierAction;
import org.cyclops.evilcraft.block.BlockPurifierConfig;
import org.cyclops.evilcraft.blockentity.BlockEntityPurifier;
import org.cyclops.evilcraft.core.algorithm.Wrapper;
import org.cyclops.evilcraft.item.ItemVengeancePickaxe;
Expand Down Expand Up @@ -36,7 +37,8 @@ public boolean canWork(BlockEntityPurifier tile) {
// Check curses
ItemStack purifyItem = tile.getPurifyItem();
EnchantmentHelpers.runIterationOnItem(purifyItem, (enchantment, level) -> {
if (enchantment.is(EnchantmentTags.CURSE) && !(purifyItem.getItem() instanceof ItemVengeancePickaxe)) {
if (enchantment.is(EnchantmentTags.CURSE) && !(purifyItem.getItem() instanceof ItemVengeancePickaxe)
&& !BlockPurifierConfig.isEnchantmentBlacklisted(enchantment)) {
done.set(true);
}
});
Expand Down Expand Up @@ -68,7 +70,8 @@ public boolean work(BlockEntityPurifier tile) {

// Try removing curses
EnchantmentHelpers.runIterationOnItem(purifyItem, (enchantment, level) -> {
if (enchantment.is(EnchantmentTags.CURSE) && !(purifyItem.getItem() instanceof ItemVengeancePickaxe)) {
if (enchantment.is(EnchantmentTags.CURSE) && !(purifyItem.getItem() instanceof ItemVengeancePickaxe)
&& !BlockPurifierConfig.isEnchantmentBlacklisted(enchantment)) {
if (removeEnchant(world, tile, purifyItem, tick, enchantment, level)) {
done.set(true);
}
Expand Down
116 changes: 116 additions & 0 deletions src/main/java/org/cyclops/evilcraft/gametest/GameTestsPurifier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.cyclops.evilcraft.gametest;

import com.google.common.collect.Lists;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.gametest.framework.GameTest;
import net.minecraft.gametest.framework.GameTestHelper;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.gametest.GameTestHolder;
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
import org.cyclops.cyclopscore.helper.FluidHelpers;
import org.cyclops.evilcraft.Reference;
import org.cyclops.evilcraft.RegistryEntries;
import org.cyclops.evilcraft.block.BlockPurifierConfig;
import org.cyclops.evilcraft.blockentity.BlockEntityPurifier;

import java.util.List;

@GameTestHolder(Reference.MOD_ID)
@PrefixGameTestTemplate(false)
public class GameTestsPurifier {

public static final String TEMPLATE_EMPTY = "empty10";
public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);

private static ItemStack createEnchantedSword(HolderLookup.Provider holders, String enchantmentId, int level) {
ItemStack sword = new ItemStack(Items.DIAMOND_SWORD);
ItemEnchantments.Mutable enchantments = new ItemEnchantments.Mutable(ItemEnchantments.EMPTY);
enchantments.set(holders.holderOrThrow(ResourceKey.create(Registries.ENCHANTMENT, ResourceLocation.parse(enchantmentId))), level);
EnchantmentHelper.setEnchantments(sword, enchantments.toImmutable());
return sword;
}

@GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200)
public void testPurifierDisenchant(GameTestHelper helper) {
HolderLookup.Provider holders = helper.getLevel().registryAccess();
helper.setBlock(POS, RegistryEntries.BLOCK_PURIFIER.get());
BlockEntityPurifier purifier = helper.getBlockEntity(POS);

// Add enchanted sword and blook
purifier.getInventory().setItem(BlockEntityPurifier.SLOT_PURIFY, createEnchantedSword(holders, "minecraft:sharpness", 1));
purifier.getInventory().setItem(BlockEntityPurifier.SLOT_ADDITIONAL, new ItemStack(RegistryEntries.ITEM_BLOOK.get()));

// Fill tank with max blood
purifier.getTank().setFluid(new FluidStack(RegistryEntries.FLUID_BLOOD, FluidHelpers.BUCKET_VOLUME * BlockEntityPurifier.MAX_BUCKETS));

helper.succeedWhen(() -> {
ItemEnchantments enchantments = purifier.getInventory().getItem(BlockEntityPurifier.SLOT_PURIFY).get(DataComponents.ENCHANTMENTS);
helper.assertTrue(enchantments == null || enchantments.isEmpty(), "Sword sharpness was not removed");
helper.assertFalse(purifier.getInventory().getItem(BlockEntityPurifier.SLOT_ADDITIONAL).isEmpty(), "Enchanted book was not produced");
});
}

@GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200)
public void testPurifierCurseRemoval(GameTestHelper helper) {
HolderLookup.Provider holders = helper.getLevel().registryAccess();
helper.setBlock(POS, RegistryEntries.BLOCK_PURIFIER.get());
BlockEntityPurifier purifier = helper.getBlockEntity(POS);

// Add item with vanishing curse
purifier.getInventory().setItem(BlockEntityPurifier.SLOT_PURIFY, createEnchantedSword(holders, "minecraft:vanishing_curse", 1));

// Fill tank with 1 bucket of blood (curse removal only needs > 0)
purifier.getTank().setFluid(new FluidStack(RegistryEntries.FLUID_BLOOD, FluidHelpers.BUCKET_VOLUME));

helper.succeedWhen(() -> {
ItemEnchantments enchantments = purifier.getInventory().getItem(BlockEntityPurifier.SLOT_PURIFY).get(DataComponents.ENCHANTMENTS);
helper.assertTrue(enchantments == null || enchantments.isEmpty(), "Vanishing curse was not removed");
});
}

/**
* Tests that blacklisted enchantments are not disenchanted and blacklisted curses are not purified.
* Runs in its own batch to avoid concurrent modification of the shared config field.
*/
@GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200, batch = "purifier_blacklist_0")
public void testPurifierEnchantmentBlacklist(GameTestHelper helper) {
HolderLookup.Provider holders = helper.getLevel().registryAccess();
List<String> originalBlacklist = BlockPurifierConfig.enchantmentIdBlacklist;
BlockPurifierConfig.enchantmentIdBlacklist =
Lists.newArrayList("minecraft:sharpness", "minecraft:vanishing_curse");

// Disenchant scenario: sword with sharpness at POS
helper.setBlock(POS, RegistryEntries.BLOCK_PURIFIER.get());
BlockEntityPurifier disenchantPurifier = helper.getBlockEntity(POS);
disenchantPurifier.getInventory().setItem(BlockEntityPurifier.SLOT_PURIFY, createEnchantedSword(holders, "minecraft:sharpness", 1));
disenchantPurifier.getInventory().setItem(BlockEntityPurifier.SLOT_ADDITIONAL, new ItemStack(RegistryEntries.ITEM_BLOOK.get()));
disenchantPurifier.getTank().setFluid(new FluidStack(RegistryEntries.FLUID_BLOOD, FluidHelpers.BUCKET_VOLUME * BlockEntityPurifier.MAX_BUCKETS));

// Curse removal scenario: sword with vanishing curse at an adjacent position
BlockPos pos2 = POS.offset(3, 0, 0);
helper.setBlock(pos2, RegistryEntries.BLOCK_PURIFIER.get());
BlockEntityPurifier cursePurifier = helper.getBlockEntity(pos2);
cursePurifier.getInventory().setItem(BlockEntityPurifier.SLOT_PURIFY, createEnchantedSword(holders, "minecraft:vanishing_curse", 1));
cursePurifier.getTank().setFluid(new FluidStack(RegistryEntries.FLUID_BLOOD, FluidHelpers.BUCKET_VOLUME));

// After enough ticks for the purifiers to have acted (if not blacklisted), verify nothing was removed
helper.runAfterDelay(150, () -> {
BlockPurifierConfig.enchantmentIdBlacklist = originalBlacklist;
ItemEnchantments disenchantEnchants = disenchantPurifier.getInventory().getItem(BlockEntityPurifier.SLOT_PURIFY).get(DataComponents.ENCHANTMENTS);
helper.assertTrue(disenchantEnchants != null && !disenchantEnchants.isEmpty(), "Sword sharpness was incorrectly removed despite enchantment blacklist");
ItemEnchantments curseEnchants = cursePurifier.getInventory().getItem(BlockEntityPurifier.SLOT_PURIFY).get(DataComponents.ENCHANTMENTS);
helper.assertTrue(curseEnchants != null && !curseEnchants.isEmpty(), "Vanishing curse was incorrectly removed despite enchantment blacklist");
helper.succeed();
});
}

}
Loading