From e4722f5f7b2016e28e68d94dd70de14265742f33 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 13:34:37 +0300 Subject: [PATCH 01/53] Add AddPoolAction --- gradle.properties | 4 +- .../loottablemodifier/LootTableModifier.java | 1 + .../api/LootModifierActionTypes.java | 28 ++ .../api/datagen/NewLootModifierProvider.java | 252 ++++++++++++++++++ .../datagen/LootTableModifierDatagen.java | 51 +++- .../resource/NewLootModifier.java | 73 +++++ .../resource/action/AddPoolAction.java | 44 +++ .../resource/action/LootModifierAction.java | 15 ++ .../action/LootModifierActionType.java | 15 ++ 9 files changed, 479 insertions(+), 4 deletions(-) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java diff --git a/gradle.properties b/gradle.properties index 306cd33..04f3ff0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,9 +8,9 @@ minecraft_version = 1.21.4 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.119.0+1.21.4 +fapi_version = 0.119.2+1.21.4 yarn_version = 1.21.4+build.8 -loader_version = 0.16.10 +loader_version = 0.16.14 # Dependencies ## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 7bb3801..b8dab4e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -9,6 +9,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; +import net.minecraft.loot.condition.LootCondition; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryOps; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java new file mode 100644 index 0000000..fdbfc98 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java @@ -0,0 +1,28 @@ +package top.offsetmonkey538.loottablemodifier.api; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; + +import java.util.HashMap; +import java.util.Map; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; + +public final class LootModifierActionTypes { + private LootModifierActionTypes() { + + } + + public static final LootModifierActionType ADD_ENTRY = register(id("add_pool"), AddPoolAction.CODEC); + + private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { + return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java new file mode 100644 index 0000000..75bd957 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -0,0 +1,252 @@ +package top.offsetmonkey538.loottablemodifier.api.datagen; + +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.data.DataOutput; +import net.minecraft.data.loottable.LootTableGenerator; +import net.minecraft.entity.EntityType; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.Identifier; +import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.resource.NewLootModifier; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Stream; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; + +/** + * FIXME: wrong + * A datagen provider for creating loot modifiers. + *
+ * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@code addModifier(...)} methods to add modifiers. + *
+ *
{@code
+ * @Override
+ * protected void generate(RegistryWrapper.WrapperLookup lookup) {
+ *     addModifier(
+ *             Identifier.of("testmod", "drop_tnt"),
+ *             LootPool.builder()
+ *                     .rolls(ConstantLootNumberProvider.create(1))
+ *                     .with(
+ *                             ItemEntry.builder(Items.TNT)
+ *                                     .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
+ *                     ),
+ *             EntityType.CREEPER,
+ *             EntityType.ZOMBIE
+ *     );
+ * }
+ * }
+ */ +public abstract class NewLootModifierProvider extends FabricCodecDataProvider { + private BiConsumer provider; + + public NewLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", NewLootModifier.CODEC); + } + + @Override + protected void configure(BiConsumer provider, RegistryWrapper.WrapperLookup lookup) { + this.provider = provider; + generate(lookup); + } + + @Override + public String getName() { + return "New Loot Table Modifiers"; + } + + /** + * Override and use {@code addModifier()} methods to add modifiers. + * + * @param lookup A lookup for registries. + */ + protected abstract void generate(RegistryWrapper.WrapperLookup lookup); + + /** + * Adds a new loot table modifier for the given {@link EntityType}s. + * + * @param name Name of this modifier + * @param builder The loot pool to add + * @param modifies The {@link EntityType} to add the modifier to + * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to + */ + protected void addModifier(Identifier name, LootModifierAction.Builder builder, EntityType modifies, EntityType... modifiesAdditional) { + addModifier( + name, + List.of(builder), + modifies, + modifiesAdditional + ); + } + + /** + * Adds a new loot table modifier for the given {@link EntityType}s. + * + * @param name Name of this modifier + * @param builders The loot pools to add + * @param modifies The {@link EntityType} to add the modifier to + * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to + */ + protected void addModifier(Identifier name, List builders, EntityType modifies, EntityType... modifiesAdditional) { + addModifier( + name, + builders, + Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(EntityLootTableIdGetter.get).toList() + ); + } + + /** + * Adds a new loot table modifier for the given {@link RegistryKey}s. + * + * @param name Name of this modifier + * @param builder The loot pool to add + * @param modifies The {@link RegistryKey} to add the modifier to + * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to + */ + protected void addModifier(Identifier name, LootModifierAction.Builder builder, RegistryKey modifies, RegistryKey... modifiesAdditional) { + addModifier( + name, + List.of(builder), + modifies, + modifiesAdditional + ); + } + + /** + * Adds a new loot table modifier for the given {@link RegistryKey}s. + * + * @param name Name of this modifier + * @param builders The loot pools to add + * @param modifies The {@link RegistryKey} to add the modifier to + * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to + */ + protected void addModifier(Identifier name, List builders, RegistryKey modifies, RegistryKey... modifiesAdditional) { + addModifier( + name, + builders, + Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(RegistryKey::getValue).toList() + ); + } + + /** + * Adds a new loot table modifier for the given {@link Identifier}s. + * + * @param name Name of this modifier + * @param builder The loot pool to add + * @param modifies The {@link Identifier} to add the modifier to + * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to + */ + protected void addModifier(Identifier name, LootModifierAction.Builder builder, Identifier modifies, Identifier... modifiesAdditional) { + addModifier( + name, + List.of(builder), + modifies, + modifiesAdditional + ); + } + + /** + * Adds a new loot table modifier for the given {@link Identifier}s. + * + * @param name Name of this modifier + * @param builders The loot pools to add + * @param modifies The {@link Identifier} to add the modifier to + * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to + */ + protected void addModifier(Identifier name, List builders, Identifier modifies, Identifier... modifiesAdditional) { + addModifier( + name, + builders, + Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).toList() + ); + } + + private void addModifier(Identifier name, List builders, List modifies) { + provider.accept(name, new NewLootModifier( + new ArrayList<>(modifies), + builders.stream() + .map(LootModifierAction.Builder::build) + .toList() + )); + } + + + + private static class EntityLootTableIdGetter { + // Resolver returns the provided name (like 'method_16351') when it fails to map it + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); + + // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); + + public static final Function, Identifier> get; + + // Should be executed when class is first loaded/accessed + static { + try { + //final Class entityType = EntityType.class; + final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); + final Method method; + + // 1.21.2 to future:tm: + if (isMethod(entityType, V1d21d2)) { + method = entityType.getDeclaredMethod(V1d21d2); + method.setAccessible(true); + get = entity -> { + try { + @SuppressWarnings("unchecked") + final Optional> optional = (Optional>) method.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if (isMethod(entityType, V1d20d5)) { + method = entityType.getDeclaredMethod(V1d20d5); + method.setAccessible(true); + get = entity -> { + try { + return ((RegistryKey) method.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } catch (NoSuchMethodException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static boolean isMethod(Class clazz, String method) { + try { + clazz.getDeclaredMethod(method); + return true; + } catch (NoSuchMethodException e) { + LOGGER.warn("", e); + return false; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index d9e1613..4324dc7 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -14,6 +14,8 @@ import net.minecraft.loot.provider.number.UniformLootNumberProvider; import net.minecraft.registry.RegistryWrapper; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; +import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; +import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import java.util.concurrent.CompletableFuture; @@ -25,7 +27,8 @@ public class LootTableModifierDatagen implements DataGeneratorEntrypoint { public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { final FabricDataGenerator.Pack pack = fabricDataGenerator.createBuiltinResourcePack(id("example_pack")); - pack.addProvider(ModLootModifierProvider::new); + //pack.addProvider(ModLootModifierProvider::new); + pack.addProvider(NewModLootModifierProvider::new); } private static class ModLootModifierProvider extends LootModifierProvider { @@ -59,7 +62,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ItemEntry.builder(Items.NETHERITE_SWORD).apply( EnchantWithLevelsLootFunction .builder(lookup, UniformLootNumberProvider.create(20, 39)) - //.builder(UniformLootNumberProvider.create(20, 39)) + //.builder(UniformLootNumberProvider.create(20, 39)) ) ), @@ -67,4 +70,48 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ); } } + + private static class NewModLootModifierProvider extends NewLootModifierProvider { + public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + super(dataOutput, registriesFuture); + } + + @Override + protected void generate(RegistryWrapper.WrapperLookup lookup) { + addModifier( + id("drop_tnt"), + + + AddPoolAction.builder( + LootPool.builder() + .rolls(ConstantLootNumberProvider.create(1)) + .with( + ItemEntry.builder(Items.TNT) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ), + + EntityType.CREEPER, + EntityType.ZOMBIE + ); + + //todo: temp + addModifier( + id("test"), + + AddPoolAction.builder( + LootPool.builder() + .with( + ItemEntry.builder(Items.NETHERITE_SWORD).apply( + EnchantWithLevelsLootFunction + .builder(lookup, UniformLootNumberProvider.create(20, 39)) + //.builder(UniformLootNumberProvider.create(20, 39)) + ) + ) + ), + + LootTables.ABANDONED_MINESHAFT_CHEST + ); + } + } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java new file mode 100644 index 0000000..b69a11c --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java @@ -0,0 +1,73 @@ +package top.offsetmonkey538.loottablemodifier.resource; + +import com.google.common.collect.ImmutableList; +import com.ibm.icu.impl.EmojiProps; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.fabric.api.loot.v3.FabricLootPoolBuilder; +import net.fabricmc.fabric.api.loot.v3.FabricLootTableBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.condition.LootCondition; +import net.minecraft.loot.condition.LootConditionConsumingBuilder; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.loot.function.LootFunction; +import net.minecraft.loot.function.LootFunctionConsumingBuilder; +import net.minecraft.loot.provider.number.ConstantLootNumberProvider; +import net.minecraft.loot.provider.number.LootNumberProvider; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; + +import java.util.ArrayList; +import java.util.List; + +public record NewLootModifier(List modifies, List actions) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(NewLootModifier::modifiesEither), + Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).fieldOf("actions").forGetter(NewLootModifier::actionsEither) + //LootModifierAction.CODEC.listOf().fieldOf("") + //Codec.mapEither(LootPool.CODEC.listOf().fieldOf("pools"), LootPool.CODEC.listOf().fieldOf("loot_pools")).forGetter(NewLootModifier::poolsEither) + ).apply(instance, NewLootModifier::new)); + + private NewLootModifier(Either> modifiesEither, Either> actionsEither) { + this( + new ArrayList<>(modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow()))), + new ArrayList<>(actionsEither.right().orElseGet(() -> List.of(actionsEither.left().orElseThrow()))) + ); + } + + private Either> modifiesEither() { + if (modifies.size() == 1) return Either.left(modifies.get(0)); + return Either.right(modifies); + } + + private Either> actionsEither() { + if (actions.size() == 1) return Either.left(actions.get(0)); + return Either.right(actions); + } + + // Don't think this is needed? public static NewLootModifier.Builder builder() { + // Don't think this is needed? return new NewLootModifier.Builder(); + // Don't think this is needed? } + + // Don't think this is needed? public static class Builder { + // Don't think this is needed? private final ImmutableList.Builder modifies = ImmutableList.builder(); + // Don't think this is needed? private final ImmutableList.Builder actions = ImmutableList.builder(); + // Don't think this is needed? + // Don't think this is needed? public NewLootModifier.Builder modifies(@NotNull Identifier... modifies) { + // Don't think this is needed? this.modifies.add(modifies); + // Don't think this is needed? return this; + // Don't think this is needed? } + // Don't think this is needed? + // Don't think this is needed? public NewLootModifier.Builder conditionally(@NotNull LootModifierAction.Builder action) { + // Don't think this is needed? this.actions.add(action.build()); + // Don't think this is needed? return this; + // Don't think this is needed? } + // Don't think this is needed? + // Don't think this is needed? public NewLootModifier build() { + // Don't think this is needed? return new NewLootModifier(this.modifies.build(), this.actions.build()); + // Don't think this is needed? } + // Don't think this is needed? } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java new file mode 100644 index 0000000..4877913 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java @@ -0,0 +1,44 @@ +package top.offsetmonkey538.loottablemodifier.resource.action; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.Block; +import net.minecraft.loot.LootPool; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; + +import java.util.List; + +public record AddPoolAction(List pools) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPool.CODEC.listOf().fieldOf("pools").forGetter(AddPoolAction::pools) + ).apply(instance, AddPoolAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ADD_ENTRY; + } + + public static AddPoolAction.Builder builder(@NotNull LootPool.Builder poolBuilder) { + return new AddPoolAction.Builder(poolBuilder); + } + + public static class Builder implements LootModifierAction.Builder { + private final ImmutableList.Builder pools = ImmutableList.builder(); + + private Builder(@NotNull LootPool.Builder poolBuilder) { + pools.add(poolBuilder.build()); + } + + public AddPoolAction.Builder pool(LootPool.Builder poolBuilder) { + this.pools.add(poolBuilder.build()); + return this; + } + + @Override + public AddPoolAction build() { + return new AddPoolAction(pools.build()); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java new file mode 100644 index 0000000..3d1924b --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.loottablemodifier.resource.action; + +import com.mojang.serialization.Codec; +import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; + +public interface LootModifierAction { + Codec CODEC = LootModifierActionType.REGISTRY.getCodec().dispatch(LootModifierAction::getType, LootModifierActionType::codec); + + LootModifierActionType getType(); + + @FunctionalInterface + interface Builder { + LootModifierAction build(); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java new file mode 100644 index 0000000..3d8646f --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.loottablemodifier.resource.action; + +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.MapCodec; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.LootTableModifier; + +public record LootModifierActionType(@NotNull MapCodec codec) { + public static final Registry REGISTRY = new SimpleRegistry<>( + RegistryKey.ofRegistry(LootTableModifier.id("loot_modifier_action_types")), Lifecycle.stable() + ); +} From b9ee027c11638df542c7b05bffb23434b4288089 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 14:51:32 +0300 Subject: [PATCH 02/53] Implement loading and applying NewLootModifier, remove usages of original LootModifier --- .../loottablemodifier/LootTableModifier.java | 84 +++++-------------- .../api/LootModifierActionTypes.java | 4 + .../api/datagen/NewLootModifierProvider.java | 1 - .../resource/NewLootModifier.java | 79 ++++++++++++++++- .../resource/action/AddPoolAction.java | 15 ++++ .../resource/action/LootModifierAction.java | 9 ++ 6 files changed, 122 insertions(+), 70 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index b8dab4e..91658cc 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -19,11 +19,13 @@ import net.minecraft.resource.ResourceManager; import net.minecraft.text.Text; import net.minecraft.util.Identifier; +import net.minecraft.util.Pair; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.resource.NewLootModifier; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -37,27 +39,32 @@ public class LootTableModifier implements ModInitializer { @Override public void onInitialize() { + LootModifierActionTypes.register(); + if(FabricLoader.getInstance().isDevelopmentEnvironment()) ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); } public static void runModification(ResourceManager resourceManager, Registry lootRegistry, RegistryOps registryOps) { - final Map modifiers = loadModifiers(resourceManager, registryOps); + final Map modifiers = loadModifiers(resourceManager, registryOps); + final Map failedModifiers = new HashMap<>(0); int amountModified = 0; LOGGER.info("Applying loot table modifiers..."); - for (RegistryEntry.Reference tableReference : getRegistryAsWrapper(lootRegistry).streamEntries().toList()) { - final RegistryKey tableKey = tableReference.registryKey(); + for (Map.Entry modifierEntry : modifiers.entrySet()) { + final NewLootModifier modifier = modifierEntry.getValue(); + + amountModified += modifier.apply(lootRegistry); - amountModified += applyModifiers(lootRegistry.get(tableKey), tableKey, modifiers); + if (!modifier.modifies().isEmpty()) failedModifiers.put(modifierEntry.getKey(), modifier); } - modifiersApplied(amountModified, modifiers); + modifiersApplied(amountModified, failedModifiers); } - private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { + private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { LOGGER.info("Loading loot table modifiers..."); - final Map result = new HashMap<>(); + final Map result = new HashMap<>(); for (Map.Entry entry : resourceManager.findResources(MOD_ID + "/loot_modifier", path -> path.toString().endsWith(".json")).entrySet()) { final Identifier id = entry.getKey(); @@ -65,7 +72,7 @@ private static Map loadModifiers(ResourceManager resou try { result.put( id, - LootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() + NewLootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() ); } catch (IOException e) { //noinspection StringConcatenationArgumentToLogCall @@ -78,44 +85,13 @@ private static Map loadModifiers(ResourceManager resou return result; } - // Returns: amount of loot tables modified - private static int applyModifiers(LootTable table, RegistryKey tableKey, Map modifiers) { - final Identifier currentId = tableKey.getValue(); - final List usable = modifiers.keySet().stream().filter(entry -> modifiers.get(entry).modifies().contains(currentId)).toList(); - - if (usable.isEmpty()) return 0; - - final List newPools = ImmutableList.builder() - .addAll(table.pools) - .addAll( - usable.stream() - .map(modifiers::get) - .map(LootModifier::pools) - .flatMap(List::stream) - .toList() - ) - .build(); - - ((LootTableAccessor) table).setPools(newPools); - - for (Identifier modifierId : usable) { - final LootModifier modifier = modifiers.get(modifierId); - - modifier.modifies().remove(currentId); - - if (modifier.modifies().isEmpty()) modifiers.remove(modifierId); - } - - return usable.size(); - } - - private static void modifiersApplied(int amountModified, Map modifiers) { + private static void modifiersApplied(int amountModified, Map failedModifiers) { LOGGER.info("Modified {} loot tables!", amountModified); - if (modifiers.isEmpty()) return; + if (failedModifiers.isEmpty()) return; LOGGER.warn("There were unused modifiers:"); - for (Map.Entry entry : modifiers.entrySet()) { + for (Map.Entry entry : failedModifiers.entrySet()) { LOGGER.warn("\tModifier '{}' failed to modify loot table for entities: ", entry.getKey()); for (Identifier id : entry.getValue().modifies()) { LOGGER.warn("\t\t- {}", id); @@ -123,28 +99,6 @@ private static void modifiersApplied(int amountModified, Map RegistryWrapper getRegistryAsWrapper(@NotNull Registry registry) { - //noinspection ConstantValue,RedundantSuppression: On lower versions, Registry doesn't extend RegistryWrapper and thus the 'isAssignableFrom' check can be false. The redundant supression is for the unchecked cast below. - if (RegistryWrapper.class.isAssignableFrom(registry.getClass())) - //noinspection unchecked,RedundantCast: I swear it casts 🤞 - return (RegistryWrapper) registry; - - try { - //noinspection unchecked: Seriously I swear 🤞🤞 - return (RegistryWrapper) registry.getClass().getDeclaredMethod(FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_2378", "method_46771", "()Lnet/minecraft/class_7225$class_7226;")).invoke(registry); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - public static Identifier id(String path) { return Identifier.of(MOD_ID, path); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java index fdbfc98..4802aef 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java @@ -25,4 +25,8 @@ private LootModifierActionTypes() { private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); } + + public static void register() { + // Registers action types by loading the class + } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index 75bd957..430896e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -12,7 +12,6 @@ import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.NewLootModifier; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java index b69a11c..3717e6a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java @@ -7,6 +7,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.fabricmc.fabric.api.loot.v3.FabricLootPoolBuilder; import net.fabricmc.fabric.api.loot.v3.FabricLootTableBuilder; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import net.minecraft.loot.condition.LootCondition; @@ -16,14 +17,21 @@ import net.minecraft.loot.function.LootFunctionConsumingBuilder; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.LootNumberProvider; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; -public record NewLootModifier(List modifies, List actions) { +// Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied +public record NewLootModifier(@NotNull ArrayList modifies, @NotNull List actions) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(NewLootModifier::modifiesEither), Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).fieldOf("actions").forGetter(NewLootModifier::actionsEither) @@ -31,10 +39,16 @@ public record NewLootModifier(List modifies, List> modifiesEither, Either> actionsEither) { + private NewLootModifier(@NotNull Either> modifiesEither, @NotNull Either> actionsEither) { this( - new ArrayList<>(modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow()))), - new ArrayList<>(actionsEither.right().orElseGet(() -> List.of(actionsEither.left().orElseThrow()))) + modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow())), + actionsEither.right().orElseGet(() -> List.of(actionsEither.left().orElseThrow())) + ); + } + public NewLootModifier(@NotNull List modifies, @NotNull List actions) { + this( + new ArrayList<>(modifies), + actions ); } @@ -48,6 +62,63 @@ private Either> actionsEither() { return Either.right(actions); } + /** + * @param tableRegistry registry of loot tables to modify + * @return amount of loot tables modified + */ + public int apply(final @NotNull Registry tableRegistry) { + final List> tableKeys = getRegistryAsWrapper(tableRegistry).streamEntries().map(RegistryEntry.Reference::registryKey).filter(key -> modifies.contains(key.getValue())).toList(); + if (tableKeys.isEmpty()) return 0; + // At this point only ones in 'modifies' remain + + int modified = 0; + + for (RegistryKey key : tableKeys) { + final LootTable table = tableRegistry.get(key); + + if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); + + modified += apply(table) ? 1 : 0; + modifies.remove(key.getValue()); + } + + return modified; + } + + /** + * @param table table to modify + * @return true when any of the 'actions' could be applied, false otherwise + */ + private boolean apply(final @NotNull LootTable table) { + boolean result = false; + for (LootModifierAction action : actions) { + if (action.apply(table)) result = true; + } + return result; + } + + /* + In 1.21.4, the 'Registry' class extends 'RegistryWrapper' and inherits the 'streamEntries' method from *it*. + In 1.20.5, the 'Registry' class *doesn't* extend the 'RegistryWrapper' and implements its own 'streamEntries' method. + Compiling on both versions works, because the names of the methods are the same, but they compile to different intermediary names, thus a jar compiled for 1.20.5 doesn't work on 1.21.4 and vice versa. + Solution: Turn the 'Registry' into a 'RegistryWrapper' as its 'streamEntries' retains the same intermediary on both versions. + If 'Registry' implements 'RegistryWrapper': cast it + Else: call 'getReadOnlyWrapper' on the registry (doesn't exist on 1.21.4, otherwise would've used 'registry.getReadOnlyWrapper().streamEntries()') + */ + private static RegistryWrapper getRegistryAsWrapper(@NotNull Registry registry) { + //noinspection ConstantValue,RedundantSuppression: On lower versions, Registry doesn't extend RegistryWrapper and thus the 'isAssignableFrom' check can be false. The redundant supression is for the unchecked cast below. + if (RegistryWrapper.class.isAssignableFrom(registry.getClass())) + //noinspection unchecked,RedundantCast: I swear it casts 🤞 + return (RegistryWrapper) registry; + + try { + //noinspection unchecked: Seriously I swear 🤞🤞 + return (RegistryWrapper) registry.getClass().getDeclaredMethod(FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_2378", "method_46771", "()Lnet/minecraft/class_7225$class_7226;")).invoke(registry); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + // Don't think this is needed? public static NewLootModifier.Builder builder() { // Don't think this is needed? return new NewLootModifier.Builder(); // Don't think this is needed? } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java index 4877913..574cd94 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java @@ -5,8 +5,11 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.Block; import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import java.util.List; @@ -20,6 +23,18 @@ public LootModifierActionType getType() { return LootModifierActionTypes.ADD_ENTRY; } + @Override + public boolean apply(@NotNull LootTable table) { + final List newPools = ImmutableList.builder() + .addAll(table.pools) + .addAll(this.pools) + .build(); + + ((LootTableAccessor) table).setPools(newPools); + + return true; + } + public static AddPoolAction.Builder builder(@NotNull LootPool.Builder poolBuilder) { return new AddPoolAction.Builder(poolBuilder); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java index 3d1924b..c5064d4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java @@ -1,6 +1,8 @@ package top.offsetmonkey538.loottablemodifier.resource.action; import com.mojang.serialization.Codec; +import net.minecraft.loot.LootTable; +import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; public interface LootModifierAction { @@ -8,6 +10,13 @@ public interface LootModifierAction { LootModifierActionType getType(); + /** + * Applies this action to the provided table + * @param table the table to apply to + * @return true when table was modified, false otherwise + */ + boolean apply(final @NotNull LootTable table); + @FunctionalInterface interface Builder { LootModifierAction build(); From 97d677a6fcd710e7341744b4c040b77534393e1e Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 15:52:47 +0300 Subject: [PATCH 03/53] Make NewLootModifier compatible with old format (the one with just 'pools' (or the very old one with 'loot_pools') next to 'modifies') --- .../loottablemodifier/LootTableModifier.java | 7 ++-- .../resource/NewLootModifier.java | 41 +++++++++++++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 91658cc..d7f6285 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -70,17 +70,18 @@ private static Map loadModifiers(ResourceManager re final Identifier id = entry.getKey(); try { + LOGGER.debug("Loading load loot table modifier from '%s'".formatted(id)); result.put( id, NewLootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() ); - } catch (IOException e) { + } catch (Exception e) { //noinspection StringConcatenationArgumentToLogCall - LOGGER.error("Failed to load loot table modifier from '" + id + "'!", e); + LOGGER.error("Failed to load loot table modifier from '%s'!".formatted(id), e); } } - LOGGER.info("Loaded {} loot modifiers", result.size()); + LOGGER.info("Loaded {} loot modifiers!", result.size()); return result; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java index 3717e6a..77105bd 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java @@ -4,6 +4,7 @@ import com.ibm.icu.impl.EmojiProps; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.fabricmc.fabric.api.loot.v3.FabricLootPoolBuilder; import net.fabricmc.fabric.api.loot.v3.FabricLootTableBuilder; @@ -24,42 +25,64 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied public record NewLootModifier(@NotNull ArrayList modifies, @NotNull List actions) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(NewLootModifier::modifiesEither), - Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).fieldOf("actions").forGetter(NewLootModifier::actionsEither) - //LootModifierAction.CODEC.listOf().fieldOf("") - //Codec.mapEither(LootPool.CODEC.listOf().fieldOf("pools"), LootPool.CODEC.listOf().fieldOf("loot_pools")).forGetter(NewLootModifier::poolsEither) + Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).optionalFieldOf("actions").forGetter(NewLootModifier::actionsOptionalEither), + LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), + LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) ).apply(instance, NewLootModifier::new)); - private NewLootModifier(@NotNull Either> modifiesEither, @NotNull Either> actionsEither) { + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah + private NewLootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { this( modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow())), - actionsEither.right().orElseGet(() -> List.of(actionsEither.left().orElseThrow())) + getActions(actions, pools, lootPools) ); } public NewLootModifier(@NotNull List modifies, @NotNull List actions) { this( new ArrayList<>(modifies), - actions + Collections.unmodifiableList(actions) ); } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah + private static List getActions(@NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { + List result = null; + + if (actions.isPresent()) result = actions.get().right().orElseGet(() -> actions.get().left().isEmpty() ? null : List.of(actions.get().left().orElseThrow())); + + if (result != null && pools.isPresent()) throw new IllegalStateException("Both \"actions\" and \"pools\" present in loot modifier!"); + if (result != null && lootPools.isPresent()) throw new IllegalStateException("Both \"actions\" and \"loot_pools\" present in loot modifier!"); + if (pools.isPresent() && lootPools.isPresent()) throw new IllegalStateException("Both \"pools\" and \"loot_pools\" present in loot modifier!"); + + if (result == null && pools.isPresent()) result = List.of(new AddPoolAction(pools.get())); + if (result == null && lootPools.isPresent()) result = List.of(new AddPoolAction(lootPools.get())); + + if (result == null) throw new IllegalStateException("Neither \"actions\" nor \"pools\" present in loot modifier!"); + + return result; + } + private Either> modifiesEither() { if (modifies.size() == 1) return Either.left(modifies.get(0)); return Either.right(modifies); } - private Either> actionsEither() { - if (actions.size() == 1) return Either.left(actions.get(0)); - return Either.right(actions); + private Optional>> actionsOptionalEither() { + if (actions.size() == 1) return Optional.of(Either.left(actions.get(0))); + return Optional.of(Either.right(actions)); } /** From 13f4e9697432aeabe4f5b749d55ccdc082f084bc Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 15:54:05 +0300 Subject: [PATCH 04/53] Remove original LootModifier --- .../api/datagen/LootModifierProvider.java | 248 ------------------ .../datagen/LootTableModifierDatagen.java | 42 --- .../resource/LootModifier.java | 34 --- 3 files changed, 324 deletions(-) delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java deleted file mode 100644 index 789f549..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java +++ /dev/null @@ -1,248 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.datagen; - -import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; -import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.MappingResolver; -import net.minecraft.data.DataOutput; -import net.minecraft.entity.EntityType; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryWrapper; -import net.minecraft.util.Identifier; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.stream.Stream; - -import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; - -/** - * A datagen provider for creating loot modifiers. - *
- * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@code addModifier(...)} methods to add modifiers. - *
- *
{@code
- * @Override
- * protected void generate(RegistryWrapper.WrapperLookup lookup) {
- *     addModifier(
- *             Identifier.of("testmod", "drop_tnt"),
- *             LootPool.builder()
- *                     .rolls(ConstantLootNumberProvider.create(1))
- *                     .with(
- *                             ItemEntry.builder(Items.TNT)
- *                                     .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
- *                     ),
- *             EntityType.CREEPER,
- *             EntityType.ZOMBIE
- *     );
- * }
- * }
- */ -public abstract class LootModifierProvider extends FabricCodecDataProvider { - private BiConsumer provider; - - public LootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { - super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", LootModifier.CODEC); - } - - @Override - protected void configure(BiConsumer provider, RegistryWrapper.WrapperLookup lookup) { - this.provider = provider; - generate(lookup); - } - - @Override - public String getName() { - return "Loot Table Modifiers"; - } - - /** - * Override and use {@code addModifier()} methods to add modifiers. - * - * @param lookup A lookup for registries. - */ - protected abstract void generate(RegistryWrapper.WrapperLookup lookup); - - /** - * Adds a new loot table modifier for the given {@link EntityType}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link EntityType} to add the modifier to - * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to - */ - protected void addModifier(Identifier name, LootPool.Builder builder, EntityType modifies, EntityType... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link EntityType}s. - * - * @param name Name of this modifier - * @param builders The loot pools to add - * @param modifies The {@link EntityType} to add the modifier to - * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, EntityType modifies, EntityType... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(EntityLootTableIdGetter.get).toList() - ); - } - - /** - * Adds a new loot table modifier for the given {@link RegistryKey}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link RegistryKey} to add the modifier to - * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to - */ - protected void addModifier(Identifier name, LootPool.Builder builder, RegistryKey modifies, RegistryKey... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link RegistryKey}s. - * - * @param name Name of this modifier - * @param builders The loot pools to add - * @param modifies The {@link RegistryKey} to add the modifier to - * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, RegistryKey modifies, RegistryKey... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(RegistryKey::getValue).toList() - ); - } - - /** - * Adds a new loot table modifier for the given {@link Identifier}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link Identifier} to add the modifier to - * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to - */ - protected void addModifier(Identifier name, LootPool.Builder builder, Identifier modifies, Identifier... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link Identifier}s. - * - * @param name Name of this modifier - * @param builders The loot pools to add - * @param modifies The {@link Identifier} to add the modifier to - * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, Identifier modifies, Identifier... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).toList() - ); - } - - private void addModifier(Identifier name, List builders, List modifies) { - provider.accept(name, new LootModifier( - new ArrayList<>(modifies), - builders.stream().map(LootPool.Builder::build).toList() - )); - } - - - - private static class EntityLootTableIdGetter { - // Resolver returns the provided name (like 'method_16351') when it fails to map it - private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); - - private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); - private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); - - // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); - - public static final Function, Identifier> get; - - // Should be executed when class is first loaded/accessed - static { - try { - //final Class entityType = EntityType.class; - final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); - final Method method; - - // 1.21.2 to future:tm: - if (isMethod(entityType, V1d21d2)) { - method = entityType.getDeclaredMethod(V1d21d2); - method.setAccessible(true); - get = entity -> { - try { - @SuppressWarnings("unchecked") - final Optional> optional = (Optional>) method.invoke(entity); - if (optional.isPresent()) return optional.get().getValue(); - throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - // 1.20.5 to 1.21.1 - else if (isMethod(entityType, V1d20d5)) { - method = entityType.getDeclaredMethod(V1d20d5); - method.setAccessible(true); - get = entity -> { - try { - return ((RegistryKey) method.invoke(entity)).getValue(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - else { - throw new IllegalStateException("No valid way to get entity loot table id found!"); - } - } catch (NoSuchMethodException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private static boolean isMethod(Class clazz, String method) { - try { - clazz.getDeclaredMethod(method); - return true; - } catch (NoSuchMethodException e) { - LOGGER.warn("", e); - return false; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 4324dc7..7a09e41 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -13,7 +13,6 @@ import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; import net.minecraft.registry.RegistryWrapper; -import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; @@ -27,50 +26,9 @@ public class LootTableModifierDatagen implements DataGeneratorEntrypoint { public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { final FabricDataGenerator.Pack pack = fabricDataGenerator.createBuiltinResourcePack(id("example_pack")); - //pack.addProvider(ModLootModifierProvider::new); pack.addProvider(NewModLootModifierProvider::new); } - private static class ModLootModifierProvider extends LootModifierProvider { - public ModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { - super(dataOutput, registriesFuture); - } - - @Override - protected void generate(RegistryWrapper.WrapperLookup lookup) { - addModifier( - id("drop_tnt"), - - - LootPool.builder() - .rolls(ConstantLootNumberProvider.create(1)) - .with( - ItemEntry.builder(Items.TNT) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ), - - EntityType.CREEPER, - EntityType.ZOMBIE - ); - - //todo: temp - addModifier( - id("test"), - - LootPool.builder() - .with( - ItemEntry.builder(Items.NETHERITE_SWORD).apply( - EnchantWithLevelsLootFunction - .builder(lookup, UniformLootNumberProvider.create(20, 39)) - //.builder(UniformLootNumberProvider.create(20, 39)) - ) - ), - - LootTables.ABANDONED_MINESHAFT_CHEST - ); - } - } - private static class NewModLootModifierProvider extends NewLootModifierProvider { public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { super(dataOutput, registriesFuture); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java deleted file mode 100644 index 7925d93..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ /dev/null @@ -1,34 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource; - -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootPool; -import net.minecraft.util.Identifier; - -import java.util.ArrayList; -import java.util.List; - -public record LootModifier(ArrayList modifies, List pools) { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), - Codec.mapEither(LootPool.CODEC.listOf().fieldOf("pools"), LootPool.CODEC.listOf().fieldOf("loot_pools")).forGetter(LootModifier::poolsEither) - ).apply(instance, LootModifier::new)); - - private LootModifier(Either> modifiesEither, Either, List> poolsEither) { - this( - new ArrayList<>(modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow()))), - new ArrayList<>(poolsEither.left().orElseGet(() -> poolsEither.right().orElseThrow())) - ); - } - - private Either> modifiesEither() { - if (modifies.size() == 1) return Either.left(modifies.get(0)); - return Either.right(modifies); - } - - // Left is "pools", right is "loot_pools". Want datagen to use "pools" so left it is. - private Either, List> poolsEither() { - return Either.left(pools); - } -} From 48b4d93f973ae61d681beec1c77b2325b2c1f37d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 15:55:14 +0300 Subject: [PATCH 05/53] Rename NewLootModifier to LootModifier --- .../loottablemodifier/LootTableModifier.java | 20 +++++++++---------- .../api/datagen/NewLootModifierProvider.java | 12 +++++------ ...NewLootModifier.java => LootModifier.java} | 14 ++++++------- 3 files changed, 23 insertions(+), 23 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/resource/{NewLootModifier.java => LootModifier.java} (91%) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index d7f6285..af41c84 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -25,7 +25,7 @@ import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -import top.offsetmonkey538.loottablemodifier.resource.NewLootModifier; +import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -45,13 +45,13 @@ public void onInitialize() { } public static void runModification(ResourceManager resourceManager, Registry lootRegistry, RegistryOps registryOps) { - final Map modifiers = loadModifiers(resourceManager, registryOps); - final Map failedModifiers = new HashMap<>(0); + final Map modifiers = loadModifiers(resourceManager, registryOps); + final Map failedModifiers = new HashMap<>(0); int amountModified = 0; LOGGER.info("Applying loot table modifiers..."); - for (Map.Entry modifierEntry : modifiers.entrySet()) { - final NewLootModifier modifier = modifierEntry.getValue(); + for (Map.Entry modifierEntry : modifiers.entrySet()) { + final LootModifier modifier = modifierEntry.getValue(); amountModified += modifier.apply(lootRegistry); @@ -61,10 +61,10 @@ public static void runModification(ResourceManager resourceManager, Registry loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { + private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { LOGGER.info("Loading loot table modifiers..."); - final Map result = new HashMap<>(); + final Map result = new HashMap<>(); for (Map.Entry entry : resourceManager.findResources(MOD_ID + "/loot_modifier", path -> path.toString().endsWith(".json")).entrySet()) { final Identifier id = entry.getKey(); @@ -73,7 +73,7 @@ private static Map loadModifiers(ResourceManager re LOGGER.debug("Loading load loot table modifier from '%s'".formatted(id)); result.put( id, - NewLootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() + LootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() ); } catch (Exception e) { //noinspection StringConcatenationArgumentToLogCall @@ -86,13 +86,13 @@ private static Map loadModifiers(ResourceManager re return result; } - private static void modifiersApplied(int amountModified, Map failedModifiers) { + private static void modifiersApplied(int amountModified, Map failedModifiers) { LOGGER.info("Modified {} loot tables!", amountModified); if (failedModifiers.isEmpty()) return; LOGGER.warn("There were unused modifiers:"); - for (Map.Entry entry : failedModifiers.entrySet()) { + for (Map.Entry entry : failedModifiers.entrySet()) { LOGGER.warn("\tModifier '{}' failed to modify loot table for entities: ", entry.getKey()); for (Identifier id : entry.getValue().modifies()) { LOGGER.warn("\t\t- {}", id); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index 430896e..a5dde43 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -12,7 +12,7 @@ import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; -import top.offsetmonkey538.loottablemodifier.resource.NewLootModifier; +import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; @@ -48,15 +48,15 @@ * } * } */ -public abstract class NewLootModifierProvider extends FabricCodecDataProvider { - private BiConsumer provider; +public abstract class NewLootModifierProvider extends FabricCodecDataProvider { + private BiConsumer provider; public NewLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { - super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", NewLootModifier.CODEC); + super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", LootModifier.CODEC); } @Override - protected void configure(BiConsumer provider, RegistryWrapper.WrapperLookup lookup) { + protected void configure(BiConsumer provider, RegistryWrapper.WrapperLookup lookup) { this.provider = provider; generate(lookup); } @@ -173,7 +173,7 @@ protected void addModifier(Identifier name, List bui } private void addModifier(Identifier name, List builders, List modifies) { - provider.accept(name, new NewLootModifier( + provider.accept(name, new LootModifier( new ArrayList<>(modifies), builders.stream() .map(LootModifierAction.Builder::build) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java similarity index 91% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 77105bd..5511760 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/NewLootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -35,22 +35,22 @@ import java.util.Optional; // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied -public record NewLootModifier(@NotNull ArrayList modifies, @NotNull List actions) { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(NewLootModifier::modifiesEither), - Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).optionalFieldOf("actions").forGetter(NewLootModifier::actionsOptionalEither), +public record LootModifier(@NotNull ArrayList modifies, @NotNull List actions) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), + Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).optionalFieldOf("actions").forGetter(LootModifier::actionsOptionalEither), LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) - ).apply(instance, NewLootModifier::new)); + ).apply(instance, LootModifier::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah - private NewLootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { + private LootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { this( modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow())), getActions(actions, pools, lootPools) ); } - public NewLootModifier(@NotNull List modifies, @NotNull List actions) { + public LootModifier(@NotNull List modifies, @NotNull List actions) { this( new ArrayList<>(modifies), Collections.unmodifiableList(actions) From bb7fdc08caf344066e81ada6984f198adf6a47a0 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 15:55:54 +0300 Subject: [PATCH 06/53] Cleanup --- .../loottablemodifier/LootTableModifier.java | 12 ------------ .../api/LootModifierActionTypes.java | 5 ----- .../api/datagen/NewLootModifierProvider.java | 2 -- .../loottablemodifier/resource/LootModifier.java | 13 ------------- .../resource/action/AddPoolAction.java | 2 -- .../resource/action/LootModifierAction.java | 1 - 6 files changed, 35 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index af41c84..eff52e4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -1,36 +1,24 @@ package top.offsetmonkey538.loottablemodifier; -import com.google.common.collect.ImmutableList; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; -import net.minecraft.loot.condition.LootCondition; import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryOps; -import net.minecraft.registry.RegistryWrapper; -import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.text.Text; import net.minecraft.util.Identifier; -import net.minecraft.util.Pair; -import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; -import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.util.HashMap; -import java.util.List; import java.util.Map; public class LootTableModifier implements ModInitializer { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java index 4802aef..cca0964 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java @@ -1,8 +1,6 @@ package top.offsetmonkey538.loottablemodifier.api; -import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; @@ -10,9 +8,6 @@ import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; -import java.util.HashMap; -import java.util.Map; - import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; public final class LootModifierActionTypes { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index a5dde43..56ef6a1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -5,9 +5,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.MappingResolver; import net.minecraft.data.DataOutput; -import net.minecraft.data.loottable.LootTableGenerator; import net.minecraft.entity.EntityType; -import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 5511760..2ffdcd4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -1,30 +1,17 @@ package top.offsetmonkey538.loottablemodifier.resource; -import com.google.common.collect.ImmutableList; -import com.ibm.icu.impl.EmojiProps; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.fabricmc.fabric.api.loot.v3.FabricLootPoolBuilder; -import net.fabricmc.fabric.api.loot.v3.FabricLootTableBuilder; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; -import net.minecraft.loot.condition.LootCondition; -import net.minecraft.loot.condition.LootConditionConsumingBuilder; -import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.loot.function.LootFunction; -import net.minecraft.loot.function.LootFunctionConsumingBuilder; -import net.minecraft.loot.provider.number.ConstantLootNumberProvider; -import net.minecraft.loot.provider.number.LootNumberProvider; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java index 574cd94..004b24e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java @@ -3,10 +3,8 @@ import com.google.common.collect.ImmutableList; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.block.Block; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java index c5064d4..8d56f6e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java @@ -3,7 +3,6 @@ import com.mojang.serialization.Codec; import net.minecraft.loot.LootTable; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; public interface LootModifierAction { Codec CODEC = LootModifierActionType.REGISTRY.getCodec().dispatch(LootModifierAction::getType, LootModifierActionType::codec); From 5ed8e2ced8e19a9487bc9e0433035a8fe06703e8 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 18:15:03 +0300 Subject: [PATCH 07/53] Allow regex for matching loot table, allow matching loot table by type --- .../loottablemodifier/LootTableModifier.java | 24 +++++---- .../api/datagen/NewLootModifierProvider.java | 4 +- .../resource/LootModifier.java | 29 +++++----- .../resource/LootTablePredicate.java | 53 +++++++++++++++++++ .../loot-table-modifier.accesswidener | 3 +- .../resources/loot-table-modifier.mixins.json | 18 +++---- 6 files changed, 94 insertions(+), 37 deletions(-) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index eff52e4..f53f096 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -1,5 +1,6 @@ package top.offsetmonkey538.loottablemodifier; +import com.google.common.base.Stopwatch; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import net.fabricmc.api.ModInitializer; @@ -17,6 +18,7 @@ import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.resource.LootTablePredicate; import java.util.HashMap; import java.util.Map; @@ -37,20 +39,24 @@ public static void runModification(ResourceManager resourceManager, Registry failedModifiers = new HashMap<>(0); int amountModified = 0; - LOGGER.info("Applying loot table modifiers..."); + LOGGER.info("Modifying loot tables..."); + final Stopwatch stopwatch = Stopwatch.createStarted(); for (Map.Entry modifierEntry : modifiers.entrySet()) { final LootModifier modifier = modifierEntry.getValue(); amountModified += modifier.apply(lootRegistry); - if (!modifier.modifies().isEmpty()) failedModifiers.put(modifierEntry.getKey(), modifier); + // TODO: try to figure smt out abt this: if (!modifier.modifies().isEmpty()) failedModifiers.put(modifierEntry.getKey(), modifier); } - modifiersApplied(amountModified, failedModifiers); + + LOGGER.info("Modified {} loot tables in {}!", amountModified, stopwatch); + modifiersApplied(failedModifiers); } private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { LOGGER.info("Loading loot table modifiers..."); + final Stopwatch stopwatch = Stopwatch.createStarted(); final Map result = new HashMap<>(); @@ -69,21 +75,19 @@ private static Map loadModifiers(ResourceManager resou } } - LOGGER.info("Loaded {} loot modifiers!", result.size()); + LOGGER.info("Loaded {} loot modifiers in {}!", result.size(), stopwatch); return result; } - private static void modifiersApplied(int amountModified, Map failedModifiers) { - LOGGER.info("Modified {} loot tables!", amountModified); - + private static void modifiersApplied(Map failedModifiers) { if (failedModifiers.isEmpty()) return; LOGGER.warn("There were unused modifiers:"); for (Map.Entry entry : failedModifiers.entrySet()) { - LOGGER.warn("\tModifier '{}' failed to modify loot table for entities: ", entry.getKey()); - for (Identifier id : entry.getValue().modifies()) { - LOGGER.warn("\t\t- {}", id); + LOGGER.warn("\tModifier '{}' failed to modify loot table for predicates: ", entry.getKey()); + for (LootTablePredicate predicate : entry.getValue().modifies()) { + LOGGER.warn("\t\t- {}", predicate); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index 56ef6a1..20b4a96 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -11,6 +11,7 @@ import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.resource.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; @@ -19,6 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Stream; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; @@ -172,7 +174,7 @@ protected void addModifier(Identifier name, List bui private void addModifier(Identifier name, List builders, List modifies) { provider.accept(name, new LootModifier( - new ArrayList<>(modifies), + modifies.stream().map(identifier -> new LootTablePredicate(Pattern.compile(identifier.toString()))).toList(), builders.stream() .map(LootModifierAction.Builder::build) .toList() diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 2ffdcd4..40da5cd 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -10,34 +10,31 @@ import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied -public record LootModifier(@NotNull ArrayList modifies, @NotNull List actions) { +public record LootModifier(@NotNull ArrayList modifies, @NotNull @UnmodifiableView List actions) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), + Codec.either(LootTablePredicate.CODEC, LootTablePredicate.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).optionalFieldOf("actions").forGetter(LootModifier::actionsOptionalEither), LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) ).apply(instance, LootModifier::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah - private LootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { + private LootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { this( modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow())), getActions(actions, pools, lootPools) ); } - public LootModifier(@NotNull List modifies, @NotNull List actions) { + public LootModifier(@NotNull List modifies, @NotNull List actions) { this( new ArrayList<>(modifies), Collections.unmodifiableList(actions) @@ -62,7 +59,7 @@ private static List getActions(@NotNull Optional> modifiesEither() { + private Either> modifiesEither() { if (modifies.size() == 1) return Either.left(modifies.get(0)); return Either.right(modifies); } @@ -77,19 +74,19 @@ private Optional>> actionsOp * @return amount of loot tables modified */ public int apply(final @NotNull Registry tableRegistry) { - final List> tableKeys = getRegistryAsWrapper(tableRegistry).streamEntries().map(RegistryEntry.Reference::registryKey).filter(key -> modifies.contains(key.getValue())).toList(); - if (tableKeys.isEmpty()) return 0; - // At this point only ones in 'modifies' remain - int modified = 0; - for (RegistryKey key : tableKeys) { + for (Iterator> it = getRegistryAsWrapper(tableRegistry).streamEntries().iterator(); it.hasNext(); ) { + final RegistryEntry.Reference entry = it.next(); + + final RegistryKey key = entry.registryKey(); final LootTable table = tableRegistry.get(key); if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); + if (modifies.stream().noneMatch(predicate -> predicate.matches(table, key.getValue()))) continue; + modified += apply(table) ? 1 : 0; - modifies.remove(key.getValue()); } return modified; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java new file mode 100644 index 0000000..d2acb49 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java @@ -0,0 +1,53 @@ +package top.offsetmonkey538.loottablemodifier.resource; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.regex.Pattern; + +public record LootTablePredicate(@NotNull Pattern identifier, @Nullable Pattern type) { + private static final Codec PATTERN_CODEC = Codec.STRING.xmap(Pattern::compile, Pattern::pattern); + private static final Codec INLINE_CODEC = PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); + private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + PATTERN_CODEC.fieldOf("identifier").forGetter(LootTablePredicate::identifier), + PATTERN_CODEC.optionalFieldOf("type").forGetter(LootTablePredicate::optionalType) + ).apply(instance, LootTablePredicate::new)); + public static final Codec CODEC = Codec.either(LootTablePredicate.INLINE_CODEC, LootTablePredicate.FULL_CODEC).xmap(lootTablePredicateLootTablePredicateEither -> lootTablePredicateLootTablePredicateEither.left().orElseGet(() -> lootTablePredicateLootTablePredicateEither.right().orElseThrow()), + lootTablePredicate -> { + if (lootTablePredicate.type == null) { + return Either.left(lootTablePredicate); + } + return Either.right(lootTablePredicate); + }); + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me + private LootTablePredicate(@NotNull Pattern pattern, @NotNull Optional optionalType) { + this(pattern, optionalType.orElse(null)); + } + + public LootTablePredicate(@NotNull Pattern pattern) { + this(pattern, (Pattern) null); + } + + private Optional optionalType() { + return Optional.ofNullable(type); + } + + + public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { + boolean result = identifier.matcher(tableId.toString()).matches(); + + if (type != null) { + result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); + } + + return result; + } +} diff --git a/src/main/resources/loot-table-modifier.accesswidener b/src/main/resources/loot-table-modifier.accesswidener index 46a644a..1cbe777 100644 --- a/src/main/resources/loot-table-modifier.accesswidener +++ b/src/main/resources/loot-table-modifier.accesswidener @@ -1,3 +1,4 @@ accessWidener v1 named -accessible field net/minecraft/loot/LootTable pools Ljava/util/List; \ No newline at end of file +accessible field net/minecraft/loot/LootTable pools Ljava/util/List; +accessible field net/minecraft/loot/context/LootContextTypes MAP Lcom/google/common/collect/BiMap; \ No newline at end of file diff --git a/src/main/resources/loot-table-modifier.mixins.json b/src/main/resources/loot-table-modifier.mixins.json index e306650..52a1daa 100644 --- a/src/main/resources/loot-table-modifier.mixins.json +++ b/src/main/resources/loot-table-modifier.mixins.json @@ -1,12 +1,12 @@ { - "required": true, - "package": "top.offsetmonkey538.loottablemodifier.mixin", - "compatibilityLevel": "JAVA_17", - "mixins": [ - "LootTableAccessor", - "ReloadableRegistriesMixin" - ], - "injectors": { - "defaultRequire": 1 + "required": true, + "package": "top.offsetmonkey538.loottablemodifier.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "LootTableAccessor", + "ReloadableRegistriesMixin" + ], + "injectors": { + "defaultRequire": 1 } } From 2c9ab5fa21354163670da739947696edfbf9a680 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 6 May 2025 19:22:19 +0300 Subject: [PATCH 08/53] Allow matching loot table by function type Functions (and conditions in the future) only support matching by type, couldn't figure out a good way to match specific ones --- .../loottablemodifier/LootTableModifier.java | 2 +- .../api/datagen/NewLootModifierProvider.java | 2 +- .../resource/LootModifier.java | 1 + .../resource/LootTablePredicate.java | 53 -------------- .../predicate/LootFunctionPredicate.java | 20 ++++++ .../predicate/LootTablePredicate.java | 72 +++++++++++++++++++ .../resource/predicate/Util.java | 13 ++++ .../loot-table-modifier.accesswidener | 4 +- 8 files changed, 111 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index f53f096..60d1648 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootTablePredicate; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index 20b4a96..4ebd742 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -11,7 +11,7 @@ import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 40da5cd..62b6d2e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -14,6 +14,7 @@ import org.jetbrains.annotations.UnmodifiableView; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootTablePredicate; import java.lang.reflect.InvocationTargetException; import java.util.*; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java deleted file mode 100644 index d2acb49..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootTablePredicate.java +++ /dev/null @@ -1,53 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource; - -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.context.LootContextTypes; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Optional; -import java.util.regex.Pattern; - -public record LootTablePredicate(@NotNull Pattern identifier, @Nullable Pattern type) { - private static final Codec PATTERN_CODEC = Codec.STRING.xmap(Pattern::compile, Pattern::pattern); - private static final Codec INLINE_CODEC = PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); - private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( - PATTERN_CODEC.fieldOf("identifier").forGetter(LootTablePredicate::identifier), - PATTERN_CODEC.optionalFieldOf("type").forGetter(LootTablePredicate::optionalType) - ).apply(instance, LootTablePredicate::new)); - public static final Codec CODEC = Codec.either(LootTablePredicate.INLINE_CODEC, LootTablePredicate.FULL_CODEC).xmap(lootTablePredicateLootTablePredicateEither -> lootTablePredicateLootTablePredicateEither.left().orElseGet(() -> lootTablePredicateLootTablePredicateEither.right().orElseThrow()), - lootTablePredicate -> { - if (lootTablePredicate.type == null) { - return Either.left(lootTablePredicate); - } - return Either.right(lootTablePredicate); - }); - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Pattern pattern, @NotNull Optional optionalType) { - this(pattern, optionalType.orElse(null)); - } - - public LootTablePredicate(@NotNull Pattern pattern) { - this(pattern, (Pattern) null); - } - - private Optional optionalType() { - return Optional.ofNullable(type); - } - - - public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { - boolean result = identifier.matcher(tableId.toString()).matches(); - - if (type != null) { - result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); - } - - return result; - } -} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java new file mode 100644 index 0000000..31f1786 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java @@ -0,0 +1,20 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate; + +import com.mojang.serialization.Codec; +import net.minecraft.loot.function.LootFunction; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; + +public record LootFunctionPredicate(@NotNull Pattern functionPattern) { + public static final Codec CODEC = Util.PATTERN_CODEC.xmap(LootFunctionPredicate::new, LootFunctionPredicate::functionPattern); + + public boolean matches(final @NotNull LootFunction function) { + final Identifier functionId = Registries.LOOT_FUNCTION_TYPE.getId(function.getType()); + if (functionId == null) return false; + + return functionPattern.matcher(functionId.toString()).matches(); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java new file mode 100644 index 0000000..8b98b99 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java @@ -0,0 +1,72 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; + +public record LootTablePredicate(@Nullable Pattern identifier, @Nullable Pattern type, @Nullable List functions) { + private static final Codec INLINE_CODEC = Util.PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); + private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Util.PATTERN_CODEC.optionalFieldOf("id").forGetter(LootTablePredicate::optionalIdentifier), + Util.PATTERN_CODEC.optionalFieldOf("type").forGetter(LootTablePredicate::optionalType), + Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootTablePredicate::optionalFunctions) + ).apply(instance, LootTablePredicate::new)); + public static final Codec CODEC = Codec.either(LootTablePredicate.INLINE_CODEC, LootTablePredicate.FULL_CODEC).xmap(lootTablePredicateLootTablePredicateEither -> lootTablePredicateLootTablePredicateEither.left().orElseGet(() -> lootTablePredicateLootTablePredicateEither.right().orElseThrow()), + lootTablePredicate -> { + if (lootTablePredicate.identifier != null && lootTablePredicate.type == null && (lootTablePredicate.functions == null || lootTablePredicate.functions.isEmpty())) { + return Either.left(lootTablePredicate); + } + return Either.right(lootTablePredicate); + }); + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me + private LootTablePredicate(@NotNull Optional optionalIdentifier, @NotNull Optional optionalType, @NotNull Optional>> optionalFunctions) { + this(optionalIdentifier.orElse(null), optionalType.orElse(null), optionalFunctions.map(functionsEither -> functionsEither.map(List::of, patterns -> patterns)).orElse(null)); + } + + public LootTablePredicate(@NotNull Pattern pattern) { + this(pattern, null, null); + } + + private Optional optionalIdentifier() { + return Optional.ofNullable(identifier); + } + private Optional optionalType() { + return Optional.ofNullable(type); + } + private Optional>> optionalFunctions() { + if (functions == null) return Optional.empty(); + if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); + return Optional.of(Either.right(functions)); + } + + + public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { + boolean result = true; + + if (identifier != null) { + result = identifier.matcher(tableId.toString()).matches(); + } + + if (type != null) { + result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); + } + + if (functions != null) { + for (LootFunctionPredicate functionPredicate : functions) { + result = result && table.functions.stream().anyMatch(functionPredicate::matches); + } + } + + return result; + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java new file mode 100644 index 0000000..41e6cb0 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java @@ -0,0 +1,13 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate; + +import com.mojang.serialization.Codec; + +import java.util.regex.Pattern; + +public final class Util { + private Util() { + + } + + public static final Codec PATTERN_CODEC = Codec.STRING.xmap(Pattern::compile, Pattern::pattern); +} diff --git a/src/main/resources/loot-table-modifier.accesswidener b/src/main/resources/loot-table-modifier.accesswidener index 1cbe777..1eee9e0 100644 --- a/src/main/resources/loot-table-modifier.accesswidener +++ b/src/main/resources/loot-table-modifier.accesswidener @@ -1,4 +1,6 @@ accessWidener v1 named accessible field net/minecraft/loot/LootTable pools Ljava/util/List; -accessible field net/minecraft/loot/context/LootContextTypes MAP Lcom/google/common/collect/BiMap; \ No newline at end of file +accessible field net/minecraft/loot/LootTable functions Ljava/util/List; + +accessible field net/minecraft/loot/context/LootContextTypes MAP Lcom/google/common/collect/BiMap; From 2e5f8e80db9d132dfc74e571a257dd984367c56d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 12 Jun 2025 14:41:31 +0300 Subject: [PATCH 09/53] Lots of broken stuff --- README.md | 4 + .../loottablemodifier/LootTableModifier.java | 119 +++++++++++++-- .../api/LootModifierPredicateTypes.java | 40 ++++++ .../api/datagen/NewLootModifierProvider.java | 8 +- .../resource/LootModifier.java | 58 ++++---- .../resource/LootModifierContext.java | 21 +++ .../resource/action/LootModifierAction.java | 6 +- .../resource/action/RemovePoolAction.java | 43 ++++++ .../predicate/LootFunctionPredicate.java | 20 --- .../predicate/LootModifierPredicate.java | 79 ++++++++++ .../predicate/LootModifierPredicateType.java | 15 ++ .../condition/LootConditionPredicate.java | 21 +++ .../predicate/entry/LootEntryPredicate.java | 55 +++++++ .../entry/leaf/EmptyEntryPredicate.java | 40 ++++++ .../entry/leaf/LeafEntryPredicate.java | 44 ++++++ .../function/LootFunctionPredicate.java | 22 +++ .../predicate/op/AllOfLootPredicate.java | 46 ++++++ .../predicate/op/AnyOfLootPredicate.java | 47 ++++++ .../predicate/op/InvertedLootPredicate.java | 29 ++++ .../predicate/op/TermsLootPredicate.java | 72 ++++++++++ .../predicate/pool/LootPoolPredicate.java | 136 ++++++++++++++++++ .../{ => table}/LootTablePredicate.java | 14 +- .../loot-table-modifier.accesswidener | 2 + 23 files changed, 871 insertions(+), 70 deletions(-) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java rename src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/{ => table}/LootTablePredicate.java (85%) diff --git a/README.md b/README.md index d55497d..b3af05c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ Allows datapacks (and thus mods as well) to add to loot tables, instead of just overwriting them. +This mod shouldn't impact performance while playing the game, but only when datapacks are reloading (joining a world, starting a server, `/reload` command, whatever else). +Performance impact during pack reloading varies depending on the datapacks. +The mod writes how long applying modifiers took in the console. + Also provides a datagen provider for creating loot table modifiers in mods. A modifier json file includes two components: diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 60d1648..a820300 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -7,20 +7,32 @@ import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryOps; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.text.Text; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicateTypes; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; public class LootTableModifier implements ModInitializer { @@ -30,8 +42,10 @@ public class LootTableModifier implements ModInitializer { @Override public void onInitialize() { LootModifierActionTypes.register(); + LootEntryPredicateTypes.register(); - if(FabricLoader.getInstance().isDevelopmentEnvironment()) ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); + if (FabricLoader.getInstance().isDevelopmentEnvironment()) + ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); } public static void runModification(ResourceManager resourceManager, Registry lootRegistry, RegistryOps registryOps) { @@ -41,16 +55,71 @@ public static void runModification(ResourceManager resourceManager, Registry modifierEntry : modifiers.entrySet()) { - final LootModifier modifier = modifierEntry.getValue(); + // TODO: Would looping through all tables here be faster than doing that for each modifier? To test I guess + + + // TODO: so about that never-nesting............ + // fixme: don't think modifying for example tables without any pools or entries would work with this..... + // TODO: increment "amountModified" somewhere... + // todo. just fucking rewrite this when your brain actually works and can think + for (Iterator> it = getRegistryAsWrapper(lootRegistry).streamEntries().iterator(); it.hasNext(); ) { + final RegistryEntry.Reference registryEntry = it.next(); + final RegistryKey key = registryEntry.registryKey(); + + final LootTable table = lootRegistry.get(key); + if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); + + + for (LootPool pool : table.pools) { + for (LootPoolEntry entry : pool.entries) { + final LootModifierContext context = new LootModifierContext(table, key.getValue(), pool, entry); + for (Map.Entry modifierEntry : modifiers.entrySet()) { + final LootModifier modifier = modifierEntry.getValue(); + if (!modifier.testModifies(context)) continue; + modifier.apply(context); + } + } + } + + //for (Map.Entry modifierEntry : modifiers.entrySet()) { + // final LootModifier modifier = modifierEntry.getValue(); + + // for (LootModifierPredicate modifiesPredicate : modifier.modifies()) { + // if (modifiesPredicate.requiredContext() == LootModifierContext.REQUIRES_TABLE && !modifiesPredicate.test(tableContext)) continue; + + // for (LootPool pool : table.pools) { + // final LootModifierContext poolContext = new LootModifierContext(table, key.getValue(), pool); + + // if (modifiesPredicate.requiredContext() == LootModifierContext.REQUIRES_POOL && !modifiesPredicate.test(poolContext)) continue; - amountModified += modifier.apply(lootRegistry); + // for (LootPoolEntry entry : pool.entries) { + // final LootModifierContext entryContext = new LootModifierContext(table, key.getValue(), pool, entry); - // TODO: try to figure smt out abt this: if (!modifier.modifies().isEmpty()) failedModifiers.put(modifierEntry.getKey(), modifier); + // if (modifiesPredicate.requiredContext() == LootModifierContext.REQUIRES_POOL && !modifiesPredicate.test(entryContext)) continue; + + // for (LootModifierAction action : modifier.actions()) { + // action.apply(entryContext); + // } + // } + // } + // } + //} + + + // todo: modified += apply(table) ? 1 : 0; } - LOGGER.info("Modified {} loot tables in {}!", amountModified, stopwatch); + //for (Map.Entry modifierEntry : modifiers.entrySet()) { + // final LootModifier modifier = modifierEntry.getValue(); + + // amountModified += modifier.apply(lootRegistry); + + // // TODO: try to figure smt out abt this: if (!modifier.modifies().isEmpty()) failedModifiers.put(modifierEntry.getKey(), modifier); + //} + + + LOGGER.info("Applied {} modifiers and modified {} loot tables in {}!", modifiers.size(), amountModified, stopwatch); modifiersApplied(failedModifiers); } @@ -64,7 +133,7 @@ private static Map loadModifiers(ResourceManager resou final Identifier id = entry.getKey(); try { - LOGGER.debug("Loading load loot table modifier from '%s'".formatted(id)); + LOGGER.debug("Loading load loot table modifier from '{}'", id); result.put( id, LootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() @@ -84,15 +153,37 @@ private static void modifiersApplied(Map failedModifie if (failedModifiers.isEmpty()) return; LOGGER.warn("There were unused modifiers:"); - for (Map.Entry entry : failedModifiers.entrySet()) { - LOGGER.warn("\tModifier '{}' failed to modify loot table for predicates: ", entry.getKey()); - for (LootTablePredicate predicate : entry.getValue().modifies()) { - LOGGER.warn("\t\t- {}", predicate); - } - } + //for (Map.Entry entry : failedModifiers.entrySet()) { + // LOGGER.warn("\tModifier '{}' failed to modify loot table for predicates: ", entry.getKey()); + // for (LootTablePredicate predicate : entry.getValue().modifies()) { + // LOGGER.warn("\t\t- {}", predicate); + // } + //} } public static Identifier id(String path) { return Identifier.of(MOD_ID, path); } + + /* + In 1.21.4, the 'Registry' class extends 'RegistryWrapper' and inherits the 'streamEntries' method from *it*. + In 1.20.5, the 'Registry' class *doesn't* extend the 'RegistryWrapper' and implements its own 'streamEntries' method. + Compiling on both versions works, because the names of the methods are the same, but they compile to different intermediary names, thus a jar compiled for 1.20.5 doesn't work on 1.21.4 and vice versa. + Solution: Turn the 'Registry' into a 'RegistryWrapper' as its 'streamEntries' retains the same intermediary on both versions. + If 'Registry' implements 'RegistryWrapper': cast it + Else: call 'getReadOnlyWrapper' on the registry (doesn't exist on 1.21.4, otherwise would've used 'registry.getReadOnlyWrapper().streamEntries()') + */ + private static RegistryWrapper getRegistryAsWrapper(@NotNull Registry registry) { + //noinspection ConstantValue,RedundantSuppression: On lower versions, Registry doesn't extend RegistryWrapper and thus the 'isAssignableFrom' check can be false. The redundant supression is for the unchecked cast below. + if (RegistryWrapper.class.isAssignableFrom(registry.getClass())) + //noinspection unchecked,RedundantCast: I swear it casts 🤞 + return (RegistryWrapper) registry; + + try { + //noinspection unchecked: Seriously I swear 🤞🤞 + return (RegistryWrapper) registry.getClass().getDeclaredMethod(FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_2378", "method_46771", "()Lnet/minecraft/class_7225$class_7226;")).invoke(registry); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java new file mode 100644 index 0000000..2f9b0df --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java @@ -0,0 +1,40 @@ +package top.offsetmonkey538.loottablemodifier.api; + +import com.mojang.serialization.MapCodec; +import net.minecraft.loot.condition.AllOfLootCondition; +import net.minecraft.loot.condition.AnyOfLootCondition; +import net.minecraft.loot.condition.InvertedLootCondition; +import net.minecraft.loot.condition.LootConditionType; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.op.InvertedLootPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; + +public final class LootModifierPredicateTypes { + private LootModifierPredicateTypes() { + + } + + public static final LootModifierPredicateType INVERTED = register(id("inverted"), InvertedLootPredicate.CODEC); + public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfLootPredicate.CODEC); + public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfLootPredicate.CODEC); + + public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); + + private static LootModifierPredicateType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { + return Registry.register(LootModifierPredicateType.REGISTRY, id, new LootModifierPredicateType(codec)); + } + + public static void register() { + // Registers action types by loading the class + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index 4ebd742..192f273 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -11,7 +11,7 @@ import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; @@ -94,7 +94,7 @@ protected void addModifier(Identifier name, LootModifierAction.Builder builder, * Adds a new loot table modifier for the given {@link EntityType}s. * * @param name Name of this modifier - * @param builders The loot pools to add + * @param builders The loot poolPredicates to add * @param modifies The {@link EntityType} to add the modifier to * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to */ @@ -127,7 +127,7 @@ protected void addModifier(Identifier name, LootModifierAction.Builder builder, * Adds a new loot table modifier for the given {@link RegistryKey}s. * * @param name Name of this modifier - * @param builders The loot pools to add + * @param builders The loot poolPredicates to add * @param modifies The {@link RegistryKey} to add the modifier to * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to */ @@ -160,7 +160,7 @@ protected void addModifier(Identifier name, LootModifierAction.Builder builder, * Adds a new loot table modifier for the given {@link Identifier}s. * * @param name Name of this modifier - * @param builders The loot pools to add + * @param builders The loot poolPredicates to add * @param modifies The {@link Identifier} to add the modifier to * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to */ diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 62b6d2e..ba4d1d2 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -14,28 +14,29 @@ import org.jetbrains.annotations.UnmodifiableView; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; import java.lang.reflect.InvocationTargetException; import java.util.*; // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied -public record LootModifier(@NotNull ArrayList modifies, @NotNull @UnmodifiableView List actions) { +public record LootModifier(@NotNull ArrayList modifies, @NotNull @UnmodifiableView List actions) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either(LootTablePredicate.CODEC, LootTablePredicate.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), + Codec.either(LootModifierPredicate.CODEC, LootModifierPredicate.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).optionalFieldOf("actions").forGetter(LootModifier::actionsOptionalEither), LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) ).apply(instance, LootModifier::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah - private LootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { + private LootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { this( modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow())), getActions(actions, pools, lootPools) ); } - public LootModifier(@NotNull List modifies, @NotNull List actions) { + public LootModifier(@NotNull List modifies, @NotNull List actions) { this( new ArrayList<>(modifies), Collections.unmodifiableList(actions) @@ -60,7 +61,7 @@ private static List getActions(@NotNull Optional> modifiesEither() { + private Either> modifiesEither() { if (modifies.size() == 1) return Either.left(modifies.get(0)); return Either.right(modifies); } @@ -70,41 +71,48 @@ private Optional>> actionsOp return Optional.of(Either.right(actions)); } - /** - * @param tableRegistry registry of loot tables to modify - * @return amount of loot tables modified - */ - public int apply(final @NotNull Registry tableRegistry) { - int modified = 0; + ///** + // * @param tableRegistry registry of loot tables to modify + // * @return amount of loot tables modified + // */ + //public int apply(final @NotNull Registry tableRegistry) { + // int modified = 0; - for (Iterator> it = getRegistryAsWrapper(tableRegistry).streamEntries().iterator(); it.hasNext(); ) { - final RegistryEntry.Reference entry = it.next(); + // for (Iterator> it = getRegistryAsWrapper(tableRegistry).streamEntries().iterator(); it.hasNext(); ) { + // final RegistryEntry.Reference entry = it.next(); - final RegistryKey key = entry.registryKey(); - final LootTable table = tableRegistry.get(key); + // final RegistryKey key = entry.registryKey(); + // final LootTable table = tableRegistry.get(key); - if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); + // if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); - if (modifies.stream().noneMatch(predicate -> predicate.matches(table, key.getValue()))) continue; + // if (modifies.stream().noneMatch(predicate -> predicate.matches(table, key.getValue()))) continue; - modified += apply(table) ? 1 : 0; - } + // modified += apply(table) ? 1 : 0; + // } - return modified; - } + // return modified; + //} /** - * @param table table to modify + * @param context context to modify * @return true when any of the 'actions' could be applied, false otherwise */ - private boolean apply(final @NotNull LootTable table) { + public boolean apply(final @NotNull LootModifierContext context) { boolean result = false; for (LootModifierAction action : actions) { - if (action.apply(table)) result = true; + if (action.apply(context)) result = true; } return result; } + public boolean testModifies(final @NotNull LootModifierContext context) { + for (LootModifierPredicate modifiesPredicate : modifies) { + if (!modifiesPredicate.test(context)) return false; + } + return true; + } + /* In 1.21.4, the 'Registry' class extends 'RegistryWrapper' and inherits the 'streamEntries' method from *it*. In 1.20.5, the 'Registry' class *doesn't* extend the 'RegistryWrapper' and implements its own 'streamEntries' method. diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java new file mode 100644 index 0000000..63ec55b --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java @@ -0,0 +1,21 @@ +package top.offsetmonkey538.loottablemodifier.resource; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record LootModifierContext(@NotNull LootTable table, @NotNull Identifier tableId, @Nullable LootPool pool, @Nullable LootPoolEntry entry) { + public static final byte REQUIRES_TABLE = 2; + public static final byte REQUIRES_POOL = 1; + public static final byte REQUIRES_ENTRY = 0; + + public LootModifierContext(@NotNull final LootTable table, @NotNull final Identifier tableId, @NotNull final LootPool pool) { + this(table, tableId, pool, null); + } + public LootModifierContext(@NotNull final LootTable table, @NotNull final Identifier tableId) { + this(table, tableId, null, null); + } +} \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java index 8d56f6e..e17c5eb 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java @@ -1,8 +1,8 @@ package top.offsetmonkey538.loottablemodifier.resource.action; import com.mojang.serialization.Codec; -import net.minecraft.loot.LootTable; import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; public interface LootModifierAction { Codec CODEC = LootModifierActionType.REGISTRY.getCodec().dispatch(LootModifierAction::getType, LootModifierActionType::codec); @@ -11,10 +11,10 @@ public interface LootModifierAction { /** * Applies this action to the provided table - * @param table the table to apply to + * @param context the context to apply to * @return true when table was modified, false otherwise */ - boolean apply(final @NotNull LootTable table); + boolean apply(final @NotNull LootModifierContext context); @FunctionalInterface interface Builder { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java new file mode 100644 index 0000000..66291d5 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java @@ -0,0 +1,43 @@ +package top.offsetmonkey538.loottablemodifier.resource.action; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; + +import java.util.ArrayList; +import java.util.List; + +public record RemovePoolAction(LootPoolPredicate poolPredicate) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPoolPredicate.CODEC.fieldOf("poolPredicate").forGetter(RemovePoolAction::poolPredicate) + ).apply(instance, RemovePoolAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ADD_ENTRY; + } + + @Override + public boolean apply(@NotNull final LootModifierContext context) { + if (!poolPredicate.test(context)) return false; + + List newPools = new ArrayList<>(context.table().pools); + newPools.remove(context.pool()); + newPools = ImmutableList.copyOf(newPools); + + ((LootTableAccessor) context.table()).setPools(newPools); + + return true; + } + + public static LootModifierAction.Builder builder(@NotNull LootPoolPredicate.Builder poolBuilder) { + return () -> new RemovePoolAction(poolBuilder.build()); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java deleted file mode 100644 index 31f1786..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootFunctionPredicate.java +++ /dev/null @@ -1,20 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate; - -import com.mojang.serialization.Codec; -import net.minecraft.loot.function.LootFunction; -import net.minecraft.registry.Registries; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; - -import java.util.regex.Pattern; - -public record LootFunctionPredicate(@NotNull Pattern functionPattern) { - public static final Codec CODEC = Util.PATTERN_CODEC.xmap(LootFunctionPredicate::new, LootFunctionPredicate::functionPattern); - - public boolean matches(final @NotNull LootFunction function) { - final Identifier functionId = Registries.LOOT_FUNCTION_TYPE.getId(function.getType()); - if (functionId == null) return false; - - return functionPattern.matcher(functionId.toString()).matches(); - } -} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java new file mode 100644 index 0000000..a279df5 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java @@ -0,0 +1,79 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.condition.AllOfLootCondition; +import net.minecraft.loot.condition.AnyOfLootCondition; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.op.InvertedLootPredicate; + +import java.util.function.Predicate; + +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; + +public interface LootModifierPredicate extends Predicate { + Codec CODEC = LootModifierPredicateType.REGISTRY.getCodec().dispatch(LootModifierPredicate::getType, LootModifierPredicateType::codec); + + LootModifierPredicateType getType(); + + byte requiredContext(); + + /** + * Tests if this predicate matches the provided context + * @param context the context to test against + * @return true when this predicate matches the provided context, false when not + */ + + default boolean test(final @NotNull LootModifierContext context) { + return switch (requiredContext()) { + case REQUIRES_TABLE -> test(context, context.table(), context.tableId()); + case REQUIRES_POOL -> { + if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); + yield test(context, context.table(), context.tableId(), context.pool()); + } + case REQUIRES_ENTRY -> { + if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); + if (context.entry() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have an entry set!"); + yield test(context, context.table(), context.tableId(), context.pool(), context.entry()); + } + default -> throw new UnsupportedOperationException("Loot modifier predicate defines unsupported requiredContext value '%s', only values 0 - 2 are supported.".formatted(requiredContext())); + }; + } + + default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId) { + throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, but says it requires a table"); + } + default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { + throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table and pool, but says it requires a pool"); + } + default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { + throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, pool and entry, but says it requires an entry"); + } + + @FunctionalInterface + interface Builder { + LootModifierPredicate build(); + + default Builder invert() { + return InvertedLootPredicate.builder(this); + } + + default LootModifierPredicate.Builder or(LootModifierPredicate.Builder otherPredicate) { + return AnyOfLootPredicate.builder(this, otherPredicate); + } + + default LootModifierPredicate.Builder and(LootModifierPredicate.Builder otherPredicate) { + return AllOfLootPredicate.builder(this, otherPredicate); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java new file mode 100644 index 0000000..207c997 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate; + +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.MapCodec; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.LootTableModifier; + +public record LootModifierPredicateType(@NotNull MapCodec codec) { + public static final Registry REGISTRY = new SimpleRegistry<>( + RegistryKey.ofRegistry(LootTableModifier.id("loot_modifier_predicate_types")), Lifecycle.stable() + ); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java new file mode 100644 index 0000000..5742100 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java @@ -0,0 +1,21 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.condition; + +import com.mojang.serialization.Codec; +import net.minecraft.loot.condition.LootCondition; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; + +import java.util.regex.Pattern; + +public record LootConditionPredicate(@NotNull Pattern functionPattern) { + public static final Codec CODEC = Util.PATTERN_CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); + + public boolean matches(final @NotNull LootCondition condition) { + final Identifier functionId = Registries.LOOT_CONDITION_TYPE.getId(condition.getType()); + if (functionId == null) return false; + + return functionPattern.matcher(functionId.toString()).matches(); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java new file mode 100644 index 0000000..2eee534 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java @@ -0,0 +1,55 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.entry; + +import com.mojang.datafixers.Products; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.LootTableModifier; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; + +import java.util.List; +import java.util.Optional; + +public abstract class LootEntryPredicate implements LootModifierPredicate { + protected final @Nullable List conditions; + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // da codecccccc + protected LootEntryPredicate(final @NotNull Optional> conditions) { + this.conditions = conditions.orElse(null); + } + + protected static Products.P1, Optional>> addConditionsToCodec(RecordCodecBuilder.Instance instance) { + return instance.group(LootConditionPredicate.CODEC.listOf().optionalFieldOf("conditions").forGetter(entry -> Optional.ofNullable(entry.conditions))); + } + + + public abstract LootModifierPredicateType getType(); + + /** + * Checks if this predicate matches the provided loot pool entry + * @param entry the entry to check against + * @return true when the provided loot pool entry matches, false otherwise + */ + @Override + public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { + final int conditionCount = conditions == null ? 0 : conditions.size(); + final int entryConditionsCount = entry.conditions == null ? 0 : entry.conditions.size(); + + if (conditionCount == entryConditionsCount) return true; + if (conditionCount == 0) return false; + + for (LootConditionPredicate predicate : this.conditions) { + + } + + return conditions.stream().allMatch(predicate -> entry.conditions.stream().anyMatch(predicate::matches)); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java new file mode 100644 index 0000000..b1982c1 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java @@ -0,0 +1,40 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.entry.leaf; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; + +import java.util.List; +import java.util.Optional; + +public class EmptyEntryPredicate extends LeafEntryPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> addLeafFieldsToCodec(instance).apply(instance, EmptyEntryPredicate::new)); + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // given by da codecc + protected EmptyEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { + super(weight, quality, conditions, functions); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.EMPTY_ENTRY; + } + + @Override + public boolean test(@NotNull LootModifierContext context, @NotNull LootTable table, @NotNull Identifier tableId) { + return super.test(context, table, tableId); + } ItemEntry + + @Override + public byte requiredContext() { + return LootModifierContext.REQUIRES_TABLE; + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java new file mode 100644 index 0000000..b6fda9f --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java @@ -0,0 +1,44 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.entry.leaf; + +import com.mojang.datafixers.Products; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.loot.entry.LootPoolEntry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; + +import java.util.List; +import java.util.Optional; + +public abstract class LeafEntryPredicate extends LootEntryPredicate { + protected final @Nullable Integer weight; + protected final @Nullable Integer quality; + protected final @Nullable List functions; + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // codec giv it + protected LeafEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { + super(conditions); + this.weight = weight.orElse(null); + this.quality = quality.orElse(null); + this.functions = functions.orElse(null); + } + + protected static Products.P4, Optional, Optional, Optional>, Optional>> addLeafFieldsToCodec(RecordCodecBuilder.Instance instance) { + return instance.group( + Codec.INT.optionalFieldOf("weight").forGetter(entry -> Optional.ofNullable(entry.weight)), + Codec.INT.optionalFieldOf("quality").forGetter(entry -> Optional.ofNullable(entry.weight)) + ) + .and(addConditionsToCodec(instance).t1()) + .and(LootFunctionPredicate.CODEC.listOf().optionalFieldOf("functions").forGetter(entry -> Optional.ofNullable(entry.functions))); + + } +ItemEntry + // todo: @Override + // todo: public boolean matches(@NotNull LootPoolEntry entry) { + // todo: return super.matches(entry); + // todo: } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java new file mode 100644 index 0000000..e011083 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java @@ -0,0 +1,22 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.function; + +import com.mojang.serialization.Codec; +import net.minecraft.loot.function.LootFunction; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; + +import java.util.regex.Pattern; + +// TODO: maybe in the future? nou iea +//public record LootFunctionPredicate(@NotNull Pattern functionPattern) { +// public static final Codec CODEC = Util.PATTERN_CODEC.xmap(LootFunctionPredicate::new, LootFunctionPredicate::functionPattern); +// +// public boolean matches(final @NotNull LootFunction function) { +// final Identifier functionId = Registries.LOOT_FUNCTION_TYPE.getId(function.getType()); +// if (functionId == null) return false; +// +// return functionPattern.matcher(functionId.toString()).matches(); +// } +//} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java new file mode 100644 index 0000000..58ba46d --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java @@ -0,0 +1,46 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.op; + +import com.mojang.serialization.MapCodec; +import net.minecraft.util.Util; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; + +import java.util.List; + +public class AllOfLootPredicate extends TermsLootPredicate { + public static final MapCodec CODEC = createCodec(AllOfLootPredicate::new); + + private AllOfLootPredicate(final List terms) { + super(terms, Util.allOf(terms)); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.ALL_OF; + } + + public static AllOfLootPredicate.Builder builder(LootModifierPredicate.Builder... terms) { + return new AllOfLootPredicate.Builder(terms); + } + + public static class Builder extends TermsLootPredicate.Builder { + public Builder(LootModifierPredicate.Builder... builders) { + super(builders); + } + + @Override + public LootModifierPredicate.Builder and(LootModifierPredicate.Builder builder) { + this.add(builder); + return this; + } + + @Override + protected AllOfLootPredicate build(List terms) { + return new AllOfLootPredicate(terms); + } + } +} + + + diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java new file mode 100644 index 0000000..2249e18 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java @@ -0,0 +1,47 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.op; + +import com.mojang.serialization.MapCodec; +import net.minecraft.loot.condition.AnyOfLootCondition; +import net.minecraft.util.Util; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; + +import java.util.List; + +public class AnyOfLootPredicate extends TermsLootPredicate { + public static final MapCodec CODEC = createCodec(AnyOfLootPredicate::new); + + private AnyOfLootPredicate(final List terms) { + super(terms, Util.anyOf(terms)); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.ANY_OF; + } + + public static AnyOfLootPredicate.Builder builder(LootModifierPredicate.Builder... terms) { + return new AnyOfLootPredicate.Builder(terms); + } + + public static class Builder extends TermsLootPredicate.Builder { + public Builder(LootModifierPredicate.Builder... builders) { + super(builders); + } + + @Override + public LootModifierPredicate.Builder or(LootModifierPredicate.Builder builder) { + this.add(builder); + return this; + } + + @Override + protected AnyOfLootPredicate build(List terms) { + return new AnyOfLootPredicate(terms); + } + } +} + + + diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java new file mode 100644 index 0000000..de3f9ef --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java @@ -0,0 +1,29 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.op; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootTable; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; + +public record InvertedLootPredicate(LootModifierPredicate term) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(LootModifierPredicate.CODEC.fieldOf("term").forGetter(InvertedLootPredicate::term)).apply(instance, InvertedLootPredicate::new) + ); + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.INVERTED; + } + + @Override + public boolean test(@NotNull PredicateContext context) { + return !term.test(context); + } + + public static LootModifierPredicate.Builder builder(LootModifierPredicate.Builder term) { + return () -> new InvertedLootPredicate(term.build()); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java new file mode 100644 index 0000000..a6067ac --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java @@ -0,0 +1,72 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.op; + +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.loader.impl.lib.sat4j.minisat.constraints.cnf.Lits; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.condition.AlternativeLootCondition; +import net.minecraft.loot.condition.LootCondition; +import net.minecraft.loot.context.LootContext; +import net.minecraft.util.Util; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +public abstract class TermsLootPredicate implements LootModifierPredicate { + protected final List terms; + private final Predicate builtPredicate; + + protected TermsLootPredicate(final List terms, final Predicate builtPredicate) { + this.terms = terms; + this.builtPredicate = builtPredicate; + } + + protected static MapCodec createCodec(final Function, T> constructor) { + return RecordCodecBuilder.mapCodec( + instance -> instance.group(Codec.either(LootModifierPredicate.CODEC, LootModifierPredicate.CODEC.listOf()).fieldOf("terms").forGetter(TermsLootPredicate::eitherTerms)).apply(instance, eitherToFunction(constructor)) + ); + } + + private static Function>, T> eitherToFunction(final Function, T> constructor) { + return eitherTerms -> constructor.apply(eitherTerms.right().orElseGet(() -> List.of(eitherTerms.left().orElseThrow()))); + } + + private Either> eitherTerms() { + if (terms.size() == 1) return Either.left(terms.get(0)); + return Either.right(terms); + } + + @Override + public boolean test(@NotNull PredicateContext context) { + return builtPredicate.test(context.table()); + } + + public abstract static class Builder implements LootModifierPredicate.Builder { + private final ImmutableList.Builder terms = ImmutableList.builder(); + + protected Builder(LootModifierPredicate.Builder... terms) { + for (LootModifierPredicate.Builder builder : terms) { + this.terms.add(builder.build()); + } + } + + public void add(LootModifierPredicate.Builder builder) { + this.terms.add(builder.build()); + } + + @Override + public LootModifierPredicate build() { + return this.build(this.terms.build()); + } + + protected abstract TermsLootPredicate build(List terms); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java new file mode 100644 index 0000000..00e7ba7 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java @@ -0,0 +1,136 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.pool; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.condition.AllOfLootCondition; +import net.minecraft.loot.condition.AnyOfLootCondition; +import net.minecraft.loot.condition.InvertedLootCondition; +import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; + +import java.util.List; +import java.util.Optional; + +public record LootPoolPredicate(@Nullable Integer rolls, @Nullable Integer bonusRolls, @Nullable List entries, @Nullable List functions, @Nullable List conditions) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + // TODO: smt complex that allows like matching integer expressions? Yknow like >=2 // TODO: ok actually Minecraft uses some number provider stuff that I need to match. Not always just a single int + Codec.INT.optionalFieldOf("rolls").forGetter(LootPoolPredicate::optionalRolls), + Codec.INT.optionalFieldOf("bonusRolls").forGetter(LootPoolPredicate::optionalBonusRolls), + Codec.either(LootEntryPredicate.CODEC, LootEntryPredicate.CODEC.listOf()).optionalFieldOf("entries").forGetter(LootPoolPredicate::optionalEntries), + Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootPoolPredicate::optionalFunctions), + Codec.either(LootConditionPredicate.CODEC, LootConditionPredicate.CODEC.listOf()).optionalFieldOf("conditions").forGetter(LootPoolPredicate::optionalConditions) + ).apply(instance, LootPoolPredicate::new)); + + private Optional optionalRolls() { + return Optional.ofNullable(rolls); + } + + private Optional optionalBonusRolls() { + return Optional.ofNullable(bonusRolls); + } + + private Optional>> optionalEntries() { + if (entries == null) return Optional.empty(); + if (entries.size() == 1) return Optional.of(Either.left(entries.get(0))); + return Optional.of(Either.right(entries)); + } + + private Optional>> optionalFunctions() { + if (functions == null) return Optional.empty(); + if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); + return Optional.of(Either.right(functions)); + } + + private Optional>> optionalConditions() { + if (conditions == null) return Optional.empty(); + if (conditions.size() == 1) return Optional.of(Either.left(conditions.get(0))); + return Optional.of(Either.right(conditions)); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // 'cause of codecccc + private LootPoolPredicate( + @NotNull Optional optionalRolls, + @NotNull Optional optionalBonusRolls, + @NotNull Optional>> optionalEntries, + @NotNull Optional>> optionalFunctions, + @NotNull Optional>> optionalConditions + ) { + this( + optionalRolls.orElse(null), + optionalBonusRolls.orElse(null), + optionalEntries.map(entriesEither -> entriesEither.map(List::of, entries -> entries)).orElse(null), + optionalFunctions.map(functionsEither -> functionsEither.map(List::of, functions -> functions)).orElse(null), + optionalConditions.map(conditionsEither -> conditionsEither.map(List::of, conditions -> conditions)).orElse(null) + ); + } + + + //public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { + @Override + public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { + boolean result = true; + + // todo: if (rolls != null) { + // todo: result = rolls == pool.rolls.; + // todo: } + + // todo: if (bonusRolls != null) { + // todo: result = result && bonusRolls == pool.rolls.; + // todo: } + + if (entries != null) { + for (LootEntryPredicate entryPredicate : entries) { + result = result && entryPredicate.test(context); + } + result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); + } + + if (functions != null) { + for (LootFunctionPredicate functionPredicate : functions) { + result = result && table.functions.stream().anyMatch(functionPredicate::matches); + } + } + + return result; + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.LOOT_POOL; + } + + @Override + public byte requiredContext() { + return LootModifierContext.REQUIRES_ENTRY; + } + + //@FunctionalInterface + //public interface Builder { + // LootPoolPredicate build(); + + // default LootPoolPredicate.Builder invert() { + // return InvertedLootCondition.builder(this); + // } + + // default AnyOfLootCondition.Builder or(LootPoolPredicate.Builder otherPredicate) { + // return AnyOfLootCondition.builder(this, otherPredicate); + // } + + // default AllOfLootCondition.Builder and(LootPoolPredicate.Builder otherPredicate) { + // return AllOfLootCondition.builder(this, otherPredicate); + // } + //} +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java similarity index 85% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java index 8b98b99..854b6bf 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java @@ -1,19 +1,24 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate; +package top.offsetmonkey538.loottablemodifier.resource.predicate.table; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import net.minecraft.loot.context.LootContextTypes; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; +import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; -public record LootTablePredicate(@Nullable Pattern identifier, @Nullable Pattern type, @Nullable List functions) { +// TODO: add entries predicate too +public record LootTablePredicate(@Nullable Pattern identifier, @Nullable Pattern type, @Nullable List functions /* TODO: no need to use list cause anyOf and allOf are a thing */) implements LootModifierPredicate { private static final Codec INLINE_CODEC = Util.PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( Util.PATTERN_CODEC.optionalFieldOf("id").forGetter(LootTablePredicate::optionalIdentifier), @@ -49,8 +54,9 @@ private Optional>> opt return Optional.of(Either.right(functions)); } - - public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { + // TODO: also loops over pools and checks that + @Override + public boolean test(final @NotNull LootTable table, final @NotNull Identifier tableId) { boolean result = true; if (identifier != null) { diff --git a/src/main/resources/loot-table-modifier.accesswidener b/src/main/resources/loot-table-modifier.accesswidener index 1eee9e0..7a25a76 100644 --- a/src/main/resources/loot-table-modifier.accesswidener +++ b/src/main/resources/loot-table-modifier.accesswidener @@ -4,3 +4,5 @@ accessible field net/minecraft/loot/LootTable pools Ljava/util/List; accessible field net/minecraft/loot/LootTable functions Ljava/util/List; accessible field net/minecraft/loot/context/LootContextTypes MAP Lcom/google/common/collect/BiMap; + +accessible field net/minecraft/loot/entry/LootPoolEntry conditions Ljava/util/List; From f9b98e628f97e90b92be4453c7da317d239f9e54 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 12 Jun 2025 19:04:11 +0300 Subject: [PATCH 10/53] Add SetItemAction and ItemEntryPredicate --- gradle.properties | 2 +- .../loottablemodifier/LootTableModifier.java | 20 +- .../api/LootModifierActionTypes.java | 3 + .../api/LootModifierPredicateTypes.java | 6 +- .../api/datagen/NewLootModifierProvider.java | 14 +- .../datagen/NewNewLootModifierProvider.java | 145 ++++++++++ .../datagen/LootTableModifierDatagen.java | 91 +++--- .../mixin/ItemEntryAccessor.java | 16 ++ .../mixin/LootPoolAccessor.java | 18 ++ .../resource/LootModifier.java | 163 ++++++----- .../resource/LootModifierContext.java | 11 +- .../resource/action/AddPoolAction.java | 7 +- .../resource/action/RemovePoolAction.java | 82 +++--- .../resource/action/SetItemAction.java | 85 ++++++ .../predicate/LootModifierPredicate.java | 61 ++-- .../predicate/entry/ItemEntryPredicate.java | 36 +++ .../predicate/entry/LootEntryPredicate.java | 106 +++---- .../entry/leaf/EmptyEntryPredicate.java | 77 ++--- .../entry/leaf/LeafEntryPredicate.java | 85 +++--- .../predicate/op/InvertedLootPredicate.java | 3 +- .../predicate/op/TermsLootPredicate.java | 12 +- .../predicate/pool/LootPoolPredicate.java | 269 +++++++++--------- .../predicate/table/LootTablePredicate.java | 153 +++++----- .../loot-table-modifier.accesswidener | 2 + .../resources/loot-table-modifier.mixins.json | 2 + 25 files changed, 905 insertions(+), 564 deletions(-) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java diff --git a/gradle.properties b/gradle.properties index 468d4bf..3f9dbcb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version = 1.21.5 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.125.3+1.21.5 +fapi_version = 0.126.0+1.21.5 yarn_version = 1.21.5+build.1 loader_version = 0.16.14 diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index a820300..f17b1f5 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -23,12 +23,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicateTypes; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; @@ -39,12 +36,20 @@ public class LootTableModifier implements ModInitializer { public static final String MOD_ID = "loot-table-modifier"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + public static final boolean IS_DEV; + static { + final String isDev = System.getProperty("lootTableModifierDev", ""); + if (isDev.equalsIgnoreCase("true")) IS_DEV = true; + else if (isDev.equalsIgnoreCase("false")) IS_DEV = false; // This way it can be disabled in devenv too. + else IS_DEV = FabricLoader.getInstance().isDevelopmentEnvironment(); + } + @Override public void onInitialize() { LootModifierActionTypes.register(); - LootEntryPredicateTypes.register(); + LootModifierPredicateTypes.register(); - if (FabricLoader.getInstance().isDevelopmentEnvironment()) + if (IS_DEV) ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); } @@ -76,7 +81,8 @@ public static void runModification(ResourceManager resourceManager, Registry modifierEntry : modifiers.entrySet()) { final LootModifier modifier = modifierEntry.getValue(); if (!modifier.testModifies(context)) continue; - modifier.apply(context); + if (IS_DEV) LOGGER.error("Modifier {} can modify table {}", modifierEntry.getKey(), key.getValue()); + modifier.apply(context); // FIXME: stuff modifying table or pool may apply multiple times } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java index cca0964..b1249ce 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java @@ -7,6 +7,7 @@ import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.resource.action.SetItemAction; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -17,6 +18,8 @@ private LootModifierActionTypes() { public static final LootModifierActionType ADD_ENTRY = register(id("add_pool"), AddPoolAction.CODEC); + public static final LootModifierActionType SET_ITEM = register(id("set_item"), SetItemAction.CODEC); + private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java index 2f9b0df..57ed4af 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java @@ -12,10 +12,10 @@ import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.InvertedLootPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -28,7 +28,9 @@ private LootModifierPredicateTypes() { public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfLootPredicate.CODEC); public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfLootPredicate.CODEC); - public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); + public static final LootModifierPredicateType ITEM_ENTRY = register(id("item_entry"), ItemEntryPredicate.CODEC); + + //public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); private static LootModifierPredicateType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierPredicateType.REGISTRY, id, new LootModifierPredicateType(codec)); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java index 192f273..3d636ea 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java @@ -11,7 +11,6 @@ import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; @@ -173,12 +172,13 @@ protected void addModifier(Identifier name, List bui } private void addModifier(Identifier name, List builders, List modifies) { - provider.accept(name, new LootModifier( - modifies.stream().map(identifier -> new LootTablePredicate(Pattern.compile(identifier.toString()))).toList(), - builders.stream() - .map(LootModifierAction.Builder::build) - .toList() - )); + //provider.accept(name, new LootModifier( + // builders.stream() + // .map(LootModifierAction.Builder::build) + // .toList(), + // null + // //FIXME: modifies.stream().map(identifier -> new LootTablePredicate(Pattern.compile(identifier.toString()))).toList() + //)); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java new file mode 100644 index 0000000..e5cbe21 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java @@ -0,0 +1,145 @@ +package top.offsetmonkey538.loottablemodifier.api.datagen; + +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.data.DataOutput; +import net.minecraft.entity.EntityType; +import net.minecraft.loot.LootTable; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Stream; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; + +/** + * FIXME: wrong + * A datagen provider for creating loot modifiers. + *
+ * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@code addModifier(...)} methods to add modifiers. + *
+ *
{@code
+ * @Override
+ * protected void generate(RegistryWrapper.WrapperLookup lookup) {
+ *     addModifier(
+ *             Identifier.of("testmod", "drop_tnt"),
+ *             LootPool.builder()
+ *                     .rolls(ConstantLootNumberProvider.create(1))
+ *                     .with(
+ *                             ItemEntry.builder(Items.TNT)
+ *                                     .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
+ *                     ),
+ *             EntityType.CREEPER,
+ *             EntityType.ZOMBIE
+ *     );
+ * }
+ * }
+ */ +public abstract class NewNewLootModifierProvider extends FabricCodecDataProvider { + private BiConsumer provider; + + public NewNewLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", LootModifier.CODEC); + } + + @Override + protected void configure(BiConsumer provider, RegistryWrapper.WrapperLookup lookup) { + this.provider = provider; + generate(lookup); + } + + @Override + public String getName() { + return "New New Loot Table Modifiers"; // todo: change + } + + /** + * Override and use {@code addModifier()} methods to add modifiers. + * + * @param lookup A lookup for registries. + */ + protected abstract void generate(RegistryWrapper.WrapperLookup lookup); + + protected void addModifier(@NotNull Identifier name, @NotNull LootModifier modifier) { + provider.accept(name, modifier); + } + + private static class EntityLootTableIdGetter { + // Resolver returns the provided name (like 'method_16351') when it fails to map it + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); + + // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); + + public static final Function, Identifier> get; + + // Should be executed when class is first loaded/accessed + static { + try { + //final Class entityType = EntityType.class; + final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); + final Method method; + + // 1.21.2 to future:tm: + if (isMethod(entityType, V1d21d2)) { + method = entityType.getDeclaredMethod(V1d21d2); + method.setAccessible(true); + get = entity -> { + try { + @SuppressWarnings("unchecked") + final Optional> optional = (Optional>) method.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if (isMethod(entityType, V1d20d5)) { + method = entityType.getDeclaredMethod(V1d20d5); + method.setAccessible(true); + get = entity -> { + try { + return ((RegistryKey) method.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } catch (NoSuchMethodException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static boolean isMethod(Class clazz, String method) { + try { + clazz.getDeclaredMethod(method); + return true; + } catch (NoSuchMethodException e) { + LOGGER.warn("", e); + return false; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 7a09e41..2e6cdec 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -14,9 +14,14 @@ import net.minecraft.loot.provider.number.UniformLootNumberProvider; import net.minecraft.registry.RegistryWrapper; import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; +import top.offsetmonkey538.loottablemodifier.api.datagen.NewNewLootModifierProvider; +import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.resource.action.SetItemAction; +import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; import java.util.concurrent.CompletableFuture; +import java.util.regex.Pattern; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -29,46 +34,66 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { pack.addProvider(NewModLootModifierProvider::new); } - private static class NewModLootModifierProvider extends NewLootModifierProvider { - public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { - super(dataOutput, registriesFuture); - } + //private static class NewModLootModifierProvider extends NewLootModifierProvider { + // public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + // super(dataOutput, registriesFuture); + // } - @Override - protected void generate(RegistryWrapper.WrapperLookup lookup) { - addModifier( - id("drop_tnt"), + // @Override + // protected void generate(RegistryWrapper.WrapperLookup lookup) { + // addModifier( + // id("drop_tnt"), - AddPoolAction.builder( - LootPool.builder() - .rolls(ConstantLootNumberProvider.create(1)) - .with( - ItemEntry.builder(Items.TNT) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ) - ), + // AddPoolAction.builder( + // LootPool.builder() + // .rolls(ConstantLootNumberProvider.create(1)) + // .with( + // ItemEntry.builder(Items.TNT) + // .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + // ) + // ), - EntityType.CREEPER, - EntityType.ZOMBIE - ); + // EntityType.CREEPER, + // EntityType.ZOMBIE + // ); - //todo: temp - addModifier( - id("test"), + // //todo: temp + // addModifier( + // id("test"), - AddPoolAction.builder( - LootPool.builder() - .with( - ItemEntry.builder(Items.NETHERITE_SWORD).apply( - EnchantWithLevelsLootFunction - .builder(lookup, UniformLootNumberProvider.create(20, 39)) - //.builder(UniformLootNumberProvider.create(20, 39)) - ) - ) - ), + // AddPoolAction.builder( + // LootPool.builder() + // .with( + // ItemEntry.builder(Items.NETHERITE_SWORD).apply( + // EnchantWithLevelsLootFunction + // .builder(lookup, UniformLootNumberProvider.create(20, 39)) + // //.builder(UniformLootNumberProvider.create(20, 39)) + // ) + // ) + // ), - LootTables.ABANDONED_MINESHAFT_CHEST + // LootTables.ABANDONED_MINESHAFT_CHEST + // ); + // } + //} + private static class NewModLootModifierProvider extends NewNewLootModifierProvider { + public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + super(dataOutput, registriesFuture); + } + + @Override + protected void generate(RegistryWrapper.WrapperLookup lookup) { + addModifier( + id("replace_ingots_with_command_block"), + LootModifier.builder() + .conditionally( + ItemEntryPredicate.builder(Pattern.compile("minecraft:.*_ingot")) + ) + .action( + SetItemAction.builder(Items.COMMAND_BLOCK) + ) + .build() ); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java new file mode 100644 index 0000000..642b041 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java @@ -0,0 +1,16 @@ +package top.offsetmonkey538.loottablemodifier.mixin; + +import net.minecraft.item.Item; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.registry.entry.RegistryEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ItemEntry.class) +public interface ItemEntryAccessor { + + @Mutable + @Accessor + void setItem(RegistryEntry item); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java new file mode 100644 index 0000000..1da07bf --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java @@ -0,0 +1,18 @@ +package top.offsetmonkey538.loottablemodifier.mixin; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.LootPoolEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(LootPool.class) +public interface LootPoolAccessor { + + @Mutable + @Accessor + void setEntries(List entries); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index ba4d1d2..4663c89 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -1,6 +1,8 @@ package top.offsetmonkey538.loottablemodifier.resource; +import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.fabricmc.loader.api.FabricLoader; @@ -15,61 +17,80 @@ import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; import java.lang.reflect.InvocationTargetException; import java.util.*; -// Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied -public record LootModifier(@NotNull ArrayList modifies, @NotNull @UnmodifiableView List actions) { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either(LootModifierPredicate.CODEC, LootModifierPredicate.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), - Codec.either(LootModifierAction.CODEC, LootModifierAction.CODEC.listOf()).optionalFieldOf("actions").forGetter(LootModifier::actionsOptionalEither), +// Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied TODO: I don't think I do this anymore? TODO: make sure that changing it to a normal List is fine +public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) { +//public record LootModifier(@NotNull @UnmodifiableView ArrayList actions) { + private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( + // TODO: this should use the table predicate, once that exists + Codec.either(LootModifierPredicate.CODEC, LootModifierPredicate.CODEC.listOf()).fieldOf("modifies").forGetter(modifier -> { + if (modifier.predicates.size() == 1) return Either.left(modifier.predicates.get(0)); + return Either.right(modifier.predicates); + }), LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) - ).apply(instance, LootModifier::new)); + ).apply(instance, LootModifier::fromLegacyCodec)); + + private static final Codec CURRENT_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.either( + LootModifierAction.CODEC, + LootModifierAction.CODEC.listOf() + ).fieldOf("actions").forGetter(modifier -> { + if (modifier.actions.size() == 1) return Either.left(modifier.actions.get(0)); + return Either.right(modifier.actions); + }), + Codec.either( + LootModifierPredicate.CODEC, + LootModifierPredicate.CODEC.listOf() + ).fieldOf("predicates").forGetter(modifier -> { + if (modifier.predicates.size() == 1) return Either.left(modifier.predicates.get(0)); + return Either.right(modifier.predicates); + }) + ).apply(instance, LootModifier::fromCurrentCodec)); + + public static final Codec CODEC = Codec.either( + LEGACY_CODEC, + CURRENT_CODEC + ).xmap(either -> either.map(it -> it, it -> it), Either::right); // Always encode as current codec, which is on the right. @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah - private LootModifier(@NotNull Either> modifiesEither, @NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { - this( - modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow())), - getActions(actions, pools, lootPools) - ); - } - public LootModifier(@NotNull List modifies, @NotNull List actions) { - this( - new ArrayList<>(modifies), - Collections.unmodifiableList(actions) - ); - } - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah - private static List getActions(@NotNull Optional>> actions, @NotNull Optional> pools, @NotNull Optional> lootPools) { - List result = null; + private static LootModifier fromLegacyCodec(@NotNull Either> modifiesEither, @NotNull Optional> pools, @NotNull Optional> lootPools) { + List actions = null; - if (actions.isPresent()) result = actions.get().right().orElseGet(() -> actions.get().left().isEmpty() ? null : List.of(actions.get().left().orElseThrow())); - - if (result != null && pools.isPresent()) throw new IllegalStateException("Both \"actions\" and \"pools\" present in loot modifier!"); - if (result != null && lootPools.isPresent()) throw new IllegalStateException("Both \"actions\" and \"loot_pools\" present in loot modifier!"); if (pools.isPresent() && lootPools.isPresent()) throw new IllegalStateException("Both \"pools\" and \"loot_pools\" present in loot modifier!"); - if (result == null && pools.isPresent()) result = List.of(new AddPoolAction(pools.get())); - if (result == null && lootPools.isPresent()) result = List.of(new AddPoolAction(lootPools.get())); + if (pools.isPresent()) actions = List.of(new AddPoolAction(pools.get())); + if (lootPools.isPresent()) actions = List.of(new AddPoolAction(lootPools.get())); - if (result == null) throw new IllegalStateException("Neither \"actions\" nor \"pools\" present in loot modifier!"); + if (actions == null) throw new IllegalStateException("Neither \"pools\" nor \"loot_pools\" present in loot modifier!"); - return result; - } - private Either> modifiesEither() { - if (modifies.size() == 1) return Either.left(modifies.get(0)); - return Either.right(modifies); + return new LootModifier( + actions, + new ArrayList<>(modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow()))) + ); } - private Optional>> actionsOptionalEither() { - if (actions.size() == 1) return Optional.of(Either.left(actions.get(0))); - return Optional.of(Either.right(actions)); + private static LootModifier fromCurrentCodec(Either> actionsEither, Either> predicatesEither) { + return new LootModifier( + actionsEither.map(List::of, it -> it), + new ArrayList<>(predicatesEither.map(List::of, it -> it)) + ); } + //private static Pair>, Either>> toCurrentCodec(LootModifier modifier) { + // final Either> actionsEither; + // final Either> predicatesEither; + + // if (modifier.actions.size() == 1) actionsEither = Either.left(modifier.actions.get(0)); + // else actionsEither = Either.right(modifier.actions); + // if (modifier.predicates.size() == 1) predicatesEither = Either.left(modifier.predicates.get(0)); + // else predicatesEither = Either.right(modifier.predicates); + + // return Pair.of(actionsEither, predicatesEither); + //} ///** // * @param tableRegistry registry of loot tables to modify @@ -107,54 +128,32 @@ public boolean apply(final @NotNull LootModifierContext context) { } public boolean testModifies(final @NotNull LootModifierContext context) { - for (LootModifierPredicate modifiesPredicate : modifies) { - if (!modifiesPredicate.test(context)) return false; + for (LootModifierPredicate predicate : predicates) { + if (!predicate.test(context)) return false; } return true; } - /* - In 1.21.4, the 'Registry' class extends 'RegistryWrapper' and inherits the 'streamEntries' method from *it*. - In 1.20.5, the 'Registry' class *doesn't* extend the 'RegistryWrapper' and implements its own 'streamEntries' method. - Compiling on both versions works, because the names of the methods are the same, but they compile to different intermediary names, thus a jar compiled for 1.20.5 doesn't work on 1.21.4 and vice versa. - Solution: Turn the 'Registry' into a 'RegistryWrapper' as its 'streamEntries' retains the same intermediary on both versions. - If 'Registry' implements 'RegistryWrapper': cast it - Else: call 'getReadOnlyWrapper' on the registry (doesn't exist on 1.21.4, otherwise would've used 'registry.getReadOnlyWrapper().streamEntries()') - */ - private static RegistryWrapper getRegistryAsWrapper(@NotNull Registry registry) { - //noinspection ConstantValue,RedundantSuppression: On lower versions, Registry doesn't extend RegistryWrapper and thus the 'isAssignableFrom' check can be false. The redundant supression is for the unchecked cast below. - if (RegistryWrapper.class.isAssignableFrom(registry.getClass())) - //noinspection unchecked,RedundantCast: I swear it casts 🤞 - return (RegistryWrapper) registry; - - try { - //noinspection unchecked: Seriously I swear 🤞🤞 - return (RegistryWrapper) registry.getClass().getDeclaredMethod(FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_2378", "method_46771", "()Lnet/minecraft/class_7225$class_7226;")).invoke(registry); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } + public static LootModifier.Builder builder() { + return new LootModifier.Builder(); } - // Don't think this is needed? public static NewLootModifier.Builder builder() { - // Don't think this is needed? return new NewLootModifier.Builder(); - // Don't think this is needed? } - - // Don't think this is needed? public static class Builder { - // Don't think this is needed? private final ImmutableList.Builder modifies = ImmutableList.builder(); - // Don't think this is needed? private final ImmutableList.Builder actions = ImmutableList.builder(); - // Don't think this is needed? - // Don't think this is needed? public NewLootModifier.Builder modifies(@NotNull Identifier... modifies) { - // Don't think this is needed? this.modifies.add(modifies); - // Don't think this is needed? return this; - // Don't think this is needed? } - // Don't think this is needed? - // Don't think this is needed? public NewLootModifier.Builder conditionally(@NotNull LootModifierAction.Builder action) { - // Don't think this is needed? this.actions.add(action.build()); - // Don't think this is needed? return this; - // Don't think this is needed? } - // Don't think this is needed? - // Don't think this is needed? public NewLootModifier build() { - // Don't think this is needed? return new NewLootModifier(this.modifies.build(), this.actions.build()); - // Don't think this is needed? } - // Don't think this is needed? } + public static class Builder { + private final ImmutableList.Builder actions = ImmutableList.builder(); + private final ImmutableList.Builder predicates = ImmutableList.builder(); + + public LootModifier.Builder action(@NotNull LootModifierAction.Builder action) { + this.actions.add(action.build()); + return this; + } + + public LootModifier.Builder conditionally(@NotNull LootModifierPredicate.Builder predicate) { + this.predicates.add(predicate.build()); + return this; + } + + public LootModifier build() { + return new LootModifier(this.actions.build(), this.predicates.build()); + } + } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java index 63ec55b..69704ca 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java @@ -7,15 +7,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public record LootModifierContext(@NotNull LootTable table, @NotNull Identifier tableId, @Nullable LootPool pool, @Nullable LootPoolEntry entry) { - public static final byte REQUIRES_TABLE = 2; - public static final byte REQUIRES_POOL = 1; - public static final byte REQUIRES_ENTRY = 0; +public record LootModifierContext(@NotNull LootTable table, @NotNull Identifier tableId, @NotNull LootPool pool, @NotNull LootPoolEntry entry) { - public LootModifierContext(@NotNull final LootTable table, @NotNull final Identifier tableId, @NotNull final LootPool pool) { - this(table, tableId, pool, null); - } - public LootModifierContext(@NotNull final LootTable table, @NotNull final Identifier tableId) { - this(table, tableId, null, null); - } } \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java index 004b24e..6c39c2c 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import java.util.List; @@ -22,13 +23,13 @@ public LootModifierActionType getType() { } @Override - public boolean apply(@NotNull LootTable table) { + public boolean apply(@NotNull LootModifierContext context) { final List newPools = ImmutableList.builder() - .addAll(table.pools) + .addAll(context.table().pools) .addAll(this.pools) .build(); - ((LootTableAccessor) table).setPools(newPools); + ((LootTableAccessor) context.table()).setPools(newPools); return true; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java index 66291d5..b7715ea 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java @@ -1,43 +1,43 @@ package top.offsetmonkey538.loottablemodifier.resource.action; -import com.google.common.collect.ImmutableList; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; -import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; - -import java.util.ArrayList; -import java.util.List; - -public record RemovePoolAction(LootPoolPredicate poolPredicate) implements LootModifierAction { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - LootPoolPredicate.CODEC.fieldOf("poolPredicate").forGetter(RemovePoolAction::poolPredicate) - ).apply(instance, RemovePoolAction::new)); - - @Override - public LootModifierActionType getType() { - return LootModifierActionTypes.ADD_ENTRY; - } - - @Override - public boolean apply(@NotNull final LootModifierContext context) { - if (!poolPredicate.test(context)) return false; - - List newPools = new ArrayList<>(context.table().pools); - newPools.remove(context.pool()); - newPools = ImmutableList.copyOf(newPools); - - ((LootTableAccessor) context.table()).setPools(newPools); - - return true; - } - - public static LootModifierAction.Builder builder(@NotNull LootPoolPredicate.Builder poolBuilder) { - return () -> new RemovePoolAction(poolBuilder.build()); - } -} +//import com.google.common.collect.ImmutableList; +//import com.mojang.serialization.MapCodec; +//import com.mojang.serialization.codecs.RecordCodecBuilder; +//import net.minecraft.loot.LootPool; +//import net.minecraft.loot.LootTable; +//import org.jetbrains.annotations.NotNull; +//import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +//import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; +// +//import java.util.ArrayList; +//import java.util.List; + +//public record RemovePoolAction(LootPoolPredicate poolPredicate) implements LootModifierAction { +// public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( +// LootPoolPredicate.CODEC.fieldOf("poolPredicate").forGetter(RemovePoolAction::poolPredicate) +// ).apply(instance, RemovePoolAction::new)); +// +// @Override +// public LootModifierActionType getType() { +// return LootModifierActionTypes.ADD_ENTRY; +// } +// +// @Override +// public boolean apply(@NotNull final LootModifierContext context) { +// if (!poolPredicate.test(context)) return false; +// +// List newPools = new ArrayList<>(context.table().pools); +// newPools.remove(context.pool()); +// newPools = ImmutableList.copyOf(newPools); +// +// ((LootTableAccessor) context.table()).setPools(newPools); +// +// return true; +// } +// +// public static LootModifierAction.Builder builder(@NotNull LootPoolPredicate.Builder poolBuilder) { +// return () -> new RemovePoolAction(poolBuilder.build()); +// } +//} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java new file mode 100644 index 0000000..3c8dfeb --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java @@ -0,0 +1,85 @@ +package top.offsetmonkey538.loottablemodifier.resource.action; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.Item; +import net.minecraft.item.ItemConvertible; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.loot.entry.LootPoolEntryTypes; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.ItemEntryAccessor; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; + +import java.util.List; + +public record SetItemAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Item.ENTRY_CODEC.fieldOf("name").forGetter(SetItemAction::item), + Codec.BOOL.optionalFieldOf("canReplaceEntry", false).forGetter(SetItemAction::canReplaceEntry) + ).apply(instance, SetItemAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.SET_ITEM; + } + + @Override + public boolean apply(@NotNull LootModifierContext context) { + final LootPoolEntry entry = context.entry(); + if (entry == null) return false; + + if (entry instanceof ItemEntry itemEntry) { + ((ItemEntryAccessor) itemEntry).setItem(item); + return true; + } + // Matched entry is not an ItemEntry, check if entry replacing is on + if (!canReplaceEntry) return false; + + final LootPool pool = context.pool(); + if (pool == null) return false; + + final ImmutableList.Builder newEntriesBuilder = ImmutableList.builder(); + + for (LootPoolEntry originalEntry : pool.entries) { + if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? + newEntriesBuilder.add(originalEntry); + } + ((LootPoolAccessor) context.pool()).setEntries(newEntriesBuilder.build()); + + return true; + } + + public static SetItemAction.Builder builder(@NotNull ItemConvertible item) { + return new SetItemAction.Builder(item); + } + + public static class Builder implements LootModifierAction.Builder { + private final RegistryEntry item; + private boolean canReplaceEntry; + + private Builder(@NotNull ItemConvertible item) { + //noinspection deprecation Minecraft seems to use it still? + this.item = item.asItem().getRegistryEntry(); + } + + public SetItemAction.Builder setCanReplaceEntry(boolean canReplaceEntry) { + this.canReplaceEntry = canReplaceEntry; + return this; + } + + @Override + public SetItemAction build() { + return new SetItemAction(item, canReplaceEntry); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java index a279df5..8b94de2 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java @@ -26,39 +26,40 @@ public interface LootModifierPredicate extends Predicate { LootModifierPredicateType getType(); - byte requiredContext(); + //byte requiredContext(); - /** - * Tests if this predicate matches the provided context - * @param context the context to test against - * @return true when this predicate matches the provided context, false when not - */ + ///** + // * Tests if this predicate matches the provided context + // * @param context the context to test against + // * @return true when this predicate matches the provided context, false when not + // */ - default boolean test(final @NotNull LootModifierContext context) { - return switch (requiredContext()) { - case REQUIRES_TABLE -> test(context, context.table(), context.tableId()); - case REQUIRES_POOL -> { - if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); - yield test(context, context.table(), context.tableId(), context.pool()); - } - case REQUIRES_ENTRY -> { - if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); - if (context.entry() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have an entry set!"); - yield test(context, context.table(), context.tableId(), context.pool(), context.entry()); - } - default -> throw new UnsupportedOperationException("Loot modifier predicate defines unsupported requiredContext value '%s', only values 0 - 2 are supported.".formatted(requiredContext())); - }; - } + //default boolean test(final @NotNull LootModifierContext context) { + // return switch (requiredContext()) { + // case REQUIRES_TABLE -> test(context, context.table(), context.tableId()); + // case REQUIRES_POOL -> { + // if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); + // yield test(context, context.table(), context.tableId(), context.pool()); + // } + // case REQUIRES_ENTRY -> { + // if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); + // if (context.entry() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have an entry set!"); + // yield test(context, context.table(), context.tableId(), context.pool(), context.entry()); + // } + // default -> throw new UnsupportedOperationException("Loot modifier predicate defines unsupported requiredContext value '%s', only values 0 - 2 are supported.".formatted(requiredContext())); + // }; + //} - default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId) { - throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, but says it requires a table"); - } - default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { - throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table and pool, but says it requires a pool"); - } - default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { - throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, pool and entry, but says it requires an entry"); - } + //default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId) { + // throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, but says it requires a table"); + //} + //default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { + // throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table and pool, but says it requires a pool"); + //} + //default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { + // throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, pool and entry, but says it requires an entry"); + //} + boolean test(final @NotNull LootModifierContext context); @FunctionalInterface interface Builder { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java new file mode 100644 index 0000000..8716c8d --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java @@ -0,0 +1,36 @@ +package top.offsetmonkey538.loottablemodifier.resource.predicate.entry; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.loot.entry.ItemEntry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; + +import java.util.regex.Pattern; + +public record ItemEntryPredicate(Pattern name) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(Util.PATTERN_CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) + ); + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.ITEM_ENTRY; + } + + @Override + public boolean test(@NotNull LootModifierContext context) { + if (!(context.entry() instanceof ItemEntry itemEntry)) return false; + + return name.matcher(itemEntry.item.getIdAsString()).matches(); + } + + public static LootModifierPredicate.Builder builder(Pattern name) { + return () -> new ItemEntryPredicate(name); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java index 2eee534..363902a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java @@ -1,55 +1,55 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.entry; -import com.mojang.datafixers.Products; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.LootTableModifier; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; - -import java.util.List; -import java.util.Optional; - -public abstract class LootEntryPredicate implements LootModifierPredicate { - protected final @Nullable List conditions; - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // da codecccccc - protected LootEntryPredicate(final @NotNull Optional> conditions) { - this.conditions = conditions.orElse(null); - } - - protected static Products.P1, Optional>> addConditionsToCodec(RecordCodecBuilder.Instance instance) { - return instance.group(LootConditionPredicate.CODEC.listOf().optionalFieldOf("conditions").forGetter(entry -> Optional.ofNullable(entry.conditions))); - } - - - public abstract LootModifierPredicateType getType(); - - /** - * Checks if this predicate matches the provided loot pool entry - * @param entry the entry to check against - * @return true when the provided loot pool entry matches, false otherwise - */ - @Override - public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { - final int conditionCount = conditions == null ? 0 : conditions.size(); - final int entryConditionsCount = entry.conditions == null ? 0 : entry.conditions.size(); - - if (conditionCount == entryConditionsCount) return true; - if (conditionCount == 0) return false; - - for (LootConditionPredicate predicate : this.conditions) { - - } - - return conditions.stream().allMatch(predicate -> entry.conditions.stream().anyMatch(predicate::matches)); - } -} +//import com.mojang.datafixers.Products; +//import com.mojang.serialization.Codec; +//import com.mojang.serialization.codecs.RecordCodecBuilder; +//import net.minecraft.loot.LootPool; +//import net.minecraft.loot.LootTable; +//import net.minecraft.loot.entry.LootPoolEntry; +//import net.minecraft.util.Identifier; +//import org.jetbrains.annotations.NotNull; +//import org.jetbrains.annotations.Nullable; +//import top.offsetmonkey538.loottablemodifier.LootTableModifier; +//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +// +//import java.util.List; +//import java.util.Optional; +// +//public abstract class LootEntryPredicate implements LootModifierPredicate { +// protected final @Nullable List conditions; +// +// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // da codecccccc +// protected LootEntryPredicate(final @NotNull Optional> conditions) { +// this.conditions = conditions.orElse(null); +// } +// +// protected static Products.P1, Optional>> addConditionsToCodec(RecordCodecBuilder.Instance instance) { +// return instance.group(LootConditionPredicate.CODEC.listOf().optionalFieldOf("conditions").forGetter(entry -> Optional.ofNullable(entry.conditions))); +// } +// +// +// public abstract LootModifierPredicateType getType(); +// +// /** +// * Checks if this predicate matches the provided loot pool entry +// * @param entry the entry to check against +// * @return true when the provided loot pool entry matches, false otherwise +// */ +// @Override +// public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { +// final int conditionCount = conditions == null ? 0 : conditions.size(); +// final int entryConditionsCount = entry.conditions == null ? 0 : entry.conditions.size(); +// +// if (conditionCount == entryConditionsCount) return true; +// if (conditionCount == 0) return false; +// +// for (LootConditionPredicate predicate : this.conditions) { +// +// } +// +// return conditions.stream().allMatch(predicate -> entry.conditions.stream().anyMatch(predicate::matches)); +// } +//} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java index b1982c1..177d0ac 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java @@ -1,40 +1,41 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.entry.leaf; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.entry.ItemEntry; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; - -import java.util.List; -import java.util.Optional; - -public class EmptyEntryPredicate extends LeafEntryPredicate { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> addLeafFieldsToCodec(instance).apply(instance, EmptyEntryPredicate::new)); - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // given by da codecc - protected EmptyEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { - super(weight, quality, conditions, functions); - } - - @Override - public LootModifierPredicateType getType() { - return LootModifierPredicateTypes.EMPTY_ENTRY; - } - - @Override - public boolean test(@NotNull LootModifierContext context, @NotNull LootTable table, @NotNull Identifier tableId) { - return super.test(context, table, tableId); - } ItemEntry - - @Override - public byte requiredContext() { - return LootModifierContext.REQUIRES_TABLE; - } -} +//import com.mojang.serialization.MapCodec; +//import com.mojang.serialization.codecs.RecordCodecBuilder; +//import net.minecraft.loot.LootTable; +//import net.minecraft.loot.entry.ItemEntry; +//import net.minecraft.util.Identifier; +//import org.jetbrains.annotations.NotNull; +//import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; +// +//import java.util.List; +//import java.util.Optional; +// +//public class EmptyEntryPredicate extends LeafEntryPredicate { +// public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> addLeafFieldsToCodec(instance).apply(instance, EmptyEntryPredicate::new)); +// +// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // given by da codecc +// protected EmptyEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { +// super(weight, quality, conditions, functions); +// } +// +// @Override +// public LootModifierPredicateType getType() { +// return LootModifierPredicateTypes.EMPTY_ENTRY; +// } +// +// @Override +// public boolean test(@NotNull LootModifierContext context, @NotNull LootTable table, @NotNull Identifier tableId) { +// return super.test(context, table, tableId); +// } ItemEntry +// +// @Override +// public byte requiredContext() { +// return LootModifierContext.REQUIRES_TABLE; +// } +//} +// \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java index b6fda9f..881fde1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java @@ -1,44 +1,45 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.entry.leaf; -import com.mojang.datafixers.Products; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.entry.ItemEntry; -import net.minecraft.loot.entry.LootPoolEntry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; - -import java.util.List; -import java.util.Optional; - -public abstract class LeafEntryPredicate extends LootEntryPredicate { - protected final @Nullable Integer weight; - protected final @Nullable Integer quality; - protected final @Nullable List functions; - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // codec giv it - protected LeafEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { - super(conditions); - this.weight = weight.orElse(null); - this.quality = quality.orElse(null); - this.functions = functions.orElse(null); - } - - protected static Products.P4, Optional, Optional, Optional>, Optional>> addLeafFieldsToCodec(RecordCodecBuilder.Instance instance) { - return instance.group( - Codec.INT.optionalFieldOf("weight").forGetter(entry -> Optional.ofNullable(entry.weight)), - Codec.INT.optionalFieldOf("quality").forGetter(entry -> Optional.ofNullable(entry.weight)) - ) - .and(addConditionsToCodec(instance).t1()) - .and(LootFunctionPredicate.CODEC.listOf().optionalFieldOf("functions").forGetter(entry -> Optional.ofNullable(entry.functions))); - - } -ItemEntry - // todo: @Override - // todo: public boolean matches(@NotNull LootPoolEntry entry) { - // todo: return super.matches(entry); - // todo: } -} +//import com.mojang.datafixers.Products; +//import com.mojang.serialization.Codec; +//import com.mojang.serialization.codecs.RecordCodecBuilder; +//import net.minecraft.loot.entry.ItemEntry; +//import net.minecraft.loot.entry.LootPoolEntry; +//import org.jetbrains.annotations.NotNull; +//import org.jetbrains.annotations.Nullable; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; +// +//import java.util.List; +//import java.util.Optional; +// +//public abstract class LeafEntryPredicate extends LootEntryPredicate { +// protected final @Nullable Integer weight; +// protected final @Nullable Integer quality; +// protected final @Nullable List functions; +// +// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // codec giv it +// protected LeafEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { +// super(conditions); +// this.weight = weight.orElse(null); +// this.quality = quality.orElse(null); +// this.functions = functions.orElse(null); +// } +// +// protected static Products.P4, Optional, Optional, Optional>, Optional>> addLeafFieldsToCodec(RecordCodecBuilder.Instance instance) { +// return instance.group( +// Codec.INT.optionalFieldOf("weight").forGetter(entry -> Optional.ofNullable(entry.weight)), +// Codec.INT.optionalFieldOf("quality").forGetter(entry -> Optional.ofNullable(entry.weight)) +// ) +// .and(addConditionsToCodec(instance).t1()) +// .and(LootFunctionPredicate.CODEC.listOf().optionalFieldOf("functions").forGetter(entry -> Optional.ofNullable(entry.functions))); +// +// } +//ItemEntry +// // todo: @Override +// // todo: public boolean matches(@NotNull LootPoolEntry entry) { +// // todo: return super.matches(entry); +// // todo: } +//} +// \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java index de3f9ef..437e660 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java @@ -5,6 +5,7 @@ import net.minecraft.loot.LootTable; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; @@ -19,7 +20,7 @@ public LootModifierPredicateType getType() { } @Override - public boolean test(@NotNull PredicateContext context) { + public boolean test(@NotNull LootModifierContext context) { return !term.test(context); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java index a6067ac..ab9b068 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java @@ -6,13 +6,17 @@ import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.fabricmc.loader.impl.lib.sat4j.minisat.constraints.cnf.Lits; +import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import net.minecraft.loot.condition.AlternativeLootCondition; import net.minecraft.loot.condition.LootCondition; import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.util.Identifier; import net.minecraft.util.Util; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; @@ -22,9 +26,9 @@ public abstract class TermsLootPredicate implements LootModifierPredicate { protected final List terms; - private final Predicate builtPredicate; + private final Predicate builtPredicate; - protected TermsLootPredicate(final List terms, final Predicate builtPredicate) { + protected TermsLootPredicate(final List terms, final Predicate builtPredicate) { this.terms = terms; this.builtPredicate = builtPredicate; } @@ -45,8 +49,8 @@ private Either> eitherTerms() } @Override - public boolean test(@NotNull PredicateContext context) { - return builtPredicate.test(context.table()); + public boolean test(final @NotNull LootModifierContext context) { + return builtPredicate.test(context); } public abstract static class Builder implements LootModifierPredicate.Builder { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java index 00e7ba7..2662e24 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java @@ -1,136 +1,137 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.pool; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.condition.AllOfLootCondition; -import net.minecraft.loot.condition.AnyOfLootCondition; -import net.minecraft.loot.condition.InvertedLootCondition; -import net.minecraft.loot.context.LootContextTypes; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; - -import java.util.List; -import java.util.Optional; - -public record LootPoolPredicate(@Nullable Integer rolls, @Nullable Integer bonusRolls, @Nullable List entries, @Nullable List functions, @Nullable List conditions) implements LootModifierPredicate { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - // TODO: smt complex that allows like matching integer expressions? Yknow like >=2 // TODO: ok actually Minecraft uses some number provider stuff that I need to match. Not always just a single int - Codec.INT.optionalFieldOf("rolls").forGetter(LootPoolPredicate::optionalRolls), - Codec.INT.optionalFieldOf("bonusRolls").forGetter(LootPoolPredicate::optionalBonusRolls), - Codec.either(LootEntryPredicate.CODEC, LootEntryPredicate.CODEC.listOf()).optionalFieldOf("entries").forGetter(LootPoolPredicate::optionalEntries), - Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootPoolPredicate::optionalFunctions), - Codec.either(LootConditionPredicate.CODEC, LootConditionPredicate.CODEC.listOf()).optionalFieldOf("conditions").forGetter(LootPoolPredicate::optionalConditions) - ).apply(instance, LootPoolPredicate::new)); - - private Optional optionalRolls() { - return Optional.ofNullable(rolls); - } - - private Optional optionalBonusRolls() { - return Optional.ofNullable(bonusRolls); - } - - private Optional>> optionalEntries() { - if (entries == null) return Optional.empty(); - if (entries.size() == 1) return Optional.of(Either.left(entries.get(0))); - return Optional.of(Either.right(entries)); - } - - private Optional>> optionalFunctions() { - if (functions == null) return Optional.empty(); - if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); - return Optional.of(Either.right(functions)); - } - - private Optional>> optionalConditions() { - if (conditions == null) return Optional.empty(); - if (conditions.size() == 1) return Optional.of(Either.left(conditions.get(0))); - return Optional.of(Either.right(conditions)); - } - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // 'cause of codecccc - private LootPoolPredicate( - @NotNull Optional optionalRolls, - @NotNull Optional optionalBonusRolls, - @NotNull Optional>> optionalEntries, - @NotNull Optional>> optionalFunctions, - @NotNull Optional>> optionalConditions - ) { - this( - optionalRolls.orElse(null), - optionalBonusRolls.orElse(null), - optionalEntries.map(entriesEither -> entriesEither.map(List::of, entries -> entries)).orElse(null), - optionalFunctions.map(functionsEither -> functionsEither.map(List::of, functions -> functions)).orElse(null), - optionalConditions.map(conditionsEither -> conditionsEither.map(List::of, conditions -> conditions)).orElse(null) - ); - } - - - //public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { - @Override - public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { - boolean result = true; - - // todo: if (rolls != null) { - // todo: result = rolls == pool.rolls.; - // todo: } - - // todo: if (bonusRolls != null) { - // todo: result = result && bonusRolls == pool.rolls.; - // todo: } - - if (entries != null) { - for (LootEntryPredicate entryPredicate : entries) { - result = result && entryPredicate.test(context); - } - result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); - } - - if (functions != null) { - for (LootFunctionPredicate functionPredicate : functions) { - result = result && table.functions.stream().anyMatch(functionPredicate::matches); - } - } - - return result; - } - - @Override - public LootModifierPredicateType getType() { - return LootModifierPredicateTypes.LOOT_POOL; - } - - @Override - public byte requiredContext() { - return LootModifierContext.REQUIRES_ENTRY; - } - - //@FunctionalInterface - //public interface Builder { - // LootPoolPredicate build(); - - // default LootPoolPredicate.Builder invert() { - // return InvertedLootCondition.builder(this); - // } - - // default AnyOfLootCondition.Builder or(LootPoolPredicate.Builder otherPredicate) { - // return AnyOfLootCondition.builder(this, otherPredicate); - // } - - // default AllOfLootCondition.Builder and(LootPoolPredicate.Builder otherPredicate) { - // return AllOfLootCondition.builder(this, otherPredicate); - // } - //} -} +//import com.mojang.datafixers.util.Either; +//import com.mojang.serialization.Codec; +//import com.mojang.serialization.MapCodec; +//import com.mojang.serialization.codecs.RecordCodecBuilder; +//import net.minecraft.loot.LootPool; +//import net.minecraft.loot.LootTable; +//import net.minecraft.loot.condition.AllOfLootCondition; +//import net.minecraft.loot.condition.AnyOfLootCondition; +//import net.minecraft.loot.condition.InvertedLootCondition; +//import net.minecraft.loot.context.LootContextTypes; +//import net.minecraft.util.Identifier; +//import org.jetbrains.annotations.NotNull; +//import org.jetbrains.annotations.Nullable; +//import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; +// +//import java.util.List; +//import java.util.Optional; +// +//public record LootPoolPredicate(@Nullable Integer rolls, @Nullable Integer bonusRolls, @Nullable List entries, @Nullable List functions, @Nullable List conditions) implements LootModifierPredicate { +// public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( +// // TODO: smt complex that allows like matching integer expressions? Yknow like >=2 // TODO: ok actually Minecraft uses some number provider stuff that I need to match. Not always just a single int +// Codec.INT.optionalFieldOf("rolls").forGetter(LootPoolPredicate::optionalRolls), +// Codec.INT.optionalFieldOf("bonusRolls").forGetter(LootPoolPredicate::optionalBonusRolls), +// Codec.either(LootEntryPredicate.CODEC, LootEntryPredicate.CODEC.listOf()).optionalFieldOf("entries").forGetter(LootPoolPredicate::optionalEntries), +// Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootPoolPredicate::optionalFunctions), +// Codec.either(LootConditionPredicate.CODEC, LootConditionPredicate.CODEC.listOf()).optionalFieldOf("conditions").forGetter(LootPoolPredicate::optionalConditions) +// ).apply(instance, LootPoolPredicate::new)); +// +// private Optional optionalRolls() { +// return Optional.ofNullable(rolls); +// } +// +// private Optional optionalBonusRolls() { +// return Optional.ofNullable(bonusRolls); +// } +// +// private Optional>> optionalEntries() { +// if (entries == null) return Optional.empty(); +// if (entries.size() == 1) return Optional.of(Either.left(entries.get(0))); +// return Optional.of(Either.right(entries)); +// } +// +// private Optional>> optionalFunctions() { +// if (functions == null) return Optional.empty(); +// if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); +// return Optional.of(Either.right(functions)); +// } +// +// private Optional>> optionalConditions() { +// if (conditions == null) return Optional.empty(); +// if (conditions.size() == 1) return Optional.of(Either.left(conditions.get(0))); +// return Optional.of(Either.right(conditions)); +// } +// +// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // 'cause of codecccc +// private LootPoolPredicate( +// @NotNull Optional optionalRolls, +// @NotNull Optional optionalBonusRolls, +// @NotNull Optional>> optionalEntries, +// @NotNull Optional>> optionalFunctions, +// @NotNull Optional>> optionalConditions +// ) { +// this( +// optionalRolls.orElse(null), +// optionalBonusRolls.orElse(null), +// optionalEntries.map(entriesEither -> entriesEither.map(List::of, entries -> entries)).orElse(null), +// optionalFunctions.map(functionsEither -> functionsEither.map(List::of, functions -> functions)).orElse(null), +// optionalConditions.map(conditionsEither -> conditionsEither.map(List::of, conditions -> conditions)).orElse(null) +// ); +// } +// +// +// //public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { +// @Override +// public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { +// boolean result = true; +// +// // todo: if (rolls != null) { +// // todo: result = rolls == pool.rolls.; +// // todo: } +// +// // todo: if (bonusRolls != null) { +// // todo: result = result && bonusRolls == pool.rolls.; +// // todo: } +// +// if (entries != null) { +// for (LootEntryPredicate entryPredicate : entries) { +// result = result && entryPredicate.test(context); +// } +// result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); +// } +// +// if (functions != null) { +// for (LootFunctionPredicate functionPredicate : functions) { +// result = result && table.functions.stream().anyMatch(functionPredicate::matches); +// } +// } +// +// return result; +// } +// +// @Override +// public LootModifierPredicateType getType() { +// return LootModifierPredicateTypes.LOOT_POOL; +// } +// +// @Override +// public byte requiredContext() { +// return LootModifierContext.REQUIRES_ENTRY; +// } +// +// //@FunctionalInterface +// //public interface Builder { +// // LootPoolPredicate build(); +// +// // default LootPoolPredicate.Builder invert() { +// // return InvertedLootCondition.builder(this); +// // } +// +// // default AnyOfLootCondition.Builder or(LootPoolPredicate.Builder otherPredicate) { +// // return AnyOfLootCondition.builder(this, otherPredicate); +// // } +// +// // default AllOfLootCondition.Builder and(LootPoolPredicate.Builder otherPredicate) { +// // return AllOfLootCondition.builder(this, otherPredicate); +// // } +// //} +//} +// \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java index 854b6bf..cad38f2 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java @@ -1,78 +1,79 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.table; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.context.LootContextTypes; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; -import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; - -import java.util.List; -import java.util.Optional; -import java.util.regex.Pattern; - -// TODO: add entries predicate too -public record LootTablePredicate(@Nullable Pattern identifier, @Nullable Pattern type, @Nullable List functions /* TODO: no need to use list cause anyOf and allOf are a thing */) implements LootModifierPredicate { - private static final Codec INLINE_CODEC = Util.PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); - private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( - Util.PATTERN_CODEC.optionalFieldOf("id").forGetter(LootTablePredicate::optionalIdentifier), - Util.PATTERN_CODEC.optionalFieldOf("type").forGetter(LootTablePredicate::optionalType), - Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootTablePredicate::optionalFunctions) - ).apply(instance, LootTablePredicate::new)); - public static final Codec CODEC = Codec.either(LootTablePredicate.INLINE_CODEC, LootTablePredicate.FULL_CODEC).xmap(lootTablePredicateLootTablePredicateEither -> lootTablePredicateLootTablePredicateEither.left().orElseGet(() -> lootTablePredicateLootTablePredicateEither.right().orElseThrow()), - lootTablePredicate -> { - if (lootTablePredicate.identifier != null && lootTablePredicate.type == null && (lootTablePredicate.functions == null || lootTablePredicate.functions.isEmpty())) { - return Either.left(lootTablePredicate); - } - return Either.right(lootTablePredicate); - }); - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Optional optionalIdentifier, @NotNull Optional optionalType, @NotNull Optional>> optionalFunctions) { - this(optionalIdentifier.orElse(null), optionalType.orElse(null), optionalFunctions.map(functionsEither -> functionsEither.map(List::of, patterns -> patterns)).orElse(null)); - } - - public LootTablePredicate(@NotNull Pattern pattern) { - this(pattern, null, null); - } - - private Optional optionalIdentifier() { - return Optional.ofNullable(identifier); - } - private Optional optionalType() { - return Optional.ofNullable(type); - } - private Optional>> optionalFunctions() { - if (functions == null) return Optional.empty(); - if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); - return Optional.of(Either.right(functions)); - } - - // TODO: also loops over pools and checks that - @Override - public boolean test(final @NotNull LootTable table, final @NotNull Identifier tableId) { - boolean result = true; - - if (identifier != null) { - result = identifier.matcher(tableId.toString()).matches(); - } - - if (type != null) { - result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); - } - - if (functions != null) { - for (LootFunctionPredicate functionPredicate : functions) { - result = result && table.functions.stream().anyMatch(functionPredicate::matches); - } - } - - return result; - } -} +//import com.mojang.datafixers.util.Either; +//import com.mojang.serialization.Codec; +//import com.mojang.serialization.codecs.RecordCodecBuilder; +//import net.minecraft.loot.LootPool; +//import net.minecraft.loot.LootTable; +//import net.minecraft.loot.context.LootContextTypes; +//import net.minecraft.util.Identifier; +//import org.jetbrains.annotations.NotNull; +//import org.jetbrains.annotations.Nullable; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; +// +//import java.util.List; +//import java.util.Optional; +//import java.util.regex.Pattern; +// +//// TODO: add entries predicate too +//public record LootTablePredicate(@Nullable Pattern identifier, @Nullable Pattern type, @Nullable List functions /* TODO: no need to use list cause anyOf and allOf are a thing */) implements LootModifierPredicate { +// private static final Codec INLINE_CODEC = Util.PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); +// private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( +// Util.PATTERN_CODEC.optionalFieldOf("id").forGetter(LootTablePredicate::optionalIdentifier), +// Util.PATTERN_CODEC.optionalFieldOf("type").forGetter(LootTablePredicate::optionalType), +// Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootTablePredicate::optionalFunctions) +// ).apply(instance, LootTablePredicate::new)); +// public static final Codec CODEC = Codec.either(LootTablePredicate.INLINE_CODEC, LootTablePredicate.FULL_CODEC).xmap(lootTablePredicateLootTablePredicateEither -> lootTablePredicateLootTablePredicateEither.left().orElseGet(() -> lootTablePredicateLootTablePredicateEither.right().orElseThrow()), +// lootTablePredicate -> { +// if (lootTablePredicate.identifier != null && lootTablePredicate.type == null && (lootTablePredicate.functions == null || lootTablePredicate.functions.isEmpty())) { +// return Either.left(lootTablePredicate); +// } +// return Either.right(lootTablePredicate); +// }); +// +// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me +// private LootTablePredicate(@NotNull Optional optionalIdentifier, @NotNull Optional optionalType, @NotNull Optional>> optionalFunctions) { +// this(optionalIdentifier.orElse(null), optionalType.orElse(null), optionalFunctions.map(functionsEither -> functionsEither.map(List::of, patterns -> patterns)).orElse(null)); +// } +// +// public LootTablePredicate(@NotNull Pattern pattern) { +// this(pattern, null, null); +// } +// +// private Optional optionalIdentifier() { +// return Optional.ofNullable(identifier); +// } +// private Optional optionalType() { +// return Optional.ofNullable(type); +// } +// private Optional>> optionalFunctions() { +// if (functions == null) return Optional.empty(); +// if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); +// return Optional.of(Either.right(functions)); +// } +// +// // TODO: also loops over pools and checks that +// @Override +// public boolean test(final @NotNull LootTable table, final @NotNull Identifier tableId) { +// boolean result = true; +// +// if (identifier != null) { +// result = identifier.matcher(tableId.toString()).matches(); +// } +// +// if (type != null) { +// result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); +// } +// +// if (functions != null) { +// for (LootFunctionPredicate functionPredicate : functions) { +// result = result && table.functions.stream().anyMatch(functionPredicate::matches); +// } +// } +// +// return result; +// } +//} +// \ No newline at end of file diff --git a/src/main/resources/loot-table-modifier.accesswidener b/src/main/resources/loot-table-modifier.accesswidener index 7a25a76..9bcda25 100644 --- a/src/main/resources/loot-table-modifier.accesswidener +++ b/src/main/resources/loot-table-modifier.accesswidener @@ -6,3 +6,5 @@ accessible field net/minecraft/loot/LootTable functions Ljava/util/List; accessible field net/minecraft/loot/context/LootContextTypes MAP Lcom/google/common/collect/BiMap; accessible field net/minecraft/loot/entry/LootPoolEntry conditions Ljava/util/List; + +accessible field net/minecraft/loot/entry/ItemEntry item Lnet/minecraft/registry/entry/RegistryEntry; \ No newline at end of file diff --git a/src/main/resources/loot-table-modifier.mixins.json b/src/main/resources/loot-table-modifier.mixins.json index 52a1daa..2352da9 100644 --- a/src/main/resources/loot-table-modifier.mixins.json +++ b/src/main/resources/loot-table-modifier.mixins.json @@ -3,6 +3,8 @@ "package": "top.offsetmonkey538.loottablemodifier.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ + "ItemEntryAccessor", + "LootPoolAccessor", "LootTableAccessor", "ReloadableRegistriesMixin" ], From dd8d04b779164eead250eef091f79e3597f8b676 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 12 Jun 2025 20:39:46 +0300 Subject: [PATCH 11/53] Correctly keep track of how many loot tables, pools and entries have been modified --- .../loottablemodifier/LootTableModifier.java | 40 +++++++++++++------ .../datagen/NewNewLootModifierProvider.java | 4 +- .../datagen/LootTableModifierDatagen.java | 18 ++++++++- .../resource/LootModifier.java | 10 +++-- .../resource/LootModifierContext.java | 8 ++-- .../resource/action/AddPoolAction.java | 4 +- .../resource/action/LootModifierAction.java | 2 +- .../resource/action/SetItemAction.java | 15 +++---- 8 files changed, 70 insertions(+), 31 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index f17b1f5..1d4cf04 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -19,6 +19,7 @@ import net.minecraft.resource.ResourceManager; import net.minecraft.text.Text; import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,9 +29,9 @@ import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; + +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; public class LootTableModifier implements ModInitializer { public static final String MOD_ID = "loot-table-modifier"; @@ -57,34 +58,49 @@ public static void runModification(ResourceManager resourceManager, Registry modifiers = loadModifiers(resourceManager, registryOps); final Map failedModifiers = new HashMap<>(0); - int amountModified = 0; + final List modifiedTableIds = new ArrayList<>(); // todo: will be used for exporting modified ones + int poolsModified = 0, entriesModified = 0; + boolean tableModified, poolModified; LOGGER.info("Modifying loot tables..."); final Stopwatch stopwatch = Stopwatch.createStarted(); // TODO: Would looping through all tables here be faster than doing that for each modifier? To test I guess // TODO: so about that never-nesting............ - // fixme: don't think modifying for example tables without any pools or entries would work with this..... - // TODO: increment "amountModified" somewhere... - // todo. just fucking rewrite this when your brain actually works and can think + // fixme: don't think modifying for example tables without any pools or entries would work with this..... Maybe try a do-while instead of for loop? Then I'd have to for (Iterator> it = getRegistryAsWrapper(lootRegistry).streamEntries().iterator(); it.hasNext(); ) { final RegistryEntry.Reference registryEntry = it.next(); final RegistryKey key = registryEntry.registryKey(); final LootTable table = lootRegistry.get(key); + final Identifier tableId = key.getValue(); if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); - for (LootPool pool : table.pools) { + tableModified = false; for (LootPoolEntry entry : pool.entries) { - final LootModifierContext context = new LootModifierContext(table, key.getValue(), pool, entry); + poolModified = false; for (Map.Entry modifierEntry : modifiers.entrySet()) { + // todo: I'm creating a lot of these... Could it make more sense to not use a record so it's modifiable and then keep passing the same instance but modify the values? Think making the values in there protected would mean that only things in the same package could access it? So move it into this package (doesn't really make sense in 'resource' anyway) and that way I can make sure only this modifies stuff + final LootModifierContext context = new LootModifierContext(table, tableId, pool, entry, tableModified, poolModified); + final LootModifier modifier = modifierEntry.getValue(); if (!modifier.testModifies(context)) continue; - if (IS_DEV) LOGGER.error("Modifier {} can modify table {}", modifierEntry.getKey(), key.getValue()); - modifier.apply(context); // FIXME: stuff modifying table or pool may apply multiple times + + if (IS_DEV) LOGGER.warn("Modifier {} can modify table {}", modifierEntry.getKey(), tableId); + + + int result = modifier.apply(context); + + if (IS_DEV && result != MODIFIED_NONE) LOGGER.warn("Modifier {} modified table {} with modified mask {}", modifierEntry.getKey(), tableId, Integer.toUnsignedString(result, 2)); + + if ((result & MODIFIED_TABLE) == MODIFIED_TABLE) tableModified = true; + if ((result & MODIFIED_POOL) == MODIFIED_POOL) poolModified = true; + if ((result & MODIFIED_ENTRY) == MODIFIED_ENTRY) entriesModified++; } + poolsModified += poolModified ? 1 : 0; } + if (tableModified) modifiedTableIds.add(tableId); } //for (Map.Entry modifierEntry : modifiers.entrySet()) { @@ -125,7 +141,7 @@ public static void runModification(ResourceManager resourceManager, Registry newPools = ImmutableList.builder() .addAll(context.table().pools) .addAll(this.pools) @@ -31,7 +31,7 @@ public boolean apply(@NotNull LootModifierContext context) { ((LootTableAccessor) context.table()).setPools(newPools); - return true; + return LootModifierContext.MODIFIED_TABLE; } public static AddPoolAction.Builder builder(@NotNull LootPool.Builder poolBuilder) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java index e17c5eb..d83719e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java @@ -14,7 +14,7 @@ public interface LootModifierAction { * @param context the context to apply to * @return true when table was modified, false otherwise */ - boolean apply(final @NotNull LootModifierContext context); + int apply(final @NotNull LootModifierContext context); @FunctionalInterface interface Builder { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java index 3c8dfeb..5e2c848 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java @@ -22,6 +22,9 @@ import java.util.List; +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_ENTRY; +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; + public record SetItemAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( Item.ENTRY_CODEC.fieldOf("name").forGetter(SetItemAction::item), @@ -34,29 +37,27 @@ public LootModifierActionType getType() { } @Override - public boolean apply(@NotNull LootModifierContext context) { + public int apply(@NotNull LootModifierContext context) { final LootPoolEntry entry = context.entry(); - if (entry == null) return false; if (entry instanceof ItemEntry itemEntry) { ((ItemEntryAccessor) itemEntry).setItem(item); - return true; + return MODIFIED_ENTRY; } // Matched entry is not an ItemEntry, check if entry replacing is on - if (!canReplaceEntry) return false; + if (!canReplaceEntry) return MODIFIED_NONE; final LootPool pool = context.pool(); - if (pool == null) return false; final ImmutableList.Builder newEntriesBuilder = ImmutableList.builder(); for (LootPoolEntry originalEntry : pool.entries) { - if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? + if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? TODO: test newEntriesBuilder.add(originalEntry); } ((LootPoolAccessor) context.pool()).setEntries(newEntriesBuilder.build()); - return true; + return MODIFIED_ENTRY; } public static SetItemAction.Builder builder(@NotNull ItemConvertible item) { From de23bc1695934c6fec41b01d2d5e456e3069f111 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 12 Jun 2025 22:51:09 +0300 Subject: [PATCH 12/53] Add loot table predicate zombies and creepers drop two tnt for some reason?? --- .../loottablemodifier/LootTableModifier.java | 64 ++-- .../api/LootModifierPredicateTypes.java | 3 + .../datagen/LootTableModifierDatagen.java | 46 ++- .../resource/LootModifier.java | 13 +- .../resource/action/AddPoolAction.java | 3 + .../predicate/table/LootTablePredicate.java | 281 +++++++++++++----- 6 files changed, 305 insertions(+), 105 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 1d4cf04..0aead55 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -1,25 +1,30 @@ package top.offsetmonkey538.loottablemodifier; import com.google.common.base.Stopwatch; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JsonOps; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryOps; -import net.minecraft.registry.RegistryWrapper; +import net.minecraft.registry.*; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; +import net.minecraft.server.MinecraftServer; import net.minecraft.text.Text; import net.minecraft.util.Identifier; -import net.minecraft.util.profiler.Profiler; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.file.PathUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +33,10 @@ import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; @@ -76,8 +84,10 @@ public static void runModification(ResourceManager resourceManager, Registry modifierEntry : modifiers.entrySet()) { @@ -85,7 +95,7 @@ public static void runModification(ResourceManager resourceManager, Registry exportModifiedTables(modifiedTableIds, server)); + } private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { LOGGER.info("Loading loot table modifiers..."); @@ -171,16 +184,33 @@ private static Map loadModifiers(ResourceManager resou return result; } - private static void modifiersApplied(Map failedModifiers) { - if (failedModifiers.isEmpty()) return; + private static void exportModifiedTables(List tableIDs, MinecraftServer server) { + final DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, server.getRegistryManager()); + try { + final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier_export"); + PathUtils.deleteDirectory(exportDir); + LOGGER.warn("Exporting modified tables to {}", exportDir); + + final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - LOGGER.warn("There were unused modifiers:"); - //for (Map.Entry entry : failedModifiers.entrySet()) { - // LOGGER.warn("\tModifier '{}' failed to modify loot table for predicates: ", entry.getKey()); - // for (LootTablePredicate predicate : entry.getValue().modifies()) { - // LOGGER.warn("\t\t- {}", predicate); - // } - //} + for (Identifier id : tableIDs) { + final LootTable table = server.getReloadableRegistries().getLootTable(RegistryKey.of(RegistryKeys.LOOT_TABLE, id)); + final Path file = exportDir.resolve(id.getNamespace()).resolve(id.getPath() + ".json"); + Files.createDirectories(file.getParent()); + + LOGGER.warn("Exporting loot table to {}", file); + DataResult dataResult = LootTable.CODEC.encodeStart(ops, table); + final Optional optionalResult = dataResult.resultOrPartial(LOGGER::error); + final JsonElement result = optionalResult.orElseThrow(); + + + LOGGER.warn("Writing loot table to {}", file); + Files.writeString(file, gson.toJson(result)); + } + + } catch (IOException e) { + throw new RuntimeException("Failed to export modified tables!", e); + } } public static Identifier id(String path) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java index 57ed4af..c8b05a5 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java @@ -16,6 +16,7 @@ import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.InvertedLootPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -30,6 +31,8 @@ private LootModifierPredicateTypes() { public static final LootModifierPredicateType ITEM_ENTRY = register(id("item_entry"), ItemEntryPredicate.CODEC); + public static final LootModifierPredicateType LOOT_TABLE = register(id("loot_table"), LootTablePredicate.CODEC); + //public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); private static LootModifierPredicateType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 68fae1e..1d879c8 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -3,24 +3,37 @@ import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricLootTableProvider; +import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider; +import net.minecraft.data.DataOutput; +import net.minecraft.data.loottable.LootTableProvider; import net.minecraft.entity.EntityType; import net.minecraft.item.Items; import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; import net.minecraft.loot.LootTables; +import net.minecraft.loot.context.LootContextTypes; import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.function.EnchantWithLevelsLootFunction; import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.context.ContextType; import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.datagen.NewNewLootModifierProvider; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.SetItemAction; import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; +import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import java.util.regex.Pattern; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -32,6 +45,7 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { final FabricDataGenerator.Pack pack = fabricDataGenerator.createBuiltinResourcePack(id("example_pack")); pack.addProvider(NewModLootModifierProvider::new); + pack.addProvider(LootProvider::new); } //private static class NewModLootModifierProvider extends NewLootModifierProvider { @@ -95,7 +109,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ) ); addModifier( - id("drop_tnt"), + id("sugarcane_drop_tnt"), LootModifier.builder() .conditionally( ItemEntryPredicate.builder(Pattern.compile("minecraft:sugar_cane")) @@ -111,6 +125,36 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ) ) ); + addModifier( + id("mobs_drop_tnt"), + LootModifier.builder() + .conditionally( + LootTablePredicate.builder() + .name(EntityType.CREEPER) + .name(EntityType.ZOMBIE) + ) + .action( + AddPoolAction.builder( + LootPool.builder() + .rolls(ConstantLootNumberProvider.create(1)) + .with( + ItemEntry.builder(Items.TNT) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ) + ) + ); + } + } + + private static class LootProvider extends SimpleFabricLootTableProvider { + public LootProvider(FabricDataOutput output, CompletableFuture registryLookup) { + super(output, registryLookup, LootContextTypes.CHEST); + } + + @Override + public void accept(BiConsumer, LootTable.Builder> lootTableBiConsumer) { + lootTableBiConsumer.accept(RegistryKey.of(RegistryKeys.LOOT_TABLE, id("test_empty_table")), LootTable.builder()); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 3f96d3a..795dc3a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -2,27 +2,20 @@ import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryWrapper; -import net.minecraft.registry.entry.RegistryEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.function.Predicate; // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied TODO: I don't think I do this anymore? TODO: make sure that changing it to a normal List is fine -public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) { +public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) implements Predicate { //public record LootModifier(@NotNull @UnmodifiableView ArrayList actions) { private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( // TODO: this should use the table predicate, once that exists @@ -131,7 +124,7 @@ public int apply(final @NotNull LootModifierContext context) { return result; } - public boolean testModifies(final @NotNull LootModifierContext context) { + public boolean test(final @NotNull LootModifierContext context) { for (LootModifierPredicate predicate : predicates) { if (!predicate.test(context)) return false; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java index 5ff4677..c313ff3 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java @@ -6,6 +6,7 @@ import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.LootTableModifier; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; @@ -24,6 +25,8 @@ public LootModifierActionType getType() { @Override public int apply(@NotNull LootModifierContext context) { + if (context.tableAlreadyModified()) return LootModifierContext.MODIFIED_NONE; + final List newPools = ImmutableList.builder() .addAll(context.table().pools) .addAll(this.pools) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java index cad38f2..56f06b2 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java @@ -1,79 +1,206 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.table; -//import com.mojang.datafixers.util.Either; -//import com.mojang.serialization.Codec; -//import com.mojang.serialization.codecs.RecordCodecBuilder; -//import net.minecraft.loot.LootPool; -//import net.minecraft.loot.LootTable; -//import net.minecraft.loot.context.LootContextTypes; -//import net.minecraft.util.Identifier; -//import org.jetbrains.annotations.NotNull; -//import org.jetbrains.annotations.Nullable; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; -// -//import java.util.List; -//import java.util.Optional; -//import java.util.regex.Pattern; -// -//// TODO: add entries predicate too -//public record LootTablePredicate(@Nullable Pattern identifier, @Nullable Pattern type, @Nullable List functions /* TODO: no need to use list cause anyOf and allOf are a thing */) implements LootModifierPredicate { -// private static final Codec INLINE_CODEC = Util.PATTERN_CODEC.xmap(LootTablePredicate::new, LootTablePredicate::identifier); -// private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( -// Util.PATTERN_CODEC.optionalFieldOf("id").forGetter(LootTablePredicate::optionalIdentifier), -// Util.PATTERN_CODEC.optionalFieldOf("type").forGetter(LootTablePredicate::optionalType), -// Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootTablePredicate::optionalFunctions) -// ).apply(instance, LootTablePredicate::new)); -// public static final Codec CODEC = Codec.either(LootTablePredicate.INLINE_CODEC, LootTablePredicate.FULL_CODEC).xmap(lootTablePredicateLootTablePredicateEither -> lootTablePredicateLootTablePredicateEither.left().orElseGet(() -> lootTablePredicateLootTablePredicateEither.right().orElseThrow()), -// lootTablePredicate -> { -// if (lootTablePredicate.identifier != null && lootTablePredicate.type == null && (lootTablePredicate.functions == null || lootTablePredicate.functions.isEmpty())) { -// return Either.left(lootTablePredicate); -// } -// return Either.right(lootTablePredicate); -// }); -// -// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me -// private LootTablePredicate(@NotNull Optional optionalIdentifier, @NotNull Optional optionalType, @NotNull Optional>> optionalFunctions) { -// this(optionalIdentifier.orElse(null), optionalType.orElse(null), optionalFunctions.map(functionsEither -> functionsEither.map(List::of, patterns -> patterns)).orElse(null)); -// } -// -// public LootTablePredicate(@NotNull Pattern pattern) { -// this(pattern, null, null); -// } -// -// private Optional optionalIdentifier() { -// return Optional.ofNullable(identifier); -// } -// private Optional optionalType() { -// return Optional.ofNullable(type); -// } -// private Optional>> optionalFunctions() { -// if (functions == null) return Optional.empty(); -// if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); -// return Optional.of(Either.right(functions)); -// } -// -// // TODO: also loops over pools and checks that -// @Override -// public boolean test(final @NotNull LootTable table, final @NotNull Identifier tableId) { -// boolean result = true; -// -// if (identifier != null) { -// result = identifier.matcher(tableId.toString()).matches(); -// } -// -// if (type != null) { -// result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); -// } -// -// if (functions != null) { -// for (LootFunctionPredicate functionPredicate : functions) { -// result = result && table.functions.stream().anyMatch(functionPredicate::matches); -// } -// } -// -// return result; -// } -//} -// \ No newline at end of file +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.entity.EntityType; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.registry.RegistryKey; +import net.minecraft.util.Identifier; +import net.minecraft.util.context.ContextType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.regex.Pattern; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; + + +public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.either(Util.PATTERN_CODEC, Util.PATTERN_CODEC.listOf()).optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalEitherIdentifier), + Codec.either(Util.PATTERN_CODEC, Util.PATTERN_CODEC.listOf()).optionalFieldOf("types").forGetter(LootTablePredicate::optionalEitherType) + ).apply(instance, LootTablePredicate::new)); + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me + private LootTablePredicate(@NotNull Optional>> optionalIdentifier, @NotNull Optional>> optionalType) { + this( + optionalIdentifier.map(it -> it.map(List::of, it2 -> it2)).orElse(null), + optionalType.map(it -> it.map(List::of, it2 -> it2)).orElse(null) + ); + } + + private Optional>> optionalEitherIdentifier() { + if (identifiers == null || identifiers.isEmpty()) return Optional.empty(); + + if (identifiers.size() == 1) return Optional.of(Either.left(identifiers.get(0))); + return Optional.of(Either.right(identifiers)); + } + private Optional>> optionalEitherType() { + if (types == null || types.isEmpty()) return Optional.empty(); + + if (types.size() == 1) return Optional.of(Either.left(types.get(0))); + return Optional.of(Either.right(types)); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.LOOT_TABLE; + } + + @Override + public boolean test(@NotNull LootModifierContext context) { + boolean result = true; + + if (identifiers != null) { + boolean idResult = false; + final String tableIdString = context.tableId().toString(); + for (Pattern pattern : identifiers) idResult = idResult || pattern.matcher(tableIdString).matches(); + result = idResult; + } + + if (types != null) { + boolean typeResult = false; + final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); + for (Pattern pattern : types) typeResult = typeResult || pattern.matcher(tableTypeString).matches(); + result = result && typeResult; + } + + return result; + } + + public static LootTablePredicate.Builder builder() { + return new LootTablePredicate.Builder(); + } + + public static class Builder implements LootModifierPredicate.Builder { + private final ImmutableList.Builder names = ImmutableList.builder(); + private final ImmutableList.Builder types = ImmutableList.builder(); + + + public LootTablePredicate.Builder name(@NotNull EntityType... names) { + for (EntityType name : names) name(EntityLootTableIdGetter.get.apply(name)); + return this; + } + public LootTablePredicate.Builder name(@NotNull RegistryKey... names) { + for (RegistryKey name : names) name(name.getValue()); + return this; + } + public LootTablePredicate.Builder name(@NotNull Identifier... names) { + for (Identifier name : names) name(name.toString()); + return this; + } + public LootTablePredicate.Builder name(@NotNull String... names) { + for (String name : names) this.names.add(Pattern.compile(Pattern.quote(name))); + return this; + } + public LootTablePredicate.Builder name(@NotNull Pattern... names) { + this.names.add(names); + return this; + } + + public LootTablePredicate.Builder type(@NotNull ContextType... types) { + final BiMap inverse = LootContextTypes.MAP.inverse(); + for (ContextType type : types) type(inverse.get(type)); + return this; + } + public LootTablePredicate.Builder type(@NotNull Identifier... types) { + for (Identifier type : types) type(type.toString()); + return this; + } + public LootTablePredicate.Builder type(@NotNull String... types) { + for (String type : types) type(Pattern.compile(Pattern.quote(type))); + return this; + } + public LootTablePredicate.Builder type(@NotNull Pattern... types) { + this.types.add(types); + return this; + } + + public LootTablePredicate build() { + return new LootTablePredicate(names.build(), types.build()); + } + + private static class EntityLootTableIdGetter { + // Resolver returns the provided name (like 'method_16351') when it fails to map it + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); + + // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); + + public static final Function, Identifier> get; + + // Should be executed when class is first loaded/accessed + static { + try { + //final Class entityType = EntityType.class; + final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); + final Method method; + + // 1.21.2 to future:tm: + if (isMethod(entityType, V1d21d2)) { + method = entityType.getDeclaredMethod(V1d21d2); + method.setAccessible(true); + get = entity -> { + try { + @SuppressWarnings("unchecked") + final Optional> optional = (Optional>) method.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if (isMethod(entityType, V1d20d5)) { + method = entityType.getDeclaredMethod(V1d20d5); + method.setAccessible(true); + get = entity -> { + try { + return ((RegistryKey) method.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } catch (NoSuchMethodException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static boolean isMethod(Class clazz, String method) { + try { + clazz.getDeclaredMethod(method); + return true; + } catch (NoSuchMethodException e) { + LOGGER.warn("", e); + return false; + } + } + } + } +} From e203e3a2a50597ec10da67a1068ecd8c0c8b5f48 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:23:11 +0300 Subject: [PATCH 13/53] Add OptionalPattern By default, stuff will be turned into literal matchers. Regex can be enabled by setting `regex` to true. This way literal strings won't need any escaping or surrounded with `\Qtext\E` --- .../datagen/LootTableModifierDatagen.java | 5 +- .../resource/OptionalPattern.java | 63 +++++++++++++++++++ .../resource/predicate/Util.java | 13 ---- .../condition/LootConditionPredicate.java | 8 +-- .../predicate/entry/ItemEntryPredicate.java | 9 +-- .../function/LootFunctionPredicate.java | 1 - .../predicate/table/LootTablePredicate.java | 30 ++++----- 7 files changed, 89 insertions(+), 40 deletions(-) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 1d879c8..288702d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -25,6 +25,7 @@ import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.datagen.NewNewLootModifierProvider; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.SetItemAction; import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; @@ -102,7 +103,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("replace_ingots_with_command_block"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(Pattern.compile("minecraft:.*_ingot")) + ItemEntryPredicate.builder(OptionalPattern.compile("minecraft:.*_ingot")) ) .action( SetItemAction.builder(Items.COMMAND_BLOCK) @@ -112,7 +113,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("sugarcane_drop_tnt"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(Pattern.compile("minecraft:sugar_cane")) + ItemEntryPredicate.builder(OptionalPattern.compile(Pattern.quote("minecraft:sugar_cane"))) ) .action( AddPoolAction.builder( diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java new file mode 100644 index 0000000..4dc8493 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java @@ -0,0 +1,63 @@ +package top.offsetmonkey538.loottablemodifier.resource; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public record OptionalPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { + private static final Codec INLINE_CODEC = Codec.STRING.xmap(OptionalPattern::new, OptionalPattern::patternString); + private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.BOOL.optionalFieldOf("regex", false).forGetter(OptionalPattern::isRegex), + Codec.STRING.fieldOf("pattern").forGetter(OptionalPattern::patternString) + ).apply(instance, OptionalPattern::new)); + public static final Codec CODEC = Codec.either( + INLINE_CODEC, + FULL_CODEC + ).xmap( + either -> either.map(it -> it, it -> it), + pattern -> pattern.isRegex ? Either.right(pattern) : Either.left(pattern) + ); + + private OptionalPattern(final String stringPattern) { + this( + false, + stringPattern, + Pattern.compile(Pattern.quote(stringPattern)) + ); + } + + private OptionalPattern(final boolean isRegex, final String stringPattern) { + this( + isRegex, + stringPattern, + Pattern.compile(isRegex ? stringPattern : Pattern.quote(stringPattern)) + ); + } + + public static OptionalPattern compile(String patternString) { + return new OptionalPattern( + true, + patternString + ); + } + + public static OptionalPattern literal(String patternString) { + return new OptionalPattern( + patternString + ); + } + + public Matcher matcher(CharSequence input) { + return pattern.matcher(input); + } + + public boolean matches(CharSequence input) { + if (isRegex) return matcher(input).matches(); + return patternString.contentEquals(input); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java deleted file mode 100644 index 41e6cb0..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/Util.java +++ /dev/null @@ -1,13 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate; - -import com.mojang.serialization.Codec; - -import java.util.regex.Pattern; - -public final class Util { - private Util() { - - } - - public static final Codec PATTERN_CODEC = Codec.STRING.xmap(Pattern::compile, Pattern::pattern); -} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java index 5742100..f6c3ec6 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java @@ -5,12 +5,10 @@ import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; +import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; -import java.util.regex.Pattern; - -public record LootConditionPredicate(@NotNull Pattern functionPattern) { - public static final Codec CODEC = Util.PATTERN_CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); +public record LootConditionPredicate(@NotNull OptionalPattern functionPattern) { + public static final Codec CODEC = OptionalPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); public boolean matches(final @NotNull LootCondition condition) { final Identifier functionId = Registries.LOOT_CONDITION_TYPE.getId(condition.getType()); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java index 8716c8d..7c0f8bf 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java @@ -7,15 +7,15 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; import java.util.regex.Pattern; -public record ItemEntryPredicate(Pattern name) implements LootModifierPredicate { +public record ItemEntryPredicate(OptionalPattern name) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(Util.PATTERN_CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) + instance -> instance.group(OptionalPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) ); @Override @@ -30,7 +30,8 @@ public boolean test(@NotNull LootModifierContext context) { return name.matcher(itemEntry.item.getIdAsString()).matches(); } - public static LootModifierPredicate.Builder builder(Pattern name) { + // TODO: overrides which can take like identifiers and items and shiz + public static LootModifierPredicate.Builder builder(OptionalPattern name) { return () -> new ItemEntryPredicate(name); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java index e011083..d3ea88e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java @@ -5,7 +5,6 @@ import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; import java.util.regex.Pattern; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java index 56f06b2..63aece1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java @@ -18,9 +18,9 @@ import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.Util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -32,27 +32,27 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; -public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { +public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Codec.either(Util.PATTERN_CODEC, Util.PATTERN_CODEC.listOf()).optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalEitherIdentifier), - Codec.either(Util.PATTERN_CODEC, Util.PATTERN_CODEC.listOf()).optionalFieldOf("types").forGetter(LootTablePredicate::optionalEitherType) + Codec.either(OptionalPattern.CODEC, OptionalPattern.CODEC.listOf()).optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalEitherIdentifier), + Codec.either(OptionalPattern.CODEC, OptionalPattern.CODEC.listOf()).optionalFieldOf("types").forGetter(LootTablePredicate::optionalEitherType) ).apply(instance, LootTablePredicate::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Optional>> optionalIdentifier, @NotNull Optional>> optionalType) { + private LootTablePredicate(@NotNull Optional>> optionalIdentifier, @NotNull Optional>> optionalType) { this( optionalIdentifier.map(it -> it.map(List::of, it2 -> it2)).orElse(null), optionalType.map(it -> it.map(List::of, it2 -> it2)).orElse(null) ); } - private Optional>> optionalEitherIdentifier() { + private Optional>> optionalEitherIdentifier() { if (identifiers == null || identifiers.isEmpty()) return Optional.empty(); if (identifiers.size() == 1) return Optional.of(Either.left(identifiers.get(0))); return Optional.of(Either.right(identifiers)); } - private Optional>> optionalEitherType() { + private Optional>> optionalEitherType() { if (types == null || types.isEmpty()) return Optional.empty(); if (types.size() == 1) return Optional.of(Either.left(types.get(0))); @@ -71,14 +71,14 @@ public boolean test(@NotNull LootModifierContext context) { if (identifiers != null) { boolean idResult = false; final String tableIdString = context.tableId().toString(); - for (Pattern pattern : identifiers) idResult = idResult || pattern.matcher(tableIdString).matches(); + for (OptionalPattern pattern : identifiers) idResult = idResult || pattern.matcher(tableIdString).matches(); result = idResult; } if (types != null) { boolean typeResult = false; final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); - for (Pattern pattern : types) typeResult = typeResult || pattern.matcher(tableTypeString).matches(); + for (OptionalPattern pattern : types) typeResult = typeResult || pattern.matcher(tableTypeString).matches(); result = result && typeResult; } @@ -90,8 +90,8 @@ public static LootTablePredicate.Builder builder() { } public static class Builder implements LootModifierPredicate.Builder { - private final ImmutableList.Builder names = ImmutableList.builder(); - private final ImmutableList.Builder types = ImmutableList.builder(); + private final ImmutableList.Builder names = ImmutableList.builder(); + private final ImmutableList.Builder types = ImmutableList.builder(); public LootTablePredicate.Builder name(@NotNull EntityType... names) { @@ -107,10 +107,10 @@ public LootTablePredicate.Builder name(@NotNull Identifier... names) { return this; } public LootTablePredicate.Builder name(@NotNull String... names) { - for (String name : names) this.names.add(Pattern.compile(Pattern.quote(name))); + for (String name : names) this.names.add(OptionalPattern.literal(name)); return this; } - public LootTablePredicate.Builder name(@NotNull Pattern... names) { + public LootTablePredicate.Builder name(@NotNull OptionalPattern... names) { this.names.add(names); return this; } @@ -125,10 +125,10 @@ public LootTablePredicate.Builder type(@NotNull Identifier... types) { return this; } public LootTablePredicate.Builder type(@NotNull String... types) { - for (String type : types) type(Pattern.compile(Pattern.quote(type))); + for (String type : types) this.types.add(OptionalPattern.literal(type)); return this; } - public LootTablePredicate.Builder type(@NotNull Pattern... types) { + public LootTablePredicate.Builder type(@NotNull OptionalPattern... types) { this.types.add(types); return this; } From db4271d9f9b75e34db920bdbf52c8fc695f8ddef Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:36:22 +0300 Subject: [PATCH 14/53] Add overloads for 'ItemEntryPredicate.builder()' take take identifier and item Turns out 'Item.getRegistryEntry()' is *actually* deprecated so using Registries.ITEM.get... instead --- .../datagen/LootTableModifierDatagen.java | 2 +- .../loottablemodifier/resource/action/SetItemAction.java | 4 ++-- .../resource/predicate/entry/ItemEntryPredicate.java | 9 ++++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 288702d..fb12b1c 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -113,7 +113,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("sugarcane_drop_tnt"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(OptionalPattern.compile(Pattern.quote("minecraft:sugar_cane"))) + ItemEntryPredicate.builder(Items.SUGAR_CANE) ) .action( AddPoolAction.builder( diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java index 5e2c848..60f0512 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java @@ -11,6 +11,7 @@ import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntryTypes; +import net.minecraft.registry.Registries; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; @@ -69,8 +70,7 @@ public static class Builder implements LootModifierAction.Builder { private boolean canReplaceEntry; private Builder(@NotNull ItemConvertible item) { - //noinspection deprecation Minecraft seems to use it still? - this.item = item.asItem().getRegistryEntry(); + this.item = Registries.ITEM.getEntry(item.asItem()); } public SetItemAction.Builder setCanReplaceEntry(boolean canReplaceEntry) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java index 7c0f8bf..334970e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java @@ -4,6 +4,8 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.item.ItemConvertible; import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; @@ -30,7 +32,12 @@ public boolean test(@NotNull LootModifierContext context) { return name.matcher(itemEntry.item.getIdAsString()).matches(); } - // TODO: overrides which can take like identifiers and items and shiz + public static LootModifierPredicate.Builder builder(ItemConvertible name) { + return builder(Registries.ITEM.getId(name.asItem())); + } + public static LootModifierPredicate.Builder builder(Identifier name) { + return () -> new ItemEntryPredicate(OptionalPattern.literal(name.toString())); + } public static LootModifierPredicate.Builder builder(OptionalPattern name) { return () -> new ItemEntryPredicate(name); } From 9c8f78c4e284c85a159233fe59999794cb19ba37 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:47:08 +0300 Subject: [PATCH 15/53] Use 'OptionalPattern#matches' instead of 'OptionalPattern#matcher' --- .../loottablemodifier/resource/OptionalPattern.java | 8 ++++++++ .../predicate/condition/LootConditionPredicate.java | 2 +- .../resource/predicate/entry/ItemEntryPredicate.java | 2 +- .../resource/predicate/table/LootTablePredicate.java | 4 ++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java index 4dc8493..3286128 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java @@ -52,6 +52,14 @@ public static OptionalPattern literal(String patternString) { ); } + /** + * Not deprecated for removal. + *

+ * Use this only if you really need an instance of the {@link Matcher}. Otherwise, use {@link #matches(CharSequence)}, because it removes the overhead the {@link Matcher} when {@link #isRegex} is false. + * @see #matches(CharSequence) + */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated() public Matcher matcher(CharSequence input) { return pattern.matcher(input); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java index f6c3ec6..af00d83 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java @@ -14,6 +14,6 @@ public boolean matches(final @NotNull LootCondition condition) { final Identifier functionId = Registries.LOOT_CONDITION_TYPE.getId(condition.getType()); if (functionId == null) return false; - return functionPattern.matcher(functionId.toString()).matches(); + return functionPattern.matches(functionId.toString()); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java index 334970e..26d9504 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java @@ -29,7 +29,7 @@ public LootModifierPredicateType getType() { public boolean test(@NotNull LootModifierContext context) { if (!(context.entry() instanceof ItemEntry itemEntry)) return false; - return name.matcher(itemEntry.item.getIdAsString()).matches(); + return name.matches(itemEntry.item.getIdAsString()); } public static LootModifierPredicate.Builder builder(ItemConvertible name) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java index 63aece1..f599948 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java @@ -71,14 +71,14 @@ public boolean test(@NotNull LootModifierContext context) { if (identifiers != null) { boolean idResult = false; final String tableIdString = context.tableId().toString(); - for (OptionalPattern pattern : identifiers) idResult = idResult || pattern.matcher(tableIdString).matches(); + for (OptionalPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); result = idResult; } if (types != null) { boolean typeResult = false; final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); - for (OptionalPattern pattern : types) typeResult = typeResult || pattern.matcher(tableTypeString).matches(); + for (OptionalPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); result = result && typeResult; } From dc0a8bb37a0797e8db325e4af683f4983937b237 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:57:51 +0300 Subject: [PATCH 16/53] Fix modifiers being applied multiple times when target table has multiple pools tableModified check was inside the LootPool loop poolModified check was inside the LootPoolEntry loop such a dumbass :facepalm: --- .../loottablemodifier/LootTableModifier.java | 8 ++++---- .../loottablemodifier/resource/LootModifier.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 0aead55..c044f27 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -85,11 +85,11 @@ public static void runModification(ResourceManager resourceManager, Registry modifierEntry : modifiers.entrySet()) { // todo: I'm creating a lot of these... Could it make more sense to not use a record so it's modifiable and then keep passing the same instance but modify the values? Think making the values in there protected would mean that only things in the same package could access it? So move it into this package (doesn't really make sense in 'resource' anyway) and that way I can make sure only this modifies stuff final LootModifierContext context = new LootModifierContext(table, tableId, pool, entry, tableModified, poolModified); @@ -108,10 +108,10 @@ public static void runModification(ResourceManager resourceManager, Registry modifierEntry : modifiers.entrySet()) { // final LootModifier modifier = modifierEntry.getValue(); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 795dc3a..2aac078 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -117,7 +117,7 @@ private static LootModifier fromCurrentCodec(Either Date: Fri, 13 Jun 2025 18:02:47 +0300 Subject: [PATCH 17/53] Use same json writing logic as vanilla (ordering, etc) and export modified loot tables after /reload command too --- .../loottablemodifier/LootTableModifier.java | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index c044f27..b744007 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -1,10 +1,9 @@ package top.offsetmonkey538.loottablemodifier; import com.google.common.base.Stopwatch; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import com.google.gson.stream.JsonWriter; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; @@ -13,6 +12,7 @@ import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.data.DataProvider; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; import net.minecraft.loot.entry.LootPoolEntry; @@ -23,7 +23,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.text.Text; import net.minecraft.util.Identifier; -import org.apache.commons.io.FileUtils; +import net.minecraft.util.JsonHelper; import org.apache.commons.io.file.PathUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -35,6 +35,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -45,6 +46,8 @@ public class LootTableModifier implements ModInitializer { public static final String MOD_ID = "loot-table-modifier"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + private static MinecraftServer minecraftServer; + public static final boolean IS_DEV; static { final String isDev = System.getProperty("lootTableModifierDev", ""); @@ -138,7 +141,7 @@ public static void runModification(ResourceManager resourceManager, Registry exportModifiedTables(modifiedTableIds, server)); + ServerLifecycleEvents.SERVER_STARTED.register(server -> { + minecraftServer = server; + exportModifiedTables(modifiedTableIds, server); + }); } private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { @@ -179,7 +191,7 @@ private static Map loadModifiers(ResourceManager resou } } - LOGGER.info("Loaded {} loot modifiers in {}!", result.size(), stopwatch); + LOGGER.info("Loaded {} loot modifiers in {}!", result.size(), stopwatch.stop()); return result; } @@ -188,10 +200,10 @@ private static void exportModifiedTables(List tableIDs, MinecraftSer final DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, server.getRegistryManager()); try { final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier_export"); - PathUtils.deleteDirectory(exportDir); - LOGGER.warn("Exporting modified tables to {}", exportDir); + if (Files.exists(exportDir)) PathUtils.deleteDirectory(exportDir); - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + LOGGER.warn("Exporting modified tables to {}", exportDir); + final Stopwatch stopwatch = Stopwatch.createStarted(); for (Identifier id : tableIDs) { final LootTable table = server.getReloadableRegistries().getLootTable(RegistryKey.of(RegistryKeys.LOOT_TABLE, id)); @@ -203,11 +215,15 @@ private static void exportModifiedTables(List tableIDs, MinecraftSer final Optional optionalResult = dataResult.resultOrPartial(LOGGER::error); final JsonElement result = optionalResult.orElseThrow(); - LOGGER.warn("Writing loot table to {}", file); - Files.writeString(file, gson.toJson(result)); - } + try (JsonWriter jsonWriter = new JsonWriter(Files.newBufferedWriter(file, StandardCharsets.UTF_8))) { + jsonWriter.setSerializeNulls(false); + jsonWriter.setIndent(" "); + JsonHelper.writeSorted(jsonWriter, result, DataProvider.JSON_KEY_SORTING_COMPARATOR); + } + } + LOGGER.warn("Exported {} modified tables in {}", tableIDs.size(), stopwatch.stop()); } catch (IOException e) { throw new RuntimeException("Failed to export modified tables!", e); } From 11c3fbe09714119926584bf83cb2c8d41092d49f Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 19:25:56 +0300 Subject: [PATCH 18/53] Allow empty tables to be modified Pool and entry in context are @Nullable again --- .../loottablemodifier/LootTableModifier.java | 27 +++++++++++-------- .../datagen/LootTableModifierDatagen.java | 18 +++++++++++++ .../resource/LootModifierContext.java | 3 ++- .../resource/action/SetItemAction.java | 4 ++- .../predicate/entry/ItemEntryPredicate.java | 1 + 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index b744007..63efa67 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -26,6 +26,7 @@ import net.minecraft.util.JsonHelper; import org.apache.commons.io.file.PathUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; @@ -67,18 +68,14 @@ public void onInitialize() { public static void runModification(ResourceManager resourceManager, Registry lootRegistry, RegistryOps registryOps) { final Map modifiers = loadModifiers(resourceManager, registryOps); - final Map failedModifiers = new HashMap<>(0); - final List modifiedTableIds = new ArrayList<>(); // todo: will be used for exporting modified ones + final List modifiedTableIds = new ArrayList<>(); // Used for exporting modified ones int poolsModified = 0, entriesModified = 0; boolean tableModified, poolModified; + LOGGER.info("Modifying loot tables..."); final Stopwatch stopwatch = Stopwatch.createStarted(); - // TODO: Would looping through all tables here be faster than doing that for each modifier? To test I guess - - // TODO: so about that never-nesting............ - // fixme: don't think modifying for example tables without any pools or entries would work with this..... Maybe try a do-while instead of for loop? Then I'd have to for (Iterator> it = getRegistryAsWrapper(lootRegistry).streamEntries().iterator(); it.hasNext(); ) { final RegistryEntry.Reference registryEntry = it.next(); final RegistryKey key = registryEntry.registryKey(); @@ -87,14 +84,21 @@ public static void runModification(ResourceManager resourceManager, Registry poolsCopy = new LinkedList<>(table.pools); + int poolsSize = Math.max(1, poolsCopy.size()); + for (int i = 0; i < poolsSize; i++) { + final @Nullable LootPool pool = poolsCopy.isEmpty() ? null : poolsCopy.get(i); poolModified = false; - for (LootPoolEntry entry : pool.entries) { + + final List entriesCopy = pool == null ? List.of() : new LinkedList<>(pool.entries); + int entriesSize = Math.max(1, entriesCopy.size()); + for (int j = 0; j < entriesSize; j++) { + final @Nullable LootPoolEntry entry = entriesCopy.isEmpty() ? null : entriesCopy.get(j); + for (Map.Entry modifierEntry : modifiers.entrySet()) { - // todo: I'm creating a lot of these... Could it make more sense to not use a record so it's modifiable and then keep passing the same instance but modify the values? Think making the values in there protected would mean that only things in the same package could access it? So move it into this package (doesn't really make sense in 'resource' anyway) and that way I can make sure only this modifies stuff + // Everything is so fast anyway that there's probably no point in doing what the to-do here said final LootModifierContext context = new LootModifierContext(table, tableId, pool, entry, tableModified, poolModified); final LootModifier modifier = modifierEntry.getValue(); @@ -112,6 +116,7 @@ public static void runModification(ResourceManager resourceManager, Registry newEntriesBuilder = ImmutableList.builder(); for (LootPoolEntry originalEntry : pool.entries) { - if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? TODO: test + if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? newEntriesBuilder.add(originalEntry); } ((LootPoolAccessor) context.pool()).setEntries(newEntriesBuilder.build()); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java index 26d9504..2fb1731 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java @@ -27,6 +27,7 @@ public LootModifierPredicateType getType() { @Override public boolean test(@NotNull LootModifierContext context) { + // No need for a separate null check of entry as null isn't an instance of ItemEntry if (!(context.entry() instanceof ItemEntry itemEntry)) return false; return name.matches(itemEntry.item.getIdAsString()); From d586a815bb4a796ed5316ef0b122a61f160ff4a5 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 19:31:45 +0300 Subject: [PATCH 19/53] Remove New datagen provider, rename NewNew provider --- ...rovider.java => LootModifierProvider.java} | 7 +- .../api/datagen/NewLootModifierProvider.java | 251 ------------------ .../datagen/LootTableModifierDatagen.java | 13 +- 3 files changed, 4 insertions(+), 267 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/{NewNewLootModifierProvider.java => LootModifierProvider.java} (94%) delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java similarity index 94% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java index 1fbf4bc..c67ec2c 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewNewLootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java @@ -12,16 +12,13 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.stream.Stream; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; @@ -48,10 +45,10 @@ * } * } */ -public abstract class NewNewLootModifierProvider extends FabricCodecDataProvider { +public abstract class LootModifierProvider extends FabricCodecDataProvider { private BiConsumer provider; - public NewNewLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + public LootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", LootModifier.CODEC); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java deleted file mode 100644 index 3d636ea..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/NewLootModifierProvider.java +++ /dev/null @@ -1,251 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.datagen; - -import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; -import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.MappingResolver; -import net.minecraft.data.DataOutput; -import net.minecraft.entity.EntityType; -import net.minecraft.loot.LootTable; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryWrapper; -import net.minecraft.util.Identifier; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; - -/** - * FIXME: wrong - * A datagen provider for creating loot modifiers. - *
- * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@code addModifier(...)} methods to add modifiers. - *
- *

{@code
- * @Override
- * protected void generate(RegistryWrapper.WrapperLookup lookup) {
- *     addModifier(
- *             Identifier.of("testmod", "drop_tnt"),
- *             LootPool.builder()
- *                     .rolls(ConstantLootNumberProvider.create(1))
- *                     .with(
- *                             ItemEntry.builder(Items.TNT)
- *                                     .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
- *                     ),
- *             EntityType.CREEPER,
- *             EntityType.ZOMBIE
- *     );
- * }
- * }
- */ -public abstract class NewLootModifierProvider extends FabricCodecDataProvider { - private BiConsumer provider; - - public NewLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { - super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, MOD_ID + "/loot_modifier", LootModifier.CODEC); - } - - @Override - protected void configure(BiConsumer provider, RegistryWrapper.WrapperLookup lookup) { - this.provider = provider; - generate(lookup); - } - - @Override - public String getName() { - return "New Loot Table Modifiers"; - } - - /** - * Override and use {@code addModifier()} methods to add modifiers. - * - * @param lookup A lookup for registries. - */ - protected abstract void generate(RegistryWrapper.WrapperLookup lookup); - - /** - * Adds a new loot table modifier for the given {@link EntityType}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link EntityType} to add the modifier to - * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to - */ - protected void addModifier(Identifier name, LootModifierAction.Builder builder, EntityType modifies, EntityType... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link EntityType}s. - * - * @param name Name of this modifier - * @param builders The loot poolPredicates to add - * @param modifies The {@link EntityType} to add the modifier to - * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, EntityType modifies, EntityType... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(EntityLootTableIdGetter.get).toList() - ); - } - - /** - * Adds a new loot table modifier for the given {@link RegistryKey}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link RegistryKey} to add the modifier to - * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to - */ - protected void addModifier(Identifier name, LootModifierAction.Builder builder, RegistryKey modifies, RegistryKey... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link RegistryKey}s. - * - * @param name Name of this modifier - * @param builders The loot poolPredicates to add - * @param modifies The {@link RegistryKey} to add the modifier to - * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, RegistryKey modifies, RegistryKey... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(RegistryKey::getValue).toList() - ); - } - - /** - * Adds a new loot table modifier for the given {@link Identifier}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link Identifier} to add the modifier to - * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to - */ - protected void addModifier(Identifier name, LootModifierAction.Builder builder, Identifier modifies, Identifier... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link Identifier}s. - * - * @param name Name of this modifier - * @param builders The loot poolPredicates to add - * @param modifies The {@link Identifier} to add the modifier to - * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, Identifier modifies, Identifier... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).toList() - ); - } - - private void addModifier(Identifier name, List builders, List modifies) { - //provider.accept(name, new LootModifier( - // builders.stream() - // .map(LootModifierAction.Builder::build) - // .toList(), - // null - // //FIXME: modifies.stream().map(identifier -> new LootTablePredicate(Pattern.compile(identifier.toString()))).toList() - //)); - } - - - - private static class EntityLootTableIdGetter { - // Resolver returns the provided name (like 'method_16351') when it fails to map it - private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); - - private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); - private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); - - // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); - - public static final Function, Identifier> get; - - // Should be executed when class is first loaded/accessed - static { - try { - //final Class entityType = EntityType.class; - final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); - final Method method; - - // 1.21.2 to future:tm: - if (isMethod(entityType, V1d21d2)) { - method = entityType.getDeclaredMethod(V1d21d2); - method.setAccessible(true); - get = entity -> { - try { - @SuppressWarnings("unchecked") - final Optional> optional = (Optional>) method.invoke(entity); - if (optional.isPresent()) return optional.get().getValue(); - throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - // 1.20.5 to 1.21.1 - else if (isMethod(entityType, V1d20d5)) { - method = entityType.getDeclaredMethod(V1d20d5); - method.setAccessible(true); - get = entity -> { - try { - return ((RegistryKey) method.invoke(entity)).getValue(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - else { - throw new IllegalStateException("No valid way to get entity loot table id found!"); - } - } catch (NoSuchMethodException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private static boolean isMethod(Class clazz, String method) { - try { - clazz.getDeclaredMethod(method); - return true; - } catch (NoSuchMethodException e) { - LOGGER.warn("", e); - return false; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 40cfffa..6436c99 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -3,27 +3,21 @@ import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; -import net.fabricmc.fabric.api.datagen.v1.provider.FabricLootTableProvider; import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider; -import net.minecraft.data.DataOutput; -import net.minecraft.data.loottable.LootTableProvider; import net.minecraft.entity.EntityType; import net.minecraft.item.Items; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; -import net.minecraft.loot.LootTables; import net.minecraft.loot.context.LootContextTypes; import net.minecraft.loot.entry.ItemEntry; -import net.minecraft.loot.function.EnchantWithLevelsLootFunction; import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; -import net.minecraft.util.context.ContextType; import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; -import top.offsetmonkey538.loottablemodifier.api.datagen.NewNewLootModifierProvider; +import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; @@ -31,11 +25,8 @@ import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; -import java.util.List; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; -import java.util.regex.Pattern; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -92,7 +83,7 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { // ); // } //} - private static class NewModLootModifierProvider extends NewNewLootModifierProvider { + private static class NewModLootModifierProvider extends LootModifierProvider { public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { super(dataOutput, registriesFuture); } From 0ecdb8de690a5ef7c25f074be2483defb65d9b2d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 13 Jun 2025 20:01:46 +0300 Subject: [PATCH 20/53] apply method javadoc --- .../resource/LootModifier.java | 42 +++---------------- .../resource/action/LootModifierAction.java | 8 +++- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 2aac078..666d6b6 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -14,6 +14,8 @@ import java.util.*; import java.util.function.Predicate; +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; + // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied TODO: I don't think I do this anymore? TODO: make sure that changing it to a normal List is fine public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) implements Predicate { //public record LootModifier(@NotNull @UnmodifiableView ArrayList actions) { @@ -73,51 +75,17 @@ private static LootModifier fromCurrentCodec(Either(predicatesEither.map(List::of, it -> it)) ); } - //private static Pair>, Either>> toCurrentCodec(LootModifier modifier) { - // final Either> actionsEither; - // final Either> predicatesEither; - - // if (modifier.actions.size() == 1) actionsEither = Either.left(modifier.actions.get(0)); - // else actionsEither = Either.right(modifier.actions); - // if (modifier.predicates.size() == 1) predicatesEither = Either.left(modifier.predicates.get(0)); - // else predicatesEither = Either.right(modifier.predicates); - - // return Pair.of(actionsEither, predicatesEither); - //} - - ///** - // * @param tableRegistry registry of loot tables to modify - // * @return amount of loot tables modified - // */ - //public int apply(final @NotNull Registry tableRegistry) { - // int modified = 0; - - // for (Iterator> it = getRegistryAsWrapper(tableRegistry).streamEntries().iterator(); it.hasNext(); ) { - // final RegistryEntry.Reference entry = it.next(); - - // final RegistryKey key = entry.registryKey(); - // final LootTable table = tableRegistry.get(key); - - // if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); - - // if (modifies.stream().noneMatch(predicate -> predicate.matches(table, key.getValue()))) continue; - - // modified += apply(table) ? 1 : 0; - // } - - // return modified; - //} - // TODO: count each time an action is applied? Maybe use the mask to reserve like a few bits to store how many times each was like done or idk /** * @param context context to modify - * @return true when any of the 'actions' could be applied, false otherwise + * @return highest modification level from applied actions. {@link LootModifierContext#MODIFIED_NONE} when no action was applied. + * @see LootModifierContext#MODIFIED_NONE * @see LootModifierContext#MODIFIED_TABLE * @see LootModifierContext#MODIFIED_POOL * @see LootModifierContext#MODIFIED_ENTRY */ public int apply(final @NotNull LootModifierContext context) { - int result = 0b0; + int result = MODIFIED_NONE; for (LootModifierAction action : actions) { result = result | action.apply(context); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java index d83719e..99e0e04 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierAction.java @@ -10,9 +10,13 @@ public interface LootModifierAction { LootModifierActionType getType(); /** - * Applies this action to the provided table + * Applies this action to the provided context * @param context the context to apply to - * @return true when table was modified, false otherwise + * @return the applied modification level + * @see LootModifierContext#MODIFIED_NONE + * @see LootModifierContext#MODIFIED_TABLE + * @see LootModifierContext#MODIFIED_POOL + * @see LootModifierContext#MODIFIED_ENTRY */ int apply(final @NotNull LootModifierContext context); From 4bda3d8a087dc79f8a19a7c44e1759ee75c45b59 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 14 Jun 2025 00:23:13 +0300 Subject: [PATCH 21/53] Add command for exporting loot tables instead of trying to do it automatically Initial solution wouldn't work with /reload, would run it multiple times after exiting and reentering a world, had all sorts of other problems with stuff not being a specific way at that poin tand whatever else Now it will (most likely) always run after a reload is fully complete. --- .../loottablemodifier/LootTableModifier.java | 100 +++++++++++------- 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 63efa67..43916c8 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -4,11 +4,12 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.gson.stream.JsonWriter; +import com.mojang.brigadier.context.CommandContext; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.FabricLoader; @@ -21,7 +22,9 @@ import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.server.MinecraftServer; -import net.minecraft.text.Text; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.*; +import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; import org.apache.commons.io.file.PathUtils; @@ -41,13 +44,15 @@ import java.nio.file.Path; import java.util.*; +import static net.minecraft.server.command.CommandManager.literal; import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; public class LootTableModifier implements ModInitializer { public static final String MOD_ID = "loot-table-modifier"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); - private static MinecraftServer minecraftServer; + // Only used when IS_DEV is true + private static final List MODIFIED_TABLE_IDs = Collections.synchronizedList(new ArrayList<>(0)); public static final boolean IS_DEV; static { @@ -62,8 +67,7 @@ public void onInitialize() { LootModifierActionTypes.register(); LootModifierPredicateTypes.register(); - if (IS_DEV) - ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); + if (IS_DEV) enableDebug(); } public static void runModification(ResourceManager resourceManager, Registry lootRegistry, RegistryOps registryOps) { @@ -162,17 +166,12 @@ public static void runModification(ResourceManager resourceManager, Registry { - minecraftServer = server; - exportModifiedTables(modifiedTableIds, server); - }); + LOGGER.warn("Dev mode enabled, modified loot tables can be exported using the '/loot-table-modifier debug export' command"); + synchronized (MODIFIED_TABLE_IDs) { + MODIFIED_TABLE_IDs.clear(); + MODIFIED_TABLE_IDs.addAll(modifiedTableIds); + } } private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { @@ -201,36 +200,61 @@ private static Map loadModifiers(ResourceManager resou return result; } - private static void exportModifiedTables(List tableIDs, MinecraftServer server) { - final DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, server.getRegistryManager()); - try { - final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier_export"); - if (Files.exists(exportDir)) PathUtils.deleteDirectory(exportDir); + private static void enableDebug() { + ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); + CommandRegistrationCallback.EVENT.register((dispatcher, commandRegistryAccess, registrationEnvironment) -> { + dispatcher.register( + literal(MOD_ID) + .then( + literal("debug") + .then( + literal("export") + .executes( + LootTableModifier::executeExportCommand + ) + ) + ) + ); + }); + } - LOGGER.warn("Exporting modified tables to {}", exportDir); - final Stopwatch stopwatch = Stopwatch.createStarted(); + private static int executeExportCommand(CommandContext context) { + synchronized (MODIFIED_TABLE_IDs) { + final ServerCommandSource source = context.getSource(); + final MinecraftServer server = source.getServer(); - for (Identifier id : tableIDs) { - final LootTable table = server.getReloadableRegistries().getLootTable(RegistryKey.of(RegistryKeys.LOOT_TABLE, id)); - final Path file = exportDir.resolve(id.getNamespace()).resolve(id.getPath() + ".json"); - Files.createDirectories(file.getParent()); + final DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, server.getRegistryManager()); + try { + final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier_export"); + if (Files.exists(exportDir)) PathUtils.deleteDirectory(exportDir); + + source.sendFeedback(() -> Text.literal("Exporting modified tables to ").append(Text.literal(exportDir.toString()).setStyle(Style.EMPTY.withUnderline(true).withColor(Formatting.WHITE).withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to copy"))).withClickEvent(new ClickEvent.CopyToClipboard(exportDir.toAbsolutePath().toString())))), true); + final Stopwatch stopwatch = Stopwatch.createStarted(); + + for (Identifier id : MODIFIED_TABLE_IDs) { + final LootTable table = server.getReloadableRegistries().getLootTable(RegistryKey.of(RegistryKeys.LOOT_TABLE, id)); + final Path file = exportDir.resolve(id.getNamespace()).resolve(id.getPath() + ".json"); + Files.createDirectories(file.getParent()); - LOGGER.warn("Exporting loot table to {}", file); - DataResult dataResult = LootTable.CODEC.encodeStart(ops, table); - final Optional optionalResult = dataResult.resultOrPartial(LOGGER::error); - final JsonElement result = optionalResult.orElseThrow(); + LOGGER.warn("Exporting loot table to {}", file); + DataResult dataResult = LootTable.CODEC.encodeStart(ops, table); + final Optional optionalResult = dataResult.resultOrPartial(LOGGER::error); + final JsonElement result = optionalResult.orElseThrow(); - LOGGER.warn("Writing loot table to {}", file); + LOGGER.warn("Writing loot table to {}", file); - try (JsonWriter jsonWriter = new JsonWriter(Files.newBufferedWriter(file, StandardCharsets.UTF_8))) { - jsonWriter.setSerializeNulls(false); - jsonWriter.setIndent(" "); - JsonHelper.writeSorted(jsonWriter, result, DataProvider.JSON_KEY_SORTING_COMPARATOR); + try (JsonWriter jsonWriter = new JsonWriter(Files.newBufferedWriter(file, StandardCharsets.UTF_8))) { + jsonWriter.setSerializeNulls(false); + jsonWriter.setIndent(" "); + JsonHelper.writeSorted(jsonWriter, result, DataProvider.JSON_KEY_SORTING_COMPARATOR); + } } + source.sendFeedback(() -> Text.literal("Exported %s modified tables in %s".formatted(MODIFIED_TABLE_IDs.size(), stopwatch.stop())), true); + } catch (IOException e) { + throw new RuntimeException("Failed to export modified tables!", e); } - LOGGER.warn("Exported {} modified tables in {}", tableIDs.size(), stopwatch.stop()); - } catch (IOException e) { - throw new RuntimeException("Failed to export modified tables!", e); + + return 1; } } From dc9ec6ef2d8340d22bf02f04b9b70c12fc0b9fd0 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 14 Jun 2025 00:25:09 +0300 Subject: [PATCH 22/53] Add remove_pool action Might work? Got too occupied with fixing the exporting stuff and have no idea if I made sure this worked or not :sweat_smile: --- .../loottablemodifier/LootTableModifier.java | 4 +- .../datagen/LootTableModifierDatagen.java | 60 +++++-------------- .../mixin/ReloadableRegistriesMixin.java | 1 - .../resource/LootModifier.java | 2 +- .../action}/LootModifierActionTypes.java | 12 ++-- .../resource/action/RemovePoolAction.java | 2 +- .../action/{ => entry}/SetItemAction.java | 12 ++-- .../action/{ => pool}/AddPoolAction.java | 17 +++--- .../action/pool/RemovePoolAction.java | 54 +++++++++++++++++ .../LootModifierPredicateTypes.java | 10 +--- .../predicate/entry/ItemEntryPredicate.java | 4 +- .../entry/leaf/EmptyEntryPredicate.java | 2 +- .../predicate/op/AllOfLootPredicate.java | 2 +- .../predicate/op/AnyOfLootPredicate.java | 3 +- .../predicate/op/InvertedLootPredicate.java | 3 +- .../predicate/op/TermsLootPredicate.java | 11 ---- .../predicate/pool/LootPoolPredicate.java | 2 +- .../predicate/table/LootTablePredicate.java | 3 +- 18 files changed, 100 insertions(+), 104 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/{api => resource/action}/LootModifierActionTypes.java (60%) rename src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/{ => entry}/SetItemAction.java (90%) rename src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/{ => pool}/AddPoolAction.java (73%) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java rename src/main/java/top/offsetmonkey538/loottablemodifier/{api => resource/predicate}/LootModifierPredicateTypes.java (74%) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 43916c8..07481f0 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -32,8 +32,8 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 6436c99..9e84279 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -10,18 +10,19 @@ import net.minecraft.loot.LootTable; import net.minecraft.loot.context.LootContextTypes; import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.loot.function.EnchantWithLevelsLootFunction; import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; -import top.offsetmonkey538.loottablemodifier.api.datagen.NewLootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; -import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.resource.action.SetItemAction; +import top.offsetmonkey538.loottablemodifier.resource.action.pool.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.resource.action.entry.SetItemAction; +import top.offsetmonkey538.loottablemodifier.resource.action.pool.RemovePoolAction; import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; @@ -40,49 +41,6 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { pack.addProvider(LootProvider::new); } - //private static class NewModLootModifierProvider extends NewLootModifierProvider { - // public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { - // super(dataOutput, registriesFuture); - // } - - // @Override - // protected void generate(RegistryWrapper.WrapperLookup lookup) { - // addModifier( - // id("drop_tnt"), - - - // AddPoolAction.builder( - // LootPool.builder() - // .rolls(ConstantLootNumberProvider.create(1)) - // .with( - // ItemEntry.builder(Items.TNT) - // .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - // ) - // ), - - // EntityType.CREEPER, - // EntityType.ZOMBIE - // ); - - // //todo: temp - // addModifier( - // id("test"), - - // AddPoolAction.builder( - // LootPool.builder() - // .with( - // ItemEntry.builder(Items.NETHERITE_SWORD).apply( - // EnchantWithLevelsLootFunction - // .builder(lookup, UniformLootNumberProvider.create(20, 39)) - // //.builder(UniformLootNumberProvider.create(20, 39)) - // ) - // ) - // ), - - // LootTables.ABANDONED_MINESHAFT_CHEST - // ); - // } - //} private static class NewModLootModifierProvider extends LootModifierProvider { public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { super(dataOutput, registriesFuture); @@ -154,6 +112,16 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ) ) ); + addModifier( + id("remove_pools_with_sticks"), + LootModifier.builder() + .conditionally( + ItemEntryPredicate.builder(Items.STICK) + ) + .action( + RemovePoolAction.builder() + ) + ); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java index 62d2659..cb65664 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java @@ -40,7 +40,6 @@ public abstract class ReloadableRegistriesMixin { ) private static void loottablemodifier$modifyLootTables(LootDataType lootDataType, ResourceManager resourceManager, RegistryOps registryOps, CallbackInfoReturnable> cir) { if (lootDataType != LootDataType.LOOT_TABLES) return; - //noinspection unchecked LootTableModifier.runModification(resourceManager, (Registry) cir.getReturnValue(), registryOps); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java index 666d6b6..92a0a7a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java @@ -7,7 +7,7 @@ import net.minecraft.loot.LootPool; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionTypes.java similarity index 60% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionTypes.java index b1249ce..e81dfbf 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionTypes.java @@ -1,13 +1,12 @@ -package top.offsetmonkey538.loottablemodifier.api; +package top.offsetmonkey538.loottablemodifier.resource.action; import com.mojang.serialization.MapCodec; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; -import top.offsetmonkey538.loottablemodifier.resource.action.SetItemAction; +import top.offsetmonkey538.loottablemodifier.resource.action.pool.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.resource.action.entry.SetItemAction; +import top.offsetmonkey538.loottablemodifier.resource.action.pool.RemovePoolAction; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -16,7 +15,8 @@ private LootModifierActionTypes() { } - public static final LootModifierActionType ADD_ENTRY = register(id("add_pool"), AddPoolAction.CODEC); + public static final LootModifierActionType ADD_POOL = register(id("add_pool"), AddPoolAction.CODEC); + public static final LootModifierActionType REMOVE_POOL = register(id("remove_pool"), RemovePoolAction.CODEC); public static final LootModifierActionType SET_ITEM = register(id("set_item"), SetItemAction.CODEC); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java index b7715ea..8eb5e88 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java @@ -6,7 +6,7 @@ //import net.minecraft.loot.LootPool; //import net.minecraft.loot.LootTable; //import org.jetbrains.annotations.NotNull; -//import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +//import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; //import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; //import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; //import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/entry/SetItemAction.java similarity index 90% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/entry/SetItemAction.java index 7bc9155..4659c39 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/entry/SetItemAction.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.action; +package top.offsetmonkey538.loottablemodifier.resource.action.entry; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; @@ -7,21 +7,17 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemConvertible; import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.loot.entry.LootPoolEntryTypes; import net.minecraft.registry.Registries; import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.ItemEntryAccessor; import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; -import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; - -import java.util.List; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_ENTRY; import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/AddPoolAction.java similarity index 73% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/AddPoolAction.java index c313ff3..617761a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/AddPoolAction.java @@ -1,18 +1,21 @@ -package top.offsetmonkey538.loottablemodifier.resource.action; +package top.offsetmonkey538.loottablemodifier.resource.action.pool; import com.google.common.collect.ImmutableList; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.LootTableModifier; -import top.offsetmonkey538.loottablemodifier.api.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; import java.util.List; +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_POOL; + public record AddPoolAction(List pools) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( LootPool.CODEC.listOf().fieldOf("pools").forGetter(AddPoolAction::pools) @@ -20,12 +23,12 @@ public record AddPoolAction(List pools) implements LootModifierAction @Override public LootModifierActionType getType() { - return LootModifierActionTypes.ADD_ENTRY; + return LootModifierActionTypes.ADD_POOL; } @Override public int apply(@NotNull LootModifierContext context) { - if (context.tableAlreadyModified()) return LootModifierContext.MODIFIED_NONE; + if (context.tableAlreadyModified()) return MODIFIED_NONE; final List newPools = ImmutableList.builder() .addAll(context.table().pools) @@ -34,7 +37,7 @@ public int apply(@NotNull LootModifierContext context) { ((LootTableAccessor) context.table()).setPools(newPools); - return LootModifierContext.MODIFIED_TABLE; + return MODIFIED_POOL; } public static AddPoolAction.Builder builder(@NotNull LootPool.Builder poolBuilder) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java new file mode 100644 index 0000000..76a6214 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java @@ -0,0 +1,54 @@ +package top.offsetmonkey538.loottablemodifier.resource.action.pool; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapCodec; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.LootPoolEntry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; + +import java.util.List; + +import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; + +public record RemovePoolAction() implements LootModifierAction { + public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemovePoolAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.REMOVE_POOL; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + if (context.tableAlreadyModified()) return MODIFIED_NONE; + + final LootTable table = context.table(); + final LootPool pool = context.pool(); + if (pool == null) return MODIFIED_NONE; + + final ImmutableList.Builder newPoolsBuilder = ImmutableList.builder(); + + for (LootPool originalPool : table.pools) { + if (originalPool == pool) continue; // I think we do want '==' here as the references should be the same? + newPoolsBuilder.add(originalPool); + } + + ((LootTableAccessor) table).setPools(newPoolsBuilder.build()); + + return MODIFIED_POOL; + } + + public static RemovePoolAction.Builder builder() { + return RemovePoolAction::new; + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateTypes.java similarity index 74% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateTypes.java index c8b05a5..c6e5d98 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateTypes.java @@ -1,17 +1,9 @@ -package top.offsetmonkey538.loottablemodifier.api; +package top.offsetmonkey538.loottablemodifier.resource.predicate; import com.mojang.serialization.MapCodec; -import net.minecraft.loot.condition.AllOfLootCondition; -import net.minecraft.loot.condition.AnyOfLootCondition; -import net.minecraft.loot.condition.InvertedLootCondition; -import net.minecraft.loot.condition.LootConditionType; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.action.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java index 2fb1731..e4d1f79 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java @@ -7,14 +7,12 @@ import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -import java.util.regex.Pattern; - public record ItemEntryPredicate(OptionalPattern name) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(OptionalPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java index 177d0ac..c274fe3 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java @@ -6,7 +6,7 @@ //import net.minecraft.loot.entry.ItemEntry; //import net.minecraft.util.Identifier; //import org.jetbrains.annotations.NotNull; -//import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; //import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; //import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java index 58ba46d..70a3cd6 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java @@ -2,7 +2,7 @@ import com.mojang.serialization.MapCodec; import net.minecraft.util.Util; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java index 2249e18..5a8ffcc 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java @@ -1,9 +1,8 @@ package top.offsetmonkey538.loottablemodifier.resource.predicate.op; import com.mojang.serialization.MapCodec; -import net.minecraft.loot.condition.AnyOfLootCondition; import net.minecraft.util.Util; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java index 437e660..46ff016 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java @@ -2,9 +2,8 @@ import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootTable; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java index ab9b068..0a03bfc 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java @@ -5,20 +5,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.fabricmc.loader.impl.lib.sat4j.minisat.constraints.cnf.Lits; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.condition.AlternativeLootCondition; -import net.minecraft.loot.condition.LootCondition; -import net.minecraft.loot.context.LootContext; -import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.util.Identifier; -import net.minecraft.util.Util; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; import java.util.List; import java.util.function.Function; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java index 2662e24..e31f143 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java @@ -13,7 +13,7 @@ //import net.minecraft.util.Identifier; //import org.jetbrains.annotations.NotNull; //import org.jetbrains.annotations.Nullable; -//import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; //import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java index f599948..56e8d27 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java @@ -16,7 +16,7 @@ import net.minecraft.util.context.ContextType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.api.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; @@ -27,7 +27,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Function; -import java.util.regex.Pattern; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; From cc1e8fade4729071697eeae091fc3c786643bbc9 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:57:27 +0300 Subject: [PATCH 23/53] Force object version of OptionalPattern to always use regex --- .../resource/OptionalPattern.java | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java index 3286128..d62e0c4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java @@ -5,16 +5,14 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import org.jetbrains.annotations.NotNull; -import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; public record OptionalPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { - private static final Codec INLINE_CODEC = Codec.STRING.xmap(OptionalPattern::new, OptionalPattern::patternString); + private static final Codec INLINE_CODEC = Codec.STRING.xmap(OptionalPattern::literal, OptionalPattern::patternString); private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.BOOL.optionalFieldOf("regex", false).forGetter(OptionalPattern::isRegex), - Codec.STRING.fieldOf("pattern").forGetter(OptionalPattern::patternString) - ).apply(instance, OptionalPattern::new)); + Codec.STRING.fieldOf("regexPattern").forGetter(OptionalPattern::patternString) + ).apply(instance, OptionalPattern::compile)); public static final Codec CODEC = Codec.either( INLINE_CODEC, FULL_CODEC @@ -23,32 +21,19 @@ public record OptionalPattern(boolean isRegex, @NotNull String patternString, @N pattern -> pattern.isRegex ? Either.right(pattern) : Either.left(pattern) ); - private OptionalPattern(final String stringPattern) { - this( + public static OptionalPattern literal(final String literalString) { + return new OptionalPattern( false, - stringPattern, - Pattern.compile(Pattern.quote(stringPattern)) - ); - } - - private OptionalPattern(final boolean isRegex, final String stringPattern) { - this( - isRegex, - stringPattern, - Pattern.compile(isRegex ? stringPattern : Pattern.quote(stringPattern)) + literalString, + Pattern.compile(Pattern.quote(literalString)) ); } - public static OptionalPattern compile(String patternString) { + public static OptionalPattern compile(String regexPattern) { return new OptionalPattern( true, - patternString - ); - } - - public static OptionalPattern literal(String patternString) { - return new OptionalPattern( - patternString + regexPattern, + Pattern.compile(regexPattern) ); } From f126d2fcdaf45c9f4f9639602be314cf54b93b8c Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:57:41 +0300 Subject: [PATCH 24/53] Update fapi --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3f9dbcb..fd12bfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version = 1.21.5 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.126.0+1.21.5 +fapi_version = 0.127.0+1.21.5 yarn_version = 1.21.5+build.1 loader_version = 0.16.14 From 17f405809a0f5d8ead828916e9aa08395fadc317 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 19 Jun 2025 12:38:08 +0300 Subject: [PATCH 25/53] Refactoring --- .../loottablemodifier/LootTableModifier.java | 20 +++-- .../api/datagen/LootModifierProvider.java | 79 +----------------- .../{ => api}/resource/LootModifier.java | 21 ++--- .../resource/action/LootModifierAction.java | 17 ++-- .../action/LootModifierActionType.java | 2 +- .../action/LootModifierActionTypes.java | 8 +- .../resource/action/entry/SetItemAction.java | 13 ++- .../resource/action/pool/AddPoolAction.java | 13 ++- .../action/pool/RemovePoolAction.java | 16 ++-- .../predicate/LootModifierPredicate.java | 35 ++++++++ .../predicate/LootModifierPredicateType.java | 2 +- .../predicate/LootModifierPredicateTypes.java | 12 +-- .../condition/LootConditionPredicate.java | 4 +- .../predicate/entry/ItemEntryPredicate.java | 12 +-- .../predicate/entry/LootEntryPredicate.java | 4 +- .../entry/leaf/EmptyEntryPredicate.java | 6 +- .../entry/leaf/LeafEntryPredicate.java | 4 +- .../function/LootFunctionPredicate.java | 2 +- .../predicate/op/AllOfLootPredicate.java | 8 +- .../predicate/op/AnyOfLootPredicate.java | 8 +- .../predicate/op/InvertedLootPredicate.java | 10 +-- .../predicate/op/TermsLootPredicate.java | 8 +- .../predicate/pool/LootPoolPredicate.java | 6 +- .../predicate/table/LootTablePredicate.java | 12 +-- .../resource/util}/LootModifierContext.java | 9 +-- .../resource/util}/OptionalPattern.java | 2 +- .../datagen/LootTableModifierDatagen.java | 15 ++-- .../resource/action/RemovePoolAction.java | 43 ---------- .../predicate/LootModifierPredicate.java | 80 ------------------- 29 files changed, 151 insertions(+), 320 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/LootModifier.java (88%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/action/LootModifierAction.java (61%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/action/LootModifierActionType.java (90%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/action/LootModifierActionTypes.java (75%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/action/entry/SetItemAction.java (83%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/action/pool/AddPoolAction.java (76%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/action/pool/RemovePoolAction.java (71%) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/LootModifierPredicateType.java (90%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/LootModifierPredicateTypes.java (72%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/condition/LootConditionPredicate.java (82%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/entry/ItemEntryPredicate.java (74%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/entry/LootEntryPredicate.java (94%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/entry/leaf/EmptyEntryPredicate.java (91%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/entry/leaf/LeafEntryPredicate.java (96%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/function/LootFunctionPredicate.java (91%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/op/AllOfLootPredicate.java (76%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/op/AnyOfLootPredicate.java (76%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/op/InvertedLootPredicate.java (67%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/op/TermsLootPredicate.java (88%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/pool/LootPoolPredicate.java (97%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{ => api}/resource/predicate/table/LootTablePredicate.java (94%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{resource => api/resource/util}/LootModifierContext.java (63%) rename src/main/java/top/offsetmonkey538/loottablemodifier/{resource => api/resource/util}/OptionalPattern.java (97%) delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 07481f0..621bc7d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -32,10 +32,10 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -45,15 +45,12 @@ import java.util.*; import static net.minecraft.server.command.CommandManager.literal; -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; +import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.*; public class LootTableModifier implements ModInitializer { public static final String MOD_ID = "loot-table-modifier"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); - // Only used when IS_DEV is true - private static final List MODIFIED_TABLE_IDs = Collections.synchronizedList(new ArrayList<>(0)); - public static final boolean IS_DEV; static { final String isDev = System.getProperty("lootTableModifierDev", ""); @@ -62,6 +59,13 @@ public class LootTableModifier implements ModInitializer { else IS_DEV = FabricLoader.getInstance().isDevelopmentEnvironment(); } + // Only used when IS_DEV is true + private static final List MODIFIED_TABLE_IDs; + static { + if (IS_DEV) MODIFIED_TABLE_IDs = Collections.synchronizedList(new ArrayList<>(0)); + else MODIFIED_TABLE_IDs = null; + } + @Override public void onInitialize() { LootModifierActionTypes.register(); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java index c67ec2c..5ecb59d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java @@ -2,23 +2,14 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.MappingResolver; import net.minecraft.data.DataOutput; -import net.minecraft.entity.EntityType; -import net.minecraft.loot.LootTable; -import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; -import java.util.function.Function; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; @@ -73,70 +64,4 @@ public String getName() { protected void addModifier(@NotNull Identifier name, @NotNull LootModifier.Builder builder) { provider.accept(name, builder.build()); } - - private static class EntityLootTableIdGetter { - // Resolver returns the provided name (like 'method_16351') when it fails to map it - private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); - - private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); - private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); - - // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); - - public static final Function, Identifier> get; - - // Should be executed when class is first loaded/accessed - static { - try { - //final Class entityType = EntityType.class; - final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); - final Method method; - - // 1.21.2 to future:tm: - if (isMethod(entityType, V1d21d2)) { - method = entityType.getDeclaredMethod(V1d21d2); - method.setAccessible(true); - get = entity -> { - try { - @SuppressWarnings("unchecked") - final Optional> optional = (Optional>) method.invoke(entity); - if (optional.isPresent()) return optional.get().getValue(); - throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - // 1.20.5 to 1.21.1 - else if (isMethod(entityType, V1d20d5)) { - method = entityType.getDeclaredMethod(V1d20d5); - method.setAccessible(true); - get = entity -> { - try { - return ((RegistryKey) method.invoke(entity)).getValue(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - else { - throw new IllegalStateException("No valid way to get entity loot table id found!"); - } - } catch (NoSuchMethodException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private static boolean isMethod(Class clazz, String method) { - try { - clazz.getDeclaredMethod(method); - return true; - } catch (NoSuchMethodException e) { - LOGGER.warn("", e); - return false; - } - } - } -} \ No newline at end of file +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java similarity index 88% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index 92a0a7a..a2c6461 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource; +package top.offsetmonkey538.loottablemodifier.api.resource; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; @@ -7,14 +7,15 @@ import net.minecraft.loot.LootPool; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -import top.offsetmonkey538.loottablemodifier.resource.action.pool.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import java.util.*; import java.util.function.Predicate; -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; +import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.MODIFIED_NONE; // Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied TODO: I don't think I do this anymore? TODO: make sure that changing it to a normal List is fine public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) implements Predicate { @@ -78,11 +79,11 @@ private static LootModifier fromCurrentCodec(Either CODEC = LootModifierActionType.REGISTRY.getCodec().dispatch(LootModifierAction::getType, LootModifierActionType::codec); + int MODIFIED_NONE = 0b000; + int MODIFIED_TABLE = 0b001; + int MODIFIED_POOL = 0b011; + int MODIFIED_ENTRY = 0b111; + LootModifierActionType getType(); /** * Applies this action to the provided context * @param context the context to apply to * @return the applied modification level - * @see LootModifierContext#MODIFIED_NONE - * @see LootModifierContext#MODIFIED_TABLE - * @see LootModifierContext#MODIFIED_POOL - * @see LootModifierContext#MODIFIED_ENTRY + * @see #MODIFIED_NONE + * @see #MODIFIED_TABLE + * @see #MODIFIED_POOL + * @see #MODIFIED_ENTRY */ int apply(final @NotNull LootModifierContext context); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java similarity index 90% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java index 3d8646f..ff2be73 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionType.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.action; +package top.offsetmonkey538.loottablemodifier.api.resource.action; import com.mojang.serialization.Lifecycle; import com.mojang.serialization.MapCodec; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java similarity index 75% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionTypes.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java index e81dfbf..980b1ba 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java @@ -1,12 +1,12 @@ -package top.offsetmonkey538.loottablemodifier.resource.action; +package top.offsetmonkey538.loottablemodifier.api.resource.action; import com.mojang.serialization.MapCodec; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.action.pool.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.resource.action.entry.SetItemAction; -import top.offsetmonkey538.loottablemodifier.resource.action.pool.RemovePoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/entry/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java similarity index 83% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/entry/SetItemAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java index 4659c39..33f9302 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/entry/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.action.entry; +package top.offsetmonkey538.loottablemodifier.api.resource.action.entry; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; @@ -12,15 +12,12 @@ import net.minecraft.registry.Registries; import net.minecraft.registry.entry.RegistryEntry; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.ItemEntryAccessor; import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; - -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_ENTRY; -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; public record SetItemAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java similarity index 76% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/AddPoolAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java index 617761a..acd0831 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java @@ -1,21 +1,18 @@ -package top.offsetmonkey538.loottablemodifier.resource.action.pool; +package top.offsetmonkey538.loottablemodifier.api.resource.action.pool; import com.google.common.collect.ImmutableList; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.loot.LootPool; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; import java.util.List; -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_NONE; -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.MODIFIED_POOL; - public record AddPoolAction(List pools) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( LootPool.CODEC.listOf().fieldOf("pools").forGetter(AddPoolAction::pools) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java similarity index 71% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java index 76a6214..af0819c 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/pool/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.action.pool; +package top.offsetmonkey538.loottablemodifier.api.resource.action.pool; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; @@ -7,18 +7,12 @@ import com.mojang.serialization.MapCodec; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; -import net.minecraft.loot.entry.LootPoolEntry; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; - -import java.util.List; - -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; public record RemovePoolAction() implements LootModifierAction { public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemovePoolAction::new)); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java new file mode 100644 index 0000000..aa2a976 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java @@ -0,0 +1,35 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate; + +import com.mojang.serialization.Codec; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedLootPredicate; + +import java.util.function.Predicate; + +public interface LootModifierPredicate extends Predicate { + Codec CODEC = LootModifierPredicateType.REGISTRY.getCodec().dispatch(LootModifierPredicate::getType, LootModifierPredicateType::codec); + + LootModifierPredicateType getType(); + + boolean test(final @NotNull LootModifierContext context); + + @FunctionalInterface + interface Builder { + LootModifierPredicate build(); + + default Builder invert() { + return InvertedLootPredicate.builder(this); + } + + default LootModifierPredicate.Builder or(LootModifierPredicate.Builder otherPredicate) { + return AnyOfLootPredicate.builder(this, otherPredicate); + } + + default LootModifierPredicate.Builder and(LootModifierPredicate.Builder otherPredicate) { + return AllOfLootPredicate.builder(this, otherPredicate); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java similarity index 90% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java index 207c997..2cf97bf 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateType.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate; import com.mojang.serialization.Lifecycle; import com.mojang.serialization.MapCodec; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java similarity index 72% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateTypes.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java index c6e5d98..65c48c0 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java @@ -1,14 +1,14 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate; import com.mojang.serialization.MapCodec; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.op.InvertedLootPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.ItemEntryPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java similarity index 82% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java index af00d83..45d3446 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/condition/LootConditionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java @@ -1,11 +1,11 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.condition; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.condition; import com.mojang.serialization.Codec; import net.minecraft.loot.condition.LootCondition; import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; public record LootConditionPredicate(@NotNull OptionalPattern functionPattern) { public static final Codec CODEC = OptionalPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java similarity index 74% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java index e4d1f79..1a6282f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.entry; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -7,11 +7,11 @@ import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; public record ItemEntryPredicate(OptionalPattern name) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java similarity index 94% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java index 363902a..6859a11 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/LootEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.entry; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry; //import com.mojang.datafixers.Products; //import com.mojang.serialization.Codec; @@ -10,7 +10,7 @@ //import org.jetbrains.annotations.NotNull; //import org.jetbrains.annotations.Nullable; //import top.offsetmonkey538.loottablemodifier.LootTableModifier; -//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; //import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java similarity index 91% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java index c274fe3..f53c1f9 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/EmptyEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.entry.leaf; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.leaf; //import com.mojang.serialization.MapCodec; //import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -7,7 +7,7 @@ //import net.minecraft.util.Identifier; //import org.jetbrains.annotations.NotNull; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; //import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; //import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; @@ -38,4 +38,4 @@ // return LootModifierContext.REQUIRES_TABLE; // } //} -// \ No newline at end of file +// diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java similarity index 96% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java index 881fde1..f091d7d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/entry/leaf/LeafEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.entry.leaf; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.leaf; //import com.mojang.datafixers.Products; //import com.mojang.serialization.Codec; @@ -42,4 +42,4 @@ // // todo: return super.matches(entry); // // todo: } //} -// \ No newline at end of file +// diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java similarity index 91% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java index d3ea88e..ae3f158 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/function/LootFunctionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.function; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.function; import com.mojang.serialization.Codec; import net.minecraft.loot.function.LootFunction; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java similarity index 76% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java index 70a3cd6..918222f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AllOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java @@ -1,10 +1,10 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.op; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; import com.mojang.serialization.MapCodec; import net.minecraft.util.Util; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; import java.util.List; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java similarity index 76% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java index 5a8ffcc..1105d8a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/AnyOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java @@ -1,10 +1,10 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.op; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; import com.mojang.serialization.MapCodec; import net.minecraft.util.Util; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; import java.util.List; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java similarity index 67% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java index 46ff016..afae38b 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/InvertedLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java @@ -1,12 +1,12 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.op; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; public record InvertedLootPredicate(LootModifierPredicate term) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java similarity index 88% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java index 0a03bfc..e89ad73 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.op; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; @@ -6,14 +6,14 @@ import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; -public abstract class TermsLootPredicate implements LootModifierPredicate { +abstract class TermsLootPredicate implements LootModifierPredicate { protected final List terms; private final Predicate builtPredicate; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java similarity index 97% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java index e31f143..7c855d1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/pool/LootPoolPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.pool; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.pool; //import com.mojang.datafixers.util.Either; //import com.mojang.serialization.Codec; @@ -14,7 +14,7 @@ //import org.jetbrains.annotations.NotNull; //import org.jetbrains.annotations.Nullable; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; +//import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; //import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; //import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; @@ -134,4 +134,4 @@ // // } // //} //} -// \ No newline at end of file +// diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java similarity index 94% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 56e8d27..623b32a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate.table; +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.table; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableList; @@ -16,11 +16,11 @@ import net.minecraft.util.context.ContextType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java similarity index 63% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java index 91910c3..1602168 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifierContext.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource; +package top.offsetmonkey538.loottablemodifier.api.resource.util; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; @@ -8,8 +8,5 @@ import org.jetbrains.annotations.Nullable; public record LootModifierContext(@NotNull LootTable table, @NotNull Identifier tableId, @Nullable LootPool pool, @Nullable LootPoolEntry entry, boolean tableAlreadyModified, boolean poolAlreadyModified) { - public static final int MODIFIED_NONE = 0b000; - public static final int MODIFIED_TABLE = 0b001; - public static final int MODIFIED_POOL = 0b011; - public static final int MODIFIED_ENTRY = 0b111; -} \ No newline at end of file + +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalPattern.java similarity index 97% rename from src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalPattern.java index d62e0c4..f2daf0c 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/OptionalPattern.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalPattern.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.loottablemodifier.resource; +package top.offsetmonkey538.loottablemodifier.api.resource.util; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 9e84279..7482816 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -10,7 +10,6 @@ import net.minecraft.loot.LootTable; import net.minecraft.loot.context.LootContextTypes; import net.minecraft.loot.entry.ItemEntry; -import net.minecraft.loot.function.EnchantWithLevelsLootFunction; import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; @@ -18,13 +17,13 @@ import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.resource.OptionalPattern; -import top.offsetmonkey538.loottablemodifier.resource.action.pool.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.resource.action.entry.SetItemAction; -import top.offsetmonkey538.loottablemodifier.resource.action.pool.RemovePoolAction; -import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.ItemEntryPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.table.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.ItemEntryPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java deleted file mode 100644 index 8eb5e88..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/action/RemovePoolAction.java +++ /dev/null @@ -1,43 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource.action; - -//import com.google.common.collect.ImmutableList; -//import com.mojang.serialization.MapCodec; -//import com.mojang.serialization.codecs.RecordCodecBuilder; -//import net.minecraft.loot.LootPool; -//import net.minecraft.loot.LootTable; -//import org.jetbrains.annotations.NotNull; -//import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionTypes; -//import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -//import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.pool.LootPoolPredicate; -// -//import java.util.ArrayList; -//import java.util.List; - -//public record RemovePoolAction(LootPoolPredicate poolPredicate) implements LootModifierAction { -// public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( -// LootPoolPredicate.CODEC.fieldOf("poolPredicate").forGetter(RemovePoolAction::poolPredicate) -// ).apply(instance, RemovePoolAction::new)); -// -// @Override -// public LootModifierActionType getType() { -// return LootModifierActionTypes.ADD_ENTRY; -// } -// -// @Override -// public boolean apply(@NotNull final LootModifierContext context) { -// if (!poolPredicate.test(context)) return false; -// -// List newPools = new ArrayList<>(context.table().pools); -// newPools.remove(context.pool()); -// newPools = ImmutableList.copyOf(newPools); -// -// ((LootTableAccessor) context.table()).setPools(newPools); -// -// return true; -// } -// -// public static LootModifierAction.Builder builder(@NotNull LootPoolPredicate.Builder poolBuilder) { -// return () -> new RemovePoolAction(poolBuilder.build()); -// } -//} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java deleted file mode 100644 index 8b94de2..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/predicate/LootModifierPredicate.java +++ /dev/null @@ -1,80 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource.predicate; - -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.loot.condition.AllOfLootCondition; -import net.minecraft.loot.condition.AnyOfLootCondition; -import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.loottablemodifier.resource.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierAction; -import top.offsetmonkey538.loottablemodifier.resource.action.LootModifierActionType; -import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AllOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.op.AnyOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.resource.predicate.op.InvertedLootPredicate; - -import java.util.function.Predicate; - -import static top.offsetmonkey538.loottablemodifier.resource.LootModifierContext.*; - -public interface LootModifierPredicate extends Predicate { - Codec CODEC = LootModifierPredicateType.REGISTRY.getCodec().dispatch(LootModifierPredicate::getType, LootModifierPredicateType::codec); - - LootModifierPredicateType getType(); - - //byte requiredContext(); - - ///** - // * Tests if this predicate matches the provided context - // * @param context the context to test against - // * @return true when this predicate matches the provided context, false when not - // */ - - //default boolean test(final @NotNull LootModifierContext context) { - // return switch (requiredContext()) { - // case REQUIRES_TABLE -> test(context, context.table(), context.tableId()); - // case REQUIRES_POOL -> { - // if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); - // yield test(context, context.table(), context.tableId(), context.pool()); - // } - // case REQUIRES_ENTRY -> { - // if (context.pool() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have a pool set!"); - // if (context.entry() == null) throw new IllegalArgumentException("PredicateContext for LootPoolPredicate doesn't have an entry set!"); - // yield test(context, context.table(), context.tableId(), context.pool(), context.entry()); - // } - // default -> throw new UnsupportedOperationException("Loot modifier predicate defines unsupported requiredContext value '%s', only values 0 - 2 are supported.".formatted(requiredContext())); - // }; - //} - - //default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId) { - // throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, but says it requires a table"); - //} - //default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { - // throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table and pool, but says it requires a pool"); - //} - //default boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { - // throw new UnsupportedOperationException("Loot modifier predicate doesn't implement test method for table, pool and entry, but says it requires an entry"); - //} - boolean test(final @NotNull LootModifierContext context); - - @FunctionalInterface - interface Builder { - LootModifierPredicate build(); - - default Builder invert() { - return InvertedLootPredicate.builder(this); - } - - default LootModifierPredicate.Builder or(LootModifierPredicate.Builder otherPredicate) { - return AnyOfLootPredicate.builder(this, otherPredicate); - } - - default LootModifierPredicate.Builder and(LootModifierPredicate.Builder otherPredicate) { - return AllOfLootPredicate.builder(this, otherPredicate); - } - } -} From 113d7c00704f55699d2d211e31279b79b12fbe1d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:07:15 +0300 Subject: [PATCH 26/53] Add AddEntryAction --- gradle.properties | 2 +- .../action/LootModifierActionTypes.java | 8 +- .../resource/action/entry/AddEntryAction.java | 66 +++++++++ .../resource/action/entry/SetItemAction.java | 4 +- .../resource/action/pool/AddPoolAction.java | 2 +- .../action/pool/RemovePoolAction.java | 2 +- .../predicate/op/TermsLootPredicate.java | 2 +- .../predicate/table/LootTablePredicate.java | 117 ++++----------- .../api/resource/util/LootTableIdGetter.java | 14 ++ .../datagen/LootTableModifierDatagen.java | 16 +++ .../impl/LootTableIdGetterImpl.java | 136 ++++++++++++++++++ 11 files changed, 267 insertions(+), 102 deletions(-) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java diff --git a/gradle.properties b/gradle.properties index fd12bfb..cbc31f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version = 1.21.5 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.127.0+1.21.5 +fapi_version = 0.127.1+1.21.5 yarn_version = 1.21.5+build.1 loader_version = 0.16.14 diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java index 980b1ba..bb38263 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java @@ -4,6 +4,7 @@ import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; @@ -15,10 +16,11 @@ private LootModifierActionTypes() { } - public static final LootModifierActionType ADD_POOL = register(id("add_pool"), AddPoolAction.CODEC); - public static final LootModifierActionType REMOVE_POOL = register(id("remove_pool"), RemovePoolAction.CODEC); + public static final LootModifierActionType POOL_ADD = register(id("pool_add"), AddPoolAction.CODEC); + public static final LootModifierActionType POOL_REMOVE = register(id("pool_remove"), RemovePoolAction.CODEC); - public static final LootModifierActionType SET_ITEM = register(id("set_item"), SetItemAction.CODEC); + public static final LootModifierActionType ENTRY_ADD = register(id("entry_add"), AddEntryAction.CODEC); + public static final LootModifierActionType ENTRY_ITEM_SET = register(id("entry_item_set"), SetItemAction.CODEC); private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java new file mode 100644 index 0000000..2879c13 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java @@ -0,0 +1,66 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.entry; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.loot.entry.LootPoolEntryTypes; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; + +import java.util.List; + +public record AddEntryAction(List entries) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPoolEntryTypes.CODEC.listOf().fieldOf("entries").forGetter(AddEntryAction::entries) + ).apply(instance, AddEntryAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ENTRY_ADD; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + if (context.poolAlreadyModified()) return MODIFIED_NONE; + + final LootPool pool = context.pool(); + if (pool == null) return MODIFIED_NONE; + + final List newEntries = ImmutableList.builder() + .addAll(pool.entries) + .addAll(this.entries) + .build(); + + ((LootPoolAccessor) pool).setEntries(newEntries); + + return MODIFIED_ENTRY; + } + + public static AddEntryAction.Builder builder(@NotNull LootPoolEntry.Builder entryBuilder) { + return new AddEntryAction.Builder(entryBuilder); + } + + public static class Builder implements LootModifierAction.Builder { + private final ImmutableList.Builder entries = ImmutableList.builder(); + + private Builder(@NotNull LootPoolEntry.Builder poolBuilder) { + entries.add(poolBuilder.build()); + } + + public AddEntryAction.Builder entry(LootPoolEntry.Builder poolBuilder) { + this.entries.add(poolBuilder.build()); + return this; + } + + @Override + public AddEntryAction build() { + return new AddEntryAction(entries.build()); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java index 33f9302..5753439 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java @@ -27,7 +27,7 @@ public record SetItemAction(RegistryEntry item, boolean canReplaceEntry) i @Override public LootModifierActionType getType() { - return LootModifierActionTypes.SET_ITEM; + return LootModifierActionTypes.ENTRY_ITEM_SET; } @Override @@ -51,7 +51,7 @@ public int apply(@NotNull LootModifierContext context) { if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? newEntriesBuilder.add(originalEntry); } - ((LootPoolAccessor) context.pool()).setEntries(newEntriesBuilder.build()); + ((LootPoolAccessor) pool).setEntries(newEntriesBuilder.build()); return MODIFIED_ENTRY; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java index acd0831..cb95202 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java @@ -20,7 +20,7 @@ public record AddPoolAction(List pools) implements LootModifierAction @Override public LootModifierActionType getType() { - return LootModifierActionTypes.ADD_POOL; + return LootModifierActionTypes.POOL_ADD; } @Override diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java index af0819c..422b318 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java @@ -19,7 +19,7 @@ public record RemovePoolAction() implements LootModifierAction { @Override public LootModifierActionType getType() { - return LootModifierActionTypes.REMOVE_POOL; + return LootModifierActionTypes.POOL_REMOVE; } @Override diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java index e89ad73..0736b86 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java @@ -60,6 +60,6 @@ public LootModifierPredicate build() { return this.build(this.terms.build()); } - protected abstract TermsLootPredicate build(List terms); + protected abstract LootModifierPredicate build(List terms); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 623b32a..4612128 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -1,13 +1,11 @@ package top.offsetmonkey538.loottablemodifier.api.resource.predicate.table; -import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.block.Block; import net.minecraft.entity.EntityType; import net.minecraft.loot.LootTable; import net.minecraft.loot.context.LootContextTypes; @@ -18,17 +16,13 @@ import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.List; import java.util.Optional; -import java.util.function.Function; - -import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { @@ -93,113 +87,50 @@ public static class Builder implements LootModifierPredicate.Builder { private final ImmutableList.Builder types = ImmutableList.builder(); - public LootTablePredicate.Builder name(@NotNull EntityType... names) { - for (EntityType name : names) name(EntityLootTableIdGetter.get.apply(name)); + public LootTablePredicate.Builder name(@NotNull EntityType name) { + name(LootTableIdGetter.INSTANCE.get(name)); + return this; + } + public LootTablePredicate.Builder name(@NotNull Block name) { + name(LootTableIdGetter.INSTANCE.get(name)); return this; } - public LootTablePredicate.Builder name(@NotNull RegistryKey... names) { - for (RegistryKey name : names) name(name.getValue()); + public LootTablePredicate.Builder name(@NotNull RegistryKey name) { + name(name.getValue()); return this; } - public LootTablePredicate.Builder name(@NotNull Identifier... names) { - for (Identifier name : names) name(name.toString()); + public LootTablePredicate.Builder name(@NotNull Identifier name) { + name(name.toString()); return this; } - public LootTablePredicate.Builder name(@NotNull String... names) { - for (String name : names) this.names.add(OptionalPattern.literal(name)); + public LootTablePredicate.Builder name(@NotNull String name) { + name(OptionalPattern.literal(name)); return this; } - public LootTablePredicate.Builder name(@NotNull OptionalPattern... names) { - this.names.add(names); + public LootTablePredicate.Builder name(@NotNull OptionalPattern name) { + this.names.add(name); return this; } - public LootTablePredicate.Builder type(@NotNull ContextType... types) { - final BiMap inverse = LootContextTypes.MAP.inverse(); - for (ContextType type : types) type(inverse.get(type)); + public LootTablePredicate.Builder type(@NotNull ContextType type) { + type(LootContextTypes.MAP.inverse().get(type)); return this; } - public LootTablePredicate.Builder type(@NotNull Identifier... types) { - for (Identifier type : types) type(type.toString()); + public LootTablePredicate.Builder type(@NotNull Identifier type) { + type(type.toString()); return this; } - public LootTablePredicate.Builder type(@NotNull String... types) { - for (String type : types) this.types.add(OptionalPattern.literal(type)); + public LootTablePredicate.Builder type(@NotNull String type) { + this.types.add(OptionalPattern.literal(type)); return this; } - public LootTablePredicate.Builder type(@NotNull OptionalPattern... types) { - this.types.add(types); + public LootTablePredicate.Builder type(@NotNull OptionalPattern type) { + this.types.add(type); return this; } public LootTablePredicate build() { return new LootTablePredicate(names.build(), types.build()); } - - private static class EntityLootTableIdGetter { - // Resolver returns the provided name (like 'method_16351') when it fails to map it - private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); - - private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); - private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); - - // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); - - public static final Function, Identifier> get; - - // Should be executed when class is first loaded/accessed - static { - try { - //final Class entityType = EntityType.class; - final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); - final Method method; - - // 1.21.2 to future:tm: - if (isMethod(entityType, V1d21d2)) { - method = entityType.getDeclaredMethod(V1d21d2); - method.setAccessible(true); - get = entity -> { - try { - @SuppressWarnings("unchecked") - final Optional> optional = (Optional>) method.invoke(entity); - if (optional.isPresent()) return optional.get().getValue(); - throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - // 1.20.5 to 1.21.1 - else if (isMethod(entityType, V1d20d5)) { - method = entityType.getDeclaredMethod(V1d20d5); - method.setAccessible(true); - get = entity -> { - try { - return ((RegistryKey) method.invoke(entity)).getValue(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - else { - throw new IllegalStateException("No valid way to get entity loot table id found!"); - } - } catch (NoSuchMethodException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private static boolean isMethod(Class clazz, String method) { - try { - clazz.getDeclaredMethod(method); - return true; - } catch (NoSuchMethodException e) { - LOGGER.warn("", e); - return false; - } - } - } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java new file mode 100644 index 0000000..b5d3d28 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java @@ -0,0 +1,14 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.util; + +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.impl.LootTableIdGetterImpl; + +public interface LootTableIdGetter { + LootTableIdGetter INSTANCE = new LootTableIdGetterImpl(); + + Identifier get(@NotNull EntityType entityType); + Identifier get(@NotNull Block block); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 7482816..3c7b4aa 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -4,6 +4,7 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider; +import net.minecraft.block.Blocks; import net.minecraft.entity.EntityType; import net.minecraft.item.Items; import net.minecraft.loot.LootPool; @@ -18,6 +19,7 @@ import net.minecraft.registry.RegistryWrapper; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; @@ -121,6 +123,20 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { RemovePoolAction.builder() ) ); + addModifier( + id("add_cake_entry_to_dirt_block"), + LootModifier.builder() + .conditionally( + LootTablePredicate.builder() + .name(Blocks.DIRT) + ) + .action( + AddEntryAction.builder( + ItemEntry.builder(Items.CAKE) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ) + ); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java b/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java new file mode 100644 index 0000000..7dfb03c --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java @@ -0,0 +1,136 @@ +package top.offsetmonkey538.loottablemodifier.impl; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.loot.LootTable; +import net.minecraft.registry.RegistryKey; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.function.Function; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; + +public class LootTableIdGetterImpl implements LootTableIdGetter { + + @Override + public Identifier get(@NotNull EntityType entityType) { + return VersionSpecific.Entity.get.apply(entityType); + } + + @Override + public Identifier get(@NotNull Block block) { + return VersionSpecific.Block.get.apply(block); + } + + + // Should be executed when class is first accessed, which I think should only happen when using action or predicate Builders which are probably only used with datagen so this should only make datagen stuff crash but still work when used by a player? + private static class VersionSpecific { + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + private static class Entity { + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); + + // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); + + private static final Function, Identifier> get; + + static { + // TODO: I had the below statement commented out, maybe it doesnt work? use this then: final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); + final Class entityType = EntityType.class; + final Method finalMethod; + Method method; + + // 1.21.2 to future:tm: + if ((method = getMethod(entityType, V1d21d2)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + @SuppressWarnings("unchecked") final Optional> optional = (Optional>) finalMethod.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if ((method = getMethod(entityType, V1d20d5)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + return ((RegistryKey) finalMethod.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } + } + private static class Block { + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_4970", "method_26162", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_4970", "method_26162", "()Lnet/minecraft/class_5321;"); + + private static final Function get; + + static { + final Class abstractBlock = AbstractBlock.class; + final Method finalMethod; + Method method; + + // 1.21.2 to future:tm: + if ((method = getMethod(abstractBlock, V1d21d2)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + @SuppressWarnings("unchecked") final Optional> optional = (Optional>) finalMethod.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if ((method = getMethod(abstractBlock, V1d20d5)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + return ((RegistryKey) finalMethod.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } + } + + private static @Nullable Method getMethod(Class clazz, String method) { + try { + return clazz.getDeclaredMethod(method); + } catch (NoSuchMethodException e) { + LOGGER.warn("Method '%s' not valid for getting loot table id on this version, trying next method...", e); + return null; + } + } + } +} From 85304b2e31682d1924992681721609b11ceb4d91 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:00:05 +0300 Subject: [PATCH 27/53] Rename debug export dir --- .../loottablemodifier/LootTableModifier.java | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 621bc7d..76486ee 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -206,20 +206,18 @@ private static Map loadModifiers(ResourceManager resou private static void enableDebug() { ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); - CommandRegistrationCallback.EVENT.register((dispatcher, commandRegistryAccess, registrationEnvironment) -> { - dispatcher.register( - literal(MOD_ID) - .then( - literal("debug") - .then( - literal("export") - .executes( - LootTableModifier::executeExportCommand - ) - ) - ) - ); - }); + CommandRegistrationCallback.EVENT.register((dispatcher, commandRegistryAccess, registrationEnvironment) -> dispatcher.register( + literal(MOD_ID) + .then( + literal("debug") + .then( + literal("export") + .executes( + LootTableModifier::executeExportCommand + ) + ) + ) + )); } private static int executeExportCommand(CommandContext context) { @@ -229,7 +227,7 @@ private static int executeExportCommand(CommandContext cont final DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, server.getRegistryManager()); try { - final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier_export"); + final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier").resolve("export"); if (Files.exists(exportDir)) PathUtils.deleteDirectory(exportDir); source.sendFeedback(() -> Text.literal("Exporting modified tables to ").append(Text.literal(exportDir.toString()).setStyle(Style.EMPTY.withUnderline(true).withColor(Formatting.WHITE).withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to copy"))).withClickEvent(new ClickEvent.CopyToClipboard(exportDir.toAbsolutePath().toString())))), true); From 11dc15a20f01b97b2a6b30c4bdd5b575f18483d8 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:05:25 +0300 Subject: [PATCH 28/53] Rename action and predicate types I uhh may or may not have already committed the action changes in a past commit... --- .../api/resource/predicate/LootModifierPredicateTypes.java | 6 +++--- .../api/resource/predicate/entry/ItemEntryPredicate.java | 2 +- .../api/resource/predicate/table/LootTablePredicate.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java index 65c48c0..b685018 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java @@ -21,9 +21,9 @@ private LootModifierPredicateTypes() { public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfLootPredicate.CODEC); public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfLootPredicate.CODEC); - public static final LootModifierPredicateType ITEM_ENTRY = register(id("item_entry"), ItemEntryPredicate.CODEC); + public static final LootModifierPredicateType ENTRY_ITEM = register(id("entry_item"), ItemEntryPredicate.CODEC); - public static final LootModifierPredicateType LOOT_TABLE = register(id("loot_table"), LootTablePredicate.CODEC); + public static final LootModifierPredicateType TABLE = register(id("table"), LootTablePredicate.CODEC); //public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); @@ -32,6 +32,6 @@ private static LootModifierPredicateType register(final @NotNull Identifier id, } public static void register() { - // Registers action types by loading the class + // Registers predicate types by loading the class } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java index 1a6282f..3a52b3d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java @@ -20,7 +20,7 @@ public record ItemEntryPredicate(OptionalPattern name) implements LootModifierPr @Override public LootModifierPredicateType getType() { - return LootModifierPredicateTypes.ITEM_ENTRY; + return LootModifierPredicateTypes.ENTRY_ITEM; } @Override diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 4612128..1e4b314 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -54,7 +54,7 @@ private Optional>> optionalEitherT @Override public LootModifierPredicateType getType() { - return LootModifierPredicateTypes.LOOT_TABLE; + return LootModifierPredicateTypes.TABLE; } @Override From 8fe96b018f8a88a99aee807b3f09630ebc7f8971 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:31:30 +0300 Subject: [PATCH 29/53] Update gradle to v8.14.3 --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9128c7d..78cb6e1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 0a0487911817931e0fbd2fd196c87a40717f2229 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:32:19 +0300 Subject: [PATCH 30/53] Update loom to v1.11 --- build.gradle | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8b86854..e69b95e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ import dex.plugins.outlet.v2.util.ReleaseType plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + id 'fabric-loom' version '1.11-SNAPSHOT' id 'io.github.dexman545.outlet' version '1.6.1' id 'com.modrinth.minotaur' version '2.+' id 'maven-publish' @@ -211,4 +211,4 @@ publishing { tasks.publishMavenPublicationToOffsetMonkey538Repository.doLast { if (System.getenv("IS_DEBUG") == "true") System.out.println("Version: " + version) } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index cbc31f6..58269ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version = 1.21.5 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.127.1+1.21.5 +fapi_version = 0.128.1+1.21.5 yarn_version = 1.21.5+build.1 loader_version = 0.16.14 From e5abbbdc37e0a52afdf4618e15bfa6a829551740 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:54:37 +0300 Subject: [PATCH 31/53] Include commit hash in builds --- .github/workflows/build_artifacts.yml | 14 ++++++- .github/workflows/publish.yml | 8 +++- build.gradle | 57 ++++++++++++++++++--------- gradle.properties | 2 +- 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 6365a50..ee8577c 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -24,16 +24,28 @@ jobs: with: cache-read-only: false + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + - name: Generate resources with Gradle run: ./gradlew runDatagenClient env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true - - name: Build with Gradle run: ./gradlew build env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} + DISABLE_PROPERTIES_UPDATE: true + + - name: Publish to Maven + run: ./gradlew publishMavenPublicationToOffsetMonkey538Repository + env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} - name: Upload build artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2e57fd4..22908d9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,20 +26,26 @@ jobs: with: cache-read-only: false + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + - name: Generate resources with Gradle run: ./gradlew runDatagenClient env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true - name: Build with Gradle run: ./gradlew build env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true - name: Upload to Modrinth run: ./gradlew modrinth env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} VERSION_NAME: ${{ github.event.release.name }} VERSION_IS_PRERELEASE: ${{ github.event.release.prerelease }} @@ -50,10 +56,10 @@ jobs: with: files: build/libs/*.jar - - name: Publish to Maven run: ./gradlew publishMavenPublicationToOffsetMonkey538Repository env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} diff --git a/build.gradle b/build.gradle index e69b95e..bba62c3 100644 --- a/build.gradle +++ b/build.gradle @@ -12,8 +12,21 @@ targetCompatibility = JavaVersion.VERSION_17 archivesBaseName = "loot-table-modifier" group = "top.offsetmonkey538.loottablemodifier" -version = "${project.mod_version}+${project.minecraft_version}" -if (System.getenv("IS_DEBUG") == "true") version = "${version}-${System.currentTimeMillis()}" + + +version = project.mod_version + +if ("true".equalsIgnoreCase(System.getenv("IS_DEBUG"))) { + version = "${version}-${System.currentTimeMillis()}" +} + +final String customVersion = System.getenv("CUSTOM_VERSION") +if (customVersion != null && !customVersion.isEmpty()) { + version = "${version}-${customVersion}" +} + +version = "${project.version}+${project.minecraft_version}" +println "Version: ${version}" outlet { maintainPropertiesFile = System.getenv("DISABLE_PROPERTIES_UPDATE") == null @@ -146,13 +159,12 @@ java { tasks.named("javadoc", Javadoc) { options.addFileOption('-add-stylesheet', project.file("javadoc-stylesheet.css")) - // Temporarily - //options { - // links( - // "https://maven.fabricmc.net/docs/fabric-api-${project.fapi_version}/", - // "https://maven.fabricmc.net/docs/yarn-${project.yarn_version}/" - // ) - //} + options { + links( + "https://maven.fabricmc.net/docs/fabric-api-${project.fapi_version}/", + "https://maven.fabricmc.net/docs/yarn-${project.yarn_version}/" + ) + } } jar { @@ -162,28 +174,43 @@ jar { } modrinth { + // Main properties token = System.getenv("MODRINTH_TOKEN") projectId = "loot-table-modifier" + gameVersions = outlet.mcVersions() + + // Version stuff def customVersionName = System.getenv("VERSION_NAME") if (customVersionName != null) versionName = customVersionName + versionNumber = "${project.version}" - versionType = "alpha" + def isPreRelease = System.getenv("VERSION_IS_PRERELEASE") - versionType = !"false".equals(isPreRelease) ? "beta" : "release" + versionType = "true".equalsIgnoreCase(isPreRelease) ? "beta" : "release" + + if (project.mod_version.contains("beta")) versionType = "beta" + else if (project.mod_version.contains("alpha")) versionType = "alpha" + + + // Files uploadFile = remapJar.archiveFile //additionalFiles = [sourcesJar.archiveFile, javadocJar.archiveFile] additionalFiles = [sourcesJar.archiveFile] - gameVersions = outlet.mcVersions() + + syncBodyFrom = rootProject.file("README.md").text def changelogEnv = System.getenv("VERSION_CHANGELOG") if (changelogEnv != null) changelog = changelogEnv + dependencies { required.project "fabric-api" } } tasks.modrinth.dependsOn(tasks.modrinthSyncBody) + + publishing { repositories { maven { @@ -205,10 +232,4 @@ publishing { from(components["java"]) } } - tasks.publishMavenPublicationToMavenLocal.doLast { - if (System.getenv("IS_DEBUG") == "true") System.out.println("Version: " + version) - } - tasks.publishMavenPublicationToOffsetMonkey538Repository.doLast { - if (System.getenv("IS_DEBUG") == "true") System.out.println("Version: " + version) - } } diff --git a/gradle.properties b/gradle.properties index 58269ac..0c86fe9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,5 +17,5 @@ loader_version = 0.16.14 devauth_version = 1.2.1 # Mod Properties -mod_version = 1.1.2 +mod_version = 2.0.0-alpha.1 supported_minecraft_versions = >=1.20.5 <=1.21.5 From 32080118a44cb679902b87aa6809e3eee5c8ef7f Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:04:43 +0300 Subject: [PATCH 32/53] Fix ids without namespaces not being matched. Misode generator omits the 'minecraft' namespace so no types matched just "block" --- .../condition/LootConditionPredicate.java | 6 +-- .../predicate/entry/ItemEntryPredicate.java | 10 ++--- .../predicate/table/LootTablePredicate.java | 38 ++++++++----------- ...rn.java => OptionalIdentifierPattern.java} | 32 +++++++++------- .../datagen/LootTableModifierDatagen.java | 18 ++++++++- 5 files changed, 57 insertions(+), 47 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/{OptionalPattern.java => OptionalIdentifierPattern.java} (51%) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java index 45d3446..1d6e981 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java @@ -5,10 +5,10 @@ import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; -public record LootConditionPredicate(@NotNull OptionalPattern functionPattern) { - public static final Codec CODEC = OptionalPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); +public record LootConditionPredicate(@NotNull OptionalIdentifierPattern functionPattern) { + public static final Codec CODEC = OptionalIdentifierPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); public boolean matches(final @NotNull LootCondition condition) { final Identifier functionId = Registries.LOOT_CONDITION_TYPE.getId(condition.getType()); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java index 3a52b3d..bd86695 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java @@ -9,13 +9,13 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; -public record ItemEntryPredicate(OptionalPattern name) implements LootModifierPredicate { +public record ItemEntryPredicate(OptionalIdentifierPattern name) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(OptionalPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) + instance -> instance.group(OptionalIdentifierPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) ); @Override @@ -35,9 +35,9 @@ public static LootModifierPredicate.Builder builder(ItemConvertible name) { return builder(Registries.ITEM.getId(name.asItem())); } public static LootModifierPredicate.Builder builder(Identifier name) { - return () -> new ItemEntryPredicate(OptionalPattern.literal(name.toString())); + return () -> new ItemEntryPredicate(OptionalIdentifierPattern.literal(name)); } - public static LootModifierPredicate.Builder builder(OptionalPattern name) { + public static LootModifierPredicate.Builder builder(OptionalIdentifierPattern name) { return () -> new ItemEntryPredicate(name); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 1e4b314..229e1c6 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -17,7 +17,7 @@ import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; @@ -25,27 +25,27 @@ import java.util.Optional; -public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { +public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Codec.either(OptionalPattern.CODEC, OptionalPattern.CODEC.listOf()).optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalEitherIdentifier), - Codec.either(OptionalPattern.CODEC, OptionalPattern.CODEC.listOf()).optionalFieldOf("types").forGetter(LootTablePredicate::optionalEitherType) + Codec.either(OptionalIdentifierPattern.CODEC, OptionalIdentifierPattern.CODEC.listOf()).optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalEitherIdentifier), + Codec.either(OptionalIdentifierPattern.CODEC, OptionalIdentifierPattern.CODEC.listOf()).optionalFieldOf("types").forGetter(LootTablePredicate::optionalEitherType) ).apply(instance, LootTablePredicate::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Optional>> optionalIdentifier, @NotNull Optional>> optionalType) { + private LootTablePredicate(@NotNull Optional>> optionalIdentifier, @NotNull Optional>> optionalType) { this( optionalIdentifier.map(it -> it.map(List::of, it2 -> it2)).orElse(null), optionalType.map(it -> it.map(List::of, it2 -> it2)).orElse(null) ); } - private Optional>> optionalEitherIdentifier() { + private Optional>> optionalEitherIdentifier() { if (identifiers == null || identifiers.isEmpty()) return Optional.empty(); if (identifiers.size() == 1) return Optional.of(Either.left(identifiers.get(0))); return Optional.of(Either.right(identifiers)); } - private Optional>> optionalEitherType() { + private Optional>> optionalEitherType() { if (types == null || types.isEmpty()) return Optional.empty(); if (types.size() == 1) return Optional.of(Either.left(types.get(0))); @@ -64,14 +64,14 @@ public boolean test(@NotNull LootModifierContext context) { if (identifiers != null) { boolean idResult = false; final String tableIdString = context.tableId().toString(); - for (OptionalPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); + for (OptionalIdentifierPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); result = idResult; } if (types != null) { boolean typeResult = false; final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); - for (OptionalPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); + for (OptionalIdentifierPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); result = result && typeResult; } @@ -83,8 +83,8 @@ public static LootTablePredicate.Builder builder() { } public static class Builder implements LootModifierPredicate.Builder { - private final ImmutableList.Builder names = ImmutableList.builder(); - private final ImmutableList.Builder types = ImmutableList.builder(); + private final ImmutableList.Builder names = ImmutableList.builder(); + private final ImmutableList.Builder types = ImmutableList.builder(); public LootTablePredicate.Builder name(@NotNull EntityType name) { @@ -100,14 +100,10 @@ public LootTablePredicate.Builder name(@NotNull RegistryKey name) { return this; } public LootTablePredicate.Builder name(@NotNull Identifier name) { - name(name.toString()); + name(OptionalIdentifierPattern.literal(name)); return this; } - public LootTablePredicate.Builder name(@NotNull String name) { - name(OptionalPattern.literal(name)); - return this; - } - public LootTablePredicate.Builder name(@NotNull OptionalPattern name) { + public LootTablePredicate.Builder name(@NotNull OptionalIdentifierPattern name) { this.names.add(name); return this; } @@ -117,14 +113,10 @@ public LootTablePredicate.Builder type(@NotNull ContextType type) { return this; } public LootTablePredicate.Builder type(@NotNull Identifier type) { - type(type.toString()); - return this; - } - public LootTablePredicate.Builder type(@NotNull String type) { - this.types.add(OptionalPattern.literal(type)); + this.types.add(OptionalIdentifierPattern.literal(type)); return this; } - public LootTablePredicate.Builder type(@NotNull OptionalPattern type) { + public LootTablePredicate.Builder type(@NotNull OptionalIdentifierPattern type) { this.types.add(type); return this; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java similarity index 51% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalPattern.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java index f2daf0c..1e088d2 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalPattern.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java @@ -3,17 +3,18 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import java.util.regex.Matcher; import java.util.regex.Pattern; -public record OptionalPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { - private static final Codec INLINE_CODEC = Codec.STRING.xmap(OptionalPattern::literal, OptionalPattern::patternString); - private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.fieldOf("regexPattern").forGetter(OptionalPattern::patternString) - ).apply(instance, OptionalPattern::compile)); - public static final Codec CODEC = Codec.either( +public record OptionalIdentifierPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { + private static final Codec INLINE_CODEC = Identifier.CODEC.xmap(OptionalIdentifierPattern::literal, instance -> Identifier.of(instance.patternString())); + private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("regexPattern").forGetter(OptionalIdentifierPattern::patternString) + ).apply(instance, OptionalIdentifierPattern::compile)); + public static final Codec CODEC = Codec.either( INLINE_CODEC, FULL_CODEC ).xmap( @@ -21,16 +22,19 @@ public record OptionalPattern(boolean isRegex, @NotNull String patternString, @N pattern -> pattern.isRegex ? Either.right(pattern) : Either.left(pattern) ); - public static OptionalPattern literal(final String literalString) { - return new OptionalPattern( + public static OptionalIdentifierPattern literal(final Identifier identifier) { + return new OptionalIdentifierPattern( false, - literalString, - Pattern.compile(Pattern.quote(literalString)) + identifier.toString(), + Pattern.compile(Pattern.quote(identifier.toString())) ); } + //public static OptionalIdentifierPattern literal(final String literalString) { + // + //} - public static OptionalPattern compile(String regexPattern) { - return new OptionalPattern( + public static OptionalIdentifierPattern compile(final String regexPattern) { + return new OptionalIdentifierPattern( true, regexPattern, Pattern.compile(regexPattern) @@ -40,11 +44,11 @@ public static OptionalPattern compile(String regexPattern) { /** * Not deprecated for removal. *

- * Use this only if you really need an instance of the {@link Matcher}. Otherwise, use {@link #matches(CharSequence)}, because it removes the overhead the {@link Matcher} when {@link #isRegex} is false. + * Use this only if you really need an instance of the {@link Matcher}. Otherwise, use {@link #matches(CharSequence)}, because it removes the overhead of the {@link Matcher} when {@link #isRegex} is false. * @see #matches(CharSequence) */ @SuppressWarnings("DeprecatedIsStillUsed") - @Deprecated() + @Deprecated(forRemoval = false) public Matcher matcher(CharSequence input) { return pattern.matcher(input); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 3c7b4aa..52c3e3d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -20,7 +20,7 @@ import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; @@ -53,7 +53,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("replace_ingots_with_command_block"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(OptionalPattern.compile("minecraft:.*_ingot")) + ItemEntryPredicate.builder(OptionalIdentifierPattern.compile("minecraft:.*_ingot")) ) .action( SetItemAction.builder(Items.COMMAND_BLOCK) @@ -137,6 +137,20 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ) ) ); + addModifier( + id("add_command_block_to_all_blocks"), + LootModifier.builder() + .conditionally( + LootTablePredicate.builder() + .type(LootContextTypes.BLOCK) + ) + .action( + AddEntryAction.builder( + ItemEntry.builder(Items.CAKE) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ) + ); } } From 2009a70787f6eddd712578e193cb28749286b41a Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:46:40 +0300 Subject: [PATCH 33/53] Add remove entry action --- .../action/LootModifierActionTypes.java | 2 + .../action/entry/RemoveEntryAction.java | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java index bb38263..81ff4f9 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java @@ -5,6 +5,7 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; @@ -20,6 +21,7 @@ private LootModifierActionTypes() { public static final LootModifierActionType POOL_REMOVE = register(id("pool_remove"), RemovePoolAction.CODEC); public static final LootModifierActionType ENTRY_ADD = register(id("entry_add"), AddEntryAction.CODEC); + public static final LootModifierActionType ENTRY_REMOVE = register(id("entry_remove"), RemoveEntryAction.CODEC); public static final LootModifierActionType ENTRY_ITEM_SET = register(id("entry_item_set"), SetItemAction.CODEC); private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java new file mode 100644 index 0000000..77944ce --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java @@ -0,0 +1,49 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.entry; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapCodec; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.entry.LootPoolEntry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; + +public record RemoveEntryAction() implements LootModifierAction { + public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemoveEntryAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ENTRY_REMOVE; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + // TODO: Should this be removed? All matching entries in a pool should be removed, right? Same with RemovePoolAction? + if (context.poolAlreadyModified()) return MODIFIED_NONE; + + final LootPool pool = context.pool(); + final LootPoolEntry entry = context.entry(); + if (pool == null || entry == null) return MODIFIED_NONE; + + final ImmutableList.Builder newEntriesBuilder = ImmutableList.builder(); + + for (final LootPoolEntry originalEntry : pool.entries) { + if (originalEntry == entry) continue; + newEntriesBuilder.add(originalEntry); + } + + ((LootPoolAccessor) pool).setEntries(newEntriesBuilder.build()); + + return MODIFIED_ENTRY; + } + + public static RemoveEntryAction.Builder builder() { + return RemoveEntryAction::new; + } +} From a8b82c32688798530a05a4992a0c7d5405998dca Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:48:30 +0300 Subject: [PATCH 34/53] Fix multiple entries/pools not being able to be removed from the same pool/table --- .../api/resource/action/entry/RemoveEntryAction.java | 3 --- .../api/resource/action/pool/RemovePoolAction.java | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java index 77944ce..edf2e6b 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java @@ -24,9 +24,6 @@ public LootModifierActionType getType() { @Override public int apply(@NotNull LootModifierContext context) { - // TODO: Should this be removed? All matching entries in a pool should be removed, right? Same with RemovePoolAction? - if (context.poolAlreadyModified()) return MODIFIED_NONE; - final LootPool pool = context.pool(); final LootPoolEntry entry = context.entry(); if (pool == null || entry == null) return MODIFIED_NONE; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java index 422b318..d4f5cb1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java @@ -24,8 +24,6 @@ public LootModifierActionType getType() { @Override public int apply(@NotNull LootModifierContext context) { - if (context.tableAlreadyModified()) return MODIFIED_NONE; - final LootTable table = context.table(); final LootPool pool = context.pool(); if (pool == null) return MODIFIED_NONE; From 0f9327736db2a38eebd8d688e0e9ce545bc54ac0 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:49:07 +0300 Subject: [PATCH 35/53] Example datagen for testing RemoveEntry --- .../datagen/LootTableModifierDatagen.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 52c3e3d..90b815e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -20,6 +20,9 @@ import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedLootPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; @@ -117,7 +120,10 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("remove_pools_with_sticks"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(Items.STICK) + AllOfLootPredicate.builder() + .and(ItemEntryPredicate.builder(Items.STICK)) + // Exclude witch to test if AllOf and Inverted work + so I can test RemoveEntry on witch. + .and(InvertedLootPredicate.builder(LootTablePredicate.builder().name(EntityType.WITCH))) ) .action( RemovePoolAction.builder() @@ -151,6 +157,18 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ) ) ); + addModifier( + id("remove_glowstone_and_gunpowder_from_witch"), + LootModifier.builder() + .conditionally(LootTablePredicate.builder().name(EntityType.WITCH)) + .conditionally( + ItemEntryPredicate.builder(Items.GLOWSTONE_DUST) + .or(ItemEntryPredicate.builder(Items.GUNPOWDER)) + ) + .action( + RemoveEntryAction.builder() + ) + ); } } From 61539e0168c619bc11811f9ca37c3533a035eeb2 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 13:21:29 +0300 Subject: [PATCH 36/53] Make codecs only accept a list when possible Simpler logic and a list with one entry doesn't seem like *too much* boilerplate --- .../api/resource/LootModifier.java | 1 - .../resource/action/entry/AddEntryAction.java | 8 +- .../resource/action/pool/AddPoolAction.java | 8 +- .../predicate/LootModifierPredicate.java | 4 +- .../predicate/op/AllOfLootPredicate.java | 8 +- .../predicate/op/AnyOfLootPredicate.java | 8 +- .../predicate/op/TermsLootPredicate.java | 21 +---- .../predicate/table/LootTablePredicate.java | 22 ++--- .../datagen/LootTableModifierDatagen.java | 84 ++++++++++--------- 9 files changed, 67 insertions(+), 97 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index a2c6461..a0e01bf 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -17,7 +17,6 @@ import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.MODIFIED_NONE; -// Using ArrayList as I want it to be modifiable because I empty it when applying, so I can check for things that weren't applied TODO: I don't think I do this anymore? TODO: make sure that changing it to a normal List is fine public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) implements Predicate { //public record LootModifier(@NotNull @UnmodifiableView ArrayList actions) { private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java index 2879c13..d68a68f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java @@ -42,17 +42,13 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_ENTRY; } - public static AddEntryAction.Builder builder(@NotNull LootPoolEntry.Builder entryBuilder) { - return new AddEntryAction.Builder(entryBuilder); + public static AddEntryAction.Builder builder() { + return new AddEntryAction.Builder(); } public static class Builder implements LootModifierAction.Builder { private final ImmutableList.Builder entries = ImmutableList.builder(); - private Builder(@NotNull LootPoolEntry.Builder poolBuilder) { - entries.add(poolBuilder.build()); - } - public AddEntryAction.Builder entry(LootPoolEntry.Builder poolBuilder) { this.entries.add(poolBuilder.build()); return this; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java index cb95202..1a72bfd 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java @@ -37,17 +37,13 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_POOL; } - public static AddPoolAction.Builder builder(@NotNull LootPool.Builder poolBuilder) { - return new AddPoolAction.Builder(poolBuilder); + public static AddPoolAction.Builder builder() { + return new AddPoolAction.Builder(); } public static class Builder implements LootModifierAction.Builder { private final ImmutableList.Builder pools = ImmutableList.builder(); - private Builder(@NotNull LootPool.Builder poolBuilder) { - pools.add(poolBuilder.build()); - } - public AddPoolAction.Builder pool(LootPool.Builder poolBuilder) { this.pools.add(poolBuilder.build()); return this; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java index aa2a976..3e19d23 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java @@ -25,11 +25,11 @@ default Builder invert() { } default LootModifierPredicate.Builder or(LootModifierPredicate.Builder otherPredicate) { - return AnyOfLootPredicate.builder(this, otherPredicate); + return AnyOfLootPredicate.builder().or(this).or(otherPredicate); } default LootModifierPredicate.Builder and(LootModifierPredicate.Builder otherPredicate) { - return AllOfLootPredicate.builder(this, otherPredicate); + return AllOfLootPredicate.builder().and(this).and(otherPredicate); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java index 918222f..ba3c44e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java @@ -20,15 +20,11 @@ public LootModifierPredicateType getType() { return LootModifierPredicateTypes.ALL_OF; } - public static AllOfLootPredicate.Builder builder(LootModifierPredicate.Builder... terms) { - return new AllOfLootPredicate.Builder(terms); + public static AllOfLootPredicate.Builder builder() { + return new AllOfLootPredicate.Builder(); } public static class Builder extends TermsLootPredicate.Builder { - public Builder(LootModifierPredicate.Builder... builders) { - super(builders); - } - @Override public LootModifierPredicate.Builder and(LootModifierPredicate.Builder builder) { this.add(builder); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java index 1105d8a..52ac941 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java @@ -20,15 +20,11 @@ public LootModifierPredicateType getType() { return LootModifierPredicateTypes.ANY_OF; } - public static AnyOfLootPredicate.Builder builder(LootModifierPredicate.Builder... terms) { - return new AnyOfLootPredicate.Builder(terms); + public static AnyOfLootPredicate.Builder builder() { + return new AnyOfLootPredicate.Builder(); } public static class Builder extends TermsLootPredicate.Builder { - public Builder(LootModifierPredicate.Builder... builders) { - super(builders); - } - @Override public LootModifierPredicate.Builder or(LootModifierPredicate.Builder builder) { this.add(builder); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java index 0736b86..9d79e91 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java @@ -1,8 +1,6 @@ package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import org.jetbrains.annotations.NotNull; @@ -24,17 +22,12 @@ protected TermsLootPredicate(final List terms, final Pred protected static MapCodec createCodec(final Function, T> constructor) { return RecordCodecBuilder.mapCodec( - instance -> instance.group(Codec.either(LootModifierPredicate.CODEC, LootModifierPredicate.CODEC.listOf()).fieldOf("terms").forGetter(TermsLootPredicate::eitherTerms)).apply(instance, eitherToFunction(constructor)) + instance -> instance.group(LootModifierPredicate.CODEC.listOf().fieldOf("terms").forGetter(TermsLootPredicate::getTerms)).apply(instance, constructor) ); } - private static Function>, T> eitherToFunction(final Function, T> constructor) { - return eitherTerms -> constructor.apply(eitherTerms.right().orElseGet(() -> List.of(eitherTerms.left().orElseThrow()))); - } - - private Either> eitherTerms() { - if (terms.size() == 1) return Either.left(terms.get(0)); - return Either.right(terms); + private List getTerms() { + return terms; } @Override @@ -45,13 +38,7 @@ public boolean test(final @NotNull LootModifierContext context) { public abstract static class Builder implements LootModifierPredicate.Builder { private final ImmutableList.Builder terms = ImmutableList.builder(); - protected Builder(LootModifierPredicate.Builder... terms) { - for (LootModifierPredicate.Builder builder : terms) { - this.terms.add(builder.build()); - } - } - - public void add(LootModifierPredicate.Builder builder) { + protected void add(LootModifierPredicate.Builder builder) { this.terms.add(builder.build()); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 229e1c6..a9cc24a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -1,8 +1,6 @@ package top.offsetmonkey538.loottablemodifier.api.resource.predicate.table; import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.Block; @@ -27,29 +25,27 @@ public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Codec.either(OptionalIdentifierPattern.CODEC, OptionalIdentifierPattern.CODEC.listOf()).optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalEitherIdentifier), - Codec.either(OptionalIdentifierPattern.CODEC, OptionalIdentifierPattern.CODEC.listOf()).optionalFieldOf("types").forGetter(LootTablePredicate::optionalEitherType) + OptionalIdentifierPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalIdentifier), + OptionalIdentifierPattern.CODEC.listOf().optionalFieldOf("types").forGetter(LootTablePredicate::optionalType) ).apply(instance, LootTablePredicate::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Optional>> optionalIdentifier, @NotNull Optional>> optionalType) { + private LootTablePredicate(@NotNull Optional> optionalIdentifier, @NotNull Optional> optionalType) { this( - optionalIdentifier.map(it -> it.map(List::of, it2 -> it2)).orElse(null), - optionalType.map(it -> it.map(List::of, it2 -> it2)).orElse(null) + optionalIdentifier.orElse(null), + optionalType.orElse(null) ); } - private Optional>> optionalEitherIdentifier() { + private Optional> optionalIdentifier() { if (identifiers == null || identifiers.isEmpty()) return Optional.empty(); - if (identifiers.size() == 1) return Optional.of(Either.left(identifiers.get(0))); - return Optional.of(Either.right(identifiers)); + return Optional.of(identifiers); } - private Optional>> optionalEitherType() { + private Optional> optionalType() { if (types == null || types.isEmpty()) return Optional.empty(); - if (types.size() == 1) return Optional.of(Either.left(types.get(0))); - return Optional.of(Either.right(types)); + return Optional.of(types); } @Override diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 90b815e..6fef36a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -21,8 +21,6 @@ import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedLootPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; @@ -69,14 +67,15 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { ItemEntryPredicate.builder(Items.SUGAR_CANE) ) .action( - AddPoolAction.builder( - LootPool.builder() - .rolls(ConstantLootNumberProvider.create(1)) - .with( - ItemEntry.builder(Items.TNT) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ) - ) + AddPoolAction.builder() + .pool( + LootPool.builder() + .rolls(ConstantLootNumberProvider.create(1)) + .with( + ItemEntry.builder(Items.TNT) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ) ) ); addModifier( @@ -88,14 +87,15 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { .name(EntityType.ZOMBIE) ) .action( - AddPoolAction.builder( - LootPool.builder() - .rolls(ConstantLootNumberProvider.create(1)) - .with( - ItemEntry.builder(Items.TNT) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ) - ) + AddPoolAction.builder() + .pool( + LootPool.builder() + .rolls(ConstantLootNumberProvider.create(1)) + .with( + ItemEntry.builder(Items.TNT) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ) ) ); addModifier( @@ -106,24 +106,24 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { .name(RegistryKey.of(RegistryKeys.LOOT_TABLE, id("test_empty_table"))) ) .action( - AddPoolAction.builder( - LootPool.builder() - .rolls(ConstantLootNumberProvider.create(1)) - .with( - ItemEntry.builder(Items.TNT) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ) - ) + AddPoolAction.builder() + .pool( + LootPool.builder() + .rolls(ConstantLootNumberProvider.create(1)) + .with( + ItemEntry.builder(Items.TNT) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) + ) ) ); addModifier( id("remove_pools_with_sticks"), LootModifier.builder() .conditionally( - AllOfLootPredicate.builder() - .and(ItemEntryPredicate.builder(Items.STICK)) + ItemEntryPredicate.builder(Items.STICK) // Exclude witch to test if AllOf and Inverted work + so I can test RemoveEntry on witch. - .and(InvertedLootPredicate.builder(LootTablePredicate.builder().name(EntityType.WITCH))) + .and(LootTablePredicate.builder().name(EntityType.WITCH).invert()) ) .action( RemovePoolAction.builder() @@ -137,10 +137,11 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { .name(Blocks.DIRT) ) .action( - AddEntryAction.builder( - ItemEntry.builder(Items.CAKE) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ) + AddEntryAction.builder() + .entry( + ItemEntry.builder(Items.CAKE) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) ) ); addModifier( @@ -151,19 +152,22 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { .type(LootContextTypes.BLOCK) ) .action( - AddEntryAction.builder( - ItemEntry.builder(Items.CAKE) - .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) - ) + AddEntryAction.builder() + .entry( + ItemEntry.builder(Items.CAKE) + .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) + ) ) ); addModifier( id("remove_glowstone_and_gunpowder_from_witch"), LootModifier.builder() - .conditionally(LootTablePredicate.builder().name(EntityType.WITCH)) .conditionally( - ItemEntryPredicate.builder(Items.GLOWSTONE_DUST) - .or(ItemEntryPredicate.builder(Items.GUNPOWDER)) + LootTablePredicate.builder().name(EntityType.WITCH) + .and( + ItemEntryPredicate.builder(Items.GLOWSTONE_DUST) + .or(ItemEntryPredicate.builder(Items.GUNPOWDER)) + ) ) .action( RemoveEntryAction.builder() From ca796f889278f0ebe9b65e46cb74426b7d300826 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 13:55:59 +0300 Subject: [PATCH 37/53] Fix loading of legacy modifiers --- .../api/resource/LootModifier.java | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index a0e01bf..6b8cafa 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -5,11 +5,15 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.loot.LootPool; +import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import java.util.*; @@ -20,14 +24,12 @@ public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) implements Predicate { //public record LootModifier(@NotNull @UnmodifiableView ArrayList actions) { private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( - // TODO: this should use the table predicate, once that exists - Codec.either(LootModifierPredicate.CODEC, LootModifierPredicate.CODEC.listOf()).fieldOf("modifies").forGetter(modifier -> { - if (modifier.predicates.size() == 1) return Either.left(modifier.predicates.get(0)); - return Either.right(modifier.predicates); + Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(modifier -> { + throw new IllegalStateException("Tried using legacy loot table modifier codec for serialization for some reason!"); }), LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) - ).apply(instance, LootModifier::fromLegacyCodec)); + ).apply(instance, (modifiesEither, pools, lootPools) -> new LootModifier(getActionsFromLegacyCodec(pools, lootPools), getPredicateFromLegacyCodec(modifiesEither)))); private static final Codec CURRENT_CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.either( @@ -51,22 +53,28 @@ public record LootModifier(@NotNull @UnmodifiableView List a CURRENT_CODEC ).xmap(either -> either.map(it -> it, it -> it), Either::right); // Always encode as current codec, which is on the right. - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // From codec soo yeah - private static LootModifier fromLegacyCodec(@NotNull Either> modifiesEither, @NotNull Optional> pools, @NotNull Optional> lootPools) { + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // from the codec + private static @NotNull List getActionsFromLegacyCodec(@NotNull Optional> pools, @NotNull Optional> lootPools) { List actions = null; - if (pools.isPresent() && lootPools.isPresent()) throw new IllegalStateException("Both \"pools\" and \"loot_pools\" present in loot modifier!"); + if (pools.isPresent() && lootPools.isPresent()) throw new IllegalStateException("Both \"pools\" and \"loot_pools\" present in legacy loot modifier!"); if (pools.isPresent()) actions = List.of(new AddPoolAction(pools.get())); if (lootPools.isPresent()) actions = List.of(new AddPoolAction(lootPools.get())); - if (actions == null) throw new IllegalStateException("Neither \"pools\" nor \"loot_pools\" present in loot modifier!"); + if (actions == null) throw new IllegalStateException("Neither \"pools\" nor \"loot_pools\" present in legacy loot modifier!"); + return actions; + } + private static @NotNull List getPredicateFromLegacyCodec(@NotNull Either> modifiesEither) { + if (modifiesEither.left().isPresent()) return List.of(LootTablePredicate.builder().name(modifiesEither.left().get()).build()); - return new LootModifier( - actions, - new ArrayList<>(modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow()))) - ); + + final LootModifierPredicate.Builder predicateBuilder = AllOfLootPredicate.builder(); + for (final Identifier currentId : modifiesEither.right().orElseGet(List::of)) { + predicateBuilder.and(LootTablePredicate.builder().name(currentId)); + } + return List.of(predicateBuilder.build()); } private static LootModifier fromCurrentCodec(Either> actionsEither, Either> predicatesEither) { From e3a6c86f52263160bff06018b9f1cd76d1d18e5d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:09:19 +0300 Subject: [PATCH 38/53] Make loot modifier store only a single predicate instead of a list --- .../api/resource/LootModifier.java | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index 6b8cafa..d12d141 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -11,7 +11,6 @@ import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; @@ -19,10 +18,10 @@ import java.util.*; import java.util.function.Predicate; +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.MODIFIED_NONE; -public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull @UnmodifiableView List predicates) implements Predicate { -//public record LootModifier(@NotNull @UnmodifiableView ArrayList actions) { +public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull LootModifierPredicate predicate) implements Predicate { private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(modifier -> { throw new IllegalStateException("Tried using legacy loot table modifier codec for serialization for some reason!"); @@ -39,13 +38,7 @@ public record LootModifier(@NotNull @UnmodifiableView List a if (modifier.actions.size() == 1) return Either.left(modifier.actions.get(0)); return Either.right(modifier.actions); }), - Codec.either( - LootModifierPredicate.CODEC, - LootModifierPredicate.CODEC.listOf() - ).fieldOf("predicates").forGetter(modifier -> { - if (modifier.predicates.size() == 1) return Either.left(modifier.predicates.get(0)); - return Either.right(modifier.predicates); - }) + LootModifierPredicate.CODEC.fieldOf("predicate").forGetter(LootModifier::predicate) ).apply(instance, LootModifier::fromCurrentCodec)); public static final Codec CODEC = Codec.either( @@ -66,21 +59,21 @@ public record LootModifier(@NotNull @UnmodifiableView List a return actions; } - private static @NotNull List getPredicateFromLegacyCodec(@NotNull Either> modifiesEither) { - if (modifiesEither.left().isPresent()) return List.of(LootTablePredicate.builder().name(modifiesEither.left().get()).build()); + private static @NotNull LootModifierPredicate getPredicateFromLegacyCodec(@NotNull Either> modifiesEither) { + if (modifiesEither.left().isPresent()) return LootTablePredicate.builder().name(modifiesEither.left().get()).build(); final LootModifierPredicate.Builder predicateBuilder = AllOfLootPredicate.builder(); for (final Identifier currentId : modifiesEither.right().orElseGet(List::of)) { predicateBuilder.and(LootTablePredicate.builder().name(currentId)); } - return List.of(predicateBuilder.build()); + return predicateBuilder.build(); } - private static LootModifier fromCurrentCodec(Either> actionsEither, Either> predicatesEither) { + private static LootModifier fromCurrentCodec(Either> actionsEither, LootModifierPredicate predicate) { return new LootModifier( actionsEither.map(List::of, it -> it), - new ArrayList<>(predicatesEither.map(List::of, it -> it)) + predicate ); } @@ -101,10 +94,7 @@ public int apply(final @NotNull LootModifierContext context) { } public boolean test(final @NotNull LootModifierContext context) { - for (LootModifierPredicate predicate : predicates) { - if (!predicate.test(context)) return false; - } - return true; + return predicate.test(context); } public static LootModifier.Builder builder() { @@ -113,7 +103,7 @@ public static LootModifier.Builder builder() { public static class Builder { private final ImmutableList.Builder actions = ImmutableList.builder(); - private final ImmutableList.Builder predicates = ImmutableList.builder(); + private LootModifierPredicate predicate; public LootModifier.Builder action(@NotNull LootModifierAction.Builder action) { this.actions.add(action.build()); @@ -121,12 +111,13 @@ public LootModifier.Builder action(@NotNull LootModifierAction.Builder action) { } public LootModifier.Builder conditionally(@NotNull LootModifierPredicate.Builder predicate) { - this.predicates.add(predicate.build()); + if (this.predicate != null) LOGGER.error("Predicate has already been set for this builder! The previously set predicate will be overwritten! Please use the 'LootModifierPredicate.Builder#and()' and 'LootModifierPredicate.Builder#or()' methods for adding multiple conditions!"); + this.predicate = predicate.build(); return this; } public LootModifier build() { - return new LootModifier(this.actions.build(), this.predicates.build()); + return new LootModifier(this.actions.build(), this.predicate); } } } From 8f41831c397d3573971b5d5e3ce86f8432bef154 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:12:33 +0300 Subject: [PATCH 39/53] Implement legacy codec more efficiently --- .../loottablemodifier/api/resource/LootModifier.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index d12d141..da89c1e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -11,7 +11,6 @@ import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; @@ -60,12 +59,9 @@ public record LootModifier(@NotNull @UnmodifiableView List a } private static @NotNull LootModifierPredicate getPredicateFromLegacyCodec(@NotNull Either> modifiesEither) { - if (modifiesEither.left().isPresent()) return LootTablePredicate.builder().name(modifiesEither.left().get()).build(); - - - final LootModifierPredicate.Builder predicateBuilder = AllOfLootPredicate.builder(); - for (final Identifier currentId : modifiesEither.right().orElseGet(List::of)) { - predicateBuilder.and(LootTablePredicate.builder().name(currentId)); + final LootTablePredicate.Builder predicateBuilder = LootTablePredicate.builder(); + for (final Identifier currentId : modifiesEither.map(List::of, it -> it)) { + predicateBuilder.name(currentId); } return predicateBuilder.build(); } From e7775bf5dfec543134ae9a382fba74877ef9c50f Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:50:51 +0300 Subject: [PATCH 40/53] Fix loot table predicate sometimes not matching tables when it should --- .../api/resource/predicate/table/LootTablePredicate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index a9cc24a..c45991f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -57,14 +57,14 @@ public LootModifierPredicateType getType() { public boolean test(@NotNull LootModifierContext context) { boolean result = true; - if (identifiers != null) { + if (identifiers != null && !identifiers.isEmpty()) { boolean idResult = false; final String tableIdString = context.tableId().toString(); for (OptionalIdentifierPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); result = idResult; } - if (types != null) { + if (types != null && !types.isEmpty()) { boolean typeResult = false; final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); for (OptionalIdentifierPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); From 7494ae20c5c0fbd7b68224fba1123a982d4d9a0d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:14:02 +0300 Subject: [PATCH 41/53] Make loot modifier only accept a list of actions --- .../api/resource/LootModifier.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index da89c1e..88d9d4f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -30,15 +30,9 @@ public record LootModifier(@NotNull @UnmodifiableView List a ).apply(instance, (modifiesEither, pools, lootPools) -> new LootModifier(getActionsFromLegacyCodec(pools, lootPools), getPredicateFromLegacyCodec(modifiesEither)))); private static final Codec CURRENT_CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either( - LootModifierAction.CODEC, - LootModifierAction.CODEC.listOf() - ).fieldOf("actions").forGetter(modifier -> { - if (modifier.actions.size() == 1) return Either.left(modifier.actions.get(0)); - return Either.right(modifier.actions); - }), + LootModifierAction.CODEC.listOf().fieldOf("actions").forGetter(LootModifier::actions), LootModifierPredicate.CODEC.fieldOf("predicate").forGetter(LootModifier::predicate) - ).apply(instance, LootModifier::fromCurrentCodec)); + ).apply(instance, LootModifier::new)); public static final Codec CODEC = Codec.either( LEGACY_CODEC, @@ -66,13 +60,6 @@ public record LootModifier(@NotNull @UnmodifiableView List a return predicateBuilder.build(); } - private static LootModifier fromCurrentCodec(Either> actionsEither, LootModifierPredicate predicate) { - return new LootModifier( - actionsEither.map(List::of, it -> it), - predicate - ); - } - /** * @param context context to modify * @return highest modification level from applied actions. {@link LootModifierAction#MODIFIED_NONE} when no action was applied. From 9e8ca3a58a503215d22f4f3fe7758f3c3c40cc3d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:19:38 +0300 Subject: [PATCH 42/53] javadoc :D --- .../api/datagen/LootModifierProvider.java | 42 ++++++---- .../api/resource/LootModifier.java | 50 ++++++++++++ .../resource/action/LootModifierAction.java | 31 ++++++++ .../action/LootModifierActionType.java | 8 ++ .../action/LootModifierActionTypes.java | 27 +++++++ .../resource/action/entry/AddEntryAction.java | 30 ++++++- .../action/entry/RemoveEntryAction.java | 10 +++ .../resource/action/entry/SetItemAction.java | 24 ++++++ .../resource/action/pool/AddPoolAction.java | 26 ++++++ .../action/pool/RemovePoolAction.java | 10 +++ .../predicate/LootModifierPredicate.java | 56 ++++++++++++- .../predicate/LootModifierPredicateType.java | 8 ++ .../predicate/LootModifierPredicateTypes.java | 27 +++++++ .../predicate/entry/ItemEntryPredicate.java | 35 +++++++- .../predicate/op/AllOfLootPredicate.java | 26 +++++- .../predicate/op/AnyOfLootPredicate.java | 27 ++++++- .../predicate/op/InvertedLootPredicate.java | 13 ++- .../predicate/op/TermsLootPredicate.java | 22 ++++++ .../predicate/table/LootTablePredicate.java | 79 ++++++++++++++++++- .../resource/util/LootModifierContext.java | 10 +++ .../api/resource/util/LootTableIdGetter.java | 22 ++++++ .../util/OptionalIdentifierPattern.java | 44 +++++++---- .../datagen/LootTableModifierDatagen.java | 9 ++- .../impl/LootTableIdGetterImpl.java | 2 + .../mixin/ItemEntryAccessor.java | 1 + 25 files changed, 588 insertions(+), 51 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java index 5ecb59d..349942b 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java @@ -14,24 +14,32 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; /** - * FIXME: wrong * A datagen provider for creating loot modifiers. *
- * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@code addModifier(...)} methods to add modifiers. + * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@link #addModifier(Identifier, LootModifier.Builder)} method to add modifiers. *
*

{@code
  * @Override
  * protected void generate(RegistryWrapper.WrapperLookup lookup) {
- *     addModifier(
- *             Identifier.of("testmod", "drop_tnt"),
- *             LootPool.builder()
- *                     .rolls(ConstantLootNumberProvider.create(1))
- *                     .with(
- *                             ItemEntry.builder(Items.TNT)
- *                                     .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
- *                     ),
- *             EntityType.CREEPER,
- *             EntityType.ZOMBIE
+ *      addModifier(
+ *          Identifier.of("testmod", "mobs_drop_tnt"),
+ *              LootModifier.builder()
+ *                  .conditionally(
+ *                      LootTablePredicate.builder()
+ *                          .name(EntityType.CREEPER)
+ *                          .name(EntityType.ZOMBIE)
+ *                  )
+ *                  .action(
+ *                      AddPoolAction.builder()
+ *                          .pool(
+ *                              LootPool.builder()
+ *                                  .rolls(ConstantLootNumberProvider.create(1))
+ *                                  .with(
+ *                                      ItemEntry.builder(Items.TNT)
+ *                                          .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
+ *                                  )
+ *                          )
+ *                  )
  *     );
  * }
  * }
@@ -51,16 +59,22 @@ protected void configure(BiConsumer provider, Registry @Override public String getName() { - return "New New Loot Table Modifiers"; // todo: change + return "Loot Table Modifiers"; } /** - * Override and use {@code addModifier()} methods to add modifiers. + * Override and use {@link #addModifier(Identifier, LootModifier.Builder)} method to add modifiers. * * @param lookup A lookup for registries. */ protected abstract void generate(RegistryWrapper.WrapperLookup lookup); + /** + * Adds a loot modifier using the provided name and builder. + * + * @param name the identifier of the modifier + * @param builder the builder to generate the loot modifier from + */ protected void addModifier(@NotNull Identifier name, @NotNull LootModifier.Builder builder) { provider.accept(name, builder.build()); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index 88d9d4f..79c2d9d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -6,8 +6,10 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.loot.LootPool; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; +import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; @@ -20,6 +22,12 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.MODIFIED_NONE; +/** + * A loot modifier. Use the {@link LootModifier.Builder} for building it and the {@link LootModifierProvider} for generating the files. + * + * @param actions a list of actions to apply + * @param predicate the predicate + */ public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull LootModifierPredicate predicate) implements Predicate { private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(modifier -> { @@ -61,6 +69,8 @@ public record LootModifier(@NotNull @UnmodifiableView List a } /** + * Applied all the actions of this modifier using the provided context. + * * @param context context to modify * @return highest modification level from applied actions. {@link LootModifierAction#MODIFIED_NONE} when no action was applied. * @see LootModifierAction#MODIFIED_NONE @@ -76,29 +86,69 @@ public int apply(final @NotNull LootModifierContext context) { return result; } + /** + * Tests the predicate of this loot modifier against the provided context. + * + * @param context the context to match against + * @return if the predicate of this loot modifier matched the provided context + */ public boolean test(final @NotNull LootModifierContext context) { return predicate.test(context); } + /** + * Creates a builder for {@link LootModifier} + * + * @return a new {@link LootModifier.Builder} + */ + @Contract("->new") public static LootModifier.Builder builder() { return new LootModifier.Builder(); } + /** + * Builder for {@link LootModifier} + */ public static class Builder { + private Builder() { + + } + private final ImmutableList.Builder actions = ImmutableList.builder(); private LootModifierPredicate predicate; + /** + * Adds an action + * + * @param action the action to add + * @return this + */ + @Contract("_->this") public LootModifier.Builder action(@NotNull LootModifierAction.Builder action) { this.actions.add(action.build()); return this; } + /** + * Sets the predicate + *
+ * Loot modifier may only have one predicate and this may only be called once! + * + * @param predicate the predicate to use + * @return this + */ + @Contract("_->this") public LootModifier.Builder conditionally(@NotNull LootModifierPredicate.Builder predicate) { if (this.predicate != null) LOGGER.error("Predicate has already been set for this builder! The previously set predicate will be overwritten! Please use the 'LootModifierPredicate.Builder#and()' and 'LootModifierPredicate.Builder#or()' methods for adding multiple conditions!"); this.predicate = predicate.build(); return this; } + /** + * Builds the {@link LootModifier} + * + * @return a built {@link LootModifier} + */ public LootModifier build() { return new LootModifier(this.actions.build(), this.predicate); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java index 2aaaea9..bdf74ab 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java @@ -4,14 +4,37 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +/** + * A loot modifier action + */ public interface LootModifierAction { + /** + * Codec containing the type id + */ Codec CODEC = LootModifierActionType.REGISTRY.getCodec().dispatch(LootModifierAction::getType, LootModifierActionType::codec); + /** + * Bitmask specifying that the action modified nothing + */ int MODIFIED_NONE = 0b000; + /** + * Bitmask specifying that the action modified a table + */ int MODIFIED_TABLE = 0b001; + /** + * Bitmask specifying that the action modified a pool + */ int MODIFIED_POOL = 0b011; + /** + * Bitmask specifying that the action modified an entry + */ int MODIFIED_ENTRY = 0b111; + /** + * Returns the type of this action. + * + * @return the {@link LootModifierActionType type} of this action. + */ LootModifierActionType getType(); /** @@ -25,8 +48,16 @@ public interface LootModifierAction { */ int apply(final @NotNull LootModifierContext context); + /** + * A builder for loot modifier actions. + */ @FunctionalInterface interface Builder { + /** + * Builds the action + * + * @return a built {@link LootModifierAction} + */ LootModifierAction build(); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java index ff2be73..780da7a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java @@ -8,7 +8,15 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.LootTableModifier; +/** + * The type of a {@link LootModifierAction}, holds the codec. + * + * @param codec the codec for this action. + */ public record LootModifierActionType(@NotNull MapCodec codec) { + /** + * Registry of {@link LootModifierActionType}s + */ public static final Registry REGISTRY = new SimpleRegistry<>( RegistryKey.ofRegistry(LootTableModifier.id("loot_modifier_action_types")), Lifecycle.stable() ); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java index 81ff4f9..c1c8660 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java @@ -3,6 +3,7 @@ import com.mojang.serialization.MapCodec; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; @@ -12,22 +13,48 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; +/** + * Contains all {@link LootModifierAction} types available in Loot Table Modifier. + *
+ * Use their builders to create them. + */ public final class LootModifierActionTypes { private LootModifierActionTypes() { } + /** + * Type of {@link AddPoolAction} + */ public static final LootModifierActionType POOL_ADD = register(id("pool_add"), AddPoolAction.CODEC); + /** + * Type of {@link RemovePoolAction} + */ public static final LootModifierActionType POOL_REMOVE = register(id("pool_remove"), RemovePoolAction.CODEC); + /** + * Type of {@link AddEntryAction} + */ public static final LootModifierActionType ENTRY_ADD = register(id("entry_add"), AddEntryAction.CODEC); + /** + * Type of {@link RemoveEntryAction} + */ public static final LootModifierActionType ENTRY_REMOVE = register(id("entry_remove"), RemoveEntryAction.CODEC); + /** + * Type of {@link SetItemAction} + */ public static final LootModifierActionType ENTRY_ITEM_SET = register(id("entry_item_set"), SetItemAction.CODEC); private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); } + /** + * Registers action types by loading the class. + *
+ * Only for the loot table modifier initializer to call, NO TOUCHY >:( + */ + @ApiStatus.Internal public static void register() { // Registers action types by loading the class } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java index d68a68f..f4600e1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java @@ -6,6 +6,7 @@ import net.minecraft.loot.LootPool; import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntryTypes; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; @@ -15,6 +16,11 @@ import java.util.List; +/** + * Adds the provided entries to matched pools + * + * @param entries the entries to add + */ public record AddEntryAction(List entries) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( LootPoolEntryTypes.CODEC.listOf().fieldOf("entries").forGetter(AddEntryAction::entries) @@ -42,15 +48,35 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_ENTRY; } + /** + * Creates a builder for {@link AddEntryAction} + * + * @return a new {@link AddEntryAction.Builder} + */ + @Contract("->new") public static AddEntryAction.Builder builder() { return new AddEntryAction.Builder(); } + /** + * Builder for {@link AddEntryAction} + */ public static class Builder implements LootModifierAction.Builder { + private Builder() { + + } + private final ImmutableList.Builder entries = ImmutableList.builder(); - public AddEntryAction.Builder entry(LootPoolEntry.Builder poolBuilder) { - this.entries.add(poolBuilder.build()); + /** + * Adds an entry + * + * @param entryBuilder The entry to add + * @return this + */ + @Contract("_->this") + public AddEntryAction.Builder entry(LootPoolEntry.Builder entryBuilder) { + this.entries.add(entryBuilder.build()); return this; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java index edf2e6b..d2a954f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java @@ -7,6 +7,7 @@ import com.mojang.serialization.MapCodec; import net.minecraft.loot.LootPool; import net.minecraft.loot.entry.LootPoolEntry; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; @@ -14,6 +15,9 @@ import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; +/** + * Removes the matched entries from their pools + */ public record RemoveEntryAction() implements LootModifierAction { public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemoveEntryAction::new)); @@ -40,6 +44,12 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_ENTRY; } + /** + * Creates a builder for {@link RemoveEntryAction} + * + * @return a new {@link RemoveEntryAction.Builder} + */ + @Contract("->new") public static RemoveEntryAction.Builder builder() { return RemoveEntryAction::new; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java index 5753439..1c11a61 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java @@ -11,6 +11,7 @@ import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.registry.Registries; import net.minecraft.registry.entry.RegistryEntry; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.ItemEntryAccessor; @@ -19,6 +20,12 @@ import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; +/** + * Sets the item in matched item entries + * + * @param item the new item to replace the existing one with + * @param canReplaceEntry if other types of entries can be replaced with a basic item entry containing the target item + */ public record SetItemAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( Item.ENTRY_CODEC.fieldOf("name").forGetter(SetItemAction::item), @@ -56,10 +63,20 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_ENTRY; } + /** + * Creates a builder for {@link SetItemAction} + * + * @param item the new item to replace the existing one with + * @return a new {@link SetItemAction.Builder} + */ + @Contract("_->new") public static SetItemAction.Builder builder(@NotNull ItemConvertible item) { return new SetItemAction.Builder(item); } + /** + * Builder for {@link SetItemAction} + */ public static class Builder implements LootModifierAction.Builder { private final RegistryEntry item; private boolean canReplaceEntry; @@ -68,6 +85,13 @@ private Builder(@NotNull ItemConvertible item) { this.item = Registries.ITEM.getEntry(item.asItem()); } + /** + * Sets if other types of entries can be replaced with a basic item entry containing the target item + * + * @param canReplaceEntry if other types of entries can be replaced with a basic item entry containing the target item + * @return this + */ + @Contract("_->this") public SetItemAction.Builder setCanReplaceEntry(boolean canReplaceEntry) { this.canReplaceEntry = canReplaceEntry; return this; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java index 1a72bfd..e55d413 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java @@ -4,6 +4,7 @@ import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.loot.LootPool; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; @@ -13,6 +14,11 @@ import java.util.List; +/** + * Adds the provided pools to matched tables + * + * @param pools the pools to add + */ public record AddPoolAction(List pools) implements LootModifierAction { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( LootPool.CODEC.listOf().fieldOf("pools").forGetter(AddPoolAction::pools) @@ -37,13 +43,33 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_POOL; } + /** + * Creates a builder for {@link AddPoolAction} + * + * @return a new {@link AddPoolAction.Builder} + */ + @Contract("->new") public static AddPoolAction.Builder builder() { return new AddPoolAction.Builder(); } + /** + * Builder for {@link AddPoolAction} + */ public static class Builder implements LootModifierAction.Builder { + private Builder() { + + } + private final ImmutableList.Builder pools = ImmutableList.builder(); + /** + * Adds a pool + * + * @param poolBuilder The pool to add + * @return this + */ + @Contract("_->this") public AddPoolAction.Builder pool(LootPool.Builder poolBuilder) { this.pools.add(poolBuilder.build()); return this; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java index d4f5cb1..9e22dee 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java @@ -7,6 +7,7 @@ import com.mojang.serialization.MapCodec; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; @@ -14,6 +15,9 @@ import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; +/** + * Removes the matched pools from their tables + */ public record RemovePoolAction() implements LootModifierAction { public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemovePoolAction::new)); @@ -40,6 +44,12 @@ public int apply(@NotNull LootModifierContext context) { return MODIFIED_POOL; } + /** + * Creates a builder for {@link RemovePoolAction} + * + * @return a new {@link RemovePoolAction.Builder} + */ + @Contract("->new") public static RemovePoolAction.Builder builder() { return RemovePoolAction::new; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java index 3e19d23..b73cba3 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java @@ -1,6 +1,7 @@ package top.offsetmonkey538.loottablemodifier.api.resource.predicate; import com.mojang.serialization.Codec; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; @@ -9,26 +10,77 @@ import java.util.function.Predicate; +/** + * A loot modifier predicate + */ public interface LootModifierPredicate extends Predicate { + /** + * Codec containing the type id + */ Codec CODEC = LootModifierPredicateType.REGISTRY.getCodec().dispatch(LootModifierPredicate::getType, LootModifierPredicateType::codec); + /** + * Returns the type of this action. + * + * @return the {@link LootModifierPredicateType type} of this action. + */ LootModifierPredicateType getType(); + /** + * Tests this predicate against the provided context. + * + * @param context the context to match against + * @return if this predicate matched the provided context + */ boolean test(final @NotNull LootModifierContext context); + /** + * A builder for loot modifier predicates. + */ @FunctionalInterface interface Builder { + /** + * Builds the predicate + * + * @return a built {@link LootModifierPredicate} + */ LootModifierPredicate build(); + /** + * Inverts this builder. + *
+ * Wraps this builder in an {@link InvertedLootPredicate} + * + * @return An inverted version of this builder. + */ + @Contract("->new") default Builder invert() { return InvertedLootPredicate.builder(this); } - default LootModifierPredicate.Builder or(LootModifierPredicate.Builder otherPredicate) { + /** + * Adds another predicate builder in an {@code OR} relationship. + *
+ * Wraps this and the provided builder in an {@link AnyOfLootPredicate} + * + * @param otherPredicate The other predicate + * @return A builder matching when this builder or the provided other builder match. + */ + @Contract("_->new") + default LootModifierPredicate.Builder or(@NotNull LootModifierPredicate.Builder otherPredicate) { return AnyOfLootPredicate.builder().or(this).or(otherPredicate); } - default LootModifierPredicate.Builder and(LootModifierPredicate.Builder otherPredicate) { + /** + * Adds another predicate builder in an {@code AND} relationship. + *
+ * Wraps this and the provided builder in an {@link AllOfLootPredicate} + * + * @param otherPredicate The other predicate + * @return A builder matching when this builder and the provided other builder match. + */ + @Contract("_->new") + default LootModifierPredicate.Builder and(@NotNull LootModifierPredicate.Builder otherPredicate) { return AllOfLootPredicate.builder().and(this).and(otherPredicate); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java index 2cf97bf..709f0aa 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java @@ -8,7 +8,15 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.LootTableModifier; +/** + * They type of a {@link LootModifierPredicate}, holds the codec. + * + * @param codec the codec for this predicate + */ public record LootModifierPredicateType(@NotNull MapCodec codec) { + /** + * Registry of {@link LootModifierPredicateType}s + */ public static final Registry REGISTRY = new SimpleRegistry<>( RegistryKey.ofRegistry(LootTableModifier.id("loot_modifier_predicate_types")), Lifecycle.stable() ); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java index b685018..aa6969e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java @@ -3,6 +3,7 @@ import com.mojang.serialization.MapCodec; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.ItemEntryPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; @@ -12,17 +13,37 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; +/** + * Contains all {@link LootModifierPredicate} types available in Loot Table Modifier + *
+ * Use their builders to create them. + */ public final class LootModifierPredicateTypes { private LootModifierPredicateTypes() { } + /** + * Type of {@link InvertedLootPredicate} + */ public static final LootModifierPredicateType INVERTED = register(id("inverted"), InvertedLootPredicate.CODEC); + /** + * Type of {@link AnyOfLootPredicate} + */ public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfLootPredicate.CODEC); + /** + * Type of {@link AllOfLootPredicate} + */ public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfLootPredicate.CODEC); + /** + * Type of {@link ItemEntryPredicate} + */ public static final LootModifierPredicateType ENTRY_ITEM = register(id("entry_item"), ItemEntryPredicate.CODEC); + /** + * Type of {@link LootTablePredicate} + */ public static final LootModifierPredicateType TABLE = register(id("table"), LootTablePredicate.CODEC); //public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); @@ -31,6 +52,12 @@ private static LootModifierPredicateType register(final @NotNull Identifier id, return Registry.register(LootModifierPredicateType.REGISTRY, id, new LootModifierPredicateType(codec)); } + /** + * Registers predicate types by loading the class. + *
+ * Only for the loot table modifier initializer to call, NO TOUCHY >:( + */ + @ApiStatus.Internal public static void register() { // Registers predicate types by loading the class } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java index bd86695..af717c7 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java @@ -6,6 +6,7 @@ import net.minecraft.loot.entry.ItemEntry; import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; @@ -13,6 +14,11 @@ import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; +/** + * Matches an item entry based on its item + * + * @param name the {@link OptionalIdentifierPattern} matching the item identifier + */ public record ItemEntryPredicate(OptionalIdentifierPattern name) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(OptionalIdentifierPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) @@ -31,13 +37,34 @@ public boolean test(@NotNull LootModifierContext context) { return name.matches(itemEntry.item.getIdAsString()); } - public static LootModifierPredicate.Builder builder(ItemConvertible name) { + /** + * Creates a builder for {@link ItemEntryPredicate} matching the provided item + * + * @param name the item to match + * @return a new {@link ItemEntryPredicate.Builder} + */ + @Contract("_->new") + public static ItemEntryPredicate.Builder builder(ItemConvertible name) { return builder(Registries.ITEM.getId(name.asItem())); } - public static LootModifierPredicate.Builder builder(Identifier name) { - return () -> new ItemEntryPredicate(OptionalIdentifierPattern.literal(name)); + /** + * Creates a builder for {@link ItemEntryPredicate} matching the item based on the provided identifier + * + * @param name the item id to match + * @return a new {@link ItemEntryPredicate.Builder} + */ + @Contract("_->new") + public static ItemEntryPredicate.Builder builder(Identifier name) { + return builder(OptionalIdentifierPattern.literal(name)); } - public static LootModifierPredicate.Builder builder(OptionalIdentifierPattern name) { + /** + * Creates a builder for {@link ItemEntryPredicate} matching the provided item + * + * @param name the {@link OptionalIdentifierPattern} to match the item id with + * @return a new {@link ItemEntryPredicate.Builder} + */ + @Contract("_->new") + public static ItemEntryPredicate.Builder builder(OptionalIdentifierPattern name) { return () -> new ItemEntryPredicate(name); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java index ba3c44e..9942e31 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java @@ -2,13 +2,21 @@ import com.mojang.serialization.MapCodec; import net.minecraft.util.Util; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; import java.util.List; +/** + * Matches when all the provided predicates match + */ public class AllOfLootPredicate extends TermsLootPredicate { + /** + * The codec + */ public static final MapCodec CODEC = createCodec(AllOfLootPredicate::new); private AllOfLootPredicate(final List terms) { @@ -20,13 +28,26 @@ public LootModifierPredicateType getType() { return LootModifierPredicateTypes.ALL_OF; } + /** + * Creates a builder for {@link AllOfLootPredicate} + * + * @return a new {@link AllOfLootPredicate.Builder} + */ public static AllOfLootPredicate.Builder builder() { return new AllOfLootPredicate.Builder(); } + /** + * Builder for {@link AllOfLootPredicate} + */ public static class Builder extends TermsLootPredicate.Builder { + private Builder() { + + } + @Override - public LootModifierPredicate.Builder and(LootModifierPredicate.Builder builder) { + @Contract("_->this") + public AllOfLootPredicate.Builder and(LootModifierPredicate.@NotNull Builder builder) { this.add(builder); return this; } @@ -37,6 +58,3 @@ protected AllOfLootPredicate build(List terms) { } } } - - - diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java index 52ac941..c5f45d7 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java @@ -2,13 +2,21 @@ import com.mojang.serialization.MapCodec; import net.minecraft.util.Util; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; import java.util.List; +/** + * Matches when any of the provided predicates match + */ public class AnyOfLootPredicate extends TermsLootPredicate { + /** + * The codec + */ public static final MapCodec CODEC = createCodec(AnyOfLootPredicate::new); private AnyOfLootPredicate(final List terms) { @@ -20,13 +28,27 @@ public LootModifierPredicateType getType() { return LootModifierPredicateTypes.ANY_OF; } + /** + * Creates a builder for {@link AnyOfLootPredicate} + * + * @return a new {@link AnyOfLootPredicate.Builder} + */ + @Contract("->new") public static AnyOfLootPredicate.Builder builder() { return new AnyOfLootPredicate.Builder(); } + /** + * Builder for {@link AnyOfLootPredicate} + */ public static class Builder extends TermsLootPredicate.Builder { + private Builder() { + + } + @Override - public LootModifierPredicate.Builder or(LootModifierPredicate.Builder builder) { + @Contract("_->this") + public AnyOfLootPredicate.Builder or(@NotNull LootModifierPredicate.Builder builder) { this.add(builder); return this; } @@ -37,6 +59,3 @@ protected AnyOfLootPredicate build(List terms) { } } } - - - diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java index afae38b..ce2ebe9 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java @@ -8,6 +8,11 @@ import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; +/** + * Matches when the provided predicate doesn't + * + * @param term the predicate to invert + */ public record InvertedLootPredicate(LootModifierPredicate term) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(LootModifierPredicate.CODEC.fieldOf("term").forGetter(InvertedLootPredicate::term)).apply(instance, InvertedLootPredicate::new) @@ -23,7 +28,13 @@ public boolean test(@NotNull LootModifierContext context) { return !term.test(context); } - public static LootModifierPredicate.Builder builder(LootModifierPredicate.Builder term) { + /** + * Creates a builder for {@link InvertedLootPredicate} + * + * @param term the predicate to invert + * @return a new {@link InvertedLootPredicate.Builder} containing the provided predicate + */ + public static InvertedLootPredicate.Builder builder(LootModifierPredicate.Builder term) { return () -> new InvertedLootPredicate(term.build()); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java index 9d79e91..24b53f4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java @@ -11,7 +11,13 @@ import java.util.function.Function; import java.util.function.Predicate; +/** + * An abstract predicate for predicates that take other predicates as terms. + */ abstract class TermsLootPredicate implements LootModifierPredicate { + /** + * The terms of this predicate + */ protected final List terms; private final Predicate builtPredicate; @@ -20,6 +26,13 @@ protected TermsLootPredicate(final List terms, final Pred this.builtPredicate = builtPredicate; } + /** + * Creates a codec for the terms predicate using the provided constructor. + * + * @param constructor the constructor of the predicate. + * @return a codec for the terms predicate using the provided constructor + * @param the terms predicate + */ protected static MapCodec createCodec(final Function, T> constructor) { return RecordCodecBuilder.mapCodec( instance -> instance.group(LootModifierPredicate.CODEC.listOf().fieldOf("terms").forGetter(TermsLootPredicate::getTerms)).apply(instance, constructor) @@ -35,6 +48,9 @@ public boolean test(final @NotNull LootModifierContext context) { return builtPredicate.test(context); } + /** + * Abstract builder for {@link TermsLootPredicate} + */ public abstract static class Builder implements LootModifierPredicate.Builder { private final ImmutableList.Builder terms = ImmutableList.builder(); @@ -47,6 +63,12 @@ public LootModifierPredicate build() { return this.build(this.terms.build()); } + /** + * Builds the predicate using the provided terms. + * + * @param terms the terms to build the predicate with + * @return a built predicate + */ protected abstract LootModifierPredicate build(List terms); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index c45991f..2896e0c 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -10,6 +10,7 @@ import net.minecraft.registry.RegistryKey; import net.minecraft.util.Identifier; import net.minecraft.util.context.ContextType; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; @@ -22,7 +23,12 @@ import java.util.List; import java.util.Optional; - +/** + * Matches loot tables based on the identifier and type patterns + * + * @param identifiers the identifiers to match. List entries are in an {@code OR} relationship + * @param types the types to match. List entries are in an {@code OR} relationship + */ public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( OptionalIdentifierPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalIdentifier), @@ -74,49 +80,118 @@ public boolean test(@NotNull LootModifierContext context) { return result; } + /** + * Creates a builder for {@link LootTablePredicate} + * + * @return a new {@link LootTablePredicate.Builder} + */ + @Contract("->new") public static LootTablePredicate.Builder builder() { return new LootTablePredicate.Builder(); } + /** + * Builder for {@link LootTablePredicate} + */ public static class Builder implements LootModifierPredicate.Builder { + private Builder() { + + } + private final ImmutableList.Builder names = ImmutableList.builder(); private final ImmutableList.Builder types = ImmutableList.builder(); - + /** + * Adds an entity type to match + * + * @param name the {@link EntityType} to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder name(@NotNull EntityType name) { name(LootTableIdGetter.INSTANCE.get(name)); return this; } + /** + * Adds a block to match + * + * @param name the {@link Block} to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder name(@NotNull Block name) { name(LootTableIdGetter.INSTANCE.get(name)); return this; } + /** + * Adds a loot table to match + * + * @param name the {@link RegistryKey} of the loot table to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder name(@NotNull RegistryKey name) { name(name.getValue()); return this; } + /** + * Adds a loot table to match + * + * @param name the {@link Identifier} of the loot table to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder name(@NotNull Identifier name) { name(OptionalIdentifierPattern.literal(name)); return this; } + /** + * Adds a {@link OptionalIdentifierPattern} to match the loot table id with. + * + * @param name the {@link OptionalIdentifierPattern} to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder name(@NotNull OptionalIdentifierPattern name) { this.names.add(name); return this; } + /** + * Adds a {@link ContextType type} to match. + * + * @param type the {@link ContextType type} to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder type(@NotNull ContextType type) { type(LootContextTypes.MAP.inverse().get(type)); return this; } + /** + * Adds a type to match. + * + * @param type the {@link Identifier} of the type to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder type(@NotNull Identifier type) { this.types.add(OptionalIdentifierPattern.literal(type)); return this; } + /** + * Adds a {@link OptionalIdentifierPattern} to match the loot table type with. + * + * @param type the {@link OptionalIdentifierPattern} of the type to match + * @return this + */ + @Contract("_->this") public LootTablePredicate.Builder type(@NotNull OptionalIdentifierPattern type) { this.types.add(type); return this; } + @Override public LootTablePredicate build() { return new LootTablePredicate(names.build(), types.build()); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java index 1602168..aa764ae 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java @@ -7,6 +7,16 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * A context for predicates to match against and actions to modify. + * + * @param table the table + * @param tableId the id of the table + * @param pool the pool + * @param entry the entry + * @param tableAlreadyModified if the table has already been modified by the current action + * @param poolAlreadyModified if the pool has already been modified by the current action + */ public record LootModifierContext(@NotNull LootTable table, @NotNull Identifier tableId, @Nullable LootPool pool, @Nullable LootPoolEntry entry, boolean tableAlreadyModified, boolean poolAlreadyModified) { } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java index b5d3d28..a6763f8 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java @@ -3,12 +3,34 @@ import net.minecraft.block.Block; import net.minecraft.entity.EntityType; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.impl.LootTableIdGetterImpl; +/** + * Provides methods for getting the identifier of loot tables on different versions. + *
+ * The implemented methods may compile fine but throw runtime exceptions on newer versions because there's reflection magic going on, but it should be fine as these methods should only be called during data generation afaik. + */ +@ApiStatus.NonExtendable public interface LootTableIdGetter { + /** + * The instance + */ LootTableIdGetter INSTANCE = new LootTableIdGetterImpl(); + /** + * Returns the identifier of an entity's loot table + * + * @param entityType the entity to get the loot table id for + * @return the identifier of the provided entity's loot table + */ Identifier get(@NotNull EntityType entityType); + /** + * Returns the identifier of a block's loot table + * + * @param block the block to get the loot table id for + * @return the identifier of the provided block's loot table + */ Identifier get(@NotNull Block block); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java index 1e088d2..57907d1 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java @@ -4,11 +4,20 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * {@link OptionalIdentifierPattern}s allow either matching {@link Identifier}s directly or using regex to do so. + *
+ * Use {@link #literal(Identifier)} for creating literal patterns and {@link #compile(String)} for creating regex patterns. + * + * @param isRegex if this {@link OptionalIdentifierPattern} is using regex or not + * @param patternString the pattern as a plain string + * @param pattern the compiled pattern + */ public record OptionalIdentifierPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { private static final Codec INLINE_CODEC = Identifier.CODEC.xmap(OptionalIdentifierPattern::literal, instance -> Identifier.of(instance.patternString())); private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( @@ -22,6 +31,13 @@ public record OptionalIdentifierPattern(boolean isRegex, @NotNull String pattern pattern -> pattern.isRegex ? Either.right(pattern) : Either.left(pattern) ); + /** + * Creates a literal {@link OptionalIdentifierPattern} from the provided {@link Identifier}. + * + * @param identifier the {@link Identifier} to match + * @return a new {@link OptionalIdentifierPattern} matching the provided {@link Identifier} + */ + @Contract("_->new") public static OptionalIdentifierPattern literal(final Identifier identifier) { return new OptionalIdentifierPattern( false, @@ -29,10 +45,14 @@ public static OptionalIdentifierPattern literal(final Identifier identifier) { Pattern.compile(Pattern.quote(identifier.toString())) ); } - //public static OptionalIdentifierPattern literal(final String literalString) { - // - //} + /** + * Compiles a {@link OptionalIdentifierPattern} from the provided regex pattern. + * + * @param regexPattern the pattern to use + * @return a new {@link OptionalIdentifierPattern} compiled from the provided regex pattern + * @see Pattern#compile(String) + */ public static OptionalIdentifierPattern compile(final String regexPattern) { return new OptionalIdentifierPattern( true, @@ -42,19 +62,13 @@ public static OptionalIdentifierPattern compile(final String regexPattern) { } /** - * Not deprecated for removal. - *

- * Use this only if you really need an instance of the {@link Matcher}. Otherwise, use {@link #matches(CharSequence)}, because it removes the overhead of the {@link Matcher} when {@link #isRegex} is false. - * @see #matches(CharSequence) + * Checks if the provided character sequence matches this. + * + * @param input the character sequence to check + * @return if the provided character sequence matches this */ - @SuppressWarnings("DeprecatedIsStillUsed") - @Deprecated(forRemoval = false) - public Matcher matcher(CharSequence input) { - return pattern.matcher(input); - } - public boolean matches(CharSequence input) { - if (isRegex) return matcher(input).matches(); + if (isRegex) return pattern.matcher(input).matches(); return patternString.contentEquals(input); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 6fef36a..de375b6 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -33,18 +33,21 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; +/** + * Datagen for loot modifiers used for testing. + */ public class LootTableModifierDatagen implements DataGeneratorEntrypoint { @Override public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { final FabricDataGenerator.Pack pack = fabricDataGenerator.createBuiltinResourcePack(id("example_pack")); - pack.addProvider(NewModLootModifierProvider::new); + pack.addProvider(ModLootModifierProvider::new); pack.addProvider(LootProvider::new); } - private static class NewModLootModifierProvider extends LootModifierProvider { - public NewModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { + private static class ModLootModifierProvider extends LootModifierProvider { + public ModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registriesFuture) { super(dataOutput, registriesFuture); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java b/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java index 7dfb03c..05722e4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java @@ -8,6 +8,7 @@ import net.minecraft.loot.LootTable; import net.minecraft.registry.RegistryKey; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; @@ -19,6 +20,7 @@ import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; +@ApiStatus.Internal public class LootTableIdGetterImpl implements LootTableIdGetter { @Override diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java index 642b041..f96296f 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java @@ -7,6 +7,7 @@ import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; +@SuppressWarnings("MissingJavadoc") @Mixin(ItemEntry.class) public interface ItemEntryAccessor { From d1cb9e355b8debf59f3b5b1ff8d5cec15b1e51c0 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:25:22 +0300 Subject: [PATCH 43/53] Rename OptionalIdentifierPattern to RegexPattern yeah... don't ask me what that name was....... --- .../condition/LootConditionPredicate.java | 6 +-- .../predicate/entry/ItemEntryPredicate.java | 14 +++---- .../predicate/table/LootTablePredicate.java | 38 +++++++++---------- ...entifierPattern.java => RegexPattern.java} | 32 ++++++++-------- .../datagen/LootTableModifierDatagen.java | 4 +- 5 files changed, 47 insertions(+), 47 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/{OptionalIdentifierPattern.java => RegexPattern.java} (52%) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java index 1d6e981..dc2030e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java @@ -5,10 +5,10 @@ import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; -public record LootConditionPredicate(@NotNull OptionalIdentifierPattern functionPattern) { - public static final Codec CODEC = OptionalIdentifierPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); +public record LootConditionPredicate(@NotNull RegexPattern functionPattern) { + public static final Codec CODEC = RegexPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); public boolean matches(final @NotNull LootCondition condition) { final Identifier functionId = Registries.LOOT_CONDITION_TYPE.getId(condition.getType()); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java index af717c7..25dacfd 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java @@ -10,18 +10,18 @@ import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; /** * Matches an item entry based on its item * - * @param name the {@link OptionalIdentifierPattern} matching the item identifier + * @param name the {@link RegexPattern} matching the item identifier */ -public record ItemEntryPredicate(OptionalIdentifierPattern name) implements LootModifierPredicate { +public record ItemEntryPredicate(RegexPattern name) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(OptionalIdentifierPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) + instance -> instance.group(RegexPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) ); @Override @@ -55,16 +55,16 @@ public static ItemEntryPredicate.Builder builder(ItemConvertible name) { */ @Contract("_->new") public static ItemEntryPredicate.Builder builder(Identifier name) { - return builder(OptionalIdentifierPattern.literal(name)); + return builder(RegexPattern.literal(name)); } /** * Creates a builder for {@link ItemEntryPredicate} matching the provided item * - * @param name the {@link OptionalIdentifierPattern} to match the item id with + * @param name the {@link RegexPattern} to match the item id with * @return a new {@link ItemEntryPredicate.Builder} */ @Contract("_->new") - public static ItemEntryPredicate.Builder builder(OptionalIdentifierPattern name) { + public static ItemEntryPredicate.Builder builder(RegexPattern name) { return () -> new ItemEntryPredicate(name); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 2896e0c..8590e48 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -16,7 +16,7 @@ import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; @@ -29,26 +29,26 @@ * @param identifiers the identifiers to match. List entries are in an {@code OR} relationship * @param types the types to match. List entries are in an {@code OR} relationship */ -public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { +public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - OptionalIdentifierPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalIdentifier), - OptionalIdentifierPattern.CODEC.listOf().optionalFieldOf("types").forGetter(LootTablePredicate::optionalType) + RegexPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalIdentifier), + RegexPattern.CODEC.listOf().optionalFieldOf("types").forGetter(LootTablePredicate::optionalType) ).apply(instance, LootTablePredicate::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Optional> optionalIdentifier, @NotNull Optional> optionalType) { + private LootTablePredicate(@NotNull Optional> optionalIdentifier, @NotNull Optional> optionalType) { this( optionalIdentifier.orElse(null), optionalType.orElse(null) ); } - private Optional> optionalIdentifier() { + private Optional> optionalIdentifier() { if (identifiers == null || identifiers.isEmpty()) return Optional.empty(); return Optional.of(identifiers); } - private Optional> optionalType() { + private Optional> optionalType() { if (types == null || types.isEmpty()) return Optional.empty(); return Optional.of(types); @@ -66,14 +66,14 @@ public boolean test(@NotNull LootModifierContext context) { if (identifiers != null && !identifiers.isEmpty()) { boolean idResult = false; final String tableIdString = context.tableId().toString(); - for (OptionalIdentifierPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); + for (RegexPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); result = idResult; } if (types != null && !types.isEmpty()) { boolean typeResult = false; final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); - for (OptionalIdentifierPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); + for (RegexPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); result = result && typeResult; } @@ -98,8 +98,8 @@ private Builder() { } - private final ImmutableList.Builder names = ImmutableList.builder(); - private final ImmutableList.Builder types = ImmutableList.builder(); + private final ImmutableList.Builder names = ImmutableList.builder(); + private final ImmutableList.Builder types = ImmutableList.builder(); /** * Adds an entity type to match @@ -142,17 +142,17 @@ public LootTablePredicate.Builder name(@NotNull RegistryKey name) { */ @Contract("_->this") public LootTablePredicate.Builder name(@NotNull Identifier name) { - name(OptionalIdentifierPattern.literal(name)); + name(RegexPattern.literal(name)); return this; } /** - * Adds a {@link OptionalIdentifierPattern} to match the loot table id with. + * Adds a {@link RegexPattern} to match the loot table id with. * - * @param name the {@link OptionalIdentifierPattern} to match + * @param name the {@link RegexPattern} to match * @return this */ @Contract("_->this") - public LootTablePredicate.Builder name(@NotNull OptionalIdentifierPattern name) { + public LootTablePredicate.Builder name(@NotNull RegexPattern name) { this.names.add(name); return this; } @@ -176,17 +176,17 @@ public LootTablePredicate.Builder type(@NotNull ContextType type) { */ @Contract("_->this") public LootTablePredicate.Builder type(@NotNull Identifier type) { - this.types.add(OptionalIdentifierPattern.literal(type)); + this.types.add(RegexPattern.literal(type)); return this; } /** - * Adds a {@link OptionalIdentifierPattern} to match the loot table type with. + * Adds a {@link RegexPattern} to match the loot table type with. * - * @param type the {@link OptionalIdentifierPattern} of the type to match + * @param type the {@link RegexPattern} of the type to match * @return this */ @Contract("_->this") - public LootTablePredicate.Builder type(@NotNull OptionalIdentifierPattern type) { + public LootTablePredicate.Builder type(@NotNull RegexPattern type) { this.types.add(type); return this; } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/RegexPattern.java similarity index 52% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/RegexPattern.java index 57907d1..f0380e4 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/OptionalIdentifierPattern.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/RegexPattern.java @@ -10,20 +10,20 @@ import java.util.regex.Pattern; /** - * {@link OptionalIdentifierPattern}s allow either matching {@link Identifier}s directly or using regex to do so. + * {@link RegexPattern}s allow either matching {@link Identifier}s directly or using regex to do so. *
* Use {@link #literal(Identifier)} for creating literal patterns and {@link #compile(String)} for creating regex patterns. * - * @param isRegex if this {@link OptionalIdentifierPattern} is using regex or not + * @param isRegex if this {@link RegexPattern} is using regex or not * @param patternString the pattern as a plain string * @param pattern the compiled pattern */ -public record OptionalIdentifierPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { - private static final Codec INLINE_CODEC = Identifier.CODEC.xmap(OptionalIdentifierPattern::literal, instance -> Identifier.of(instance.patternString())); - private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.fieldOf("regexPattern").forGetter(OptionalIdentifierPattern::patternString) - ).apply(instance, OptionalIdentifierPattern::compile)); - public static final Codec CODEC = Codec.either( +public record RegexPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { + private static final Codec INLINE_CODEC = Identifier.CODEC.xmap(RegexPattern::literal, instance -> Identifier.of(instance.patternString())); + private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("regexPattern").forGetter(RegexPattern::patternString) + ).apply(instance, RegexPattern::compile)); + public static final Codec CODEC = Codec.either( INLINE_CODEC, FULL_CODEC ).xmap( @@ -32,14 +32,14 @@ public record OptionalIdentifierPattern(boolean isRegex, @NotNull String pattern ); /** - * Creates a literal {@link OptionalIdentifierPattern} from the provided {@link Identifier}. + * Creates a literal {@link RegexPattern} from the provided {@link Identifier}. * * @param identifier the {@link Identifier} to match - * @return a new {@link OptionalIdentifierPattern} matching the provided {@link Identifier} + * @return a new {@link RegexPattern} matching the provided {@link Identifier} */ @Contract("_->new") - public static OptionalIdentifierPattern literal(final Identifier identifier) { - return new OptionalIdentifierPattern( + public static RegexPattern literal(final Identifier identifier) { + return new RegexPattern( false, identifier.toString(), Pattern.compile(Pattern.quote(identifier.toString())) @@ -47,14 +47,14 @@ public static OptionalIdentifierPattern literal(final Identifier identifier) { } /** - * Compiles a {@link OptionalIdentifierPattern} from the provided regex pattern. + * Compiles a {@link RegexPattern} from the provided regex pattern. * * @param regexPattern the pattern to use - * @return a new {@link OptionalIdentifierPattern} compiled from the provided regex pattern + * @return a new {@link RegexPattern} compiled from the provided regex pattern * @see Pattern#compile(String) */ - public static OptionalIdentifierPattern compile(final String regexPattern) { - return new OptionalIdentifierPattern( + public static RegexPattern compile(final String regexPattern) { + return new RegexPattern( true, regexPattern, Pattern.compile(regexPattern) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index de375b6..2ad436b 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -21,7 +21,7 @@ import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; -import top.offsetmonkey538.loottablemodifier.api.resource.util.OptionalIdentifierPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; @@ -57,7 +57,7 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("replace_ingots_with_command_block"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(OptionalIdentifierPattern.compile("minecraft:.*_ingot")) + ItemEntryPredicate.builder(RegexPattern.compile("minecraft:.*_ingot")) ) .action( SetItemAction.builder(Items.COMMAND_BLOCK) From 4d47607548807be820c135712bb666549c6bd104 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:30:35 +0300 Subject: [PATCH 44/53] Cleanup --- .../loottablemodifier/LootTableModifier.java | 36 ----- .../condition/LootConditionPredicate.java | 19 --- .../predicate/entry/LootEntryPredicate.java | 55 ------- .../entry/leaf/EmptyEntryPredicate.java | 41 ------ .../entry/leaf/LeafEntryPredicate.java | 45 ------ .../function/LootFunctionPredicate.java | 21 --- .../predicate/pool/LootPoolPredicate.java | 137 ------------------ .../predicate/table/LootTablePredicate.java | 2 +- .../mixin/LootPoolAccessor.java | 1 - 9 files changed, 1 insertion(+), 356 deletions(-) delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java delete mode 100644 src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 76486ee..e0c461a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -128,45 +128,9 @@ public static void runModification(ResourceManager resourceManager, Registry modifierEntry : modifiers.entrySet()) { - // final LootModifier modifier = modifierEntry.getValue(); - - // for (LootModifierPredicate modifiesPredicate : modifier.modifies()) { - // if (modifiesPredicate.requiredContext() == LootModifierContext.REQUIRES_TABLE && !modifiesPredicate.test(tableContext)) continue; - - // for (LootPool pool : table.pools) { - // final LootModifierContext poolContext = new LootModifierContext(table, key.getValue(), pool); - - // if (modifiesPredicate.requiredContext() == LootModifierContext.REQUIRES_POOL && !modifiesPredicate.test(poolContext)) continue; - - // for (LootPoolEntry entry : pool.entries) { - // final LootModifierContext entryContext = new LootModifierContext(table, key.getValue(), pool, entry); - - // if (modifiesPredicate.requiredContext() == LootModifierContext.REQUIRES_POOL && !modifiesPredicate.test(entryContext)) continue; - - // for (LootModifierAction action : modifier.actions()) { - // action.apply(entryContext); - // } - // } - // } - // } - //} - - - // t~odo: modified += apply(table) ? 1 : 0; (done?) } - //for (Map.Entry modifierEntry : modifiers.entrySet()) { - // final LootModifier modifier = modifierEntry.getValue(); - - // amountModified += modifier.apply(lootRegistry); - - // // TODO: try to figure smt out abt this: if (!modifier.modifies().isEmpty()) failedModifiers.put(modifierEntry.getKey(), modifier); - //} - - LOGGER.info("Applied {} modifiers and modified {} entries, {} pools and {} loot tables in {}!", modifiers.size(), entriesModified, poolsModified, modifiedTableIds.size(), stopwatch.stop()); if (!IS_DEV) return; diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java deleted file mode 100644 index dc2030e..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/condition/LootConditionPredicate.java +++ /dev/null @@ -1,19 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.resource.predicate.condition; - -import com.mojang.serialization.Codec; -import net.minecraft.loot.condition.LootCondition; -import net.minecraft.registry.Registries; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; - -public record LootConditionPredicate(@NotNull RegexPattern functionPattern) { - public static final Codec CODEC = RegexPattern.CODEC.xmap(LootConditionPredicate::new, LootConditionPredicate::functionPattern); - - public boolean matches(final @NotNull LootCondition condition) { - final Identifier functionId = Registries.LOOT_CONDITION_TYPE.getId(condition.getType()); - if (functionId == null) return false; - - return functionPattern.matches(functionId.toString()); - } -} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java deleted file mode 100644 index 6859a11..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/LootEntryPredicate.java +++ /dev/null @@ -1,55 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry; - -//import com.mojang.datafixers.Products; -//import com.mojang.serialization.Codec; -//import com.mojang.serialization.codecs.RecordCodecBuilder; -//import net.minecraft.loot.LootPool; -//import net.minecraft.loot.LootTable; -//import net.minecraft.loot.entry.LootPoolEntry; -//import net.minecraft.util.Identifier; -//import org.jetbrains.annotations.NotNull; -//import org.jetbrains.annotations.Nullable; -//import top.offsetmonkey538.loottablemodifier.LootTableModifier; -//import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -// -//import java.util.List; -//import java.util.Optional; -// -//public abstract class LootEntryPredicate implements LootModifierPredicate { -// protected final @Nullable List conditions; -// -// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // da codecccccc -// protected LootEntryPredicate(final @NotNull Optional> conditions) { -// this.conditions = conditions.orElse(null); -// } -// -// protected static Products.P1, Optional>> addConditionsToCodec(RecordCodecBuilder.Instance instance) { -// return instance.group(LootConditionPredicate.CODEC.listOf().optionalFieldOf("conditions").forGetter(entry -> Optional.ofNullable(entry.conditions))); -// } -// -// -// public abstract LootModifierPredicateType getType(); -// -// /** -// * Checks if this predicate matches the provided loot pool entry -// * @param entry the entry to check against -// * @return true when the provided loot pool entry matches, false otherwise -// */ -// @Override -// public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool, final @NotNull LootPoolEntry entry) { -// final int conditionCount = conditions == null ? 0 : conditions.size(); -// final int entryConditionsCount = entry.conditions == null ? 0 : entry.conditions.size(); -// -// if (conditionCount == entryConditionsCount) return true; -// if (conditionCount == 0) return false; -// -// for (LootConditionPredicate predicate : this.conditions) { -// -// } -// -// return conditions.stream().allMatch(predicate -> entry.conditions.stream().anyMatch(predicate::matches)); -// } -//} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java deleted file mode 100644 index f53c1f9..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/EmptyEntryPredicate.java +++ /dev/null @@ -1,41 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.leaf; - -//import com.mojang.serialization.MapCodec; -//import com.mojang.serialization.codecs.RecordCodecBuilder; -//import net.minecraft.loot.LootTable; -//import net.minecraft.loot.entry.ItemEntry; -//import net.minecraft.util.Identifier; -//import org.jetbrains.annotations.NotNull; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -//import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; -// -//import java.util.List; -//import java.util.Optional; -// -//public class EmptyEntryPredicate extends LeafEntryPredicate { -// public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> addLeafFieldsToCodec(instance).apply(instance, EmptyEntryPredicate::new)); -// -// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // given by da codecc -// protected EmptyEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { -// super(weight, quality, conditions, functions); -// } -// -// @Override -// public LootModifierPredicateType getType() { -// return LootModifierPredicateTypes.EMPTY_ENTRY; -// } -// -// @Override -// public boolean test(@NotNull LootModifierContext context, @NotNull LootTable table, @NotNull Identifier tableId) { -// return super.test(context, table, tableId); -// } ItemEntry -// -// @Override -// public byte requiredContext() { -// return LootModifierContext.REQUIRES_TABLE; -// } -//} -// diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java deleted file mode 100644 index f091d7d..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/leaf/LeafEntryPredicate.java +++ /dev/null @@ -1,45 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.leaf; - -//import com.mojang.datafixers.Products; -//import com.mojang.serialization.Codec; -//import com.mojang.serialization.codecs.RecordCodecBuilder; -//import net.minecraft.loot.entry.ItemEntry; -//import net.minecraft.loot.entry.LootPoolEntry; -//import org.jetbrains.annotations.NotNull; -//import org.jetbrains.annotations.Nullable; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; -// -//import java.util.List; -//import java.util.Optional; -// -//public abstract class LeafEntryPredicate extends LootEntryPredicate { -// protected final @Nullable Integer weight; -// protected final @Nullable Integer quality; -// protected final @Nullable List functions; -// -// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // codec giv it -// protected LeafEntryPredicate(@NotNull Optional weight, @NotNull Optional quality, @NotNull Optional> conditions, @NotNull Optional> functions) { -// super(conditions); -// this.weight = weight.orElse(null); -// this.quality = quality.orElse(null); -// this.functions = functions.orElse(null); -// } -// -// protected static Products.P4, Optional, Optional, Optional>, Optional>> addLeafFieldsToCodec(RecordCodecBuilder.Instance instance) { -// return instance.group( -// Codec.INT.optionalFieldOf("weight").forGetter(entry -> Optional.ofNullable(entry.weight)), -// Codec.INT.optionalFieldOf("quality").forGetter(entry -> Optional.ofNullable(entry.weight)) -// ) -// .and(addConditionsToCodec(instance).t1()) -// .and(LootFunctionPredicate.CODEC.listOf().optionalFieldOf("functions").forGetter(entry -> Optional.ofNullable(entry.functions))); -// -// } -//ItemEntry -// // todo: @Override -// // todo: public boolean matches(@NotNull LootPoolEntry entry) { -// // todo: return super.matches(entry); -// // todo: } -//} -// diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java deleted file mode 100644 index ae3f158..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/function/LootFunctionPredicate.java +++ /dev/null @@ -1,21 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.resource.predicate.function; - -import com.mojang.serialization.Codec; -import net.minecraft.loot.function.LootFunction; -import net.minecraft.registry.Registries; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; - -import java.util.regex.Pattern; - -// TODO: maybe in the future? nou iea -//public record LootFunctionPredicate(@NotNull Pattern functionPattern) { -// public static final Codec CODEC = Util.PATTERN_CODEC.xmap(LootFunctionPredicate::new, LootFunctionPredicate::functionPattern); -// -// public boolean matches(final @NotNull LootFunction function) { -// final Identifier functionId = Registries.LOOT_FUNCTION_TYPE.getId(function.getType()); -// if (functionId == null) return false; -// -// return functionPattern.matcher(functionId.toString()).matches(); -// } -//} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java deleted file mode 100644 index 7c855d1..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/pool/LootPoolPredicate.java +++ /dev/null @@ -1,137 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.api.resource.predicate.pool; - -//import com.mojang.datafixers.util.Either; -//import com.mojang.serialization.Codec; -//import com.mojang.serialization.MapCodec; -//import com.mojang.serialization.codecs.RecordCodecBuilder; -//import net.minecraft.loot.LootPool; -//import net.minecraft.loot.LootTable; -//import net.minecraft.loot.condition.AllOfLootCondition; -//import net.minecraft.loot.condition.AnyOfLootCondition; -//import net.minecraft.loot.condition.InvertedLootCondition; -//import net.minecraft.loot.context.LootContextTypes; -//import net.minecraft.util.Identifier; -//import org.jetbrains.annotations.NotNull; -//import org.jetbrains.annotations.Nullable; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateTypes; -//import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.LootModifierPredicateType; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.condition.LootConditionPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.function.LootFunctionPredicate; -//import top.offsetmonkey538.loottablemodifier.resource.predicate.entry.LootEntryPredicate; -// -//import java.util.List; -//import java.util.Optional; -// -//public record LootPoolPredicate(@Nullable Integer rolls, @Nullable Integer bonusRolls, @Nullable List entries, @Nullable List functions, @Nullable List conditions) implements LootModifierPredicate { -// public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( -// // TODO: smt complex that allows like matching integer expressions? Yknow like >=2 // TODO: ok actually Minecraft uses some number provider stuff that I need to match. Not always just a single int -// Codec.INT.optionalFieldOf("rolls").forGetter(LootPoolPredicate::optionalRolls), -// Codec.INT.optionalFieldOf("bonusRolls").forGetter(LootPoolPredicate::optionalBonusRolls), -// Codec.either(LootEntryPredicate.CODEC, LootEntryPredicate.CODEC.listOf()).optionalFieldOf("entries").forGetter(LootPoolPredicate::optionalEntries), -// Codec.either(LootFunctionPredicate.CODEC, LootFunctionPredicate.CODEC.listOf()).optionalFieldOf("functions").forGetter(LootPoolPredicate::optionalFunctions), -// Codec.either(LootConditionPredicate.CODEC, LootConditionPredicate.CODEC.listOf()).optionalFieldOf("conditions").forGetter(LootPoolPredicate::optionalConditions) -// ).apply(instance, LootPoolPredicate::new)); -// -// private Optional optionalRolls() { -// return Optional.ofNullable(rolls); -// } -// -// private Optional optionalBonusRolls() { -// return Optional.ofNullable(bonusRolls); -// } -// -// private Optional>> optionalEntries() { -// if (entries == null) return Optional.empty(); -// if (entries.size() == 1) return Optional.of(Either.left(entries.get(0))); -// return Optional.of(Either.right(entries)); -// } -// -// private Optional>> optionalFunctions() { -// if (functions == null) return Optional.empty(); -// if (functions.size() == 1) return Optional.of(Either.left(functions.get(0))); -// return Optional.of(Either.right(functions)); -// } -// -// private Optional>> optionalConditions() { -// if (conditions == null) return Optional.empty(); -// if (conditions.size() == 1) return Optional.of(Either.left(conditions.get(0))); -// return Optional.of(Either.right(conditions)); -// } -// -// @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // 'cause of codecccc -// private LootPoolPredicate( -// @NotNull Optional optionalRolls, -// @NotNull Optional optionalBonusRolls, -// @NotNull Optional>> optionalEntries, -// @NotNull Optional>> optionalFunctions, -// @NotNull Optional>> optionalConditions -// ) { -// this( -// optionalRolls.orElse(null), -// optionalBonusRolls.orElse(null), -// optionalEntries.map(entriesEither -> entriesEither.map(List::of, entries -> entries)).orElse(null), -// optionalFunctions.map(functionsEither -> functionsEither.map(List::of, functions -> functions)).orElse(null), -// optionalConditions.map(conditionsEither -> conditionsEither.map(List::of, conditions -> conditions)).orElse(null) -// ); -// } -// -// -// //public boolean matches(final @NotNull LootTable table, final @NotNull Identifier tableId) { -// @Override -// public boolean test(final @NotNull LootModifierContext context, final @NotNull LootTable table, final @NotNull Identifier tableId, final @NotNull LootPool pool) { -// boolean result = true; -// -// // todo: if (rolls != null) { -// // todo: result = rolls == pool.rolls.; -// // todo: } -// -// // todo: if (bonusRolls != null) { -// // todo: result = result && bonusRolls == pool.rolls.; -// // todo: } -// -// if (entries != null) { -// for (LootEntryPredicate entryPredicate : entries) { -// result = result && entryPredicate.test(context); -// } -// result = result && type.matcher(LootContextTypes.MAP.inverse().get(table.getType()).toString()).matches(); -// } -// -// if (functions != null) { -// for (LootFunctionPredicate functionPredicate : functions) { -// result = result && table.functions.stream().anyMatch(functionPredicate::matches); -// } -// } -// -// return result; -// } -// -// @Override -// public LootModifierPredicateType getType() { -// return LootModifierPredicateTypes.LOOT_POOL; -// } -// -// @Override -// public byte requiredContext() { -// return LootModifierContext.REQUIRES_ENTRY; -// } -// -// //@FunctionalInterface -// //public interface Builder { -// // LootPoolPredicate build(); -// -// // default LootPoolPredicate.Builder invert() { -// // return InvertedLootCondition.builder(this); -// // } -// -// // default AnyOfLootCondition.Builder or(LootPoolPredicate.Builder otherPredicate) { -// // return AnyOfLootCondition.builder(this, otherPredicate); -// // } -// -// // default AllOfLootCondition.Builder and(LootPoolPredicate.Builder otherPredicate) { -// // return AllOfLootCondition.builder(this, otherPredicate); -// // } -// //} -//} -// diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java index 8590e48..faa2b59 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java @@ -176,7 +176,7 @@ public LootTablePredicate.Builder type(@NotNull ContextType type) { */ @Contract("_->this") public LootTablePredicate.Builder type(@NotNull Identifier type) { - this.types.add(RegexPattern.literal(type)); + type(RegexPattern.literal(type)); return this; } /** diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java index 1da07bf..7a8a0ce 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java @@ -1,7 +1,6 @@ package top.offsetmonkey538.loottablemodifier.mixin; import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; import net.minecraft.loot.entry.LootPoolEntry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; From 4fd3caef0f59df41be2a6b13c711226bc90052eb Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:36:35 +0300 Subject: [PATCH 45/53] Rename things to match their registered ids --- .../api/datagen/LootModifierProvider.java | 4 +- .../api/resource/LootModifier.java | 10 ++-- .../action/LootModifierActionTypes.java | 30 +++++------ ...ddEntryAction.java => EntryAddAction.java} | 24 ++++----- ...temAction.java => EntryItemSetAction.java} | 26 +++++----- ...ntryAction.java => EntryRemoveAction.java} | 12 ++--- ...{AddPoolAction.java => PoolAddAction.java} | 24 ++++----- ...ePoolAction.java => PoolRemoveAction.java} | 12 ++--- .../predicate/LootModifierPredicate.java | 18 +++---- .../predicate/LootModifierPredicateTypes.java | 30 +++++------ ...Predicate.java => EntryItemPredicate.java} | 26 +++++----- ...LootPredicate.java => AllOfPredicate.java} | 24 ++++----- ...LootPredicate.java => AnyOfPredicate.java} | 24 ++++----- ...tPredicate.java => InvertedPredicate.java} | 14 ++--- ...LootPredicate.java => TermsPredicate.java} | 10 ++-- ...ablePredicate.java => TablePredicate.java} | 42 +++++++-------- .../datagen/LootTableModifierDatagen.java | 52 +++++++++---------- 17 files changed, 191 insertions(+), 191 deletions(-) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/{AddEntryAction.java => EntryAddAction.java} (79%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/{SetItemAction.java => EntryItemSetAction.java} (79%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/{RemoveEntryAction.java => EntryRemoveAction.java} (81%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/{AddPoolAction.java => PoolAddAction.java} (75%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/{RemovePoolAction.java => PoolRemoveAction.java} (81%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/{ItemEntryPredicate.java => EntryItemPredicate.java} (71%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/{AllOfLootPredicate.java => AllOfPredicate.java} (56%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/{AnyOfLootPredicate.java => AnyOfPredicate.java} (57%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/{InvertedLootPredicate.java => InvertedPredicate.java} (63%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/{TermsLootPredicate.java => TermsPredicate.java} (81%) rename src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/{LootTablePredicate.java => TablePredicate.java} (78%) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java index 349942b..8e52fad 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java @@ -25,12 +25,12 @@ * Identifier.of("testmod", "mobs_drop_tnt"), * LootModifier.builder() * .conditionally( - * LootTablePredicate.builder() + * TablePredicate.builder() * .name(EntityType.CREEPER) * .name(EntityType.ZOMBIE) * ) * .action( - * AddPoolAction.builder() + * PoolAddAction.builder() * .pool( * LootPool.builder() * .rolls(ConstantLootNumberProvider.create(1)) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index 79c2d9d..ff5a4fb 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -10,10 +10,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; -import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolAddAction; import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.TablePredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import java.util.*; @@ -53,15 +53,15 @@ public record LootModifier(@NotNull @UnmodifiableView List a if (pools.isPresent() && lootPools.isPresent()) throw new IllegalStateException("Both \"pools\" and \"loot_pools\" present in legacy loot modifier!"); - if (pools.isPresent()) actions = List.of(new AddPoolAction(pools.get())); - if (lootPools.isPresent()) actions = List.of(new AddPoolAction(lootPools.get())); + if (pools.isPresent()) actions = List.of(new PoolAddAction(pools.get())); + if (lootPools.isPresent()) actions = List.of(new PoolAddAction(lootPools.get())); if (actions == null) throw new IllegalStateException("Neither \"pools\" nor \"loot_pools\" present in legacy loot modifier!"); return actions; } private static @NotNull LootModifierPredicate getPredicateFromLegacyCodec(@NotNull Either> modifiesEither) { - final LootTablePredicate.Builder predicateBuilder = LootTablePredicate.builder(); + final TablePredicate.Builder predicateBuilder = TablePredicate.builder(); for (final Identifier currentId : modifiesEither.map(List::of, it -> it)) { predicateBuilder.name(currentId); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java index c1c8660..5d263f3 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java @@ -5,11 +5,11 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryRemoveAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryItemSetAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolRemoveAction; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -24,26 +24,26 @@ private LootModifierActionTypes() { } /** - * Type of {@link AddPoolAction} + * Type of {@link PoolAddAction} */ - public static final LootModifierActionType POOL_ADD = register(id("pool_add"), AddPoolAction.CODEC); + public static final LootModifierActionType POOL_ADD = register(id("pool_add"), PoolAddAction.CODEC); /** - * Type of {@link RemovePoolAction} + * Type of {@link PoolRemoveAction} */ - public static final LootModifierActionType POOL_REMOVE = register(id("pool_remove"), RemovePoolAction.CODEC); + public static final LootModifierActionType POOL_REMOVE = register(id("pool_remove"), PoolRemoveAction.CODEC); /** - * Type of {@link AddEntryAction} + * Type of {@link EntryAddAction} */ - public static final LootModifierActionType ENTRY_ADD = register(id("entry_add"), AddEntryAction.CODEC); + public static final LootModifierActionType ENTRY_ADD = register(id("entry_add"), EntryAddAction.CODEC); /** - * Type of {@link RemoveEntryAction} + * Type of {@link EntryRemoveAction} */ - public static final LootModifierActionType ENTRY_REMOVE = register(id("entry_remove"), RemoveEntryAction.CODEC); + public static final LootModifierActionType ENTRY_REMOVE = register(id("entry_remove"), EntryRemoveAction.CODEC); /** - * Type of {@link SetItemAction} + * Type of {@link EntryItemSetAction} */ - public static final LootModifierActionType ENTRY_ITEM_SET = register(id("entry_item_set"), SetItemAction.CODEC); + public static final LootModifierActionType ENTRY_ITEM_SET = register(id("entry_item_set"), EntryItemSetAction.CODEC); private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryAddAction.java similarity index 79% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryAddAction.java index f4600e1..0a9ceab 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/AddEntryAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryAddAction.java @@ -21,10 +21,10 @@ * * @param entries the entries to add */ -public record AddEntryAction(List entries) implements LootModifierAction { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - LootPoolEntryTypes.CODEC.listOf().fieldOf("entries").forGetter(AddEntryAction::entries) - ).apply(instance, AddEntryAction::new)); +public record EntryAddAction(List entries) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPoolEntryTypes.CODEC.listOf().fieldOf("entries").forGetter(EntryAddAction::entries) + ).apply(instance, EntryAddAction::new)); @Override public LootModifierActionType getType() { @@ -49,17 +49,17 @@ public int apply(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link AddEntryAction} + * Creates a builder for {@link EntryAddAction} * - * @return a new {@link AddEntryAction.Builder} + * @return a new {@link EntryAddAction.Builder} */ @Contract("->new") - public static AddEntryAction.Builder builder() { - return new AddEntryAction.Builder(); + public static EntryAddAction.Builder builder() { + return new EntryAddAction.Builder(); } /** - * Builder for {@link AddEntryAction} + * Builder for {@link EntryAddAction} */ public static class Builder implements LootModifierAction.Builder { private Builder() { @@ -75,14 +75,14 @@ private Builder() { * @return this */ @Contract("_->this") - public AddEntryAction.Builder entry(LootPoolEntry.Builder entryBuilder) { + public EntryAddAction.Builder entry(LootPoolEntry.Builder entryBuilder) { this.entries.add(entryBuilder.build()); return this; } @Override - public AddEntryAction build() { - return new AddEntryAction(entries.build()); + public EntryAddAction build() { + return new EntryAddAction(entries.build()); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryItemSetAction.java similarity index 79% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryItemSetAction.java index 1c11a61..ebdd089 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/SetItemAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryItemSetAction.java @@ -26,11 +26,11 @@ * @param item the new item to replace the existing one with * @param canReplaceEntry if other types of entries can be replaced with a basic item entry containing the target item */ -public record SetItemAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Item.ENTRY_CODEC.fieldOf("name").forGetter(SetItemAction::item), - Codec.BOOL.optionalFieldOf("canReplaceEntry", false).forGetter(SetItemAction::canReplaceEntry) - ).apply(instance, SetItemAction::new)); +public record EntryItemSetAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Item.ENTRY_CODEC.fieldOf("name").forGetter(EntryItemSetAction::item), + Codec.BOOL.optionalFieldOf("canReplaceEntry", false).forGetter(EntryItemSetAction::canReplaceEntry) + ).apply(instance, EntryItemSetAction::new)); @Override public LootModifierActionType getType() { @@ -64,18 +64,18 @@ public int apply(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link SetItemAction} + * Creates a builder for {@link EntryItemSetAction} * * @param item the new item to replace the existing one with - * @return a new {@link SetItemAction.Builder} + * @return a new {@link EntryItemSetAction.Builder} */ @Contract("_->new") - public static SetItemAction.Builder builder(@NotNull ItemConvertible item) { - return new SetItemAction.Builder(item); + public static EntryItemSetAction.Builder builder(@NotNull ItemConvertible item) { + return new EntryItemSetAction.Builder(item); } /** - * Builder for {@link SetItemAction} + * Builder for {@link EntryItemSetAction} */ public static class Builder implements LootModifierAction.Builder { private final RegistryEntry item; @@ -92,14 +92,14 @@ private Builder(@NotNull ItemConvertible item) { * @return this */ @Contract("_->this") - public SetItemAction.Builder setCanReplaceEntry(boolean canReplaceEntry) { + public EntryItemSetAction.Builder setCanReplaceEntry(boolean canReplaceEntry) { this.canReplaceEntry = canReplaceEntry; return this; } @Override - public SetItemAction build() { - return new SetItemAction(item, canReplaceEntry); + public EntryItemSetAction build() { + return new EntryItemSetAction(item, canReplaceEntry); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryRemoveAction.java similarity index 81% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryRemoveAction.java index d2a954f..d07f05e 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/RemoveEntryAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryRemoveAction.java @@ -18,8 +18,8 @@ /** * Removes the matched entries from their pools */ -public record RemoveEntryAction() implements LootModifierAction { - public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemoveEntryAction::new)); +public record EntryRemoveAction() implements LootModifierAction { + public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(EntryRemoveAction::new)); @Override public LootModifierActionType getType() { @@ -45,12 +45,12 @@ public int apply(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link RemoveEntryAction} + * Creates a builder for {@link EntryRemoveAction} * - * @return a new {@link RemoveEntryAction.Builder} + * @return a new {@link EntryRemoveAction.Builder} */ @Contract("->new") - public static RemoveEntryAction.Builder builder() { - return RemoveEntryAction::new; + public static EntryRemoveAction.Builder builder() { + return EntryRemoveAction::new; } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolAddAction.java similarity index 75% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolAddAction.java index e55d413..f750d17 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/AddPoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolAddAction.java @@ -19,10 +19,10 @@ * * @param pools the pools to add */ -public record AddPoolAction(List pools) implements LootModifierAction { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - LootPool.CODEC.listOf().fieldOf("pools").forGetter(AddPoolAction::pools) - ).apply(instance, AddPoolAction::new)); +public record PoolAddAction(List pools) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPool.CODEC.listOf().fieldOf("pools").forGetter(PoolAddAction::pools) + ).apply(instance, PoolAddAction::new)); @Override public LootModifierActionType getType() { @@ -44,17 +44,17 @@ public int apply(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link AddPoolAction} + * Creates a builder for {@link PoolAddAction} * - * @return a new {@link AddPoolAction.Builder} + * @return a new {@link PoolAddAction.Builder} */ @Contract("->new") - public static AddPoolAction.Builder builder() { - return new AddPoolAction.Builder(); + public static PoolAddAction.Builder builder() { + return new PoolAddAction.Builder(); } /** - * Builder for {@link AddPoolAction} + * Builder for {@link PoolAddAction} */ public static class Builder implements LootModifierAction.Builder { private Builder() { @@ -70,14 +70,14 @@ private Builder() { * @return this */ @Contract("_->this") - public AddPoolAction.Builder pool(LootPool.Builder poolBuilder) { + public PoolAddAction.Builder pool(LootPool.Builder poolBuilder) { this.pools.add(poolBuilder.build()); return this; } @Override - public AddPoolAction build() { - return new AddPoolAction(pools.build()); + public PoolAddAction build() { + return new PoolAddAction(pools.build()); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolRemoveAction.java similarity index 81% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolRemoveAction.java index 9e22dee..e1debda 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/RemovePoolAction.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolRemoveAction.java @@ -18,8 +18,8 @@ /** * Removes the matched pools from their tables */ -public record RemovePoolAction() implements LootModifierAction { - public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(RemovePoolAction::new)); +public record PoolRemoveAction() implements LootModifierAction { + public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(PoolRemoveAction::new)); @Override public LootModifierActionType getType() { @@ -45,12 +45,12 @@ public int apply(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link RemovePoolAction} + * Creates a builder for {@link PoolRemoveAction} * - * @return a new {@link RemovePoolAction.Builder} + * @return a new {@link PoolRemoveAction.Builder} */ @Contract("->new") - public static RemovePoolAction.Builder builder() { - return RemovePoolAction::new; + public static PoolRemoveAction.Builder builder() { + return PoolRemoveAction::new; } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java index b73cba3..a8eb983 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java @@ -4,9 +4,9 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedLootPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedPredicate; import java.util.function.Predicate; @@ -49,39 +49,39 @@ interface Builder { /** * Inverts this builder. *
- * Wraps this builder in an {@link InvertedLootPredicate} + * Wraps this builder in an {@link InvertedPredicate} * * @return An inverted version of this builder. */ @Contract("->new") default Builder invert() { - return InvertedLootPredicate.builder(this); + return InvertedPredicate.builder(this); } /** * Adds another predicate builder in an {@code OR} relationship. *
- * Wraps this and the provided builder in an {@link AnyOfLootPredicate} + * Wraps this and the provided builder in an {@link AnyOfPredicate} * * @param otherPredicate The other predicate * @return A builder matching when this builder or the provided other builder match. */ @Contract("_->new") default LootModifierPredicate.Builder or(@NotNull LootModifierPredicate.Builder otherPredicate) { - return AnyOfLootPredicate.builder().or(this).or(otherPredicate); + return AnyOfPredicate.builder().or(this).or(otherPredicate); } /** * Adds another predicate builder in an {@code AND} relationship. *
- * Wraps this and the provided builder in an {@link AllOfLootPredicate} + * Wraps this and the provided builder in an {@link AllOfPredicate} * * @param otherPredicate The other predicate * @return A builder matching when this builder and the provided other builder match. */ @Contract("_->new") default LootModifierPredicate.Builder and(@NotNull LootModifierPredicate.Builder otherPredicate) { - return AllOfLootPredicate.builder().and(this).and(otherPredicate); + return AllOfPredicate.builder().and(this).and(otherPredicate); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java index aa6969e..564fe5d 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java @@ -5,11 +5,11 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.ItemEntryPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfLootPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedLootPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.EntryItemPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.TablePredicate; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; @@ -24,27 +24,27 @@ private LootModifierPredicateTypes() { } /** - * Type of {@link InvertedLootPredicate} + * Type of {@link InvertedPredicate} */ - public static final LootModifierPredicateType INVERTED = register(id("inverted"), InvertedLootPredicate.CODEC); + public static final LootModifierPredicateType INVERTED = register(id("inverted"), InvertedPredicate.CODEC); /** - * Type of {@link AnyOfLootPredicate} + * Type of {@link AnyOfPredicate} */ - public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfLootPredicate.CODEC); + public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfPredicate.CODEC); /** - * Type of {@link AllOfLootPredicate} + * Type of {@link AllOfPredicate} */ - public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfLootPredicate.CODEC); + public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfPredicate.CODEC); /** - * Type of {@link ItemEntryPredicate} + * Type of {@link EntryItemPredicate} */ - public static final LootModifierPredicateType ENTRY_ITEM = register(id("entry_item"), ItemEntryPredicate.CODEC); + public static final LootModifierPredicateType ENTRY_ITEM = register(id("entry_item"), EntryItemPredicate.CODEC); /** - * Type of {@link LootTablePredicate} + * Type of {@link TablePredicate} */ - public static final LootModifierPredicateType TABLE = register(id("table"), LootTablePredicate.CODEC); + public static final LootModifierPredicateType TABLE = register(id("table"), TablePredicate.CODEC); //public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/EntryItemPredicate.java similarity index 71% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/EntryItemPredicate.java index 25dacfd..72d4710 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/ItemEntryPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/EntryItemPredicate.java @@ -19,9 +19,9 @@ * * @param name the {@link RegexPattern} matching the item identifier */ -public record ItemEntryPredicate(RegexPattern name) implements LootModifierPredicate { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(RegexPattern.CODEC.fieldOf("name").forGetter(ItemEntryPredicate::name)).apply(instance, ItemEntryPredicate::new) +public record EntryItemPredicate(RegexPattern name) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(RegexPattern.CODEC.fieldOf("name").forGetter(EntryItemPredicate::name)).apply(instance, EntryItemPredicate::new) ); @Override @@ -38,33 +38,33 @@ public boolean test(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link ItemEntryPredicate} matching the provided item + * Creates a builder for {@link EntryItemPredicate} matching the provided item * * @param name the item to match - * @return a new {@link ItemEntryPredicate.Builder} + * @return a new {@link EntryItemPredicate.Builder} */ @Contract("_->new") - public static ItemEntryPredicate.Builder builder(ItemConvertible name) { + public static EntryItemPredicate.Builder builder(ItemConvertible name) { return builder(Registries.ITEM.getId(name.asItem())); } /** - * Creates a builder for {@link ItemEntryPredicate} matching the item based on the provided identifier + * Creates a builder for {@link EntryItemPredicate} matching the item based on the provided identifier * * @param name the item id to match - * @return a new {@link ItemEntryPredicate.Builder} + * @return a new {@link EntryItemPredicate.Builder} */ @Contract("_->new") - public static ItemEntryPredicate.Builder builder(Identifier name) { + public static EntryItemPredicate.Builder builder(Identifier name) { return builder(RegexPattern.literal(name)); } /** - * Creates a builder for {@link ItemEntryPredicate} matching the provided item + * Creates a builder for {@link EntryItemPredicate} matching the provided item * * @param name the {@link RegexPattern} to match the item id with - * @return a new {@link ItemEntryPredicate.Builder} + * @return a new {@link EntryItemPredicate.Builder} */ @Contract("_->new") - public static ItemEntryPredicate.Builder builder(RegexPattern name) { - return () -> new ItemEntryPredicate(name); + public static EntryItemPredicate.Builder builder(RegexPattern name) { + return () -> new EntryItemPredicate(name); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfPredicate.java similarity index 56% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfPredicate.java index 9942e31..7ae03ab 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfPredicate.java @@ -13,13 +13,13 @@ /** * Matches when all the provided predicates match */ -public class AllOfLootPredicate extends TermsLootPredicate { +public class AllOfPredicate extends TermsPredicate { /** * The codec */ - public static final MapCodec CODEC = createCodec(AllOfLootPredicate::new); + public static final MapCodec CODEC = createCodec(AllOfPredicate::new); - private AllOfLootPredicate(final List terms) { + private AllOfPredicate(final List terms) { super(terms, Util.allOf(terms)); } @@ -29,32 +29,32 @@ public LootModifierPredicateType getType() { } /** - * Creates a builder for {@link AllOfLootPredicate} + * Creates a builder for {@link AllOfPredicate} * - * @return a new {@link AllOfLootPredicate.Builder} + * @return a new {@link AllOfPredicate.Builder} */ - public static AllOfLootPredicate.Builder builder() { - return new AllOfLootPredicate.Builder(); + public static AllOfPredicate.Builder builder() { + return new AllOfPredicate.Builder(); } /** - * Builder for {@link AllOfLootPredicate} + * Builder for {@link AllOfPredicate} */ - public static class Builder extends TermsLootPredicate.Builder { + public static class Builder extends TermsPredicate.Builder { private Builder() { } @Override @Contract("_->this") - public AllOfLootPredicate.Builder and(LootModifierPredicate.@NotNull Builder builder) { + public AllOfPredicate.Builder and(LootModifierPredicate.@NotNull Builder builder) { this.add(builder); return this; } @Override - protected AllOfLootPredicate build(List terms) { - return new AllOfLootPredicate(terms); + protected AllOfPredicate build(List terms) { + return new AllOfPredicate(terms); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfPredicate.java similarity index 57% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfPredicate.java index c5f45d7..a754d55 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfPredicate.java @@ -13,13 +13,13 @@ /** * Matches when any of the provided predicates match */ -public class AnyOfLootPredicate extends TermsLootPredicate { +public class AnyOfPredicate extends TermsPredicate { /** * The codec */ - public static final MapCodec CODEC = createCodec(AnyOfLootPredicate::new); + public static final MapCodec CODEC = createCodec(AnyOfPredicate::new); - private AnyOfLootPredicate(final List terms) { + private AnyOfPredicate(final List terms) { super(terms, Util.anyOf(terms)); } @@ -29,33 +29,33 @@ public LootModifierPredicateType getType() { } /** - * Creates a builder for {@link AnyOfLootPredicate} + * Creates a builder for {@link AnyOfPredicate} * - * @return a new {@link AnyOfLootPredicate.Builder} + * @return a new {@link AnyOfPredicate.Builder} */ @Contract("->new") - public static AnyOfLootPredicate.Builder builder() { - return new AnyOfLootPredicate.Builder(); + public static AnyOfPredicate.Builder builder() { + return new AnyOfPredicate.Builder(); } /** - * Builder for {@link AnyOfLootPredicate} + * Builder for {@link AnyOfPredicate} */ - public static class Builder extends TermsLootPredicate.Builder { + public static class Builder extends TermsPredicate.Builder { private Builder() { } @Override @Contract("_->this") - public AnyOfLootPredicate.Builder or(@NotNull LootModifierPredicate.Builder builder) { + public AnyOfPredicate.Builder or(@NotNull LootModifierPredicate.Builder builder) { this.add(builder); return this; } @Override - protected AnyOfLootPredicate build(List terms) { - return new AnyOfLootPredicate(terms); + protected AnyOfPredicate build(List terms) { + return new AnyOfPredicate(terms); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedPredicate.java similarity index 63% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedPredicate.java index ce2ebe9..727e12a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedPredicate.java @@ -13,9 +13,9 @@ * * @param term the predicate to invert */ -public record InvertedLootPredicate(LootModifierPredicate term) implements LootModifierPredicate { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(LootModifierPredicate.CODEC.fieldOf("term").forGetter(InvertedLootPredicate::term)).apply(instance, InvertedLootPredicate::new) +public record InvertedPredicate(LootModifierPredicate term) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(LootModifierPredicate.CODEC.fieldOf("term").forGetter(InvertedPredicate::term)).apply(instance, InvertedPredicate::new) ); @Override @@ -29,12 +29,12 @@ public boolean test(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link InvertedLootPredicate} + * Creates a builder for {@link InvertedPredicate} * * @param term the predicate to invert - * @return a new {@link InvertedLootPredicate.Builder} containing the provided predicate + * @return a new {@link InvertedPredicate.Builder} containing the provided predicate */ - public static InvertedLootPredicate.Builder builder(LootModifierPredicate.Builder term) { - return () -> new InvertedLootPredicate(term.build()); + public static InvertedPredicate.Builder builder(LootModifierPredicate.Builder term) { + return () -> new InvertedPredicate(term.build()); } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsPredicate.java similarity index 81% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsPredicate.java index 24b53f4..a0c5c20 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsLootPredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsPredicate.java @@ -14,14 +14,14 @@ /** * An abstract predicate for predicates that take other predicates as terms. */ -abstract class TermsLootPredicate implements LootModifierPredicate { +abstract class TermsPredicate implements LootModifierPredicate { /** * The terms of this predicate */ protected final List terms; private final Predicate builtPredicate; - protected TermsLootPredicate(final List terms, final Predicate builtPredicate) { + protected TermsPredicate(final List terms, final Predicate builtPredicate) { this.terms = terms; this.builtPredicate = builtPredicate; } @@ -33,9 +33,9 @@ protected TermsLootPredicate(final List terms, final Pred * @return a codec for the terms predicate using the provided constructor * @param the terms predicate */ - protected static MapCodec createCodec(final Function, T> constructor) { + protected static MapCodec createCodec(final Function, T> constructor) { return RecordCodecBuilder.mapCodec( - instance -> instance.group(LootModifierPredicate.CODEC.listOf().fieldOf("terms").forGetter(TermsLootPredicate::getTerms)).apply(instance, constructor) + instance -> instance.group(LootModifierPredicate.CODEC.listOf().fieldOf("terms").forGetter(TermsPredicate::getTerms)).apply(instance, constructor) ); } @@ -49,7 +49,7 @@ public boolean test(final @NotNull LootModifierContext context) { } /** - * Abstract builder for {@link TermsLootPredicate} + * Abstract builder for {@link TermsPredicate} */ public abstract static class Builder implements LootModifierPredicate.Builder { private final ImmutableList.Builder terms = ImmutableList.builder(); diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/TablePredicate.java similarity index 78% rename from src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java rename to src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/TablePredicate.java index faa2b59..0cdc8f8 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/LootTablePredicate.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/TablePredicate.java @@ -29,14 +29,14 @@ * @param identifiers the identifiers to match. List entries are in an {@code OR} relationship * @param types the types to match. List entries are in an {@code OR} relationship */ -public record LootTablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - RegexPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(LootTablePredicate::optionalIdentifier), - RegexPattern.CODEC.listOf().optionalFieldOf("types").forGetter(LootTablePredicate::optionalType) - ).apply(instance, LootTablePredicate::new)); +public record TablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + RegexPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(TablePredicate::optionalIdentifier), + RegexPattern.CODEC.listOf().optionalFieldOf("types").forGetter(TablePredicate::optionalType) + ).apply(instance, TablePredicate::new)); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me - private LootTablePredicate(@NotNull Optional> optionalIdentifier, @NotNull Optional> optionalType) { + private TablePredicate(@NotNull Optional> optionalIdentifier, @NotNull Optional> optionalType) { this( optionalIdentifier.orElse(null), optionalType.orElse(null) @@ -81,17 +81,17 @@ public boolean test(@NotNull LootModifierContext context) { } /** - * Creates a builder for {@link LootTablePredicate} + * Creates a builder for {@link TablePredicate} * - * @return a new {@link LootTablePredicate.Builder} + * @return a new {@link TablePredicate.Builder} */ @Contract("->new") - public static LootTablePredicate.Builder builder() { - return new LootTablePredicate.Builder(); + public static TablePredicate.Builder builder() { + return new TablePredicate.Builder(); } /** - * Builder for {@link LootTablePredicate} + * Builder for {@link TablePredicate} */ public static class Builder implements LootModifierPredicate.Builder { private Builder() { @@ -108,7 +108,7 @@ private Builder() { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder name(@NotNull EntityType name) { + public TablePredicate.Builder name(@NotNull EntityType name) { name(LootTableIdGetter.INSTANCE.get(name)); return this; } @@ -119,7 +119,7 @@ public LootTablePredicate.Builder name(@NotNull EntityType name) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder name(@NotNull Block name) { + public TablePredicate.Builder name(@NotNull Block name) { name(LootTableIdGetter.INSTANCE.get(name)); return this; } @@ -130,7 +130,7 @@ public LootTablePredicate.Builder name(@NotNull Block name) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder name(@NotNull RegistryKey name) { + public TablePredicate.Builder name(@NotNull RegistryKey name) { name(name.getValue()); return this; } @@ -141,7 +141,7 @@ public LootTablePredicate.Builder name(@NotNull RegistryKey name) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder name(@NotNull Identifier name) { + public TablePredicate.Builder name(@NotNull Identifier name) { name(RegexPattern.literal(name)); return this; } @@ -152,7 +152,7 @@ public LootTablePredicate.Builder name(@NotNull Identifier name) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder name(@NotNull RegexPattern name) { + public TablePredicate.Builder name(@NotNull RegexPattern name) { this.names.add(name); return this; } @@ -164,7 +164,7 @@ public LootTablePredicate.Builder name(@NotNull RegexPattern name) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder type(@NotNull ContextType type) { + public TablePredicate.Builder type(@NotNull ContextType type) { type(LootContextTypes.MAP.inverse().get(type)); return this; } @@ -175,7 +175,7 @@ public LootTablePredicate.Builder type(@NotNull ContextType type) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder type(@NotNull Identifier type) { + public TablePredicate.Builder type(@NotNull Identifier type) { type(RegexPattern.literal(type)); return this; } @@ -186,14 +186,14 @@ public LootTablePredicate.Builder type(@NotNull Identifier type) { * @return this */ @Contract("_->this") - public LootTablePredicate.Builder type(@NotNull RegexPattern type) { + public TablePredicate.Builder type(@NotNull RegexPattern type) { this.types.add(type); return this; } @Override - public LootTablePredicate build() { - return new LootTablePredicate(names.build(), types.build()); + public TablePredicate build() { + return new TablePredicate(names.build(), types.build()); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index 2ad436b..3bc41dc 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -19,14 +19,14 @@ import net.minecraft.registry.RegistryWrapper; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; -import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.AddEntryAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.RemoveEntryAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryRemoveAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.TablePredicate; import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; -import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.AddPoolAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.SetItemAction; -import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.RemovePoolAction; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.ItemEntryPredicate; -import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.LootTablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryItemSetAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolRemoveAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.EntryItemPredicate; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @@ -57,20 +57,20 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("replace_ingots_with_command_block"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(RegexPattern.compile("minecraft:.*_ingot")) + EntryItemPredicate.builder(RegexPattern.compile("minecraft:.*_ingot")) ) .action( - SetItemAction.builder(Items.COMMAND_BLOCK) + EntryItemSetAction.builder(Items.COMMAND_BLOCK) ) ); addModifier( id("sugarcane_drop_tnt"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(Items.SUGAR_CANE) + EntryItemPredicate.builder(Items.SUGAR_CANE) ) .action( - AddPoolAction.builder() + PoolAddAction.builder() .pool( LootPool.builder() .rolls(ConstantLootNumberProvider.create(1)) @@ -85,12 +85,12 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("mobs_drop_tnt"), LootModifier.builder() .conditionally( - LootTablePredicate.builder() + TablePredicate.builder() .name(EntityType.CREEPER) .name(EntityType.ZOMBIE) ) .action( - AddPoolAction.builder() + PoolAddAction.builder() .pool( LootPool.builder() .rolls(ConstantLootNumberProvider.create(1)) @@ -105,11 +105,11 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("empty_table_test"), LootModifier.builder() .conditionally( - LootTablePredicate.builder() + TablePredicate.builder() .name(RegistryKey.of(RegistryKeys.LOOT_TABLE, id("test_empty_table"))) ) .action( - AddPoolAction.builder() + PoolAddAction.builder() .pool( LootPool.builder() .rolls(ConstantLootNumberProvider.create(1)) @@ -124,23 +124,23 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("remove_pools_with_sticks"), LootModifier.builder() .conditionally( - ItemEntryPredicate.builder(Items.STICK) + EntryItemPredicate.builder(Items.STICK) // Exclude witch to test if AllOf and Inverted work + so I can test RemoveEntry on witch. - .and(LootTablePredicate.builder().name(EntityType.WITCH).invert()) + .and(TablePredicate.builder().name(EntityType.WITCH).invert()) ) .action( - RemovePoolAction.builder() + PoolRemoveAction.builder() ) ); addModifier( id("add_cake_entry_to_dirt_block"), LootModifier.builder() .conditionally( - LootTablePredicate.builder() + TablePredicate.builder() .name(Blocks.DIRT) ) .action( - AddEntryAction.builder() + EntryAddAction.builder() .entry( ItemEntry.builder(Items.CAKE) .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) @@ -151,11 +151,11 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("add_command_block_to_all_blocks"), LootModifier.builder() .conditionally( - LootTablePredicate.builder() + TablePredicate.builder() .type(LootContextTypes.BLOCK) ) .action( - AddEntryAction.builder() + EntryAddAction.builder() .entry( ItemEntry.builder(Items.CAKE) .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1))) @@ -166,14 +166,14 @@ protected void generate(RegistryWrapper.WrapperLookup lookup) { id("remove_glowstone_and_gunpowder_from_witch"), LootModifier.builder() .conditionally( - LootTablePredicate.builder().name(EntityType.WITCH) + TablePredicate.builder().name(EntityType.WITCH) .and( - ItemEntryPredicate.builder(Items.GLOWSTONE_DUST) - .or(ItemEntryPredicate.builder(Items.GUNPOWDER)) + EntryItemPredicate.builder(Items.GLOWSTONE_DUST) + .or(EntryItemPredicate.builder(Items.GUNPOWDER)) ) ) .action( - RemoveEntryAction.builder() + EntryRemoveAction.builder() ) ); } From 572f4deef5d219f16b8ec7b723b5ca5aba8395fc Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:36:51 +0300 Subject: [PATCH 46/53] Cleanup --- .../api/resource/predicate/LootModifierPredicateTypes.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java index 564fe5d..8981466 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java @@ -46,8 +46,6 @@ private LootModifierPredicateTypes() { */ public static final LootModifierPredicateType TABLE = register(id("table"), TablePredicate.CODEC); - //public static final LootModifierPredicateType LOOT_POOL = register(id("loot_pool"), LootPoolPredicate.CODEC); - private static LootModifierPredicateType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { return Registry.register(LootModifierPredicateType.REGISTRY, id, new LootModifierPredicateType(codec)); } From cc2c8e1a29448743c1151dbf77b2ca7ad0b45e0b Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 Date: Mon, 21 Jul 2025 00:00:43 +0300 Subject: [PATCH 47/53] Add docs (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thank you oh so so much to Mr Smart Guy ❤️❤️❤️ Also thanks the other smart guy and *other* other smart guy ❤️❤️❤️ Adds docs page made with starlight. Should be up to date with v2 alpha 1 and hopefully 🤞🤞🤞 nothing is wrong --- .github/workflows/build_artifacts.yml | 42 +- .github/workflows/publish.yml | 41 +- Dockerfile | 40 + docker-compose-template.yml | 17 + docs/.gitignore | 21 + docs/.vscode/extensions.json | 4 + docs/.vscode/launch.json | 11 + docs/README.md | 54 + docs/astro.config.mjs | 53 + docs/package.json | 18 + docs/pnpm-lock.yaml | 4306 +++++++++++++++++ docs/pnpm-workspace.yaml | 3 + docs/public/favicon.svg | 1 + docs/src/assets/face.svg | 1 + docs/src/assets/icon.svg | 1 + docs/src/content.config.ts | 7 + .../examples/creepers_and_zombies_drop_tnt.md | 121 + .../remove_glowstone_and_gunpowder_witches.md | 43 + .../content/docs/examples/remove_sticks.md | 26 + .../docs/examples/replace_ingot_w_diamond.md | 26 + docs/src/content/docs/getting_started.md | 62 + docs/src/content/docs/index.md | 36 + docs/src/content/docs/reference/actions.md | 89 + docs/src/content/docs/reference/badges.md | 21 + .../content/docs/reference/loot_modifier.md | 20 + docs/src/content/docs/reference/predicates.md | 84 + .../docs/reference/regex_identifier.md | 39 + docs/src/styles/custom.css | 37 + docs/src/utils/PageFrame.astro | 98 + docs/src/utils/SocialIcons.astro | 57 + docs/src/utils/socialIcons.ts | 39 + docs/tsconfig.json | 5 + 32 files changed, 5421 insertions(+), 2 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose-template.yml create mode 100644 docs/.gitignore create mode 100644 docs/.vscode/extensions.json create mode 100644 docs/.vscode/launch.json create mode 100644 docs/README.md create mode 100644 docs/astro.config.mjs create mode 100644 docs/package.json create mode 100644 docs/pnpm-lock.yaml create mode 100644 docs/pnpm-workspace.yaml create mode 100644 docs/public/favicon.svg create mode 100644 docs/src/assets/face.svg create mode 100644 docs/src/assets/icon.svg create mode 100644 docs/src/content.config.ts create mode 100644 docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md create mode 100644 docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md create mode 100644 docs/src/content/docs/examples/remove_sticks.md create mode 100644 docs/src/content/docs/examples/replace_ingot_w_diamond.md create mode 100644 docs/src/content/docs/getting_started.md create mode 100644 docs/src/content/docs/index.md create mode 100644 docs/src/content/docs/reference/actions.md create mode 100644 docs/src/content/docs/reference/badges.md create mode 100644 docs/src/content/docs/reference/loot_modifier.md create mode 100644 docs/src/content/docs/reference/predicates.md create mode 100644 docs/src/content/docs/reference/regex_identifier.md create mode 100644 docs/src/styles/custom.css create mode 100644 docs/src/utils/PageFrame.astro create mode 100644 docs/src/utils/SocialIcons.astro create mode 100644 docs/src/utils/socialIcons.ts create mode 100644 docs/tsconfig.json diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index ee8577c..dd6eef3 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -7,7 +7,7 @@ on: jobs: - build: + build-mod: runs-on: ubuntu-latest steps: @@ -52,3 +52,43 @@ jobs: with: name: Artifacts path: build/libs/ + + publish-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + + - uses: danielr1996/envsubst-action@1.1.0 + env: + URL_PREFIX: staging- + SHORT_COMMIT_HASH: ${{ env.short_commit_hash }} + with: + input: docker-compose-template.yml + output: docker-compose.yml + + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: registry.oracle.offsetmonkey538.top + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${{ env.short_commit_hash }} + + - run: | + tar -czf archive.tar.gz docker-compose.yml && curl -i -X POST -H "Authorization: Bearer $SEELF_TOKEN" -F environment=$SEELF_ENVIRONMENT -F archive=@archive.tar.gz $SEELF_APPLICATION_URL + env: + SEELF_TOKEN: ${{ secrets.SEELF_TOKEN }} + SEELF_ENVIRONMENT: staging + SEELF_APPLICATION_URL: https://seelf.oracle.offsetmonkey538.top/api/v1/apps/300qBnG3t1dOFrUGfw1EeWYwKYA/deployments diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 22908d9..2a7e0e1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,7 +6,7 @@ on: jobs: - publish: + publish-mod: runs-on: ubuntu-latest permissions: @@ -63,3 +63,42 @@ jobs: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + publish-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + + - uses: danielr1996/envsubst-action@1.1.0 + env: + URL_PREFIX: "" + SHORT_COMMIT_HASH: ${{ env.short_commit_hash }} + with: + input: docker-compose-template.yml + output: docker-compose.yml + + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: registry.oracle.offsetmonkey538.top + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${{ env.short_commit_hash }} + + - run: | + tar -czf archive.tar.gz docker-compose.yml && curl -i -X POST -H "Authorization: Bearer $SEELF_TOKEN" -F environment=$SEELF_ENVIRONMENT -F archive=@archive.tar.gz $SEELF_APPLICATION_URL + env: + SEELF_TOKEN: ${{ secrets.SEELF_TOKEN }} + SEELF_ENVIRONMENT: staging + SEELF_APPLICATION_URL: https://seelf.oracle.offsetmonkey538.top/api/v1/apps/300qBnG3t1dOFrUGfw1EeWYwKYA/deployments diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..93abdf3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +#FROM nginx:alpine AS runtime +#COPY ./nginx.conf /etc/nginx/nginx.conf +#COPY ./dist /usr/share/nginx/html +#CMD ["nginx", "-g", "daemon off;"] + +#FROM httpd:latest AS runtime +#COPY ./dist /usr/local/apache2/htdocs/ + +FROM node:lts-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PATH:$PNPM_HOME" +RUN corepack enable +COPY /docs /docs +WORKDIR /docs + +FROM base AS build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm run build + +FROM httpd:alpine +COPY --from=build /docs/dist /usr/local/apache2/htdocs/ +EXPOSE 80 + + +#FROM base +#COPY --from=prod-deps +#COPY package.json pnpm-lock.yaml +#RUN npm install -g pnpm + + + +#FROM node:lts AS setup +#WORKDIR /docs +#COPY ./docs . +#RUN npm install -g pnpm +#RUN pnpm i +#RUN pnpm run build + +#FROM httpd:2.4 AS runtime +#COPY --from=build /docs/dist /usr/local/apache2/htdocs/ diff --git a/docker-compose-template.yml b/docker-compose-template.yml new file mode 100644 index 0000000..96c558a --- /dev/null +++ b/docker-compose-template.yml @@ -0,0 +1,17 @@ +# Hosts a static page +services: + ${URL_PREFIX}loot-table-modifier: + image: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${SHORT_COMMIT_HASH} + restart: unless-stopped + networks: + - traefik-proxy + labels: + - "traefik.enable=true" + + - "traefik.http.routers.${URL_PREFIX}loot-table-modifier.rule=Host(`${URL_PREFIX}loot-table-modifier.docs.offsetmonkey538.top`)" + - "traefik.http.routers.${URL_PREFIX}loot-table-modifier.service=${URL_PREFIX}loot-table-modifier" + - "traefik.http.services.${URL_PREFIX}loot-table-modifier.loadbalancer.server.port=80" + +networks: + traefik-proxy: + external: true \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..6240da8 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/docs/.vscode/extensions.json b/docs/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/docs/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/docs/.vscode/launch.json b/docs/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/docs/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..70f4d8b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,54 @@ +# Starlight Starter Kit: Basics + +[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) + +``` +pnpm create astro@latest -- --template starlight +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) +[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro + Starlight project, you'll see the following folders and files: + +``` +. +├── public/ +├── src/ +│ ├── assets/ +│ ├── content/ +│ │ ├── docs/ +│ └── content.config.ts +├── astro.config.mjs +├── package.json +└── tsconfig.json +``` + +Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. + +Images can be added to `src/assets/` and embedded in Markdown with a relative link. + +Static assets, like favicons, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `pnpm install` | Installs dependencies | +| `pnpm dev` | Starts local dev server at `localhost:4321` | +| `pnpm build` | Build your production site to `./dist/` | +| `pnpm preview` | Preview your build locally, before deploying | +| `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` | +| `pnpm astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 0000000..fea992e --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,53 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import starlightSocialIcons from './src/utils/socialIcons'; +import starlight from '@astrojs/starlight'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + starlightSocialIcons({ + modrinth: "https://modrinth.com/mod/loot-table-modifier" + }), + starlight({ + title: 'Loot Table Modifier', + credits: true, + logo: { + src: './src/assets/face.svg' + }, + customCss: [ + './src/styles/custom.css' + ], + components: { + SocialIcons: './src/utils/SocialIcons.astro', + PageFrame: './src/utils/PageFrame.astro' + }, + social: [ + {icon: 'github', label: 'GitHub', href: 'https://github.com/OffsetMods538/Loot-Table-Modifier'}, + {icon: 'discord', label: 'Discord', href: 'https://discord.offsetmonkey538.top'} + ], + sidebar: [ + {label: 'Getting Started', slug: 'getting_started'}, + { + label: 'Examples', + items: [ + {label: 'Replace any ingot with a diamond', slug: 'examples/replace_ingot_w_diamond'}, + {label: 'Make Creepers and Zombies drop tnt', slug: 'examples/creepers_and_zombies_drop_tnt'}, + {label: 'Remove sticks', slug: 'examples/remove_sticks'}, + {label: 'Remove glowstone and gunpowder from witches', slug: 'examples/remove_glowstone_and_gunpowder_witches'}, + ], + }, + { + label: 'Reference', + items: [ + {label: 'Loot Modifier', slug: 'reference/loot_modifier'}, + {label: 'Actions', slug: 'reference/actions'}, + {label: 'Regex Identifier', slug: 'reference/regex_identifier'}, + {label: 'Predicates', slug: 'reference/predicates'}, + {label: 'Badges', slug: 'reference/badges'}, + ], + }, + ], + }), + ] +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..f50ef1e --- /dev/null +++ b/docs/package.json @@ -0,0 +1,18 @@ +{ + "name": "docs", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/starlight": "^0.35.1", + "astro": "^5.12.0", + "pathe": "^2.0.3", + "sharp": "^0.34.2" + } +} \ No newline at end of file diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 0000000..b25848d --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,4306 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@astrojs/starlight': + specifier: ^0.35.1 + version: 0.35.1(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)) + astro: + specifier: ^5.12.0 + version: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + pathe: + specifier: ^2.0.3 + version: 2.0.3 + sharp: + specifier: ^0.34.2 + version: 0.34.2 + +packages: + + '@astrojs/compiler@2.12.2': + resolution: {integrity: sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw==} + + '@astrojs/internal-helpers@0.6.1': + resolution: {integrity: sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A==} + + '@astrojs/markdown-remark@6.3.2': + resolution: {integrity: sha512-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q==} + + '@astrojs/markdown-remark@6.3.3': + resolution: {integrity: sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w==} + + '@astrojs/mdx@4.3.0': + resolution: {integrity: sha512-OGX2KvPeBzjSSKhkCqrUoDMyzFcjKt5nTE5SFw3RdoLf0nrhyCXBQcCyclzWy1+P+XpOamn+p+hm1EhpCRyPxw==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + astro: ^5.0.0 + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/sitemap@3.4.1': + resolution: {integrity: sha512-VjZvr1e4FH6NHyyHXOiQgLiw94LnCVY4v06wN/D0gZKchTMkg71GrAHJz81/huafcmavtLkIv26HnpfDq6/h/Q==} + + '@astrojs/starlight@0.35.1': + resolution: {integrity: sha512-/hshlAayMd3B+E+h8wY6JWT1lNmX/K1+ugiZPirW5XFo5QUcNMk/Bsa4oHgg+TFoU6kbxPtijo0VppATfD9XuA==} + peerDependencies: + astro: ^5.5.0 + + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.0': + resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} + engines: {node: '>=6.9.0'} + + '@capsizecss/unpack@2.4.0': + resolution: {integrity: sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==} + + '@ctrl/tinycolor@4.1.0': + resolution: {integrity: sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==} + engines: {node: '>=14'} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@expressive-code/core@0.41.2': + resolution: {integrity: sha512-AJW5Tp9czbLqKMzwudL9Rv4js9afXBxkSGLmCNPq1iRgAYcx9NkTPJiSNCesjKRWoVC328AdSu6fqrD22zDgDg==} + + '@expressive-code/plugin-frames@0.41.2': + resolution: {integrity: sha512-pfy0hkJI4nbaONjmksFDcuHmIuyPTFmi1JpABe4q2ajskiJtfBf+WDAL2pg595R9JNoPrrH5+aT9lbkx2noicw==} + + '@expressive-code/plugin-shiki@0.41.2': + resolution: {integrity: sha512-xD4zwqAkDccXqye+235BH5bN038jYiSMLfUrCOmMlzxPDGWdxJDk5z4uUB/aLfivEF2tXyO2zyaarL3Oqht0fQ==} + + '@expressive-code/plugin-text-markers@0.41.2': + resolution: {integrity: sha512-JFWBz2qYxxJOJkkWf96LpeolbnOqJY95TvwYc0hXIHf9oSWV0h0SY268w/5N3EtQaD9KktzDE+VIVwb9jdb3nw==} + + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-arm64@0.34.2': + resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.2': + resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.2': + resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-arm@0.34.2': + resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-s390x@0.34.2': + resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-x64@0.34.2': + resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.2': + resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.2': + resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-wasm32@0.34.2': + resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.2': + resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-ia32@0.34.2': + resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@img/sharp-win32-x64@0.34.2': + resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@mdx-js/mdx@3.1.0': + resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@pagefind/darwin-arm64@1.3.0': + resolution: {integrity: sha512-365BEGl6ChOsauRjyVpBjXybflXAOvoMROw3TucAROHIcdBvXk9/2AmEvGFU0r75+vdQI4LJdJdpH4Y6Yqaj4A==} + cpu: [arm64] + os: [darwin] + + '@pagefind/darwin-x64@1.3.0': + resolution: {integrity: sha512-zlGHA23uuXmS8z3XxEGmbHpWDxXfPZ47QS06tGUq0HDcZjXjXHeLG+cboOy828QIV5FXsm9MjfkP5e4ZNbOkow==} + cpu: [x64] + os: [darwin] + + '@pagefind/default-ui@1.3.0': + resolution: {integrity: sha512-CGKT9ccd3+oRK6STXGgfH+m0DbOKayX6QGlq38TfE1ZfUcPc5+ulTuzDbZUnMo+bubsEOIypm4Pl2iEyzZ1cNg==} + + '@pagefind/linux-arm64@1.3.0': + resolution: {integrity: sha512-8lsxNAiBRUk72JvetSBXs4WRpYrQrVJXjlRRnOL6UCdBN9Nlsz0t7hWstRk36+JqHpGWOKYiuHLzGYqYAqoOnQ==} + cpu: [arm64] + os: [linux] + + '@pagefind/linux-x64@1.3.0': + resolution: {integrity: sha512-hAvqdPJv7A20Ucb6FQGE6jhjqy+vZ6pf+s2tFMNtMBG+fzcdc91uTw7aP/1Vo5plD0dAOHwdxfkyw0ugal4kcQ==} + cpu: [x64] + os: [linux] + + '@pagefind/windows-x64@1.3.0': + resolution: {integrity: sha512-BR1bIRWOMqkf8IoU576YDhij1Wd/Zf2kX/kCI0b2qzCKC8wcc2GQJaaRMCpzvCCrmliO4vtJ6RITp/AnoYUUmQ==} + cpu: [x64] + os: [win32] + + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.44.2': + resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.44.2': + resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.44.2': + resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.44.2': + resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.44.2': + resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.44.2': + resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.44.2': + resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.44.2': + resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.44.2': + resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.44.2': + resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.44.2': + resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.44.2': + resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.44.2': + resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.44.2': + resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} + cpu: [x64] + os: [win32] + + '@shikijs/core@3.7.0': + resolution: {integrity: sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg==} + + '@shikijs/engine-javascript@3.7.0': + resolution: {integrity: sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA==} + + '@shikijs/engine-oniguruma@3.7.0': + resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} + + '@shikijs/langs@3.7.0': + resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} + + '@shikijs/themes@3.7.0': + resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} + + '@shikijs/types@3.7.0': + resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/fontkit@2.0.8': + resolution: {integrity: sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + + '@types/node@24.0.10': + resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + astro-expressive-code@0.41.2: + resolution: {integrity: sha512-HN0jWTnhr7mIV/2e6uu4PPRNNo/k4UEgTLZqbp3MrHU+caCARveG2yZxaZVBmxyiVdYqW5Pd3u3n2zjnshixbw==} + peerDependencies: + astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 + + astro@5.12.0: + resolution: {integrity: sha512-Oov5JsMFHuUmuO+Nx6plfv3nQNK1Xl/8CgLvR8lBhZTjYnraxhuPX5COVAzbom+YLgwaDfK7KBd8zOEopRf9mg==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bcp-47-match@2.0.3: + resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} + + bcp-47@2.1.0: + resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==} + + blob-to-buffer@1.2.9: + resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + ci-info@4.2.0: + resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + engines: {node: '>=8'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-selector-parser@3.1.3: + resolution: {integrity: sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + + devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + direction@2.0.1: + resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} + hasBin: true + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + expressive-code@0.41.2: + resolution: {integrity: sha512-aLZiZaqorRtNExtGpUjK9zFH9aTpWeoTXMyLo4b4IcuXfPqtLPPxhRm/QlPb8QqIcMMXnSiGRHSFpQfX0m7HJw==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.3.0: + resolution: {integrity: sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg==} + + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + h3@1.15.3: + resolution: {integrity: sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==} + + hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + + hast-util-format@1.1.0: + resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-has-property@3.0.0: + resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} + + hast-util-is-body-ok-link@3.0.1: + resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-minify-whitespace@1.0.1: + resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-phrasing@3.0.1: + resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-select@6.0.4: + resolution: {integrity: sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-string@3.0.1: + resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + html-whitespace-sensitive-tag-names@3.0.1: + resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + i18next@23.16.8: + resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-directive@3.1.0: + resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-directive@3.0.2: + resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + + node-fetch-native@1.6.6: + resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-mock-http@1.0.1: + resolution: {integrity: sha512-0gJJgENizp4ghds/Ywu2FCmcRsgBTmRQzYPZm61wy+Em2sBarSka0OhQS5huLBg6od1zkNpnWMCZloQDFVvOMQ==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + + p-limit@6.2.0: + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} + + p-queue@8.1.0: + resolution: {integrity: sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==} + engines: {node: '>=18'} + + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + + pagefind@1.3.0: + resolution: {integrity: sha512-8KPLGT5g9s+olKMRTU9LFekLizkVIu9tes90O1/aigJ0T5LmyPqTzGJrETnSw3meSYg58YH7JTzhTTW/3z6VAw==} + hasBin: true + + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.0: + resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==} + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rehype-expressive-code@0.41.2: + resolution: {integrity: sha512-vHYfWO9WxAw6kHHctddOt+P4266BtyT1mrOIuxJD+1ELuvuJAa5uBIhYt0OVMyOhlvf57hzWOXJkHnMhpaHyxw==} + + rehype-format@5.0.1: + resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + + remark-directive@3.0.1: + resolution: {integrity: sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.0: + resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + + rollup@4.44.2: + resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + sharp@0.34.2: + resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shiki@3.7.0: + resolution: {integrity: sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg==} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@8.0.0: + resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + + smol-toml@1.4.1: + resolution: {integrity: sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} + + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.5.2: + resolution: {integrity: sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + unstorage@1.16.0: + resolution: {integrity: sha512-WQ37/H5A7LcRPWfYOrDa1Ys02xAbpPJq6q5GkO88FBXVSQzHd7+BjEwfRqyaSWCv9MbsJy058GWjjPjcJ16GGA==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yocto-spinner@0.2.3: + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + + zod@3.25.74: + resolution: {integrity: sha512-J8poo92VuhKjNknViHRAIuuN6li/EwFbAC8OedzI8uxpEPGiXHGQu9wemIAioIpqgfB4SySaJhdk0mH5Y4ICBg==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@astrojs/compiler@2.12.2': {} + + '@astrojs/internal-helpers@0.6.1': {} + + '@astrojs/markdown-remark@6.3.2': + dependencies: + '@astrojs/internal-helpers': 0.6.1 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.7.0 + smol-toml: 1.4.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/markdown-remark@6.3.3': + dependencies: + '@astrojs/internal-helpers': 0.6.1 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.7.0 + smol-toml: 1.4.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/mdx@4.3.0(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3))': + dependencies: + '@astrojs/markdown-remark': 6.3.2 + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + acorn: 8.15.0 + astro: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + es-module-lexer: 1.7.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.5 + kleur: 4.1.5 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-smartypants: 3.0.2 + source-map: 0.7.4 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.3.0': + dependencies: + prismjs: 1.30.0 + + '@astrojs/sitemap@3.4.1': + dependencies: + sitemap: 8.0.0 + stream-replace-string: 2.0.0 + zod: 3.25.74 + + '@astrojs/starlight@0.35.1(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3))': + dependencies: + '@astrojs/markdown-remark': 6.3.2 + '@astrojs/mdx': 4.3.0(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)) + '@astrojs/sitemap': 3.4.1 + '@pagefind/default-ui': 1.3.0 + '@types/hast': 3.0.4 + '@types/js-yaml': 4.0.9 + '@types/mdast': 4.0.4 + astro: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + astro-expressive-code: 0.41.2(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)) + bcp-47: 2.1.0 + hast-util-from-html: 2.0.3 + hast-util-select: 6.0.4 + hast-util-to-string: 3.0.1 + hastscript: 9.0.1 + i18next: 23.16.8 + js-yaml: 4.1.0 + klona: 2.0.6 + mdast-util-directive: 3.1.0 + mdast-util-to-markdown: 2.1.2 + mdast-util-to-string: 4.0.0 + pagefind: 1.3.0 + rehype: 13.0.2 + rehype-format: 5.0.1 + remark-directive: 3.0.1 + ultrahtml: 1.6.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/telemetry@3.3.0': + dependencies: + ci-info: 4.2.0 + debug: 4.4.1 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.0 + + '@babel/runtime@7.27.6': {} + + '@babel/types@7.28.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@capsizecss/unpack@2.4.0': + dependencies: + blob-to-buffer: 1.2.9 + cross-fetch: 3.2.0 + fontkit: 2.0.4 + transitivePeerDependencies: + - encoding + + '@ctrl/tinycolor@4.1.0': {} + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + + '@expressive-code/core@0.41.2': + dependencies: + '@ctrl/tinycolor': 4.1.0 + hast-util-select: 6.0.4 + hast-util-to-html: 9.0.5 + hast-util-to-text: 4.0.2 + hastscript: 9.0.1 + postcss: 8.5.6 + postcss-nested: 6.2.0(postcss@8.5.6) + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + + '@expressive-code/plugin-frames@0.41.2': + dependencies: + '@expressive-code/core': 0.41.2 + + '@expressive-code/plugin-shiki@0.41.2': + dependencies: + '@expressive-code/core': 0.41.2 + shiki: 3.7.0 + + '@expressive-code/plugin-text-markers@0.41.2': + dependencies: + '@expressive-code/core': 0.41.2 + + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.1.0 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.1.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-arm@1.1.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.1.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.1.0 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-arm@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.1.0 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-s390x@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.1.0 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-wasm32@0.34.2': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-win32-arm64@0.34.2': + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-ia32@0.34.2': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.34.2': + optional: true + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.15.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + + '@oslojs/encoding@1.1.0': {} + + '@pagefind/darwin-arm64@1.3.0': + optional: true + + '@pagefind/darwin-x64@1.3.0': + optional: true + + '@pagefind/default-ui@1.3.0': {} + + '@pagefind/linux-arm64@1.3.0': + optional: true + + '@pagefind/linux-x64@1.3.0': + optional: true + + '@pagefind/windows-x64@1.3.0': + optional: true + + '@rollup/pluginutils@5.2.0(rollup@4.44.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.44.2 + + '@rollup/rollup-android-arm-eabi@4.44.2': + optional: true + + '@rollup/rollup-android-arm64@4.44.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.44.2': + optional: true + + '@rollup/rollup-darwin-x64@4.44.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.44.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.44.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.44.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.44.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.44.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.44.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.44.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.44.2': + optional: true + + '@shikijs/core@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + + '@shikijs/themes@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + + '@shikijs/types@3.7.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/fontkit@2.0.8': + dependencies: + '@types/node': 24.0.10 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/js-yaml@4.0.9': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/ms@2.1.0': {} + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@17.0.45': {} + + '@types/node@24.0.10': + dependencies: + undici-types: 7.8.0 + + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.0.10 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.0': {} + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} + + astring@1.9.0: {} + + astro-expressive-code@0.41.2(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)): + dependencies: + astro: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + rehype-expressive-code: 0.41.2 + + astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3): + dependencies: + '@astrojs/compiler': 2.12.2 + '@astrojs/internal-helpers': 0.6.1 + '@astrojs/markdown-remark': 6.3.3 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 2.4.0 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.2.0(rollup@4.44.2) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.2.0 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.0.2 + cssesc: 3.0.0 + debug: 4.4.1 + deterministic-object-hash: 2.0.2 + devalue: 5.1.1 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.0 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + magicast: 0.3.5 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.0 + package-manager-detector: 1.3.0 + picomatch: 4.0.2 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.2 + shiki: 3.7.0 + smol-toml: 1.4.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tsconfck: 3.1.6(typescript@5.8.3) + ultrahtml: 1.6.0 + unifont: 0.5.2 + unist-util-visit: 5.0.0 + unstorage: 1.16.0 + vfile: 6.0.3 + vite: 6.3.5(@types/node@24.0.10) + vitefu: 1.1.1(vite@6.3.5(@types/node@24.0.10)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.74 + zod-to-json-schema: 3.24.6(zod@3.25.74) + zod-to-ts: 1.2.0(typescript@5.8.3)(zod@3.25.74) + optionalDependencies: + sharp: 0.33.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - db0 + - encoding + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + base-64@1.0.0: {} + + base64-js@1.5.1: {} + + bcp-47-match@2.0.3: {} + + bcp-47@2.1.0: + dependencies: + is-alphabetical: 2.0.1 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + + blob-to-buffer@1.2.9: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.4.1 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + + camelcase@8.0.0: {} + + ccount@2.0.1: {} + + chalk@5.4.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + ci-info@4.2.0: {} + + cli-boxes@3.0.0: {} + + clone@2.1.2: {} + + clsx@2.1.1: {} + + collapse-white-space@2.1.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + comma-separated-tokens@2.0.3: {} + + common-ancestor-path@1.0.1: {} + + cookie-es@1.2.2: {} + + cookie@1.0.2: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-selector-parser@3.1.3: {} + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + defu@6.1.4: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-libc@2.0.4: {} + + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.1.1: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dfa@1.2.0: {} + + diff@5.2.0: {} + + direction@2.0.1: {} + + dlv@1.1.3: {} + + dset@3.1.4: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + entities@6.0.1: {} + + es-module-lexer@1.7.0: {} + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.15.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.2 + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.4 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + eventemitter3@5.0.1: {} + + expressive-code@0.41.2: + dependencies: + '@expressive-code/core': 0.41.2 + '@expressive-code/plugin-frames': 0.41.2 + '@expressive-code/plugin-shiki': 0.41.2 + '@expressive-code/plugin-text-markers': 0.41.2 + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + flattie@1.1.1: {} + + fontace@0.3.0: + dependencies: + '@types/fontkit': 2.0.8 + fontkit: 2.0.4 + + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.17 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + + fsevents@2.3.3: + optional: true + + get-east-asian-width@1.3.0: {} + + github-slugger@2.0.0: {} + + h3@1.15.3: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.1 + radix3: 1.1.2 + ufo: 1.6.1 + uncrypto: 0.1.3 + + hast-util-embedded@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-is-element: 3.0.0 + + hast-util-format@1.1.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.1 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.2 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-has-property@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-body-ok-link@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-minify-whitespace@1.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.0 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-phrasing@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.1 + hast-util-is-element: 3.0.0 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-select@6.0.4: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + bcp-47-match: 2.0.3 + comma-separated-tokens: 2.0.3 + css-selector-parser: 3.1.3 + devlop: 1.1.0 + direction: 2.0.1 + hast-util-has-property: 3.0.0 + hast-util-to-string: 3.0.1 + hast-util-whitespace: 3.0.0 + nth-check: 2.1.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-string@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + html-whitespace-sensitive-tag-names@3.0.1: {} + + http-cache-semantics@4.2.0: {} + + i18next@23.16.8: + dependencies: + '@babel/runtime': 7.27.6 + + import-meta-resolve@4.1.0: {} + + inline-style-parser@0.2.4: {} + + iron-webcrypto@1.2.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arrayish@0.3.2: {} + + is-decimal@2.0.1: {} + + is-docker@3.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-hexadecimal@2.0.1: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-plain-obj@4.1.0: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + klona@2.0.6: {} + + longest-streak@3.1.0: {} + + lru-cache@10.4.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 + source-map-js: 1.2.1 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + mdast-util-directive@3.1.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.12.2: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-directive@3.0.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + parse-entities: 4.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.8 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.1 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + + node-fetch-native@1.6.6: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-mock-http@1.0.1: {} + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + ofetch@1.4.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.6 + ufo: 1.6.1 + + ohash@2.0.11: {} + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + + p-limit@6.2.0: + dependencies: + yocto-queue: 1.2.1 + + p-queue@8.1.0: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.4 + + p-timeout@6.1.4: {} + + package-manager-detector@1.3.0: {} + + pagefind@1.3.0: + optionalDependencies: + '@pagefind/darwin-arm64': 1.3.0 + '@pagefind/darwin-x64': 1.3.0 + '@pagefind/linux-arm64': 1.3.0 + '@pagefind/linux-x64': 1.3.0 + '@pagefind/windows-x64': 1.3.0 + + pako@0.2.9: {} + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prismjs@1.30.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + property-information@6.5.0: {} + + property-information@7.1.0: {} + + radix3@1.1.2: {} + + readdirp@4.1.2: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.0(acorn@8.15.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.15.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.8 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rehype-expressive-code@0.41.2: + dependencies: + expressive-code: 0.41.2 + + rehype-format@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-format: 1.1.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-directive@3.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-directive: 3.1.0 + micromark-extension-directive: 3.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.0: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + restructure@3.0.2: {} + + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.0.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + + rollup@4.44.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.44.2 + '@rollup/rollup-android-arm64': 4.44.2 + '@rollup/rollup-darwin-arm64': 4.44.2 + '@rollup/rollup-darwin-x64': 4.44.2 + '@rollup/rollup-freebsd-arm64': 4.44.2 + '@rollup/rollup-freebsd-x64': 4.44.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.2 + '@rollup/rollup-linux-arm-musleabihf': 4.44.2 + '@rollup/rollup-linux-arm64-gnu': 4.44.2 + '@rollup/rollup-linux-arm64-musl': 4.44.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-musl': 4.44.2 + '@rollup/rollup-linux-s390x-gnu': 4.44.2 + '@rollup/rollup-linux-x64-gnu': 4.44.2 + '@rollup/rollup-linux-x64-musl': 4.44.2 + '@rollup/rollup-win32-arm64-msvc': 4.44.2 + '@rollup/rollup-win32-ia32-msvc': 4.44.2 + '@rollup/rollup-win32-x64-msvc': 4.44.2 + fsevents: 2.3.3 + + sax@1.4.1: {} + + semver@7.7.2: {} + + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + optional: true + + sharp@0.34.2: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.2 + '@img/sharp-darwin-x64': 0.34.2 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.2 + '@img/sharp-linux-arm64': 0.34.2 + '@img/sharp-linux-s390x': 0.34.2 + '@img/sharp-linux-x64': 0.34.2 + '@img/sharp-linuxmusl-arm64': 0.34.2 + '@img/sharp-linuxmusl-x64': 0.34.2 + '@img/sharp-wasm32': 0.34.2 + '@img/sharp-win32-arm64': 0.34.2 + '@img/sharp-win32-ia32': 0.34.2 + '@img/sharp-win32-x64': 0.34.2 + + shiki@3.7.0: + dependencies: + '@shikijs/core': 3.7.0 + '@shikijs/engine-javascript': 3.7.0 + '@shikijs/engine-oniguruma': 3.7.0 + '@shikijs/langs': 3.7.0 + '@shikijs/themes': 3.7.0 + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + sisteransi@1.0.5: {} + + sitemap@8.0.0: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + + smol-toml@1.4.1: {} + + source-map-js@1.2.1: {} + + source-map@0.7.4: {} + + space-separated-tokens@2.0.2: {} + + stream-replace-string@2.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + style-to-js@1.1.17: + dependencies: + style-to-object: 1.0.9 + + style-to-object@1.0.9: + dependencies: + inline-style-parser: 0.2.4 + + tiny-inflate@1.0.3: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tsconfck@3.1.6(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + + tslib@2.8.1: {} + + type-fest@4.41.0: {} + + typescript@5.8.3: {} + + ufo@1.6.1: {} + + ultrahtml@1.6.0: {} + + uncrypto@0.1.3: {} + + undici-types@7.8.0: {} + + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.5.2: + dependencies: + css-tree: 3.1.0 + ofetch: 1.4.1 + ohash: 2.0.11 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + unstorage@1.16.0: + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.3 + lru-cache: 10.4.3 + node-fetch-native: 1.6.6 + ofetch: 1.4.1 + ufo: 1.6.1 + + util-deprecate@1.0.2: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite@6.3.5(@types/node@24.0.10): + dependencies: + esbuild: 0.25.5 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.44.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.0.10 + fsevents: 2.3.3 + + vitefu@1.1.1(vite@6.3.5(@types/node@24.0.10)): + optionalDependencies: + vite: 6.3.5(@types/node@24.0.10) + + web-namespaces@2.0.1: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-pm-runs@1.1.0: {} + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + xxhash-wasm@1.1.0: {} + + yargs-parser@21.1.1: {} + + yocto-queue@1.2.1: {} + + yocto-spinner@0.2.3: + dependencies: + yoctocolors: 2.1.1 + + yoctocolors@2.1.1: {} + + zod-to-json-schema@3.24.6(zod@3.25.74): + dependencies: + zod: 3.25.74 + + zod-to-ts@1.2.0(typescript@5.8.3)(zod@3.25.74): + dependencies: + typescript: 5.8.3 + zod: 3.25.74 + + zod@3.25.74: {} + + zwitch@2.0.4: {} diff --git a/docs/pnpm-workspace.yaml b/docs/pnpm-workspace.yaml new file mode 100644 index 0000000..d0b7dbe --- /dev/null +++ b/docs/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +onlyBuiltDependencies: + - esbuild + - sharp diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 0000000..a24ca8b --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/face.svg b/docs/src/assets/face.svg new file mode 100644 index 0000000..a24ca8b --- /dev/null +++ b/docs/src/assets/face.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/icon.svg b/docs/src/assets/icon.svg new file mode 100644 index 0000000..77253df --- /dev/null +++ b/docs/src/assets/icon.svg @@ -0,0 +1 @@ + diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts new file mode 100644 index 0000000..d9ee8c9 --- /dev/null +++ b/docs/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from 'astro:content'; +import { docsLoader } from '@astrojs/starlight/loaders'; +import { docsSchema } from '@astrojs/starlight/schema'; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md b/docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md new file mode 100644 index 0000000..61bea66 --- /dev/null +++ b/docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md @@ -0,0 +1,121 @@ +--- +title: Make Creepers and Zombies drop tnt +--- + +There are two ways to go about doing this, depending on how you want the tnt to drop. +You can either add them in a pool that's separate from the existing drops or do it in the same pool. + +But what's the difference? +In a loot table, each pool generates drops by choosing one or multiple entries from itself. +Adding the tnt in a separate pool means that the tnt will *always* drop no matter what other drops are in the table. +Adding the tnt to an existing pool means that the tnt is one of the options that the game may drop, among the normal drops. + +## In a separate pool +```json +{ + "actions": [ + { + "type": "loot-table-modifier:pool_add", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:tnt" + } + ] + } + ] + } + ], + "predicate": { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/creeper", + "minecraft:entities/zombie" + ] + } +} +``` + +### Explanation + +The action [`pool_add`](/reference/actions#add-pool) adds the pool to the matched tables. + +The predicate [`table`](/reference/predicates#loot-table) matches either the creeper or zombie loot tables. + +## In an existing pool +```json +{ + "actions": [ + { + "type": "loot-table-modifier:entry_add", + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:tnt" + } + ] + } + ], + "predicate": { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/creeper", + "minecraft:entities/zombie" + ] + } +} +``` + +### Explanation + +The action [`entry_add`](/reference/actions#add-entry) adds the entry to a pool. + +The predicate [`table`](/reference/predicates#loot-table) matches either the creeper or zombie loot tables. + +The action will only be applied on the first pool it finds in the target table, so you may want to use a predicate like below to specify which pool it should be added to. +(Also, if adding some configuration to how many pools it would try matching or whatever would be useful for your use case, hit me up on [discord](https://discord.offsetmonkey538.top)) +```json {"Creeper predicate, matches the pool dropping gunpowder":5-20} {"Zombie predicate, matches the pool dropping rotten flesh":22-37} +{ + "predicate": { + "type": "loot-table-modifier:any_of", + "terms": [ + + { + "type": "loot-table-modifier:all_of", + "terms": [ + { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/creeper" + ] + }, + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:gunpowder" + } + ] + }, + + + { + "type": "loot-table-modifier:all_of", + "terms": [ + { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/zombie" + ] + }, + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:rotten_flesh" + } + ] + } + ] + } +} +``` diff --git a/docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md b/docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md new file mode 100644 index 0000000..7e724d7 --- /dev/null +++ b/docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md @@ -0,0 +1,43 @@ +--- +title: Remove glowstone and gunpowder from witches +--- + +For removing an item from a loot table, the [`entry_remove`](/reference/actions#remove-entry) action should be used in most cases. +Using [`pool_remove`](/reference/actions#remove-pool) would remove the whole pool, including all the other entries in there. + +```json {"First part matches the witch table":10-16} {"Second part matches either glowstone dust or gunpowder":18-31} +{ + "actions": [ + { + "type": "loot-table-modifier:entry_remove" + } + ], + "predicate": { + "type": "loot-table-modifier:all_of", + "terms": [ + + { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/witch" + ] + }, + + + { + "type": "loot-table-modifier:any_of", + "terms": [ + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:glowstone_dust" + }, + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:gunpowder" + } + ] + } + ] + } +} +``` diff --git a/docs/src/content/docs/examples/remove_sticks.md b/docs/src/content/docs/examples/remove_sticks.md new file mode 100644 index 0000000..c6fc8ab --- /dev/null +++ b/docs/src/content/docs/examples/remove_sticks.md @@ -0,0 +1,26 @@ +--- +title: Remove sticks +--- + +For removing an item from a loot table, the [`entry_remove`](/reference/actions#remove-entry) action should be used in most cases. +Using [`pool_remove`](/reference/actions#remove-pool) would remove the whole pool, including all the other entries in there. + +```json +{ + "actions": [ + { + "type": "loot-table-modifier:entry_remove" + } + ], + "predicate": { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:stick" + } +} +``` + +### Explanation + +The action [`entry_remove`](../../reference/loot_modifier#add-pool) removes the matched entries from their pools. + +The predicate [`entry_item`](../../reference/loot_modifier#loot-table) matches entries containing a stick. diff --git a/docs/src/content/docs/examples/replace_ingot_w_diamond.md b/docs/src/content/docs/examples/replace_ingot_w_diamond.md new file mode 100644 index 0000000..d024318 --- /dev/null +++ b/docs/src/content/docs/examples/replace_ingot_w_diamond.md @@ -0,0 +1,26 @@ +--- +title: Replace any Minecraft ingot with a diamond +--- + +The following loot modifier will replace any Minecraft ingot item with a diamond: +```json +{ + "actions": { + "type": "loot-table-modifier:entry_item_set", + "name": "minecraft:diamond" + }, + "predicates": { + "type": "loot-table-modifier:entry_item", + "name": { + "regexPattern": "minecraft:.*_ingot" + } + } +} +``` + +### Explanation + +The action [`entry_item_set`](/reference/actions#set-item-in-item-entry) replaces the item in an existing matched item entry. + +The predicate [`entry_item`](/reference/predicates#item-entry) matches specific item entries based on their IDs. It can match using a [regex identifier](/reference/regex_identifier), which allows using regex patterns. +The regex pattern `minecraft:.*_ingot` will match every item that has an identifier that begins with `minecraft:` and ends with `_ingot`. For example `minecraft:iron_ingot` and `minecraft:gold_ingot` will both be matched. diff --git a/docs/src/content/docs/getting_started.md b/docs/src/content/docs/getting_started.md new file mode 100644 index 0000000..7b3924f --- /dev/null +++ b/docs/src/content/docs/getting_started.md @@ -0,0 +1,62 @@ +--- +title: Getting Started +--- + +## Installing +:::note +Currently, as of v2 alpha 1, only **fabric** is supported. Running though [Sinytra Connector](https://modrinth.com/mod/connector) may or may not be possible (please do let me know on [discord](https://discord.offsetmonkey538.top) if you test it :D), but a NeoForge version is planned for the future. +::: + +To use Loot Table Modifier, you first gotta have it installed (I am so damn good at writing useful documentation, no need to thank me 👍) + +When developing a plain datapack, all you need to do is download it from [modrinth](https://modrinth.com/mod/loot-table-modifier) and put it in your mods folder. + +You may also want to show the `Requires` badge in your readme, see below for an example and [here](#badges) for more info. +[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier) + +### Mod developers +Mods can depend on Loot Table Modifier like this: +```groovy +repositories { + // ...rest of repositories block + maven { + name = "OffsetMods538" + url = "https://maven.offsetmonkey538.top/releases" + content { + includeGroup "top.offsetmonkey538.loottablemodifer" + } + } +} + +dependencies { + // ...rest of dependencies block + + implementation "top.offsetmonkey538.loottablemodifier:loot-table-modifier:VERSION_HERE" +} +``` +Make sure to replace `VERSION_HERE` with the actual version you want to use! +See my maven page [here](https://maven.offsetmonkey538.top/#/releases/top/offsetmonkey538/loottablemodifier/loot-table-modifier) for all available versions and their javadocs. + +Also add this to your `fabric.mod.json` file: +```json {" This matches the major version of 2":3-4} +{ + "depends": { + + "loot-table-modifier": ">=2.0.0 <3.0.0" + } +} +``` + +## Development Mode + +Development mode enables additional logging and the ability to export modified loot tables. + +Dev mode will automatically be enabled when Minecraft is launched from an IDE or can be enabled by setting the JVM property `lootTableModifierDev` to `true`. +That can be done with Prism Launcher by going to `Settings`, selecting `Java` and then adding `-DlootTableModifierDev=true` into `JVM arguments`. + +Mod developers who, for some weird reason, *don't* want their logs to be spammed with random unnecessary stuff can set the property to `false` to override the IDE check. + +Modified loot tables can then be exported using the command `/loot-table-modifier debug export`. + +## Next Up +Take a look at how loot modifiers work [here](/reference/loot_modifier) and then take a look at the examples. diff --git a/docs/src/content/docs/index.md b/docs/src/content/docs/index.md new file mode 100644 index 0000000..45c7e75 --- /dev/null +++ b/docs/src/content/docs/index.md @@ -0,0 +1,36 @@ +--- +title: Loot Table Modifier +template: splash +hero: + tagline: Makes it possible for datapacks to modify loot tables, instead of just replacing them. + image: + file: ../../assets/icon.svg + actions: + - text: Get Started + link: /getting_started/ + icon: right-arrow + - text: View on Modrinth + link: https://modrinth.com/mod/loot-table-modifier + icon: external + variant: minimal + - text: Chat with me on Dicord + link: https://discord.offsetmonkey538.top + icon: discord + variant: minimal +--- + +# About + +:::caution +This documentation is written for Loot Table Modifier v2 alpha 1. +Both the mod and this wiki are in development. +If you encounter any issues or have suggestions, please *please* **please** come join my [discord](https://discord.offsetmonkey538.top) and yell at me about them. +::: + + +Vanilla datapacks can only replace existing loot tables, which is not good for being compatible with other datapacks that may also try to replace the same loot tables. +This mod fixes that by implementing a system of *loot modifiers*, which do just that, modify loot (tables)! + +Loot modifiers are in datapacks, meaning mods and modpacks are able to use them too! + +Unlike some mods which replace stuff at runtime, Loot Table Modifier does all of its processing during data loading. This means that the mod will have no performance impact during gameplay! (Except of course if you make a zombie drop a bajillion stacks of diamonds, though that's not the mod's fault...) diff --git a/docs/src/content/docs/reference/actions.md b/docs/src/content/docs/reference/actions.md new file mode 100644 index 0000000..d4a3826 --- /dev/null +++ b/docs/src/content/docs/reference/actions.md @@ -0,0 +1,89 @@ +--- +title: Actions +--- + +:::note +Fields marked as `Optional` have their value set to the default ones. +::: + +Actions tell the mod how a matched loot table, pool or entry should be modified. + +Actions are json objects that consist of their identifier `type` and then any other fields specific to each action. +Below is a list of all currently supported actions: + +| | | +|-----------------------------------------------------------------|--------------------------------------------| +| [`loot-table-modifier:pool_add`](#add-pool) | Adds the provided pools to matched tables | +| [`loot-table-modifier:pool_remove`](#remove-pool) | Removes matched pools | +| [`loot-table-modifier:entry_add`](#add-entry) | Adds the provided entries to matched pools | +| [`loot-table-modifier:entry_remove`](#remove-entry) | Removes matched entries | +| [`loot-table-modifier:entry_item_set`](#set-item-in-item-entry) | Sets the item in matched item entries | + +### Add pool +```json +{ + "type": "loot-table-modifier:pool_add", + "pools": [ + { + /* Loot pool */ + }, + { + /* Loot pool */ + } + ] +} +``` +This action adds the provided loot pools to matched tables. + +See: [Make Creepers and Zombies drop tnt](/examples/creepers_and_zombies_drop_tnt) + +### Remove pool +```json +{ + "type": "loot-table-modifier:pool_remove" +} +``` +This action removes all matched pools. + +### Add entry +```json +{ + "type": "loot-table-modifier:entry_add", + "entries": [ + { + /* Loot entry */ + }, + { + /* Loot entry */ + } + ] +} +``` +This action adds the provided loot entries to matched pools. + +See: [Make Creepers and Zombies drop tnt](/examples/creepers_and_zombies_drop_tnt) + +### Remove entry +```json +{ + "type": "loot-table-modifier:entry_remove" +} +``` +This action removes all matched entries. + +See: [Remove sticks](/examples/remove_sticks), [Remove glowstone and gunpowder from witches](/examples/remove_glowstone_and_gunpowder_witches) + +### Set item in item entry +```json {" Optional":4-5} +{ + "type": "loot-table-modifier:entry_item_set", + "name": /* Identifier of an item */, + + "canReplaceEntry": false +} +``` +This action will replace the item in a matched item entry with the provided item. +If `canReplaceEntry` is enabled, any other matched entry will be replaced with an item entry containing the provided item. +By default, it will not replace other types of entries, but that can be enabled by setting `canReplaceEntry` to true. + +See: [Replace any Minecraft ingot with a diamond](/examples/replace_ingot_w_diamond) diff --git a/docs/src/content/docs/reference/badges.md b/docs/src/content/docs/reference/badges.md new file mode 100644 index 0000000..3c6ff4d --- /dev/null +++ b/docs/src/content/docs/reference/badges.md @@ -0,0 +1,21 @@ +--- +title: Badges +--- +Badges are meant for developers to put on their project pages, so users can see that Loot Table Modifier is used or required. +They're based on [Devin's Badges](https://github.com/intergrav/devins-badges). + +The `requires` badge is meant for datapacks or mods which want to display that Loot Table Modifier is required for them to function. +It can be used in Markdown like this: `[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` +And will look like this: +[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier) + +Clicking it will bring the user to the Modrinth page for Loot Table Modifier + +
+ +The `uses` badge is meant for mods or modpacks which want to display that Loot Table Modifier is included. (Honestly not sure why I made this 'cause I can't imagine anyone would do that but uhh sure 😅) +It can be used in Markdown like this: `[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` +And will look like this: +[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier) + +Clicking it will bring the user to the Modrinth page for Loot Table Modifier diff --git a/docs/src/content/docs/reference/loot_modifier.md b/docs/src/content/docs/reference/loot_modifier.md new file mode 100644 index 0000000..a8799ef --- /dev/null +++ b/docs/src/content/docs/reference/loot_modifier.md @@ -0,0 +1,20 @@ +--- +title: Loot Modifier +--- +The mod uses loot modifiers to figure out how and what it should modify. +Loot modifiers are JSON files located in datapacks at `data/namespace/loot-table-modifier/loot_modifier/modifier_name.json`. + +Loot modifiers consist of two parts: the [actions](/reference/actions) and a [predicate](/reference/predicates). + +During data loading, the mod looks through every existing loot table and its pools and entries. +For every entry, pool and table, it will try matching the predicates of loot modifiers. +When a loot modifier's predicate matches, its actions will be executed on the matched entry, pool or table. + +The mod counts less specific predicates as matching everything underneath. +This means that using the [`add_entry`](/reference/actions#add-entry) action with the [`table`](/reference/predicates#loot-table) predicate will add the entries to all pools in the matched table. +The same applies in reverse; a more specific predicate also counts as matching everything above it, meaning that using the [`add_pool`](/reference/actions#add-pool) action with the [`entry_item`](/reference/predicates#item-entry) predicate will add the pools to all tables that contain a matching item entry. + +## Generation +Loot modifiers can be generated using the misode generator available [here](https://misode-itd7xiyf1-misodes-projects.vercel.app/) under `Modded Generators` + +Mod developers can use the `LootModifierProvider` datagen provider. Javadoc can be seen in-IDE or [here](https://maven.offsetmonkey538.top/#/releases/top/offsetmonkey538/loottablemodifier/loot-table-modifier). diff --git a/docs/src/content/docs/reference/predicates.md b/docs/src/content/docs/reference/predicates.md new file mode 100644 index 0000000..c2ab931 --- /dev/null +++ b/docs/src/content/docs/reference/predicates.md @@ -0,0 +1,84 @@ +--- +title: Predicates +--- + +:::note +Predicates may use this thing I call a `RegexIdentifier`, which you can read about [here](/reference/regex_identifier). +::: + +Predicates tell the mod which loot tables, pools or entries should be modified. +Different predicates can be combined using `invert`, `any_of` and `all_of`. + +Below is a list of all supported predicates: + +| | | +|-------------------------------------------------|---------------------------------------------------| +| [`loot-table-modifier:inverted`](#invert) | Inverts the result of the provided predicate | +| [`loot-table-modifier:any_of`](#any-of) | Matches when any of the provided predicates match | +| [`loot-table-modifier:all_of`](#all-of) | Matches when all of the provided predicates match | +| [`loot-table-modifier:entry_item`](#item-entry) | Matches an item entry based on its identifier | +| [`loot-table-modifier:table`](#loot-table) | Matches a table based on its identifier or type | + +### Invert +```json +{ + "type": "loot-table-modifier:inverted", + "term": { + /* Predicate */ + } +} +``` +This predicate will match when the provided predicate doesn't. A logical `NOT` operation. + +### Any of +```json +{ + "type": "loot-table-modifier:any_of", + "terms": [ + { + /* Predicate */ + }, + { + /* Predicate */ + } + ] +} +``` +This predicate will match when any of the provided predicates match. A logical `OR` operation. + +### All of +```json +{ + "type": "loot-table-modifier:all_of", + "terms": [ + { + /* Predicate */ + }, + { + /* Predicate */ + } + ] +} +``` +This predicate will match when all of the provided predicates match. A logical `AND` operation. + +### Item Entry +```json +{ + "type": "loot-table-modifier:entry_item", + "name": /* RegexIdentifier */ +} +``` +This predicate will match item entries based on their identifiers. + +### Loot Table +```json {" Optional":3-4} {" Optional":5-6} +{ + "type": "loot-table-modifier:table", + + "identifiers": /* RegexIdentifier */, + + "types": /* RegexIdentifier */ +} +``` +This predicate will match loot tables based on their identifiers or types. diff --git a/docs/src/content/docs/reference/regex_identifier.md b/docs/src/content/docs/reference/regex_identifier.md new file mode 100644 index 0000000..3aba7f8 --- /dev/null +++ b/docs/src/content/docs/reference/regex_identifier.md @@ -0,0 +1,39 @@ +--- +title: Regex Identifier +--- + +Identifiers (officially resource locations) are used all over Minecraft for identifying stuff like items, blocks, entities, loot tables and so on. +They consist of a namespace and a path, separated by a colon. Full documentation on identifiers can be found [here](https://minecraft.wiki/w/Resource_location). + +Loot modifier predicates often use regex identifiers for matching these identifiers. +A regex identifier allows matching either a literal identifier or a regex pattern. + +Instead of matching an exact piece of text, regex allows describing a pattern to match many possible variations. +For that, regex uses *metacharacters*. The most useful combination of which for matching identifiers is `.*`, which matches any character from zero to infinite times. +This is very useful for loot modifier predicates because you can build patterns like `minecraft:.*_ingot` for matching any ingot from minecraft. + +Full documentation on supported regex syntax can be found [here](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html), and you can use a site like [regex101](https://regex101.com/) to write and test you regular expressions (just make sure to choose the `Java 8` flavor) + +A `RegexIdentifier` in json can either be an inlined string or an object with the `regexPattern` field. +Examples: +```json {"Matches only the exact identifier of minecraft:diamond_sword":2-3} {"Matches all minecraft sword types (minecraft:stone_sword, minecraft:iron_sword, etc)":5-8} {"Matches anything from minecraft":10-13} {"Matches any minecraft oak block/item, except for the stripped variants":15-18} +{ + + "value1": "minecraft:diamond_sword", + + + "value2": { + "regexPattern": "minecraft:.*_sword" + }, + + + "value3": { + "regexPattern": "minecraft:.*" + }, + + + "value4": { + "regexPattern": "minecraft:oak_.*" + } +} +``` diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css new file mode 100644 index 0000000..7cec2c0 --- /dev/null +++ b/docs/src/styles/custom.css @@ -0,0 +1,37 @@ +/* Dark mode colors. */ +:root { + --sl-color-accent-low: #420d1f; + --sl-color-accent: #c1005c; + --sl-color-accent-high: #f7b3c2; + --sl-color-white: #ffffff; + --sl-color-gray-1: #eceef2; + --sl-color-gray-2: #c6c8ce; + --sl-color-gray-3: #a0a4ae; + --sl-color-gray-4: #545861; + --sl-color-gray-5: #353841; + --sl-color-gray-6: #24272f; + --sl-color-black: #17181c; +} +/* Light mode colors. */ +:root[data-theme='light'] { + --sl-color-accent-low: #fbc7d2; + --sl-color-accent: #a2004c; + --sl-color-accent-high: #60052b; + --sl-color-white: #17181c; + --sl-color-gray-1: #24272f; + --sl-color-gray-2: #353841; + --sl-color-gray-3: #545861; + --sl-color-gray-4: #888b96; + --sl-color-gray-5: #c0c2c7; + --sl-color-gray-6: #eceef2; + --sl-color-gray-7: #f5f6f8; + --sl-color-black: #ffffff; +} + +.site-title > img { + border: 1px solid #420d1f; +} + +.site-title > span { + color: #FFFFFF; +} diff --git a/docs/src/utils/PageFrame.astro b/docs/src/utils/PageFrame.astro new file mode 100644 index 0000000..7f7ab1d --- /dev/null +++ b/docs/src/utils/PageFrame.astro @@ -0,0 +1,98 @@ +--- +import MobileMenuToggle from 'virtual:starlight/components/MobileMenuToggle'; + +const { hasSidebar } = Astro.locals.starlightRoute; +--- + +

+
+ { + hasSidebar && ( + + ) + } +
+
+ + \ No newline at end of file diff --git a/docs/src/utils/SocialIcons.astro b/docs/src/utils/SocialIcons.astro new file mode 100644 index 0000000..b073bdc --- /dev/null +++ b/docs/src/utils/SocialIcons.astro @@ -0,0 +1,57 @@ +--- +import config from "virtual:starlight/user-config"; +// @ts-expect-error - This is a custom Virtual Module +import augment from "virtual:starlight-social-icon-augment"; +import { Icon } from "@astrojs/starlight/components"; +import type { ComponentProps } from "astro/types"; + +type IconProps = ComponentProps; + +const customIcons = { + modrinth: + '', +} as const; + +const combinedLinks: { + main: { label: string; href: string; icon: IconProps["name"] }[]; + custom: { label: string; href: string; icon: keyof typeof customIcons }[]; +} = { main: config.social || [], custom: augment || [] }; +--- + +{ + combinedLinks.main.length > 0 && ( + <> + {combinedLinks.main.map(({ label, href, icon }) => ( + + {label} + + + ))} + + ) +} +{ + combinedLinks.custom.length > 0 && ( + <> + {combinedLinks.custom.map(({ label, href, icon }) => ( + + {label} + + + ))} + + ) +} + + diff --git a/docs/src/utils/socialIcons.ts b/docs/src/utils/socialIcons.ts new file mode 100644 index 0000000..6bef9aa --- /dev/null +++ b/docs/src/utils/socialIcons.ts @@ -0,0 +1,39 @@ +import type { AstroIntegration } from 'astro'; + +export function starlightSocialIcons(socials: { + modrinth: string; +}): AstroIntegration { + const labelMap = { + modrinth: 'Modrinth' + } + + return { + name: 'starlight-social-icon-augment', + hooks: { + 'astro:config:setup': ({ updateConfig }) => { + updateConfig({ + vite: { + plugins: [ + { + name: 'virtual-config-plugin', + resolveId(id) { + if (id === 'virtual:starlight-social-icon-augment') { + return '\0virtual:starlight-social-icon-augment'; // Prefix with \0 to mark as virtual + } + }, + load(id) { + if (id === '\0virtual:starlight-social-icon-augment') { + const config = socials; + return `export default ${JSON.stringify(Object.entries(config).flatMap(([key , value]) => ({ label: labelMap[key as keyof typeof labelMap], href: value, icon: key })))};`; + } + }, + } + ] + } + }) + }, + }, + }; +} + +export default starlightSocialIcons; \ No newline at end of file diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..8bf91d3 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +} From 6f4400d2828e12c80c4646680a5a7ac871a2a722 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 21 Jul 2025 00:28:43 +0300 Subject: [PATCH 48/53] Fix borked example --- .../docs/examples/replace_ingot_w_diamond.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/src/content/docs/examples/replace_ingot_w_diamond.md b/docs/src/content/docs/examples/replace_ingot_w_diamond.md index d024318..6dc9b0d 100644 --- a/docs/src/content/docs/examples/replace_ingot_w_diamond.md +++ b/docs/src/content/docs/examples/replace_ingot_w_diamond.md @@ -5,11 +5,14 @@ title: Replace any Minecraft ingot with a diamond The following loot modifier will replace any Minecraft ingot item with a diamond: ```json { - "actions": { - "type": "loot-table-modifier:entry_item_set", - "name": "minecraft:diamond" - }, - "predicates": { + "actions": + [ + { + "type": "loot-table-modifier:entry_item_set", + "name": "minecraft:diamond" + } + ], + "predicate": { "type": "loot-table-modifier:entry_item", "name": { "regexPattern": "minecraft:.*_ingot" From 83ea5883916ccfc648251774ffa33f4eddbff2c4 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 21 Jul 2025 00:33:34 +0300 Subject: [PATCH 49/53] Build on 1.21.8 and set supported version for this alpha 1.21.1 support will definitely come, 1.20.5 I'm not sure on. --- gradle.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0c86fe9..9e35644 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,12 +4,12 @@ org.gradle.parallel = true # Fabric # Check at https://fabricmc.net/develop -minecraft_version = 1.21.5 +minecraft_version = 1.21.8 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.128.1+1.21.5 -yarn_version = 1.21.5+build.1 +fapi_version = 0.129.0+1.21.8 +yarn_version = 1.21.8+build.1 loader_version = 0.16.14 # Dependencies @@ -18,4 +18,4 @@ devauth_version = 1.2.1 # Mod Properties mod_version = 2.0.0-alpha.1 -supported_minecraft_versions = >=1.20.5 <=1.21.5 +supported_minecraft_versions = >=1.21.5 <=1.21.8 From c9e025a5c80abd4d8581343f899dcaa99763d9e5 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 21 Jul 2025 00:45:05 +0300 Subject: [PATCH 50/53] Use current codec on left side of either. Errors are now actually informative and aren't just "oo me dumbass and no find pools and modifies ooo thats what you should definitely be using" --- .../loottablemodifier/api/resource/LootModifier.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java index ff5a4fb..d3a3647 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -43,9 +43,9 @@ public record LootModifier(@NotNull @UnmodifiableView List a ).apply(instance, LootModifier::new)); public static final Codec CODEC = Codec.either( - LEGACY_CODEC, - CURRENT_CODEC - ).xmap(either -> either.map(it -> it, it -> it), Either::right); // Always encode as current codec, which is on the right. + CURRENT_CODEC, + LEGACY_CODEC + ).xmap(either -> either.map(it -> it, it -> it), Either::left); // Always encode as current codec, which is on the left. @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // from the codec private static @NotNull List getActionsFromLegacyCodec(@NotNull Optional> pools, @NotNull Optional> lootPools) { From 694ecdee368b69655aa2972af221e01c5c41ff2d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 21 Jul 2025 00:45:15 +0300 Subject: [PATCH 51/53] readme --- README.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b619145..ef44504 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,19 @@ [![modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/mod/loot-table-modifier) [![Requires Fabric API](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/requires/fabric-api_vector.svg)](https://modrinth.com/mod/fabric-api) +todo: good readme for alpha version + +Allows datapacks to modify loot tables, instead of just overwriting them. +Version 2 of the mod is currently in alpha. v2 adds more ways to modify loot tables than just adding to them +v2 will be backwards-compatible with v1 modifiers, so no need to worry about them breaking. + +If you want to use v1, then see the original description below. +If you do decide to try out the alpha version of v2 (please do), then please go ahead and read the documentation [here](https://loot-table-modifier.docs.offsetmonkey538.top/) and if you encounter any problems, have suggestions for new actions/predicates or just want to say literally anything about the mod, please please please join my discord and tell me about it. I want to make this as good as I can and any sort of feedback really helps. + + + +## Original Description + Allows datapacks (and thus mods as well) to add to loot tables, instead of just overwriting them. This mod shouldn't impact performance while playing the game, but only when datapacks are reloading (joining a world, starting a server, `/reload` command, whatever else). @@ -69,21 +82,3 @@ dependencies { modImplementation "top.offsetmonkey538.loottablemodifier:loot-table-modifier:1.0.1+1.21.1" } ``` - -## Badges -###### No need to use them if you don't want to -Badges are meant for developers to put on their project pages, so users can see that Loot Table Modifier is used or requires. -They're based on [Devin's Badges](https://github.com/intergrav/devins-badges). - -The `uses` badge is meant for mods or modpacks which want to display that Loot Table Modifier is included. -It can be used in Markdown like this: `[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` -And will look like this: -[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier) -Clicking it will bring the user to the Modrinth page for Loot Table Modifier - - -The `requires` badge is meant for datapacks which want to display that Loot Table Modifier is required for the pack to function. -It can be used in Markdown like this: `[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` -And will look like this: -[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier) -Clicking it will bring the user to the Modrinth page for Loot Table Modifier \ No newline at end of file From 4e4c5ca2927cf5c21f6dbd18bdc7f7887b19c581 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 21 Jul 2025 00:51:29 +0300 Subject: [PATCH 52/53] Publish docs as a separate workflow --- .github/workflows/build_artifacts.yml | 42 +------------------ .github/workflows/docs.yml | 58 +++++++++++++++++++++++++++ .github/workflows/publish.yml | 42 +------------------ 3 files changed, 60 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index dd6eef3..ee8577c 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -7,7 +7,7 @@ on: jobs: - build-mod: + build: runs-on: ubuntu-latest steps: @@ -52,43 +52,3 @@ jobs: with: name: Artifacts path: build/libs/ - - publish-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Store short commit hash - run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" - - - uses: danielr1996/envsubst-action@1.1.0 - env: - URL_PREFIX: staging- - SHORT_COMMIT_HASH: ${{ env.short_commit_hash }} - with: - input: docker-compose-template.yml - output: docker-compose.yml - - - name: Login to container registry - uses: docker/login-action@v3 - with: - registry: registry.oracle.offsetmonkey538.top - username: ${{ secrets.REGISTRY_USER }} - password: ${{ secrets.REGISTRY_PASSWORD }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: true - tags: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${{ env.short_commit_hash }} - - - run: | - tar -czf archive.tar.gz docker-compose.yml && curl -i -X POST -H "Authorization: Bearer $SEELF_TOKEN" -F environment=$SEELF_ENVIRONMENT -F archive=@archive.tar.gz $SEELF_APPLICATION_URL - env: - SEELF_TOKEN: ${{ secrets.SEELF_TOKEN }} - SEELF_ENVIRONMENT: staging - SEELF_APPLICATION_URL: https://seelf.oracle.offsetmonkey538.top/api/v1/apps/300qBnG3t1dOFrUGfw1EeWYwKYA/deployments diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..535f0e6 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,58 @@ +name: Publish docs + +on: + push: + branches: + - "*" + release: + types: [published] + + +jobs: + publish-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set url prefix + run: | + if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + echo "url_prefix=staging-" >> "$GITHUB_ENV" + else + echo "url_prefix=" >> "$GITHUB_ENV" + fi + + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + + - uses: danielr1996/envsubst-action@1.1.0 + env: + URL_PREFIX: ${{ env.url_prefix }} + SHORT_COMMIT_HASH: ${{ env.short_commit_hash }} + with: + input: docker-compose-template.yml + output: docker-compose.yml + + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: registry.oracle.offsetmonkey538.top + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${{ env.short_commit_hash }} + + - run: | + tar -czf archive.tar.gz docker-compose.yml && curl -i -X POST -H "Authorization: Bearer $SEELF_TOKEN" -F environment=$SEELF_ENVIRONMENT -F archive=@archive.tar.gz $SEELF_APPLICATION_URL + env: + SEELF_TOKEN: ${{ secrets.SEELF_TOKEN }} + SEELF_ENVIRONMENT: staging + SEELF_APPLICATION_URL: https://seelf.oracle.offsetmonkey538.top/api/v1/apps/300qBnG3t1dOFrUGfw1EeWYwKYA/deployments diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2a7e0e1..67250bb 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,7 +6,7 @@ on: jobs: - publish-mod: + publish: runs-on: ubuntu-latest permissions: @@ -62,43 +62,3 @@ jobs: CUSTOM_VERSION: ${{ env.short_commit_hash }} MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} - - publish-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Store short commit hash - run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" - - - uses: danielr1996/envsubst-action@1.1.0 - env: - URL_PREFIX: "" - SHORT_COMMIT_HASH: ${{ env.short_commit_hash }} - with: - input: docker-compose-template.yml - output: docker-compose.yml - - - name: Login to container registry - uses: docker/login-action@v3 - with: - registry: registry.oracle.offsetmonkey538.top - username: ${{ secrets.REGISTRY_USER }} - password: ${{ secrets.REGISTRY_PASSWORD }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: true - tags: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${{ env.short_commit_hash }} - - - run: | - tar -czf archive.tar.gz docker-compose.yml && curl -i -X POST -H "Authorization: Bearer $SEELF_TOKEN" -F environment=$SEELF_ENVIRONMENT -F archive=@archive.tar.gz $SEELF_APPLICATION_URL - env: - SEELF_TOKEN: ${{ secrets.SEELF_TOKEN }} - SEELF_ENVIRONMENT: staging - SEELF_APPLICATION_URL: https://seelf.oracle.offsetmonkey538.top/api/v1/apps/300qBnG3t1dOFrUGfw1EeWYwKYA/deployments From 22ce48c3557442c4f46e9ce2bb5b7c1b6a30822d Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 21 Jul 2025 00:53:50 +0300 Subject: [PATCH 53/53] pushes should go to staging --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 535f0e6..c1778bf 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: - name: Set url prefix run: | - if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + if [[ "${{ github.event_name }}" == "push" || "${{ github.event.release.prerelease }}" == "true" ]]; then echo "url_prefix=staging-" >> "$GITHUB_ENV" else echo "url_prefix=" >> "$GITHUB_ENV"