aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/shcm/shsupercm
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/shcm/shsupercm')
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/ActiveCITs.java187
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/CITResewn.java44
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/CITResewnCommand.java147
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/OptionalCompat.java69
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/api/CITConditionContainer.java42
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/api/CITDisposable.java18
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/api/CITGlobalProperties.java26
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/api/CITTypeContainer.java87
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/ActiveCITs.java106
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/CIT.java61
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCache.java93
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCondition.java63
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITContext.java76
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITRegistry.java144
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITType.java109
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/BooleanCondition.java41
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ConstantCondition.java31
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/DoubleCondition.java157
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/EnumCondition.java77
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/FloatCondition.java157
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IdentifierCondition.java42
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IntegerCondition.java157
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ListCondition.java116
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/LongCondition.java157
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/WeightCondition.java32
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/config/BrokenPaths.java23
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfig.java47
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfigScreenFactory.java47
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnMixinConfiguration.java32
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnModMenu.java14
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITLoadException.java13
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParseException.java13
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParsingException.java17
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/ex/UnknownCITTypeException.java12
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ChatScreenMixin.java38
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ModelLoaderMixin.java29
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/AbstractFileResourcePackMixin.java18
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/IdentifierMixin.java21
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ReloadableResourceManagerImplMixin.java16
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ResourcePackCompatibilityMixin.java10
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ArmorFeatureRendererMixin.java59
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ItemStackMixin.java25
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ElytraFeatureRendererMixin.java60
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ItemStackMixin.java25
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ArmorFeatureRendererMixin.java33
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/BufferBuilderStorageAccessor.java15
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ElytraFeatureRendererMixin.java33
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemRendererMixin.java86
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemStackMixin.java36
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/MinecraftClientMixin.java17
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/RenderPhaseAccessor.java21
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemRendererMixin.java69
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemStackMixin.java40
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/JsonUnbakedModelAccessor.java22
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ModelLoaderMixin.java165
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/compat/lambdabettergrass/PackParserMixin.java26
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ChatScreenMixin.java23
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/GroupResourcePackAccessor.java14
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ModelLoaderMixin.java51
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/NbtCompoundAccessor.java14
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/SpriteAtlasTextureMixin.java18
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ZipResourcePackMixin.java60
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITPack.java65
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITParser.java119
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/GlobalProperties.java72
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/PackParser.java128
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnItemModelIdentifier.java18
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnTextureIdentifier.java14
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CIT.java435
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITArmor.java42
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITElytra.java28
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITEnchantment.java371
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITItem.java463
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertiesGroupAdapter.java101
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyGroup.java157
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyKey.java31
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertySeparator.java21
-rw-r--r--src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyValue.java20
-rw-r--r--src/main/java/shcm/shsupercm/util/logic/Loops.java60
79 files changed, 2802 insertions, 2844 deletions
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/ActiveCITs.java b/src/main/java/shcm/shsupercm/fabric/citresewn/ActiveCITs.java
deleted file mode 100644
index 8d56a35..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/ActiveCITs.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package shcm.shsupercm.fabric.citresewn;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ArmorItem;
-import net.minecraft.item.Item;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.util.Hand;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.registry.Registry;
-import net.minecraft.world.World;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-import shcm.shsupercm.fabric.citresewn.pack.cits.*;
-
-import java.util.*;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-public class ActiveCITs {
- public final List<CITPack> packs;
- public final CITPack effectiveGlobalProperties = new CITPack(null);
-
- public final List<CIT> cits;
-
- public final Map<Item, List<CITItem>> citItems = new HashMap<>();
- public final Map<ArmorItem, List<CITArmor>> citArmor = new HashMap<>();
- public final List<CITElytra> citElytra = new ArrayList<>();
- public final List<List<CITEnchantment>> citEnchantments = new ArrayList<>();
-
-
- public ActiveCITs(List<CITPack> packs, List<CIT> cits) {
- this.packs = packs;
- this.cits = cits;
-
- for (CITPack pack : packs)
- effectiveGlobalProperties.loadGlobalProperties(pack);
- for (CITPack pack : packs)
- pack.loadGlobalProperties(effectiveGlobalProperties);
-
- Map<Integer, List<CITEnchantment>> citEnchantmentLayers = new TreeMap<>(); // order citEnchantments by layers
-
- for (CIT cit : cits.stream().sorted(Comparator.<CIT>comparingInt(cit -> cit.weight).reversed().thenComparing(cit -> cit.propertiesIdentifier.toString())).collect(Collectors.toList())) {
- if (cit instanceof CITItem item)
- for (Item type : item.items)
- citItems.computeIfAbsent(type, t -> new ArrayList<>()).add(item);
- else if (cit instanceof CITArmor armor)
- for (Item type : armor.items)
- if (type instanceof ArmorItem armorType)
- citArmor.computeIfAbsent(armorType, t -> new ArrayList<>()).add(armor);
- else
- CITResewn.logErrorLoading("Ignoring item type: " + Registry.ITEM.getId(type) + " is not armor in " + cit.pack.resourcePack.getName() + " -> " + cit.propertiesIdentifier.toString());
- else if (cit instanceof CITElytra elytra)
- citElytra.add(elytra);
- else if (cit instanceof CITEnchantment enchantment)
- citEnchantmentLayers.computeIfAbsent(enchantment.layer, l -> new ArrayList<>()).add(enchantment);
- }
-
- for (List<CITEnchantment> layer : citEnchantmentLayers.values()) {
- for (CITEnchantment enchantment : layer)
- enchantment.activate();
- citEnchantments.add(layer);
- }
- }
-
- public void dispose() {
- for (CIT cit : cits)
- cit.dispose();
- cits.clear();
- citItems.clear();
- citArmor.clear();
- citElytra.clear();
- citEnchantments.clear();
- }
-
- public CITItem getCITItem(ItemStack stack, World world, LivingEntity entity) {
- Hand hand = entity != null && stack == entity.getOffHandStack() ? Hand.OFF_HAND : Hand.MAIN_HAND;
-
- ((CITItem.Cached) (Object) stack).citresewn_setMojankCIT(false);
-
- List<CITItem> citItems = this.citItems.get(stack.getItem());
- if (citItems != null)
- for (CITItem citItem : citItems)
- if (citItem.test(stack, hand, world, entity, true)) {
- if (stack.isOf(Items.TRIDENT) || stack.isOf(Items.SPYGLASS))
- ((CITItem.Cached) (Object) stack).citresewn_setMojankCIT(true);
- return citItem;
- }
- return null;
- }
-
- public CITElytra getCITElytra(ItemStack stack, World world, LivingEntity livingEntity) {
- for (CITElytra citElytra : citElytra)
- if (citElytra.test(stack, Hand.MAIN_HAND, world, livingEntity, true))
- return citElytra;
- return null;
- }
-
- public CITArmor getCITArmor(ItemStack stack, World world, LivingEntity livingEntity) {
- Item item = stack.getItem();
- if (item instanceof ArmorItem) {
- List<CITArmor> citArmor = this.citArmor.get(item);
- if (citArmor != null)
- for (CITArmor armor : citArmor)
- if (armor.test(stack, null, world, livingEntity, true))
- return armor;
- }
- return null;
- }
-
- public List<CITEnchantment> getCITEnchantment(ItemStack stack, World world, LivingEntity livingEntity) {
- Hand hand = livingEntity != null && stack == livingEntity.getOffHandStack() ? Hand.OFF_HAND : Hand.MAIN_HAND;
-
- List<CITEnchantment> applied = new ArrayList<>();
-
- for (List<CITEnchantment> layer : this.citEnchantments)
- for (CITEnchantment cit : layer)
- if (cit.test(stack, hand, world, livingEntity, false)) {
- applied.add(cit);
- break;
- }
-
- return applied;
- }
-
- public BakedModel getItemModelCached(ItemStack stack, World world, LivingEntity entity, int seed) {
- BakedModel bakedModel = null;
-
- Supplier<CITItem> realtime = () -> getCITItem(stack, world, entity);
-
- //noinspection ConstantConditions
- CITItem citItem = CITResewnConfig.INSTANCE().cache_ms == 0 ? realtime.get() : ((CITItem.Cached) (Object) stack).citresewn_getCachedCITItem(realtime);
-
- if (citItem != null)
- bakedModel = citItem.getItemModel(stack, (ClientWorld) world, entity, seed);
-
- return bakedModel;
- }
-
- public Identifier getElytraTextureCached(ItemStack stack, World world, LivingEntity livingEntity) {
- Supplier<CITElytra> realtime = () -> getCITElytra(stack, world, livingEntity);
-
- //noinspection ConstantConditions
- CITElytra citElytra = CITResewnConfig.INSTANCE().cache_ms == 0 ? realtime.get() : ((CITElytra.Cached) (Object) stack).citresewn_getCachedCITElytra(realtime);
-
- if (citElytra != null)
- return citElytra.textureIdentifier;
-
- return null;
- }
-
- public Map<String, Identifier> getArmorTexturesCached(ItemStack stack, World world, LivingEntity livingEntity) {
- Supplier<CITArmor> realtime = () -> getCITArmor(stack, world, livingEntity);
-
- //noinspection ConstantConditions
- CITArmor citArmor = CITResewnConfig.INSTANCE().cache_ms == 0 ? realtime.get() : ((CITArmor.Cached) (Object) stack).citresewn_getCachedCITArmor(realtime);
-
- if (citArmor != null)
- return citArmor.textures;
-
- return null;
- }
-
- public void setEnchantmentAppliedContextCached(ItemStack stack, World world, LivingEntity entity) {
- if (stack == null) {
- CITEnchantment.appliedContext = null;
- return;
- }
-
- Supplier<List<CITEnchantment>> realtime = () -> getCITEnchantment(stack, world, entity);
-
- //noinspection ConstantConditions
- List<CITEnchantment> citEnchantments = CITResewnConfig.INSTANCE().cache_ms == 0 ? realtime.get() : ((CITEnchantment.Cached) (Object) stack).citresewn_getCachedCITEnchantment(realtime);
-
- if (citEnchantments == null || citEnchantments.isEmpty()) {
- CITEnchantment.appliedContext = null;
- return;
- }
-
- if (effectiveGlobalProperties.method != null)
- effectiveGlobalProperties.method.applyMethod(citEnchantments, stack);
-
- CITEnchantment.appliedContext = citEnchantments;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewn.java b/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewn.java
index e53ba52..7d4be88 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewn.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewn.java
@@ -1,45 +1,55 @@
package shcm.shsupercm.fabric.citresewn;
+import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint;
import net.fabricmc.api.ClientModInitializer;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
+import net.fabricmc.loader.api.FabricLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
+import shcm.shsupercm.fabric.citresewn.cit.CITRegistry;
-@Environment(EnvType.CLIENT)
+/**
+ * Main initializer for CIT Resewn. Contains various internal utilities(just logging for now).
+ */
public class CITResewn implements ClientModInitializer {
public static final Logger LOG = LogManager.getLogger("CITResewn");
- public static CITResewn INSTANCE;
-
- public ActiveCITs activeCITs = null;
-
- public CITResewnConfig config = null;
-
-
- public boolean processingBrokenPaths = false;
+ @Entrypoint(Entrypoint.CLIENT)
+ public static final CITResewn INSTANCE = new CITResewn();
@Override
public void onInitializeClient() {
- INSTANCE = this;
-
- config = CITResewnConfig.read();
+ CITRegistry.registerAll();
- CITResewnCommand.register();
+ if (FabricLoader.getInstance().isModLoaded("fabric-command-api-v1"))
+ CITResewnCommand.register();
}
+ /**
+ * Logs an info line in CIT Resewn's name.
+ * @param message log message
+ */
public static void info(String message) {
LOG.info("[citresewn] " + message);
}
+ /**
+ * Logs a warning line in CIT Resewn's name if enabled in config.
+ * @see CITResewnConfig#mute_warns
+ * @param message warn message
+ */
public static void logWarnLoading(String message) {
- if (CITResewnConfig.INSTANCE().mute_warns)
+ if (CITResewnConfig.INSTANCE.mute_warns)
return;
LOG.error("[citresewn] " + message);
}
+ /**
+ * Logs an error line in CIT Resewn's name if enabled in config.
+ * @see CITResewnConfig#mute_errors
+ * @param message error message
+ */
public static void logErrorLoading(String message) {
- if (CITResewnConfig.INSTANCE().mute_errors)
+ if (CITResewnConfig.INSTANCE.mute_errors)
return;
LOG.error("{citresewn} " + message);
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewnCommand.java b/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewnCommand.java
index 483cbfb..e07a772 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewnCommand.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/CITResewnCommand.java
@@ -1,33 +1,160 @@
package shcm.shsupercm.fabric.citresewn;
+import com.mojang.brigadier.LiteralMessage;
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.arguments.ArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
+import com.mojang.brigadier.suggestion.Suggestions;
+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager;
import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.text.Text;
+import shcm.shsupercm.fabric.citresewn.cit.*;
import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static net.fabricmc.fabric.api.client.command.v1.ClientCommandManager.argument;
import static net.fabricmc.fabric.api.client.command.v1.ClientCommandManager.literal;
import static net.minecraft.text.Text.of;
+/**
+ * Logic for the /citresewn client command. Only enabled when Fabric API is present.<br>
+ * Structure:
+ * <pre>
+ * /citresewn - General info command
+ * /citresewn config - Opens the config gui(only when Cloth Config is present)
+ * /citresewn analyze pack &lt;pack&gt; - Displays data for the given loaded cit pack.
+ * </pre>
+ */
public class CITResewnCommand {
+ /**
+ * @see shcm.shsupercm.fabric.citresewn.mixin.ChatScreenMixin
+ */
public static boolean openConfig = false;
+ /**
+ * Registers all of CIT Resewn's commands.
+ */
public static void register() {
ClientCommandManager.DISPATCHER.register(literal("citresewn")
- .executes(context -> {
- context.getSource().sendFeedback(of("CIT Resewn v" + FabricLoader.getInstance().getModContainer("citresewn").get().getMetadata().getVersion() + ":"));
- boolean active = CITResewnConfig.INSTANCE().enabled && CITResewn.INSTANCE.activeCITs != null;
- context.getSource().sendFeedback(of(" Active: " + (active ? "yes" : "no")));
+ .executes(context -> { //citresewn
+ context.getSource().sendFeedback(of("CIT Resewn v" + FabricLoader.getInstance().getModContainer("citresewn").orElseThrow().getMetadata().getVersion() + ":"));
+ context.getSource().sendFeedback(of(" Registered: " + CITRegistry.TYPES.values().stream().distinct().count() + " types and " + CITRegistry.CONDITIONS.values().stream().distinct().count() + " conditions"));
+
+ final boolean active = CITResewnConfig.INSTANCE.enabled && ActiveCITs.isActive();
+ context.getSource().sendFeedback(of(" Active: " + (active ? "yes" : ("no, " + (CITResewnConfig.INSTANCE.enabled ? "no cit packs loaded" : "disabled in config")))));
if (active) {
- context.getSource().sendFeedback(of(" Loaded: " + CITResewn.INSTANCE.activeCITs.cits.size() + " CITs from " + CITResewn.INSTANCE.activeCITs.packs.size() + " resourcepacks"));
+ context.getSource().sendFeedback(of(" Loaded: " + ActiveCITs.getActive().cits.values().stream().mapToLong(Collection::size).sum() + " CITs from " + ActiveCITs.getActive().cits.values().stream().flatMap(Collection::stream).map(cit -> cit.packName).distinct().count() + " resourcepacks"));
}
- context.getSource().sendFeedback(of(" "));
+ context.getSource().sendFeedback(of(""));
return 1;
})
.then(literal("config")
- .executes(context -> {
- openConfig = true;
+ .executes(context -> { //citresewn config
+ openConfig = true;
+
+ return 1;
+ }))
+ .then(literal("analyze")
+ .then(literal("pack")
+ .then(argument("pack", new LoadedCITPackArgument())
+ .executes(context -> { //citresewn analyze <pack>
+ final String pack = context.getArgument("pack", String.class);
+ if (ActiveCITs.isActive()) {
+ context.getSource().sendFeedback(of("Analyzed CIT data of \"" + pack + "\u00a7r\":"));
+
+ List<Text> builder = new ArrayList<>();
+
+ for (Map.Entry<PropertyKey, Set<PropertyValue>> entry : ActiveCITs.getActive().globalProperties.properties.entrySet())
+ for (PropertyValue value : entry.getValue())
+ if (value.packName().equals(pack))
+ builder.add(of(" " + entry.getKey().toString() + (value.keyMetadata() == null ? "" : "." + value.keyMetadata()) + " = " + value.value()));
+ if (!builder.isEmpty()) {
+ context.getSource().sendFeedback(of(" Global Properties:"));
+ for (Text text : builder)
+ context.getSource().sendFeedback(text);
+
+ builder.clear();
+ }
+
+ for (Map.Entry<Class<? extends CITType>, List<CIT<?>>> entry : ActiveCITs.getActive().cits.entrySet())
+ if (!entry.getValue().isEmpty()) {
+ long count = entry.getValue().stream().filter(cit -> cit.packName.equals(pack)).count();
+ if (count > 0)
+ builder.add(of(" " + CITRegistry.idOfType(entry.getKey()).toString() + " = " + count));
+ }
+ if (!builder.isEmpty()) {
+ context.getSource().sendFeedback(of(" Types:"));
+ for (Text text : builder)
+ context.getSource().sendFeedback(text);
+
+ builder.clear();
+ }
+
+ List<CITCondition> conditions = ActiveCITs.getActive().cits.values().stream()
+ .flatMap(Collection::stream)
+ .filter(cit -> cit.packName.equals(pack))
+ .flatMap(cit -> Arrays.stream(cit.conditions))
+ .toList();
+ if (!conditions.isEmpty())
+ context.getSource().sendFeedback(of(" Utilizing " + conditions.size() + " conditions(" + conditions.stream().map(Object::getClass).distinct().count() + " unique condition types)"));
+ } else
+ context.getSource().sendFeedback(of("Not active"));
+
+ return 1;
+ })
+ )
+ )
+ )
+ );
+ }
+
+ /**
+ * Greedy string argument that is limited to cit pack names loaded in {@link shcm.shsupercm.fabric.citresewn.cit.ActiveCITs}.
+ */
+ private static class LoadedCITPackArgument implements ArgumentType<String> {
+ @Override
+ public String parse(StringReader reader) throws CommandSyntaxException {
+ StringBuilder builder = new StringBuilder();
+ while (reader.canRead())
+ builder.append(reader.read());
+
+ String pack = builder.toString().trim();
+
+ if (!getPacks().contains(pack)) {
+ LiteralMessage message = new LiteralMessage("Could not find CIT pack");
+ throw new CommandSyntaxException(new SimpleCommandExceptionType(message), message);
+ }
+
+ return pack;
+ }
+
+ @Override
+ public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
+ return CompletableFuture.supplyAsync(() -> {
+ for (String pack : getPacks()) {
+ builder.suggest(pack);
+ }
+ return builder.build();
+ });
+ }
- return 1;
- })));
+ private static Set<String> getPacks() {
+ if (ActiveCITs.isActive())
+ return ActiveCITs.getActive().cits.values().stream()
+ .flatMap(Collection::stream)
+ .map(cit -> cit.packName)
+ .collect(Collectors.toSet());
+ else
+ return Collections.emptySet();
+ }
}
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/OptionalCompat.java b/src/main/java/shcm/shsupercm/fabric/citresewn/OptionalCompat.java
deleted file mode 100644
index 580aef5..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/OptionalCompat.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package shcm.shsupercm.fabric.citresewn;
-
-import io.github.apace100.cosmetic_armor.CosmeticArmor;
-import net.fabricmc.loader.api.FabricLoader;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.screen.NoticeScreen;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.entity.EquipmentSlot;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.text.Text;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfigScreenFactory;
-
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-public final class OptionalCompat {
- private static final OptionalCompat INSTANCE = new OptionalCompat(s -> FabricLoader.getInstance().isModLoaded(s));
-
- public final CompatClothConfig compatClothConfig;
-
- public final CompatCosmeticArmor compatCosmeticArmor;
-
- private OptionalCompat(Predicate<String> isLoaded) {
- compatClothConfig = isLoaded.test("cloth-config2") ? CompatClothConfig.impl() : null;
- compatCosmeticArmor = isLoaded.test("cosmetic-armor") ? CompatCosmeticArmor.impl() : null;
- }
-
- public static Function<Screen, Screen> getModConfigScreenFactory() {
- if (INSTANCE.compatClothConfig != null) {
- return INSTANCE.compatClothConfig.getModConfigScreenFactory();
- }
-
- return parent -> new NoticeScreen(() -> MinecraftClient.getInstance().setScreen(parent), Text.of("CIT Resewn"), Text.of("CIT Resewn requires Cloth Config 2 to be able to show the config."));
- }
-
- public static ItemStack getCosmeticArmor(ItemStack original, LivingEntity entity, EquipmentSlot slot, boolean elytra) {
- if (INSTANCE.compatCosmeticArmor != null) {
- ItemStack stackInCosmeticSlot = INSTANCE.compatCosmeticArmor.getStackInCosmeticSlot(entity, slot);
- if (!stackInCosmeticSlot.isEmpty() && (!elytra || stackInCosmeticSlot.isOf(Items.ELYTRA)))
- return stackInCosmeticSlot;
- }
-
- return original;
- }
-
- /**
- * Compatibility with 'cloth-config2': Custom gui for CITResewn's config
- */
- public interface CompatClothConfig {
- private static CompatClothConfig impl() {
- return () -> CITResewnConfigScreenFactory::create;
- }
-
- Function<Screen, Screen> getModConfigScreenFactory();
- }
-
- /**
- * Compatibility with 'cosmetic-armor': Display cits for cosmetic armors instead of equipped armors
- */
- public interface CompatCosmeticArmor {
- private static CompatCosmeticArmor impl() {
- return CosmeticArmor::getCosmeticArmor;
- }
-
- ItemStack getStackInCosmeticSlot(LivingEntity entity, EquipmentSlot slot);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITConditionContainer.java b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITConditionContainer.java
new file mode 100644
index 0000000..886185c
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITConditionContainer.java
@@ -0,0 +1,42 @@
+package shcm.shsupercm.fabric.citresewn.api;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITRegistry;
+
+import java.util.function.Supplier;
+
+/**
+ * Wrapper to facilitate metadata, registry and creation of condition class types.
+ * @see #ENTRYPOINT
+ * @see CITRegistry
+ */
+public class CITConditionContainer<T extends CITCondition> {
+ /**
+ * Entrypoint for container singletons, usually kept as a static final field in the condition type's class.
+ */
+ public static final String ENTRYPOINT = "citresewn:condition";
+
+ /**
+ * Associated condition's class.
+ */
+ public final Class<T> condition;
+
+ /**
+ * Method reference to the condition's constructor or any other supplier of new condition instances.
+ */
+ public final Supplier<T> createCondition;
+
+ /**
+ * Possible names in property groups for the associated condition type.<br>
+ * Condition names are declared in groups with a mod id prefix to avoid conflicts with other 3rd party
+ * properties(formatted as "modid:alias").<br>
+ * If a modid is not declared, defaults to "citresewn" which is handled by CIT Resewn: Defaults.
+ */
+ public final String[] aliases;
+
+ public CITConditionContainer(Class<T> condition, Supplier<T> createCondition, String... aliases) {
+ this.condition = condition;
+ this.createCondition = createCondition;
+ this.aliases = aliases;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITDisposable.java b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITDisposable.java
new file mode 100644
index 0000000..dec7098
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITDisposable.java
@@ -0,0 +1,18 @@
+package shcm.shsupercm.fabric.citresewn.api;
+
+/**
+ * @see #dispose()
+ */
+@FunctionalInterface
+public interface CITDisposable {
+ /**
+ * Entrypoint for any disposing method that is not covered by CIT Resewn automatically.
+ * @see #dispose()
+ */
+ String ENTRYPOINT = "citresewn:dispose";
+
+ /**
+ * Invoked just before reloading CITs. Use to clean up and changes CIT loading made.
+ */
+ void dispose();
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITGlobalProperties.java b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITGlobalProperties.java
new file mode 100644
index 0000000..05495af
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITGlobalProperties.java
@@ -0,0 +1,26 @@
+package shcm.shsupercm.fabric.citresewn.api;
+
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import javax.annotation.Nullable;
+
+/**
+ * @see #globalProperty(String, PropertyValue)
+ */
+@FunctionalInterface
+public interface CITGlobalProperties {
+ /**
+ * Entrypoint for handlers of global properties.
+ * @see #globalProperty(String, PropertyValue)
+ */
+ String ENTRYPOINT = "citresewn:global_property";
+
+ /**
+ * Invoked before CIT parsing for any global property name associated with the handler's modid.<br>
+ * May be called multiple times for a key to overwrite its global property with higher-priority resourcepacks.<br>
+ * When unloading resourcepacks(usually before reloading), all keys that were invoked in the previous load will get called again with a null value to allow for disposal.
+ * @param key name of the property key stripped of its modid
+ * @param value the value it's been set to or null if resetting
+ */
+ void globalProperty(String key, @Nullable PropertyValue value) throws Exception;
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITTypeContainer.java b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITTypeContainer.java
new file mode 100644
index 0000000..b49b4a6
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/api/CITTypeContainer.java
@@ -0,0 +1,87 @@
+package shcm.shsupercm.fabric.citresewn.api;
+
+import shcm.shsupercm.fabric.citresewn.cit.ActiveCITs;
+import shcm.shsupercm.fabric.citresewn.cit.CIT;
+import shcm.shsupercm.fabric.citresewn.cit.CITType;
+import shcm.shsupercm.fabric.citresewn.cit.CITRegistry;
+import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Wrapper to facilitate metadata, registry and creation of CIT types.
+ * @see #ENTRYPOINT
+ * @see CITRegistry
+ */
+public abstract class CITTypeContainer<T extends CITType> implements CITDisposable {
+ /**
+ * Entrypoint for container singletons, usually kept as a static final field in the type's class.
+ */
+ public static final String ENTRYPOINT = "citresewn:type";
+
+ /**
+ * Associated type's class.
+ */
+ public final Class<T> type;
+
+ /**
+ * Method reference to the type's constructor or any other supplier of new CIT type instances.
+ */
+ public final Supplier<T> createType;
+
+ /**
+ * Identifier for this type to be used in the "type=" property.<br>
+ * When used in property groups the type's modid must be added to avoid conflicts with other
+ * 3rd party types(formatted as "modid:id").
+ */
+ public final String id;
+
+ /**
+ * True when the container should not have any CITs loaded.
+ */
+ protected boolean empty = true;
+
+ public CITTypeContainer(Class<T> type, Supplier<T> createType, String id) {
+ this.type = type;
+ this.createType = createType;
+ this.id = id;
+ }
+
+ /**
+ * Loads and keeps a copy of loaded CITs of this container's type.
+ * @param parsedCITs all loaded CITs of this container's type ordered by weight>path
+ */
+ protected abstract void load(List<CIT<T>> parsedCITs);
+
+ /**
+ * Loads and keeps a copy of loaded CITs of this container's type.
+ * @param parsedCITs all loaded CITs of this container's type ordered by weight>path
+ */
+ @SuppressWarnings("unchecked")
+ public final void loadUntyped(List<?> parsedCITs) {
+ if (!parsedCITs.isEmpty())
+ empty = false;
+ load((List<CIT<T>>) parsedCITs);
+ }
+
+ /**
+ * Unloads CITs from this container.<br>
+ * Override dispose() to add more logic.
+ * @see CITDisposable#dispose()
+ */
+ public final void unload() {
+ dispose();
+ empty = true;
+ }
+
+ /**
+ * @see #empty
+ * @see CITResewnConfig#enabled
+ * @see ActiveCITs#isActive()
+ * @return whether this container's associated type should work or not
+ */
+ public boolean active() {
+ return !empty && CITResewnConfig.INSTANCE.enabled && ActiveCITs.isActive();
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/ActiveCITs.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/ActiveCITs.java
new file mode 100644
index 0000000..82f341f
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/ActiveCITs.java
@@ -0,0 +1,106 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.util.profiler.Profiler;
+import shcm.shsupercm.fabric.citresewn.api.CITDisposable;
+import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer;
+import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
+import shcm.shsupercm.fabric.citresewn.pack.GlobalProperties;
+import shcm.shsupercm.fabric.citresewn.pack.PackParser;
+import shcm.shsupercm.fabric.citresewn.mixin.ModelLoaderMixin;
+
+import java.util.*;
+
+/**
+ * Holds and manages the currently loaded CITs.
+ * @see #getActive()
+ * @see ModelLoaderMixin
+ */
+public class ActiveCITs { private ActiveCITs() {}
+ /**
+ * @see #load(ResourceManager, Profiler)
+ * @see #getActive()
+ * @see #isActive()
+ */
+ private static ActiveCITs active = null;
+
+ /**
+ * @see #isActive()
+ * @return the current active CITs manager or null if none are loaded
+ */
+ public static ActiveCITs getActive() {
+ return active;
+ }
+
+ /**
+ * @see #getActive()
+ * @return whether there are active; loaded CITs
+ */
+ public static boolean isActive() {
+ return active != null;
+ }
+
+ /**
+ * Currently effective global properties merged from all loaded packs.
+ */
+ public final GlobalProperties globalProperties = new GlobalProperties();
+
+ /**
+ * All loaded CITs ordered by their type's class and their weight.
+ */
+ public final Map<Class<? extends CITType>, List<CIT<?>>> cits = new IdentityHashMap<>();
+
+ /**
+ * Attempts to load/activate CITs from packs in the given resource manager, disposing of any previously loaded CITs if present.
+ * @see ModelLoaderMixin
+ * @see PackParser#loadGlobalProperties(ResourceManager, GlobalProperties)
+ * @see GlobalProperties#callHandlers()
+ * @see PackParser#parseCITs(ResourceManager)
+ * @param resourceManager manager containing resourcepacks with possible CITs
+ * @param profiler loading profiler that was pushed once into "citresewn:reloading_cits" and would pop after
+ */
+ public static void load(ResourceManager resourceManager, Profiler profiler) {
+ profiler.push("citresewn:disposing");
+ for (CITDisposable disposable : FabricLoader.getInstance().getEntrypoints(CITDisposable.ENTRYPOINT, CITDisposable.class))
+ disposable.dispose();
+
+ for (CITTypeContainer<? extends CITType> typeContainer : CITRegistry.TYPES.values())
+ typeContainer.unload();
+
+ if (active != null) {
+ active.globalProperties.properties.replaceAll((key, value) -> Set.of());
+ active.globalProperties.callHandlers();
+
+ active = null;
+ }
+
+ if (!CITResewnConfig.INSTANCE.enabled) {
+ profiler.pop();
+ return;
+ }
+
+ ActiveCITs active = new ActiveCITs();
+
+ profiler.swap("citresewn:load_global_properties");
+ PackParser.loadGlobalProperties(resourceManager, active.globalProperties).callHandlers();
+
+ profiler.swap("citresewn:load_cits");
+ List<CIT<?>> cits = PackParser.parseCITs(resourceManager);
+ for (CIT<?> cit : cits)
+ active.cits.computeIfAbsent(cit.type.getClass(), type -> new ArrayList<>()).add(cit);
+ for (Map.Entry<Class<? extends CITType>, List<CIT<?>>> entry : active.cits.entrySet()) {
+ entry.getValue().sort(Comparator.<CIT<?>>comparingInt(cit -> cit.weight).reversed().thenComparing(cit -> cit.propertiesIdentifier.toString()));
+ for (CITTypeContainer<? extends CITType> typeContainer : CITRegistry.TYPES.values())
+ if (typeContainer.type == entry.getKey()) {
+ typeContainer.loadUntyped(entry.getValue());
+ break;
+ }
+ }
+
+ profiler.pop();
+
+ if (!cits.isEmpty())
+ ActiveCITs.active = active;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CIT.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CIT.java
new file mode 100644
index 0000000..7c7ba5b
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CIT.java
@@ -0,0 +1,61 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import net.minecraft.util.Identifier;
+
+/**
+ * Runtime representation of a CIT, holding its type and conditions as well as additional metadata.
+ */
+public class CIT<T extends CITType> {
+ /**
+ * The full location of this CIT in its resourcepack.
+ */
+ public final Identifier propertiesIdentifier;
+
+ /**
+ * Name of the resourcepack that contains this CIT.
+ */
+ public final String packName;
+
+ /**
+ * The CIT's type.
+ * @see CITType
+ */
+ public final T type;
+
+ /**
+ * Conditions that must be met for this CIT to work.
+ */
+ public final CITCondition[] conditions;
+
+ /**
+ * The weight of this CIT to be used when resolving multiple CIT matching conflicts.
+ */
+ public final int weight;
+
+ public CIT(Identifier propertiesIdentifier, String packName, T type, CITCondition[] conditions, int weight) {
+ this.propertiesIdentifier = propertiesIdentifier;
+ this.packName = packName;
+ this.type = type;
+ this.conditions = conditions;
+ this.weight = weight;
+ }
+
+ /**
+ * Tests the given context against all of this CIT's conditions.
+ *
+ * @see #conditions
+ * @param context context to check
+ * @return true if none of this CIT's {@link #conditions} tested false
+ */
+ public boolean test(CITContext context) {
+ try {
+ for (CITCondition condition : conditions)
+ if (!condition.test(context))
+ return false;
+
+ return true;
+ } catch (Exception ignored) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCache.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCache.java
new file mode 100644
index 0000000..31ccf04
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCache.java
@@ -0,0 +1,93 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import net.minecraft.item.ItemStack;
+import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Runtime cache for a CIT type to be stored in type implementation specific locations. (usually ducked onto {@link ItemStack}s)
+ * @see Single
+ * @see MultiList
+ */
+public abstract class CITCache<T extends CITType> {
+ /**
+ * The last time in epoch milliseconds that this cache invalidated its stored CIT.
+ */
+ public long lastCachedStamp = 0;
+
+ /**
+ * Common implementation of a single CIT per holder({@link ItemStack}) caching.
+ */
+ public static class Single<T extends CITType> extends CITCache<T> {
+ /**
+ * A reload-safe reference to the CIT that was last selected for this holder.
+ */
+ protected WeakReference<CIT<T>> cit = null;
+
+ /**
+ * Real time Context -> CIT supplier for this cache for invalidated holders.
+ */
+ protected final Function<CITContext, CIT<T>> realtime;
+
+ public Single(Function<CITContext, CIT<T>> realtime) {
+ this.realtime = realtime;
+ }
+
+ /**
+ * Retrieves the CIT reference associated with this cache and invalidates it every config-defined interval.
+ *
+ * @see CITResewnConfig#cache_ms
+ * @param context context to check
+ * @return reference to the CIT or reference to null if no CIT applied
+ */
+ public WeakReference<CIT<T>> get(CITContext context) {
+ if (this.cit == null || System.currentTimeMillis() - this.lastCachedStamp >= CITResewnConfig.INSTANCE.cache_ms) {
+ this.cit = new WeakReference<>(this.realtime.apply(context));
+ this.lastCachedStamp = System.currentTimeMillis();
+ }
+
+ return this.cit;
+ }
+ }
+
+ /**
+ * Common implementation of multiple CITs per holder({@link ItemStack}) caching.
+ */
+ public static class MultiList<T extends CITType> extends CITCache<T> {
+ /**
+ * List of reload-safe references to CITs that were last selected for this holder.
+ */
+ protected List<WeakReference<CIT<T>>> cit = null;
+
+ /**
+ * Real time Context -> CIT list supplier for this cache for invalidated holders.
+ */
+ protected final Function<CITContext, List<CIT<T>>> realtime;
+
+ public MultiList(Function<CITContext, List<CIT<T>>> realtime) {
+ this.realtime = realtime;
+ }
+
+ /**
+ * Retrieves the CIT references associated with this cache and invalidates them every config-defined interval.
+ *
+ * @see CITResewnConfig#cache_ms
+ * @param context context to check
+ * @return list of references to CITs or empty list no CIT applied
+ */
+ public List<WeakReference<CIT<T>>> get(CITContext context) {
+ if (this.cit == null || System.currentTimeMillis() - this.lastCachedStamp >= CITResewnConfig.INSTANCE.cache_ms) {
+ this.cit = new ArrayList<>();
+ for (CIT<T> realtimeCIT : this.realtime.apply(context))
+ this.cit.add(new WeakReference<>(realtimeCIT));
+ this.lastCachedStamp = System.currentTimeMillis();
+ }
+
+ return cit;
+ }
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCondition.java
new file mode 100644
index 0000000..cd4cb9d
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITCondition.java
@@ -0,0 +1,63 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import shcm.shsupercm.fabric.citresewn.CITResewn;
+import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer;
+import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Instanced parent for CIT Types that are applied to items when conditions pass.<br>
+ * Common condition types are available under {@link shcm.shsupercm.fabric.citresewn.cit.builtin.conditions}.
+ * @see CITConditionContainer
+ * @see CIT
+ */
+public abstract class CITCondition {
+ /**
+ * Parses the given property value into the condition.
+ * @param value value to read
+ * @param properties the group containing value
+ * @throws CITParsingException if errored while parsing the condition
+ */
+ public abstract void load(PropertyValue value, PropertyGroup properties) throws CITParsingException;
+
+ /**
+ * @return a set of classes of conditions that have integration with this condition
+ */
+ public Set<Class<? extends CITCondition>> siblingConditions() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Modifies the given sibling if present in the CIT and declared in {@link #siblingConditions()}.
+ * @param sibling sibling to modify or be modified by
+ * @return the sibling or null to remove it from the CIT.
+ */
+ public <T extends CITCondition> T modifySibling(T sibling) {
+ return sibling;
+ }
+
+ /**
+ * Tests the given context against this condition.
+ * @param context context to check
+ * @return true if the given context passes
+ */
+ public abstract boolean test(CITContext context);
+
+ /**
+ * Logs a warning with the given value's descriptor if enabled in config.
+ *
+ * @see CITResewn#logWarnLoading(String)
+ * @see CITResewnConfig#mute_warns
+ * @param message warning message
+ * @param value value associated with the warning
+ * @param properties property group associated with the warning
+ */
+ protected void warn(String message, PropertyValue value, PropertyGroup properties) {
+ CITResewn.logWarnLoading("Warning: " + properties.messageWithDescriptorOf(message, value.position()));
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITContext.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITContext.java
new file mode 100644
index 0000000..cfc10f8
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITContext.java
@@ -0,0 +1,76 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.enchantment.EnchantmentHelper;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.item.EnchantedBookItem;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.util.Identifier;
+import net.minecraft.world.World;
+
+import javax.annotation.Nullable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Holds momentary information to be used for CITs' condition matching and type effects.
+ */
+public class CITContext {
+ /**
+ * The main item stack to check for the CIT.
+ */
+ public final ItemStack stack;
+
+ /**
+ * The item's containing world(defaults to {@link MinecraftClient#world} if null)
+ */
+ public final World world;
+
+ /**
+ * The item's associated living entity if present. (null if not relevant)
+ */
+ @Nullable
+ public final LivingEntity entity;
+
+ /**
+ * Cached enchantment map from {@link #stack}.
+ * @see #enchantments()
+ */
+ private Map<Identifier, Integer> enchantments = null;
+
+ public CITContext(ItemStack stack, @Nullable World world, @Nullable LivingEntity entity) {
+ this.stack = stack;
+ this.world = world == null ? MinecraftClient.getInstance().world : world;
+ this.entity = entity;
+ }
+
+ /**
+ * @see #enchantments
+ * @return a map of this context item's enchantments
+ */
+ public Map<Identifier, Integer> enchantments() {
+ if (this.enchantments == null) {
+ this.enchantments = new LinkedHashMap<>();
+ for (NbtElement nbtElement : stack.isOf(Items.ENCHANTED_BOOK) ? EnchantedBookItem.getEnchantmentNbt(stack) : stack.getEnchantments())
+ this.enchantments.put(EnchantmentHelper.getIdFromNbt((NbtCompound) nbtElement), EnchantmentHelper.getLevelFromNbt((NbtCompound) nbtElement));
+ }
+ return this.enchantments;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof CITContext that &&
+ Objects.equals(this.stack, that.stack) &&
+ Objects.equals(this.world, that.world) &&
+ Objects.equals(this.entity, that.entity);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(stack, world, entity);
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITRegistry.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITRegistry.java
new file mode 100644
index 0000000..bf7c2ca
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITRegistry.java
@@ -0,0 +1,144 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.util.Identifier;
+import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer;
+import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer;
+import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ConstantCondition;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.ex.UnknownCITTypeException;
+import shcm.shsupercm.fabric.citresewn.pack.PackParser;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import java.util.*;
+
+import static shcm.shsupercm.fabric.citresewn.CITResewn.info;
+import static shcm.shsupercm.fabric.citresewn.CITResewn.logWarnLoading;
+
+/**
+ * Holds a static registry runtime for all types and conditions.
+ * @see PackParser
+ * @see CITTypeContainer
+ * @see CITConditionContainer
+ */
+public final class CITRegistry { private CITRegistry(){}
+ /**
+ * Currently registered CIT types.
+ */
+ public static final Map<Identifier, CITTypeContainer<? extends CITType>> TYPES = new HashMap<>();
+ /**
+ * Currently registered condition types.
+ */
+ public static final Map<PropertyKey, CITConditionContainer<? extends CITCondition>> CONDITIONS = new HashMap<>();
+
+ /**
+ * Fast id lookup map for types.
+ * @see #idOfType(Class)
+ */
+ private static final Map<Class<? extends CITType>, Identifier> TYPE_TO_ID = new IdentityHashMap<>();
+ /**
+ * Fast id lookup map for conditions.
+ * @see #idOfCondition(Class)
+ */
+ private static final Map<Class<? extends CITCondition>, PropertyKey> CONDITION_TO_ID = new IdentityHashMap<>();
+
+ /**
+ * Loads all available CIT and condition types to registry. (internal use only)
+ * @see CITTypeContainer
+ * @see CITConditionContainer
+ */
+ public static void registerAll() {
+ info("Registering CIT Conditions");
+ for (var entrypointContainer : FabricLoader.getInstance().getEntrypointContainers(CITConditionContainer.ENTRYPOINT, CITConditionContainer.class)) {
+ String namespace = entrypointContainer.getProvider().getMetadata().getId();
+ if (namespace.equals("citresewn-defaults"))
+ namespace = "citresewn";
+
+ for (String alias : entrypointContainer.getEntrypoint().aliases) {
+ final PropertyKey key = new PropertyKey(namespace, alias);
+ CITConditionContainer<?> container = entrypointContainer.getEntrypoint();
+
+ CONDITIONS.put(key, container);
+ CONDITION_TO_ID.putIfAbsent(container.createCondition.get().getClass(), key);
+ }
+ }
+
+ info("Registering CIT Types");
+ for (var entrypointContainer : FabricLoader.getInstance().getEntrypointContainers(CITTypeContainer.ENTRYPOINT, CITTypeContainer.class)) {
+ String namespace = entrypointContainer.getProvider().getMetadata().getId();
+ if (namespace.equals("citresewn-defaults"))
+ namespace = "citresewn";
+
+ final Identifier id = new Identifier(namespace, entrypointContainer.getEntrypoint().id);
+ CITTypeContainer<?> container = entrypointContainer.getEntrypoint();
+
+ TYPES.put(id, container);
+ TYPE_TO_ID.putIfAbsent(container.createType.get().getClass(), id);
+ }
+ }
+
+ /**
+ * Parses a condition from the given property.<br>
+ *
+ * @param key the condition's key in the group
+ * @param value the condition's value
+ * @param properties the containing property group
+ * @return the parsed condition or an always-failing {@link ConstantCondition} if unrecognized
+ * @throws CITParsingException if errored while parsing hte condition
+ */
+ public static CITCondition parseCondition(PropertyKey key, PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ CITConditionContainer<? extends CITCondition> conditionContainer = CONDITIONS.get(key);
+ if (conditionContainer == null) {
+ logWarnLoading(properties.messageWithDescriptorOf("Unknown condition type \"" + key.toString() + "\"", value.position()));
+ return new ConstantCondition(false);
+ }
+
+ CITCondition condition = conditionContainer.createCondition.get();
+ condition.load(value, properties);
+ return condition;
+ }
+
+ /**
+ * Parses a CIT type from the given property group.<br>
+ * If the group does not contain a "citresewn:type" property, defaults to "citresewn:item".
+ * @param properties group of properties to parse the CIT type from
+ * @return a new instance of the group's CIT type
+ * @throws UnknownCITTypeException if the given type is unrecognized in the registry
+ */
+ public static CITType parseType(PropertyGroup properties) throws UnknownCITTypeException {
+ Identifier type = new Identifier("citresewn", "item");
+
+ PropertyValue propertiesType = properties.getLastWithoutMetadata("citresewn", "type");
+ if (propertiesType != null) {
+ String value = propertiesType.value();
+ if (!value.contains(":"))
+ value = "citresewn:" + value;
+ type = new Identifier(value);
+ }
+
+ CITTypeContainer<? extends CITType> typeContainer = TYPES.get(type);
+ if (typeContainer == null)
+ // assert (propertiesType != null) because the default citresewn:item should always be registered
+ throw new UnknownCITTypeException(properties, propertiesType == null ? -1 : propertiesType.position());
+
+ return typeContainer.createType.get();
+ }
+
+ /**
+ * @see #TYPE_TO_ID
+ * @return the id of the given CIT type's class.
+ */
+ public static Identifier idOfType(Class<? extends CITType> clazz) {
+ return TYPE_TO_ID.get(clazz);
+ }
+
+ /**
+ * @see #CONDITION_TO_ID
+ * @return the first key of the given condition's class.
+ */
+ public static PropertyKey idOfCondition(Class<? extends CITCondition> clazz) {
+ return CONDITION_TO_ID.get(clazz);
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITType.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITType.java
new file mode 100644
index 0000000..65b3097
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/CITType.java
@@ -0,0 +1,109 @@
+package shcm.shsupercm.fabric.citresewn.cit;
+
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.util.Identifier;
+import shcm.shsupercm.fabric.citresewn.CITResewn;
+import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import java.util.*;
+
+/**
+ * Instanced parent for CIT Types that are applied to items when conditions pass.
+ * @see CITTypeContainer
+ * @see CIT
+ */
+public abstract class CITType {
+ /**
+ * Used to determine which property keys are not conditions.
+ * @return a set of property keys used by this type
+ */
+ public abstract Set<PropertyKey> typeProperties();
+
+ /**
+ * Loads the given property group into the type.
+ * @param conditions conditions that were parsed out of the property group
+ * @param properties group of properties to be read into this type
+ * @param resourceManager the CIT's containing resource manager
+ * @throws CITParsingException if errored while parsing the type
+ */
+ public abstract void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException;
+
+ protected void warn(String message, PropertyValue value, PropertyGroup properties) {
+ CITResewn.logWarnLoading("Warning: " + properties.messageWithDescriptorOf(message, value == null ? -1 : value.position()));
+ }
+
+ /**
+ * ///// PORTED FROM BETA \\\\\
+ * This shit was ported from the
+ * beta and will be rewritten at
+ * some point!
+ * \\\\\ /////
+ *
+ * Takes a defined path and resolves it to an identifier pointing to the resourcepack's path of the specified extension(returns null if no path can be resolved).<br>
+ * If definedPath is null, will try to resolve a relative file with the same name as the rootIdentifier with the extension, otherwise: <br>
+ * definedPath will be formatted to replace "\\" with "/" the extension will be appended if not there already. <br>
+ * It will first try using definedPath as an absolute path, if it cant resolve(or definedPath starts with ./), definedPath will be considered relative. <br>
+ * Relative paths support going to parent directories using "..".
+ */
+ public static Identifier resolveAsset(Identifier rootIdentifier, String path, String defaultedTypeDirectory, String extension, ResourceManager resourceManager) {
+ if (path == null) {
+ path = rootIdentifier.getPath().substring(0, rootIdentifier.getPath().length() - 11);
+ if (!path.endsWith(extension))
+ path = path + extension;
+ Identifier pathIdentifier = new Identifier(rootIdentifier.getNamespace(), path);
+ return resourceManager.containsResource(pathIdentifier) ? pathIdentifier : null;
+ }
+
+ Identifier pathIdentifier = new Identifier(path);
+
+ path = pathIdentifier.getPath().replace('\\', '/');
+ if (!path.endsWith(extension))
+ path = path + extension;
+
+ if (path.startsWith("./"))
+ path = path.substring(2);
+ else if (!path.contains("..")) {
+ pathIdentifier = new Identifier(pathIdentifier.getNamespace(), path);
+ if (resourceManager.containsResource(pathIdentifier))
+ return pathIdentifier;
+ else if (path.startsWith("assets/")) {
+ path = path.substring(7);
+ int sep = path.indexOf('/');
+ pathIdentifier = new Identifier(path.substring(0, sep), path.substring(sep + 1));
+ if (resourceManager.containsResource(pathIdentifier))
+ return pathIdentifier;
+ }
+ pathIdentifier = new Identifier(pathIdentifier.getNamespace(), defaultedTypeDirectory + "/" + path);
+ if (resourceManager.containsResource(pathIdentifier))
+ return pathIdentifier;
+ }
+
+ LinkedList<String> pathParts = new LinkedList<>(Arrays.asList(rootIdentifier.getPath().split("/")));
+ pathParts.removeLast();
+
+ if (path.contains("/")) {
+ for (String part : path.split("/")) {
+ if (part.equals("..")) {
+ if (pathParts.size() == 0)
+ return null;
+ pathParts.removeLast();
+ } else
+ pathParts.addLast(part);
+ }
+ } else
+ pathParts.addLast(path);
+ path = String.join("/", pathParts);
+
+ pathIdentifier = new Identifier(rootIdentifier.getNamespace(), path);
+
+ return resourceManager.containsResource(pathIdentifier) ? pathIdentifier : null;
+ }
+
+ public static Identifier resolveAsset(Identifier rootIdentifier, PropertyValue path, String defaultedTypeDirectory, String extension, ResourceManager resourceManager) {
+ return resolveAsset(rootIdentifier, path == null ? null : path.value(), defaultedTypeDirectory, extension, resourceManager);
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/BooleanCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/BooleanCondition.java
new file mode 100644
index 0000000..989318f
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/BooleanCondition.java
@@ -0,0 +1,41 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+/**
+ * Common condition parser for booleans.
+ */
+public abstract class BooleanCondition extends CITCondition {
+ /**
+ * Parsed boolean.
+ */
+ protected boolean value;
+
+ /**
+ * Converts the given context to a boolean to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the boolean value associated with the given context
+ */
+ protected boolean getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ if (value.value().equalsIgnoreCase("true"))
+ this.value = true;
+ else if (value.value().equalsIgnoreCase("false"))
+ this.value = false;
+ else
+ throw new CITParsingException("Not a boolean", properties, value.position());
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ return getValue(context) == this.value;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ConstantCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ConstantCondition.java
new file mode 100644
index 0000000..5bc9354
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ConstantCondition.java
@@ -0,0 +1,31 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+/**
+ * Common condition type with no parsing for constant true/false testing output.
+ */
+public class ConstantCondition extends CITCondition {
+ /**
+ * What testing contexts will always result in.
+ */
+ public final boolean value;
+
+ public ConstantCondition(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ return value;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/DoubleCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/DoubleCondition.java
new file mode 100644
index 0000000..44bb906
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/DoubleCondition.java
@@ -0,0 +1,157 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import static java.lang.Double.*;
+
+/**
+ * Common condition parser for doubles with optional support for ranges, negatives and percentages.
+ */
+public abstract class DoubleCondition extends CITCondition {
+ /**
+ * Whether this condition should accept given ranges/negatives/percentages.
+ */
+ protected final boolean supportsRanges, supportsNegatives, supportsPercentages;
+
+ /**
+ * If ranges are accepted, parsed minimum/maximum double. If not, minimum is the parsed value.
+ */
+ protected double min, max;
+
+ /**
+ * Whether the parsed value is a range/percentage.
+ */
+ protected boolean range = false, percentage = false;
+
+ protected DoubleCondition(boolean supportsRanges, boolean supportsNegatives, boolean supportsPercentages) {
+ this.supportsRanges = supportsRanges;
+ this.supportsNegatives = supportsNegatives;
+ this.supportsPercentages = supportsPercentages;
+ }
+
+ /**
+ * Converts the given context to a double to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the double value associated with the given context
+ */
+ protected double getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ /**
+ * Converts the given context to a max double to be used when percentages are enabled.
+ * @param context context to retrieve the max value from
+ * @return the max double value associated with the given context
+ */
+ protected double getPercentageTotalValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ String strValue = value.value();
+ if (supportsPercentages && (percentage = strValue.contains("%")))
+ strValue = strValue.replace("%", "");
+
+ try {
+ if (range = supportsRanges) {
+ if (supportsNegatives) {
+ switch (strValue.length() - strValue.replace("-", "").length()) { // dashesCount
+ case 0 -> {
+ range = false;
+ min = parseDouble(strValue);
+ }
+ case 1 -> {
+ if (strValue.startsWith("-")) {
+ range = false;
+ min = parseDouble(strValue);
+ } else if (strValue.endsWith("-")) {
+ min = parseDouble(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else {
+ String[] split = strValue.split("-");
+ min = parseDouble(split[0]);
+ max = parseDouble(split[1]);
+ }
+ }
+ case 2 -> {
+ if (strValue.startsWith("--")) {
+ min = MIN_VALUE;
+ max = parseDouble(strValue.substring(1));
+ } else if (strValue.startsWith("-") && strValue.endsWith("-")) {
+ min = parseDouble(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else if (strValue.startsWith("-") && !strValue.endsWith("-") && !strValue.contains("--")) {
+ int lastDash = strValue.lastIndexOf('-');
+ min = parseDouble(strValue.substring(0, lastDash));
+ max = parseDouble(strValue.substring(lastDash + 1));
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ case 3 -> {
+ if (!strValue.contains("---") && strValue.startsWith("-")) {
+ String[] split = strValue.split("--");
+ if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty())
+ throw new CITParsingException("Could not parse range", properties, value.position());
+
+ min = parseDouble(split[0]);
+ max = -parseDouble(split[1]);
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else {
+ if (range = strValue.contains("-")) {
+ if (strValue.contains("--"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ String[] split = strValue.split("-");
+ switch (split.length) {
+ case 1 -> {
+ min = parseDouble(split[0]);
+ max = MAX_VALUE;
+ }
+ case 2 -> {
+ if (strValue.endsWith("-"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ min = split[0].isEmpty() ? MIN_VALUE : parseDouble(split[0]);
+ max = split[1].isEmpty() ? MAX_VALUE : parseDouble(split[1]);
+ }
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else
+ min = parseDouble(strValue);
+ }
+ } else {
+ min = parseDouble(strValue);
+ if (!supportsNegatives && min < 0)
+ throw new CITParsingException("Negatives are not allowed", properties, value.position());
+ }
+
+ if (range) {
+ if (min == max)
+ range = false;
+ else if (min > max)
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } catch (Exception e) {
+ throw e instanceof CITParsingException citE ? citE : new CITParsingException("Could not parse double", properties, value.position(), e);
+ }
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ double value = getValue(context);
+
+ if (percentage) {
+ double percentValue = 100d * value / getPercentageTotalValue(context);
+ return range ? min <= percentValue && percentValue <= max : percentValue == min;
+ } else
+ return range ? min <= value && value <= max : value == min;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/EnumCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/EnumCondition.java
new file mode 100644
index 0000000..ed69fed
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/EnumCondition.java
@@ -0,0 +1,77 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import java.util.function.Supplier;
+
+/**
+ * Common condition parser for enum values.
+ * @see EnumCondition.Aliased
+ */
+public abstract class EnumCondition<T extends Enum<? extends EnumCondition.Aliased>> extends CITCondition {
+ /**
+ * Fetches the all of the enum's parsable values.
+ */
+ protected final Supplier<T[]> values;
+
+ /**
+ * Should letter casing be ignored when parsing the enum value. (default true)
+ */
+ protected final boolean ignoreCase;
+
+ /**
+ * Parsed enum value.
+ */
+ protected T value;
+
+ /**
+ * Converts the given context to an enum value to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the enum value associated with the given context
+ */
+ protected T getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ protected EnumCondition(Supplier<T[]> values, boolean ignoreCase) {
+ this.values = values;
+ this.ignoreCase = ignoreCase;
+ }
+
+ protected EnumCondition(Supplier<T[]> values) {
+ this(values, true);
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ for (T enumConstant : values.get())
+ for (String alias : ((Aliased) enumConstant).getAliases())
+ if (ignoreCase ? alias.equalsIgnoreCase(value.value()) : alias.equals(value.value())) {
+ this.value = enumConstant;
+ return;
+ }
+
+ throw new CITParsingException("Unrecognized value", properties, value.position());
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ return getValue(context) == this.value;
+ }
+
+ /**
+ * Gives implementing enums the ability to have multiple aliased names for parsing.
+ */
+ public interface Aliased {
+ /**
+ * @return all possible names for this enum value
+ */
+ default String[] getAliases() {
+ return new String[] { ((Enum<?>) this).name() };
+ }
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/FloatCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/FloatCondition.java
new file mode 100644
index 0000000..aadaa76
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/FloatCondition.java
@@ -0,0 +1,157 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import static java.lang.Float.*;
+
+/**
+ * Common condition parser for floats with optional support for ranges, negatives and percentages.
+ */
+public abstract class FloatCondition extends CITCondition {
+ /**
+ * Whether this condition should accept given ranges/negatives/percentages.
+ */
+ protected final boolean supportsRanges, supportsNegatives, supportsPercentages;
+
+ /**
+ * If ranges are accepted, parsed minimum/maximum float. If not, minimum is the parsed value.
+ */
+ protected float min, max;
+
+ /**
+ * Whether the parsed value is a range/percentage.
+ */
+ protected boolean range = false, percentage = false;
+
+ protected FloatCondition(boolean supportsRanges, boolean supportsNegatives, boolean supportsPercentages) {
+ this.supportsRanges = supportsRanges;
+ this.supportsNegatives = supportsNegatives;
+ this.supportsPercentages = supportsPercentages;
+ }
+
+ /**
+ * Converts the given context to a float to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the float value associated with the given context
+ */
+ protected float getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ /**
+ * Converts the given context to a max float to be used when percentages are enabled.
+ * @param context context to retrieve the max value from
+ * @return the max float value associated with the given context
+ */
+ protected float getPercentageTotalValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ String strValue = value.value();
+ if (supportsPercentages && (percentage = strValue.contains("%")))
+ strValue = strValue.replace("%", "");
+
+ try {
+ if (range = supportsRanges) {
+ if (supportsNegatives) {
+ switch (strValue.length() - strValue.replace("-", "").length()) { // dashesCount
+ case 0 -> {
+ range = false;
+ min = parseFloat(strValue);
+ }
+ case 1 -> {
+ if (strValue.startsWith("-")) {
+ range = false;
+ min = parseFloat(strValue);
+ } else if (strValue.endsWith("-")) {
+ min = parseFloat(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else {
+ String[] split = strValue.split("-");
+ min = parseFloat(split[0]);
+ max = parseFloat(split[1]);
+ }
+ }
+ case 2 -> {
+ if (strValue.startsWith("--")) {
+ min = MIN_VALUE;
+ max = parseFloat(strValue.substring(1));
+ } else if (strValue.startsWith("-") && strValue.endsWith("-")) {
+ min = parseFloat(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else if (strValue.startsWith("-") && !strValue.endsWith("-") && !strValue.contains("--")) {
+ int lastDash = strValue.lastIndexOf('-');
+ min = parseFloat(strValue.substring(0, lastDash));
+ max = parseFloat(strValue.substring(lastDash + 1));
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ case 3 -> {
+ if (!strValue.contains("---") && strValue.startsWith("-")) {
+ String[] split = strValue.split("--");
+ if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty())
+ throw new CITParsingException("Could not parse range", properties, value.position());
+
+ min = parseFloat(split[0]);
+ max = -parseFloat(split[1]);
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else {
+ if (range = strValue.contains("-")) {
+ if (strValue.contains("--"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ String[] split = strValue.split("-");
+ switch (split.length) {
+ case 1 -> {
+ min = parseFloat(split[0]);
+ max = MAX_VALUE;
+ }
+ case 2 -> {
+ if (strValue.endsWith("-"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ min = split[0].isEmpty() ? MIN_VALUE : parseFloat(split[0]);
+ max = split[1].isEmpty() ? MAX_VALUE : parseFloat(split[1]);
+ }
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else
+ min = parseFloat(strValue);
+ }
+ } else {
+ min = parseFloat(strValue);
+ if (!supportsNegatives && min < 0)
+ throw new CITParsingException("Negatives are not allowed", properties, value.position());
+ }
+
+ if (range) {
+ if (min == max)
+ range = false;
+ else if (min > max)
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } catch (Exception e) {
+ throw e instanceof CITParsingException citE ? citE : new CITParsingException("Could not parse float", properties, value.position(), e);
+ }
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ float value = getValue(context);
+
+ if (percentage) {
+ float percentValue = 100f * value / getPercentageTotalValue(context);
+ return range ? min <= percentValue && percentValue <= max : percentValue == min;
+ } else
+ return range ? min <= value && value <= max : value == min;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IdentifierCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IdentifierCondition.java
new file mode 100644
index 0000000..4d04b76
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IdentifierCondition.java
@@ -0,0 +1,42 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import net.minecraft.util.Identifier;
+import net.minecraft.util.InvalidIdentifierException;
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+/**
+ * Common condition parser for identifiers.
+ */
+public abstract class IdentifierCondition extends CITCondition {
+ /**
+ * Parsed identifier.
+ */
+ protected Identifier value;
+
+ /**
+ * Converts the given context to an identifier to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the identifier value associated with the given context
+ */
+ protected Identifier getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ try {
+ this.value = new Identifier(value.value());
+ } catch (InvalidIdentifierException e) {
+ throw new CITParsingException(e.getMessage(), properties, value.position());
+ }
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ return this.value.equals(getValue(context));
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IntegerCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IntegerCondition.java
new file mode 100644
index 0000000..80937f5
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/IntegerCondition.java
@@ -0,0 +1,157 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import static java.lang.Integer.*;
+
+/**
+ * Common condition parser for integers with optional support for ranges, negatives and percentages.
+ */
+public abstract class IntegerCondition extends CITCondition {
+ /**
+ * Whether this condition should accept given ranges/negatives/percentages.
+ */
+ protected final boolean supportsRanges, supportsNegatives, supportsPercentages;
+
+ /**
+ * If ranges are accepted, parsed minimum/maximum integers. If not, minimum is the parsed value.
+ */
+ protected int min, max;
+
+ /**
+ * Whether the parsed value is a range/percentage.
+ */
+ protected boolean range = false, percentage = false;
+
+ protected IntegerCondition(boolean supportsRanges, boolean supportsNegatives, boolean supportsPercentages) {
+ this.supportsRanges = supportsRanges;
+ this.supportsNegatives = supportsNegatives;
+ this.supportsPercentages = supportsPercentages;
+ }
+
+ /**
+ * Converts the given context to an integer to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the integer value associated with the given context
+ */
+ protected int getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ /**
+ * Converts the given context to a max integer to be used when percentages are enabled.
+ * @param context context to retrieve the max value from
+ * @return the max integer value associated with the given context
+ */
+ protected int getPercentageTotalValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ String strValue = value.value();
+ if (supportsPercentages && (percentage = strValue.contains("%")))
+ strValue = strValue.replace("%", "");
+
+ try {
+ if (range = supportsRanges) {
+ if (supportsNegatives) {
+ switch (strValue.length() - strValue.replace("-", "").length()) { // dashesCount
+ case 0 -> {
+ range = false;
+ min = parseInt(strValue);
+ }
+ case 1 -> {
+ if (strValue.startsWith("-")) {
+ range = false;
+ min = parseInt(strValue);
+ } else if (strValue.endsWith("-")) {
+ min = parseInt(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else {
+ String[] split = strValue.split("-");
+ min = parseInt(split[0]);
+ max = parseInt(split[1]);
+ }
+ }
+ case 2 -> {
+ if (strValue.startsWith("--")) {
+ min = MIN_VALUE;
+ max = parseInt(strValue.substring(1));
+ } else if (strValue.startsWith("-") && strValue.endsWith("-")) {
+ min = parseInt(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else if (strValue.startsWith("-") && !strValue.endsWith("-") && !strValue.contains("--")) {
+ int lastDash = strValue.lastIndexOf('-');
+ min = parseInt(strValue.substring(0, lastDash));
+ max = parseInt(strValue.substring(lastDash + 1));
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ case 3 -> {
+ if (!strValue.contains("---") && strValue.startsWith("-")) {
+ String[] split = strValue.split("--");
+ if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty())
+ throw new CITParsingException("Could not parse range", properties, value.position());
+
+ min = parseInt(split[0]);
+ max = -parseInt(split[1]);
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else {
+ if (range = strValue.contains("-")) {
+ if (strValue.contains("--"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ String[] split = strValue.split("-");
+ switch (split.length) {
+ case 1 -> {
+ min = parseInt(split[0]);
+ max = MAX_VALUE;
+ }
+ case 2 -> {
+ if (strValue.endsWith("-"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ min = split[0].isEmpty() ? MIN_VALUE : parseInt(split[0]);
+ max = split[1].isEmpty() ? MAX_VALUE : parseInt(split[1]);
+ }
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else
+ min = parseInt(strValue);
+ }
+ } else {
+ min = parseInt(strValue);
+ if (!supportsNegatives && min < 0)
+ throw new CITParsingException("Negatives are not allowed", properties, value.position());
+ }
+
+ if (range) {
+ if (min == max)
+ range = false;
+ else if (min > max)
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } catch (Exception e) {
+ throw e instanceof CITParsingException citE ? citE : new CITParsingException("Could not parse integer", properties, value.position(), e);
+ }
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ int value = getValue(context);
+
+ if (percentage) {
+ double percentValue = 100d * (double) value / (double) getPercentageTotalValue(context);
+ return range ? min <= percentValue && percentValue <= max : percentValue == (double) min;
+ } else
+ return range ? min <= value && value <= max : value == min;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ListCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ListCondition.java
new file mode 100644
index 0000000..e264b27
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/ListCondition.java
@@ -0,0 +1,116 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.regex.Pattern;
+
+/**
+ * Common condition parser for multiple values separated by any regex expression.
+ */
+public abstract class ListCondition<T extends CITCondition> extends CITCondition {
+ /**
+ * Regex pattern for any amount of whitespace.
+ */
+ public static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\p{Zs}+");
+
+ /**
+ * Enum class type associated with this condition.
+ */
+ private final Class<T> conditionType;
+
+ /**
+ * Determines how testing the conditions should work(either in OR checks or AND checks).
+ * @see ListCondition.Type
+ */
+ protected final Type listType;
+
+ /**
+ * Regex pattern to use to separate given input into conditions.
+ */
+ protected final Pattern delimiter;
+
+ /**
+ * Constructor for new parsed conditions.
+ */
+ protected final Supplier<T> conditionSupplier;
+
+ /**
+ * Parsed conditions.
+ */
+ protected T[] conditions;
+
+ protected ListCondition(Class<T> conditionType, Type listType, Pattern delimiter, Supplier<T> conditionSupplier) {
+ this.conditionType = conditionType;
+ this.listType = listType;
+ this.delimiter = delimiter;
+ this.conditionSupplier = conditionSupplier;
+ }
+
+ protected ListCondition(Class<T> conditionType, Supplier<T> conditionSupplier) {
+ this(conditionType, Type.OR, PATTERN_WHITESPACE, conditionSupplier);
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ List<T> conditions = new ArrayList<>();
+
+ for (String conditionValue : delimiter.split(value.value())) {
+ T condition = conditionSupplier.get();
+ condition.load(new PropertyValue(value.keyMetadata(), conditionValue, value.separator(), value.position(), value.propertiesIdentifier(), value.packName()), properties);
+ conditions.add(condition);
+ }
+
+ //noinspection unchecked
+ this.conditions = conditions.toArray((T[]) Array.newInstance(conditionType, 0));
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ return listType.test(conditions, context);
+ }
+
+ /**
+ * Provides OR and AND gates for all of the list's conditions.
+ */
+ public enum Type {
+ /**
+ * Testing passes if any of the conditions pass and fails otherwise.
+ */
+ OR {
+ @Override
+ public boolean test(CITCondition[] conditions, CITContext context) {
+ for (CITCondition condition : conditions)
+ if (condition.test(context))
+ return true;
+
+ return false;
+ }
+ },
+ /**
+ * Testing passes if all of the conditions pass and fails otherwise.
+ */
+ AND {
+ @Override
+ public boolean test(CITCondition[] conditions, CITContext context) {
+ for (CITCondition condition : conditions)
+ if (!condition.test(context))
+ return false;
+
+ return true;
+ }
+ };
+
+ /**
+ * Tests the given context against all of the conditions.
+ */
+ public abstract boolean test(CITCondition[] conditions, CITContext context);
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/LongCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/LongCondition.java
new file mode 100644
index 0000000..da68bd8
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/LongCondition.java
@@ -0,0 +1,157 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import static java.lang.Long.*;
+
+/**
+ * Common condition parser for longs with optional support for ranges, negatives and percentages.
+ */
+public abstract class LongCondition extends CITCondition {
+ /**
+ * Whether this condition should accept given ranges/negatives/percentages.
+ */
+ protected final boolean supportsRanges, supportsNegatives, supportsPercentages;
+
+ /**
+ * If ranges are accepted, parsed minimum/maximum longs. If not, minimum is the parsed value.
+ */
+ protected long min, max;
+
+ /**
+ * Whether the parsed value is a range/percentage.
+ */
+ protected boolean range = false, percentage = false;
+
+ protected LongCondition(boolean supportsRanges, boolean supportsNegatives, boolean supportsPercentages) {
+ this.supportsRanges = supportsRanges;
+ this.supportsNegatives = supportsNegatives;
+ this.supportsPercentages = supportsPercentages;
+ }
+
+ /**
+ * Converts the given context to a long to compare the parsed value to.
+ * @param context context to retrieve the compared value from
+ * @return the long value associated with the given context
+ */
+ protected long getValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ /**
+ * Converts the given context to a max long to be used when percentages are enabled.
+ * @param context context to retrieve the max value from
+ * @return the max llong value associated with the given context
+ */
+ protected long getPercentageTotalValue(CITContext context) {
+ throw new AssertionError("Not implemented by this condition");
+ }
+
+ @Override
+ public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException {
+ String strValue = value.value();
+ if (supportsPercentages && (percentage = strValue.contains("%")))
+ strValue = strValue.replace("%", "");
+
+ try {
+ if (range = supportsRanges) {
+ if (supportsNegatives) {
+ switch (strValue.length() - strValue.replace("-", "").length()) { // dashesCount
+ case 0 -> {
+ range = false;
+ min = parseLong(strValue);
+ }
+ case 1 -> {
+ if (strValue.startsWith("-")) {
+ range = false;
+ min = parseLong(strValue);
+ } else if (strValue.endsWith("-")) {
+ min = parseLong(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else {
+ String[] split = strValue.split("-");
+ min = parseLong(split[0]);
+ max = parseLong(split[1]);
+ }
+ }
+ case 2 -> {
+ if (strValue.startsWith("--")) {
+ min = MIN_VALUE;
+ max = parseLong(strValue.substring(1));
+ } else if (strValue.startsWith("-") && strValue.endsWith("-")) {
+ min = parseLong(strValue.substring(0, strValue.length() - 1));
+ max = MAX_VALUE;
+ } else if (strValue.startsWith("-") && !strValue.endsWith("-") && !strValue.contains("--")) {
+ int lastDash = strValue.lastIndexOf('-');
+ min = parseLong(strValue.substring(0, lastDash));
+ max = parseLong(strValue.substring(lastDash + 1));
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ case 3 -> {
+ if (!strValue.contains("---") && strValue.startsWith("-")) {
+ String[] split = strValue.split("--");
+ if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty())
+ throw new CITParsingException("Could not parse range", properties, value.position());
+
+ min = parseLong(split[0]);
+ max = -parseLong(split[1]);
+ } else
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else {
+ if (range = strValue.contains("-")) {
+ if (strValue.contains("--"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ String[] split = strValue.split("-");
+ switch (split.length) {
+ case 1 -> {
+ min = parseLong(split[0]);
+ max = MAX_VALUE;
+ }
+ case 2 -> {
+ if (strValue.endsWith("-"))
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ min = split[0].isEmpty() ? MIN_VALUE : parseLong(split[0]);
+ max = split[1].isEmpty() ? MAX_VALUE : parseLong(split[1]);
+ }
+ default -> throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } else
+ min = parseLong(strValue);
+ }
+ } else {
+ min = parseLong(strValue);
+ if (!supportsNegatives && min < 0)
+ throw new CITParsingException("Negatives are not allowed", properties, value.position());
+ }
+
+ if (range) {
+ if (min == max)
+ range = false;
+ else if (min > max)
+ throw new CITParsingException("Could not parse range", properties, value.position());
+ }
+ } catch (Exception e) {
+ throw e instanceof CITParsingException citE ? citE : new CITParsingException("Could not parse long", properties, value.position(), e);
+ }
+ }
+
+ @Override
+ public boolean test(CITContext context) {
+ long value = getValue(context);
+
+ if (percentage) {
+ double percentValue = 100d * (double) value / (double) getPercentageTotalValue(context);
+ return range ? min <= percentValue && percentValue <= max : percentValue == (double) min;
+ } else
+ return range ? min <= value && value <= max : value == min;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/WeightCondition.java b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/WeightCondition.java
new file mode 100644
index 0000000..a53e019
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/cit/builtin/conditions/WeightCondition.java
@@ -0,0 +1,32 @@
+package shcm.shsupercm.fabric.citresewn.cit.builtin.conditions;
+
+import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint;
+import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITContext;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+/**
+ * Internal condition used to determine the priority CITs get tested in.<br>
+ * Weights default to 0 and higher weights get chosen over lower weights.<br>
+ * When two conflicting CITs have the same weight, their path in the resourcepack is used as a tie breaker.
+ */
+public class WeightCondition extends IntegerCondition {
+ @Entrypoint(CITConditionContainer.ENTRYPOINT)
+ public static final CITConditionContainer<WeightCondition> CONTAINER = new CITConditionContainer<>(WeightCondition.class, WeightCondition::new,
+ "weight");
+
+ public WeightCondition() {
+ super(false, true, false);
+ }
+
+ public int getWeight() {
+ return this.min;
+ }
+
+ public void setWeight(int weight) {
+ this.min = weight;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/config/BrokenPaths.java b/src/main/java/shcm/shsupercm/fabric/citresewn/config/BrokenPaths.java
new file mode 100644
index 0000000..f4b2c65
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/config/BrokenPaths.java
@@ -0,0 +1,23 @@
+package shcm.shsupercm.fabric.citresewn.config;
+
+import net.minecraft.util.Identifier;
+import shcm.shsupercm.fabric.citresewn.mixin.broken_paths.*;
+
+/**
+ * Broken paths are resourcepack file paths that do not follow {@link Identifier}'s specifications.<br>
+ * When enabled in config, CIT Resewn will forcibly allow broken paths to load.<br>
+ * If not enabled, broken paths has no effect on the game.
+ * @see CITResewnConfig#broken_paths
+ * @see CITResewnMixinConfiguration#broken_paths
+ * @see ReloadableResourceManagerImplMixin
+ * @see IdentifierMixin
+ * @see AbstractFileResourcePackMixin
+ */
+public class BrokenPaths {
+ /**
+ * When enabled, {@link Identifier}s will not check for their path's validity.
+ * @see ReloadableResourceManagerImplMixin
+ * @see IdentifierMixin
+ */
+ public static boolean processingBrokenPaths = false;
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfig.java b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfig.java
index 04b3166..cd66fed 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfig.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfig.java
@@ -2,26 +2,54 @@ package shcm.shsupercm.fabric.citresewn.config;
import com.google.gson.Gson;
import com.google.gson.stream.JsonWriter;
+import net.fabricmc.loader.api.FabricLoader;
import org.apache.commons.io.IOUtils;
import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.pack.CITParser;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITItem;
import java.io.*;
+/**
+ * Contains runtime representation of CIT Resewn's config, encoded using GSON.
+ */
public class CITResewnConfig {
+ /**
+ * Whether CIT Resewn should work or not.<br>
+ * Requires a restart.
+ */
public boolean enabled = true;
+ /**
+ * Mutes pack loading errors from logs.
+ */
public boolean mute_errors = false;
+ /**
+ * Mutes pack loading warnings from logs.
+ */
public boolean mute_warns = false;
- public float citenchantment_scroll_multiplier = 8f;
+ /**
+ * Invalidating interval for CITs' caches in milliseconds. Set to 0 to disable caching.
+ */
public int cache_ms = 50;
+ /**
+ * Should broken paths be allowed in resourcepacks. Requires a restart.
+ * @see BrokenPaths
+ */
public boolean broken_paths = false;
- private static final File FILE = new File("config/citresewn.json");
- public static CITResewnConfig INSTANCE() {
- return CITResewn.INSTANCE.config;
- }
+ /**
+ * CIT Resewn's config storage file.
+ */
+ private static final File FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "citresewn.json");
+
+ /**
+ * Active instance of the current config.
+ */
+ public static final CITResewnConfig INSTANCE = read();
+ /**
+ * Reads the stored config.
+ * @see #FILE
+ * @return the read config
+ */
public static CITResewnConfig read() {
if (!FILE.exists())
return new CITResewnConfig().write();
@@ -37,6 +65,11 @@ public class CITResewnConfig {
}
}
+ /**
+ * Saves this config to file.
+ * @see #FILE
+ * @return this
+ */
public CITResewnConfig write() {
Gson gson = new Gson();
JsonWriter writer = null;
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfigScreenFactory.java b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfigScreenFactory.java
index 055c976..c1c35b2 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfigScreenFactory.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnConfigScreenFactory.java
@@ -3,15 +3,33 @@ package shcm.shsupercm.fabric.citresewn.config;
import me.shedaniel.clothconfig2.api.ConfigBuilder;
import me.shedaniel.clothconfig2.api.ConfigCategory;
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
+import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
+import java.util.function.Function;
+
+/**
+ * Cloth Config integration to CIT Resewn's config
+ * @see CITResewnConfig
+ */
public class CITResewnConfigScreenFactory {
+ /**
+ * Used to get CIT Resewn - Defaults's Cloth Config implementation.
+ */
+ public static final String DEFAULTS_CONFIG_ENTRYPOINT = "citresewn-defaults:config_screen";
+
+ /**
+ * Creates a Cloth Config screen for the current active config instance.
+ * @param parent parent to return to from the config screen
+ * @return the config screen
+ * @throws NoClassDefFoundError if Cloth Config is not present
+ */
public static Screen create(Screen parent) {
- CITResewnConfig currentConfig = CITResewnConfig.INSTANCE(), defaultConfig = new CITResewnConfig();
+ CITResewnConfig currentConfig = CITResewnConfig.INSTANCE, defaultConfig = new CITResewnConfig();
ConfigBuilder builder = ConfigBuilder.create()
.setParentScreen(parent)
@@ -32,6 +50,24 @@ public class CITResewnConfigScreenFactory {
.setDefaultValue(defaultConfig.enabled)
.build());
+ if (FabricLoader.getInstance().isModLoaded("citresewn-defaults")) {
+ class CurrentScreen { boolean prevToggle = false; } final CurrentScreen currentScreen = new CurrentScreen();
+ category.addEntry(entryBuilder.startBooleanToggle(new TranslatableText("config.citresewn-defaults.title"), false)
+ .setTooltip(new TranslatableText("config.citresewn-defaults.tooltip"))
+
+ .setYesNoTextSupplier((b) -> {
+ if (b != currentScreen.prevToggle) {
+ //noinspection unchecked
+ MinecraftClient.getInstance().setScreen((Screen) FabricLoader.getInstance().getEntrypoints(DEFAULTS_CONFIG_ENTRYPOINT, Function.class).stream().findAny().orElseThrow().apply(create(parent)));
+
+ currentScreen.prevToggle = b;
+ }
+
+ return new TranslatableText("config.citresewn.configure");
+ })
+ .build());
+ }
+
category.addEntry(entryBuilder.startBooleanToggle(new TranslatableText("config.citresewn.mute_errors.title"), currentConfig.mute_errors)
.setTooltip(new TranslatableText("config.citresewn.mute_errors.tooltip"))
.setSaveConsumer(newConfig -> currentConfig.mute_errors = newConfig)
@@ -44,17 +80,10 @@ public class CITResewnConfigScreenFactory {
.setDefaultValue(defaultConfig.mute_warns)
.build());
- category.addEntry(entryBuilder.startFloatField(new TranslatableText("config.citresewn.citenchantment_scroll_multiplier.title"), currentConfig.citenchantment_scroll_multiplier)
- .setTooltip(new TranslatableText("config.citresewn.citenchantment_scroll_multiplier.tooltip"))
- .setSaveConsumer(newConfig -> currentConfig.citenchantment_scroll_multiplier = newConfig)
- .setDefaultValue(defaultConfig.citenchantment_scroll_multiplier)
- .setMin(0f)
- .build());
-
category.addEntry(entryBuilder.startIntSlider(new TranslatableText("config.citresewn.cache_ms.title"), currentConfig.cache_ms / 50, 0, 5 * 20)
.setTooltip(new TranslatableText("config.citresewn.cache_ms.tooltip"))
.setSaveConsumer(newConfig -> currentConfig.cache_ms = newConfig * 50)
- .setDefaultValue(currentConfig.cache_ms / 50)
+ .setDefaultValue(defaultConfig.cache_ms / 50)
.setTextGetter(ticks -> {
if (ticks <= 1)
return new TranslatableText("config.citresewn.cache_ms.ticks." + ticks).formatted(Formatting.AQUA);
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnMixinConfiguration.java b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnMixinConfiguration.java
index 4247494..6e4d2ce 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnMixinConfiguration.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnMixinConfiguration.java
@@ -1,23 +1,42 @@
package shcm.shsupercm.fabric.citresewn.config;
import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.api.ModContainer;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
+import shcm.shsupercm.fabric.citresewn.CITResewn;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
+/**
+ * Mixin configuration for CIT Resewn's mixins.
+ */
public class CITResewnMixinConfiguration implements IMixinConfigPlugin {
private static final String MIXINS_ROOT = "shcm.shsupercm.fabric.citresewn.mixin";
+ /**
+ * Is Broken Paths enabled in config.
+ * @see BrokenPaths
+ * @see CITResewnConfig#broken_paths
+ */
private boolean broken_paths;
+ /**
+ * Slightly modified mod ids for loaded mods and mods with compat mixins.
+ */
+ private final Set<String> mods = new HashSet<>(), compatMods = new HashSet<>();
+
@Override
public void onLoad(String mixinPackage) {
CITResewnConfig launchConfig = CITResewnConfig.read();
this.broken_paths = launchConfig.broken_paths;
+
+ for (ModContainer mod : FabricLoader.getInstance().getAllMods())
+ mods.add(mod.getMetadata().getId().replace('-', '_'));
}
@Override
@@ -29,8 +48,17 @@ public class CITResewnMixinConfiguration implements IMixinConfigPlugin {
if (mixinClassName.startsWith("broken_paths"))
return broken_paths;
- if (mixinClassName.equals("core.GroupResourcePackAccessor"))
- return FabricLoader.getInstance().isModLoaded("fabric-resource-loader-v0");
+ if (mixinClassName.startsWith("compat.")) {
+ mixinClassName = mixinClassName.substring(7);
+ String modid = mixinClassName.substring(0, mixinClassName.indexOf('.'));
+ if (mods.contains(modid)) {
+ if (compatMods.add(modid))
+ CITResewn.info("Loading compatibility for " + modid);
+
+ return true;
+ }
+ return false;
+ }
return true;
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnModMenu.java b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnModMenu.java
index 7a4954c..095f0cb 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnModMenu.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/config/CITResewnModMenu.java
@@ -2,12 +2,22 @@ package shcm.shsupercm.fabric.citresewn.config;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
+import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint;
import net.fabricmc.loader.api.FabricLoader;
-import shcm.shsupercm.fabric.citresewn.OptionalCompat;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.NoticeScreen;
+import net.minecraft.text.Text;
+/**
+ * Mod Menu config button integration.
+ */
+@Entrypoint("modmenu")
public class CITResewnModMenu implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
- return OptionalCompat.getModConfigScreenFactory()::apply;
+ if (FabricLoader.getInstance().isModLoaded("cloth-config2"))
+ return CITResewnConfigScreenFactory::create;
+
+ return parent -> new NoticeScreen(() -> MinecraftClient.getInstance().setScreen(parent), Text.of("CIT Resewn"), Text.of("CIT Resewn requires Cloth Config to be able to show the config."));
}
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITLoadException.java b/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITLoadException.java
deleted file mode 100644
index c40bc80..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITLoadException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.ex;
-
-import net.minecraft.resource.ResourcePack;
-import net.minecraft.util.Identifier;
-
-/**
- * Thrown when a cit failed to be loaded
- */
-public class CITLoadException extends Exception {
- public CITLoadException(ResourcePack resourcePack, Identifier identifier, String message) {
- super("Couldn't load CIT: " + message + " in " + resourcePack.getName() + " -> " + identifier.toString());
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParseException.java b/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParseException.java
deleted file mode 100644
index 95bc386..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParseException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.ex;
-
-import net.minecraft.resource.ResourcePack;
-import net.minecraft.util.Identifier;
-
-/**
- * Thrown when a cit failed to be parsed
- */
-public class CITParseException extends Exception {
- public CITParseException(ResourcePack resourcePack, Identifier identifier, String message) {
- super("Skipped CIT: " + message + " in " + resourcePack.getName() + " -> " + identifier.toString());
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParsingException.java b/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParsingException.java
new file mode 100644
index 0000000..f897ba7
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/ex/CITParsingException.java
@@ -0,0 +1,17 @@
+package shcm.shsupercm.fabric.citresewn.ex;
+
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+
+/**
+ * Thrown if errored while parsing the properties of a {@link PropertyGroup}.
+ * @see PropertyGroup#messageWithDescriptorOf(String, int)
+ */
+public class CITParsingException extends Exception {
+ public CITParsingException(String message, PropertyGroup propertyGroup, int position, Throwable throwable) {
+ super("Errored while parsing CIT: " + propertyGroup.messageWithDescriptorOf(message, position), throwable);
+ }
+
+ public CITParsingException(String message, PropertyGroup propertyGroup, int position) {
+ super("Errored while parsing CIT: " + propertyGroup.messageWithDescriptorOf(message, position));
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/ex/UnknownCITTypeException.java b/src/main/java/shcm/shsupercm/fabric/citresewn/ex/UnknownCITTypeException.java
new file mode 100644
index 0000000..23f9c82
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/ex/UnknownCITTypeException.java
@@ -0,0 +1,12 @@
+package shcm.shsupercm.fabric.citresewn.ex;
+
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+
+/**
+ * Thrown when a property group contains an unrecognized value associated with the "type" key.
+ */
+public class UnknownCITTypeException extends CITParsingException {
+ public UnknownCITTypeException(PropertyGroup propertyGroup, int position) {
+ super("Unknown type", propertyGroup, position);
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ChatScreenMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ChatScreenMixin.java
new file mode 100644
index 0000000..c79143b
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ChatScreenMixin.java
@@ -0,0 +1,38 @@
+package shcm.shsupercm.fabric.citresewn.mixin;
+
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ChatScreen;
+import net.minecraft.client.gui.screen.NoticeScreen;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+import shcm.shsupercm.fabric.citresewn.CITResewnCommand;
+import shcm.shsupercm.fabric.citresewn.config.CITResewnConfigScreenFactory;
+
+import static shcm.shsupercm.fabric.citresewn.CITResewnCommand.openConfig;
+
+/**
+ * Opens the config screen when running the "/citresewn config" command.
+ * @see CITResewnCommand#openConfig
+ */
+@Mixin(ChatScreen.class)
+public class ChatScreenMixin {
+ /**
+ * If {@link CITResewnCommand#openConfig} is true, changes the screen that's opened when the chat is closed to the config screen.
+ * @see CITResewnCommand#openConfig
+ */
+ @ModifyArg(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setScreen(Lnet/minecraft/client/gui/screen/Screen;)V"))
+ public Screen citresewn$redirectConfigScreen(Screen original) {
+ if (openConfig) {
+ openConfig = false;
+ return FabricLoader.getInstance().isModLoaded("cloth-config2") ?
+ CITResewnConfigScreenFactory.create(null) :
+ new NoticeScreen(() -> MinecraftClient.getInstance().setScreen(null), Text.of("CIT Resewn"), Text.of("CIT Resewn requires Cloth Config to be able to show the config."));
+ }
+
+ return original;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ModelLoaderMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ModelLoaderMixin.java
new file mode 100644
index 0000000..df11ac9
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/ModelLoaderMixin.java
@@ -0,0 +1,29 @@
+package shcm.shsupercm.fabric.citresewn.mixin;
+
+import net.minecraft.client.color.block.BlockColors;
+import net.minecraft.client.render.model.ModelLoader;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.util.profiler.Profiler;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import shcm.shsupercm.fabric.citresewn.cit.ActiveCITs;
+
+/**
+ * Initializes the (re)loading of active cits in the resource manager.
+ * @see ActiveCITs
+ */
+@Mixin(ModelLoader.class)
+public class ModelLoaderMixin {
+ /**
+ * @see ActiveCITs#load(ResourceManager, Profiler)
+ */
+ @Inject(method = "<init>", at =
+ @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V"))
+ private void citresewn$loadCITs(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int i, CallbackInfo ci) {
+ profiler.push("citresewn:reloading_cits");
+ ActiveCITs.load(resourceManager, profiler);
+ profiler.pop();
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/AbstractFileResourcePackMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/AbstractFileResourcePackMixin.java
index 281150b..b241fff 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/AbstractFileResourcePackMixin.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/AbstractFileResourcePackMixin.java
@@ -14,33 +14,43 @@ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import shcm.shsupercm.fabric.citresewn.config.BrokenPaths;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipFile;
-/* if (CITResewnConfig.read().broken_paths) */ @Mixin(AbstractFileResourcePack.class)
+/**
+ * Adds a resourcepack compatibility error message when broken paths are enabled and are detected in a pack.
+ * @see BrokenPaths
+ * @see ResourcePackCompatibilityMixin
+ */
+@Mixin(AbstractFileResourcePack.class)
public abstract class AbstractFileResourcePackMixin implements ResourcePack {
@Shadow @Final protected File base;
@SuppressWarnings({"unchecked", "ConstantConditions", "EqualsBetweenInconvertibleTypes"})
@Inject(method = "parseMetadata(Lnet/minecraft/resource/metadata/ResourceMetadataReader;)Ljava/lang/Object;", cancellable = true, at = @At("RETURN"))
- public <T extends PackResourceMetadata> void parseMetadata(ResourceMetadataReader<T> metaReader, CallbackInfoReturnable<T> cir) {
+ public <T extends PackResourceMetadata> void citresewn$brokenpaths$parseMetadata(ResourceMetadataReader<T> metaReader, CallbackInfoReturnable<T> cir) {
if (cir.getReturnValue() != null)
try {
if (this.getClass().equals(ZipResourcePack.class)) {
try (ZipFile zipFile = new ZipFile(base)) {
zipFile.stream()
.forEach(entry -> {
- if (entry.getName().startsWith("assets"))
+ if (entry.getName().startsWith("assets") && !entry.getName().endsWith(".DS_Store"))
new Identifier("minecraft", entry.getName());
});
}
} else if (this.getClass().equals(DirectoryResourcePack.class)) {
final Path assets = new File(base, "assets").toPath();
Files.walk(assets)
- .forEach(path -> new Identifier("minecraft", assets.relativize(path).toString().replace('\\', '/')));
+ .forEach(fullPath -> {
+ String path = assets.relativize(fullPath).toString().replace('\\', '/');
+ if (!path.endsWith(".DS_Store"))
+ new Identifier("minecraft", path);
+ });
}
} catch (InvalidIdentifierException e) {
cir.setReturnValue((T) new PackResourceMetadata(cir.getReturnValue().getDescription(), Integer.MAX_VALUE - 53));
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/IdentifierMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/IdentifierMixin.java
index da9888f..0bc36c3 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/IdentifierMixin.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/IdentifierMixin.java
@@ -6,12 +6,25 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import shcm.shsupercm.fabric.citresewn.CITResewn;
+import shcm.shsupercm.fabric.citresewn.config.BrokenPaths;
-/* if (CITResewnConfig.read().broken_paths) */ @Mixin(Identifier.class)
+import static shcm.shsupercm.fabric.citresewn.config.BrokenPaths.processingBrokenPaths;
+
+/**
+ * Applies broken paths logic when active.
+ * @see BrokenPaths
+ * @see ReloadableResourceManagerImplMixin
+ */
+@Mixin(Identifier.class)
public class IdentifierMixin {
- @Inject(method = "isPathValid", cancellable = true, at = @At("HEAD"))
- private static void processBrokenPaths(String path, CallbackInfoReturnable<Boolean> cir) {
- if (CITResewn.INSTANCE != null && CITResewn.INSTANCE.processingBrokenPaths)
+ @Inject(method = "isPathValid", cancellable = true, at = @At("RETURN"))
+ private static void citresewn$brokenpaths$processBrokenPaths(String path, CallbackInfoReturnable<Boolean> cir) {
+ if (!processingBrokenPaths)
+ return;
+
+ if (!cir.getReturnValue()) {
+ CITResewn.logWarnLoading("Warning: Encountered broken path: \"" + path + "\"");
cir.setReturnValue(true);
+ }
}
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ReloadableResourceManagerImplMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ReloadableResourceManagerImplMixin.java
index e2b193a..1d94285 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ReloadableResourceManagerImplMixin.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ReloadableResourceManagerImplMixin.java
@@ -12,20 +12,28 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import shcm.shsupercm.fabric.citresewn.CITResewn;
+import shcm.shsupercm.fabric.citresewn.config.BrokenPaths;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
-/* if (CITResewnConfig.read().broken_paths) */ @Mixin(ReloadableResourceManagerImpl.class)
+import static shcm.shsupercm.fabric.citresewn.config.BrokenPaths.processingBrokenPaths;
+
+/**
+ * Starts/Stops broken paths logic.
+ * @see BrokenPaths
+ * @see IdentifierMixin
+ */
+@Mixin(ReloadableResourceManagerImpl.class)
public class ReloadableResourceManagerImplMixin {
@Shadow @Final private ResourceType type;
@Inject(method = "reload", at = @At("RETURN"))
- public void onReload(Executor prepareExecutor, Executor applyExecutor, CompletableFuture<Unit> initialStage, List<ResourcePack> packs, CallbackInfoReturnable<ResourceReload> cir) {
- if (CITResewn.INSTANCE.processingBrokenPaths = this.type == ResourceType.CLIENT_RESOURCES) {
+ public void citresewn$brokenpaths$onReload(Executor prepareExecutor, Executor applyExecutor, CompletableFuture<Unit> initialStage, List<ResourcePack> packs, CallbackInfoReturnable<ResourceReload> cir) {
+ if (processingBrokenPaths = this.type == ResourceType.CLIENT_RESOURCES) {
CITResewn.LOG.error("[citresewn] Caution! Broken paths is enabled!");
- cir.getReturnValue().whenComplete().thenRun(() -> CITResewn.INSTANCE.processingBrokenPaths = false);
+ cir.getReturnValue().whenComplete().thenRun(() -> processingBrokenPaths = false);
}
}
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ResourcePackCompatibilityMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ResourcePackCompatibilityMixin.java
index f62221b..813eb3d 100644
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ResourcePackCompatibilityMixin.java
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/broken_paths/ResourcePackCompatibilityMixin.java
@@ -7,8 +7,14 @@ import org.spongepowered.asm.mixin.gen.Invoker;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import shcm.shsupercm.fabric.citresewn.config.BrokenPaths;
-/* if (CITResewnConfig.read().broken_paths) */ @Mixin(ResourcePackCompatibility.class)
+/**
+ * Adds a resourcepack compatibility error message when broken paths are enabled and are detected in a pack.
+ * @see BrokenPaths
+ * @see AbstractFileResourcePackMixin
+ */
+@Mixin(ResourcePackCompatibility.class)
public abstract class ResourcePackCompatibilityMixin {
private static final ResourcePackCompatibility BROKEN_PATHS = ResourcePackCompatibility("BROKEN_PATHS", -1, "broken_paths");
@@ -19,7 +25,7 @@ public abstract class ResourcePackCompatibilityMixin {
}
@Inject(method = "from(ILnet/minecraft/resource/ResourceType;)Lnet/minecraft/resource/ResourcePackCompatibility;", cancellable = true, at = @At("HEAD"))
- private static void redirectBrokenPathsCompatibility(int packVersion, ResourceType type, CallbackInfoReturnable<ResourcePackCompatibility> cir) {
+ private static void citresewn$brokenpaths$redirectBrokenPathsCompatibility(int packVersion, ResourceType type, CallbackInfoReturnable<ResourcePackCompatibility> cir) {
if (packVersion == Integer.MAX_VALUE - 53)
cir.setReturnValue(BROKEN_PATHS);
}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ArmorFeatureRendererMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ArmorFeatureRendererMixin.java
deleted file mode 100644
index 72a0ade..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ArmorFeatureRendererMixin.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citarmor;
-
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
-import net.minecraft.client.render.entity.model.BipedEntityModel;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.EquipmentSlot;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ArmorItem;
-import net.minecraft.item.ItemStack;
-import net.minecraft.util.Identifier;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.OptionalCompat;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-
-import java.lang.ref.WeakReference;
-import java.util.Map;
-
-@Mixin(ArmorFeatureRenderer.class)
-public class ArmorFeatureRendererMixin<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> {
- private WeakReference<Map<String, Identifier>> armorTexturesCached = null;
-
- @Inject(method = "renderArmor", at = @At("HEAD"))
- private void renderArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) {
- if (!CITResewnConfig.INSTANCE().enabled || CITResewn.INSTANCE.activeCITs == null)
- return;
-
- ItemStack itemStack = entity.getEquippedStack(armorSlot);
-
- //compat Cosmetic Armor
- itemStack = OptionalCompat.getCosmeticArmor(itemStack, entity, armorSlot, false);
-
- Map<String, Identifier> armorTextures = CITResewn.INSTANCE.activeCITs.getArmorTexturesCached(itemStack, entity.world, entity);
- if (armorTextures != null) {
- armorTexturesCached = new WeakReference<>(armorTextures);
- return;
- }
-
- armorTexturesCached = null;
- }
-
- @Inject(method = "getArmorTexture", cancellable = true, at = @At("HEAD"))
- private void getArmorTexture(ArmorItem item, boolean legs, String overlay, CallbackInfoReturnable<Identifier> cir) {
- if (armorTexturesCached == null)
- return;
- Map<String, Identifier> armorTextures = armorTexturesCached.get();
- if (armorTextures == null)
- return;
-
- Identifier identifier = armorTextures.get(item.getMaterial().getName() + "_layer_" + (legs ? "2" : "1") + (overlay == null ? "" : "_" + overlay));
- if (identifier != null)
- cir.setReturnValue(identifier);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ItemStackMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ItemStackMixin.java
deleted file mode 100644
index 6041c9b..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citarmor/ItemStackMixin.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citarmor;
-
-import net.minecraft.item.ItemStack;
-import org.spongepowered.asm.mixin.Mixin;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITArmor;
-
-import java.lang.ref.WeakReference;
-import java.util.function.Supplier;
-
-@Mixin(ItemStack.class)
-public class ItemStackMixin implements CITArmor.Cached {
- private WeakReference<CITArmor> citresewn_cachedCITArmor = new WeakReference<>(null);
- private long citresewn_cacheTimeCITArmor = 0;
-
- @Override
- public CITArmor citresewn_getCachedCITArmor(Supplier<CITArmor> realtime) {
- if (System.currentTimeMillis() - citresewn_cacheTimeCITArmor >= CITResewnConfig.INSTANCE().cache_ms) {
- citresewn_cachedCITArmor = new WeakReference<>(realtime.get());
- citresewn_cacheTimeCITArmor = System.currentTimeMillis();
- }
-
- return citresewn_cachedCITArmor.get();
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ElytraFeatureRendererMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ElytraFeatureRendererMixin.java
deleted file mode 100644
index b4151f8..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ElytraFeatureRendererMixin.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citelytra;
-
-import net.minecraft.client.render.RenderLayer;
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.EquipmentSlot;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.util.Identifier;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.ModifyArg;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.OptionalCompat;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-
-import java.lang.ref.WeakReference;
-
-@Mixin(ElytraFeatureRenderer.class)
-public class ElytraFeatureRendererMixin {
- private WeakReference<ItemStack> elytraItemCached = new WeakReference<>(null);
- private WeakReference<LivingEntity> livingEntityCached = new WeakReference<>(null);
-
- @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at =
- @At("HEAD"))
- private void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) {
- if (!CITResewnConfig.INSTANCE().enabled || CITResewn.INSTANCE.activeCITs == null)
- return;
-
- ItemStack itemStack = livingEntity.getEquippedStack(EquipmentSlot.CHEST);
-
- //compat Cosmetic Armor
- itemStack = OptionalCompat.getCosmeticArmor(itemStack, livingEntity, EquipmentSlot.CHEST, true);
-
- this.elytraItemCached = new WeakReference<>(itemStack);
- this.livingEntityCached = new WeakReference<>(livingEntity);
- }
-
- @ModifyArg(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at =
- @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/ItemRenderer;getArmorGlintConsumer(Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/client/render/RenderLayer;ZZ)Lnet/minecraft/client/render/VertexConsumer;"), index = 1)
- private RenderLayer getArmorCutoutNoCull(RenderLayer original) {
- if (CITResewnConfig.INSTANCE().enabled && CITResewn.INSTANCE.activeCITs != null) {
- ItemStack itemStack = this.elytraItemCached.get();
- LivingEntity livingEntity = this.livingEntityCached.get();
- if (itemStack != null && itemStack.isOf(Items.ELYTRA) && livingEntity != null) {
- Identifier elytraTexture = CITResewn.INSTANCE.activeCITs.getElytraTextureCached(itemStack, livingEntity.world, livingEntity);
- this.elytraItemCached = new WeakReference<>(null);
- this.livingEntityCached = new WeakReference<>(null);
- if (elytraTexture != null)
- return RenderLayer.getArmorCutoutNoCull(elytraTexture);
- }
- }
-
- return original;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ItemStackMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ItemStackMixin.java
deleted file mode 100644
index aaf4037..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citelytra/ItemStackMixin.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citelytra;
-
-import net.minecraft.item.ItemStack;
-import org.spongepowered.asm.mixin.Mixin;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITElytra;
-
-import java.lang.ref.WeakReference;
-import java.util.function.Supplier;
-
-@Mixin(ItemStack.class)
-public class ItemStackMixin implements CITElytra.Cached {
- private WeakReference<CITElytra> citresewn_cachedCITElytra = new WeakReference<>(null);
- private long citresewn_cacheTimeCITElytra = 0;
-
- @Override
- public CITElytra citresewn_getCachedCITElytra(Supplier<CITElytra> realtime) {
- if (System.currentTimeMillis() - citresewn_cacheTimeCITElytra >= CITResewnConfig.INSTANCE().cache_ms) {
- citresewn_cachedCITElytra = new WeakReference<>(realtime.get());
- citresewn_cacheTimeCITElytra = System.currentTimeMillis();
- }
-
- return citresewn_cachedCITElytra.get();
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ArmorFeatureRendererMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ArmorFeatureRendererMixin.java
deleted file mode 100644
index b4d81c7..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ArmorFeatureRendererMixin.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
-import net.minecraft.client.render.entity.model.BipedEntityModel;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.EquipmentSlot;
-import net.minecraft.entity.LivingEntity;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITEnchantment;
-
-@Mixin(ArmorFeatureRenderer.class)
-public class ArmorFeatureRendererMixin<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> {
- @Inject(method = "renderArmor", at = @At("HEAD"))
- private void setAppliedContextAndStartApplyingArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T livingEntity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) {
- if (CITResewnConfig.INSTANCE().enabled && CITResewn.INSTANCE.activeCITs != null) {
- CITResewn.INSTANCE.activeCITs.setEnchantmentAppliedContextCached(livingEntity.getEquippedStack(armorSlot), livingEntity.world, livingEntity);
- CITEnchantment.shouldApply = true;
- }
- }
-
- @Inject(method = "renderArmor", at = @At("RETURN"))
- private void stopApplyingArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T livingEntity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) {
- CITEnchantment.shouldApply = false;
- if (CITResewn.INSTANCE.activeCITs != null)
- CITResewn.INSTANCE.activeCITs.setEnchantmentAppliedContextCached(null, null, null);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/BufferBuilderStorageAccessor.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/BufferBuilderStorageAccessor.java
deleted file mode 100644
index 619a82d..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/BufferBuilderStorageAccessor.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.client.render.BufferBuilder;
-import net.minecraft.client.render.BufferBuilderStorage;
-import net.minecraft.client.render.RenderLayer;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-import java.util.SortedMap;
-
-@Mixin(BufferBuilderStorage.class)
-public interface BufferBuilderStorageAccessor {
- @Accessor("entityBuilders")
- SortedMap<RenderLayer, BufferBuilder> entityBuilders();
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ElytraFeatureRendererMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ElytraFeatureRendererMixin.java
deleted file mode 100644
index 03fb4c0..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ElytraFeatureRendererMixin.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.EquipmentSlot;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ItemStack;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITEnchantment;
-
-@Mixin(ElytraFeatureRenderer.class)
-public class ElytraFeatureRendererMixin {
- @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At("HEAD"))
- private void setAppliedContextAndStartApplyingElytra(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) {
- if (CITResewnConfig.INSTANCE().enabled && CITResewn.INSTANCE.activeCITs != null) {
- CITResewn.INSTANCE.activeCITs.setEnchantmentAppliedContextCached(livingEntity.getEquippedStack(EquipmentSlot.CHEST), livingEntity.world, livingEntity);
- CITEnchantment.shouldApply = true;
- }
- }
-
- @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At("RETURN"))
- private void stopApplyingElytra(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) {
- CITEnchantment.shouldApply = false;
- if (CITResewn.INSTANCE.activeCITs != null)
- CITResewn.INSTANCE.activeCITs.setEnchantmentAppliedContextCached(null, null, null);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemRendererMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemRendererMixin.java
deleted file mode 100644
index 13d91c7..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemRendererMixin.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.render.*;
-import net.minecraft.client.render.item.ItemRenderer;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.json.ModelTransformation;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ItemStack;
-import net.minecraft.world.World;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITEnchantment;
-
-@Mixin(ItemRenderer.class)
-public class ItemRendererMixin {
- @Inject(method = "getModel", at = @At("TAIL"))
- private void setAppliedContext(ItemStack stack, World world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
- if (CITResewnConfig.INSTANCE().enabled && CITResewn.INSTANCE.activeCITs != null)
- CITResewn.INSTANCE.activeCITs.setEnchantmentAppliedContextCached(stack, world, entity);
- }
-
- @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At("HEAD"))
- private void startApplyingItem(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) {
- if (CITResewnConfig.INSTANCE().enabled)
- CITEnchantment.shouldApply = true;
- }
-
- @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At("TAIL"))
- private void stopApplyingItem(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) {
- CITEnchantment.shouldApply = false;
- if (CITResewn.INSTANCE.activeCITs != null)
- CITResewn.INSTANCE.activeCITs.setEnchantmentAppliedContextCached(null, null, null);
- }
-
- @Inject(method = "getArmorGlintConsumer", cancellable = true, at = @At("RETURN"))
- private static void getArmorGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) {
- if (!CITEnchantment.shouldApply)
- return;
- VertexConsumer vertexConsumer = solid ? CITEnchantment.GlintRenderLayer.ARMOR_GLINT.tryApply(cir.getReturnValue(), layer, provider) : CITEnchantment.GlintRenderLayer.ARMOR_ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider);
- if (vertexConsumer != null)
- cir.setReturnValue(vertexConsumer);
- }
-
- @Inject(method = "getCompassGlintConsumer", cancellable = true, at = @At("RETURN"))
- private static void getCompassGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, MatrixStack.Entry entry, CallbackInfoReturnable<VertexConsumer> cir) {
- if (!CITEnchantment.shouldApply)
- return;
- VertexConsumer vertexConsumer = CITEnchantment.GlintRenderLayer.GLINT.tryApply(null, layer, provider);
- if (vertexConsumer != null)
- cir.setReturnValue(VertexConsumers.union(new OverlayVertexConsumer(vertexConsumer, entry.getPositionMatrix(), entry.getNormalMatrix()), cir.getReturnValue()));
- }
-
- @Inject(method = "getDirectCompassGlintConsumer", cancellable = true, at = @At("RETURN"))
- private static void getDirectCompassGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, MatrixStack.Entry entry, CallbackInfoReturnable<VertexConsumer> cir) {
- if (!CITEnchantment.shouldApply)
- return;
- VertexConsumer vertexConsumer = CITEnchantment.GlintRenderLayer.DIRECT_GLINT.tryApply(null, layer, provider);
- if (vertexConsumer != null)
- cir.setReturnValue(VertexConsumers.union(new OverlayVertexConsumer(vertexConsumer, entry.getPositionMatrix(), entry.getNormalMatrix()), cir.getReturnValue()));
- }
-
- @Inject(method = "getItemGlintConsumer", cancellable = true, at = @At("RETURN"))
- private static void getItemGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) {
- if (!CITEnchantment.shouldApply)
- return;
- VertexConsumer vertexConsumer = MinecraftClient.isFabulousGraphicsOrBetter() && layer == TexturedRenderLayers.getItemEntityTranslucentCull() ? CITEnchantment.GlintRenderLayer.GLINT_TRANSLUCENT.tryApply(cir.getReturnValue(), layer, provider) : (solid ? CITEnchantment.GlintRenderLayer.GLINT.tryApply(cir.getReturnValue(), layer, provider) : CITEnchantment.GlintRenderLayer.ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider));
- if (vertexConsumer != null)
- cir.setReturnValue(vertexConsumer);
- }
-
- @Inject(method = "getDirectItemGlintConsumer", cancellable = true, at = @At("RETURN"))
- private static void getDirectItemGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) {
- if (!CITEnchantment.shouldApply)
- return;
- VertexConsumer vertexConsumer = solid ? CITEnchantment.GlintRenderLayer.DIRECT_GLINT.tryApply(cir.getReturnValue(), layer, provider) : CITEnchantment.GlintRenderLayer.DIRECT_ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider);
- if (vertexConsumer != null)
- cir.setReturnValue(vertexConsumer);
- }
-} \ No newline at end of file
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemStackMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemStackMixin.java
deleted file mode 100644
index 033bf8e..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/ItemStackMixin.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.item.ItemStack;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITEnchantment;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-import java.util.function.Supplier;
-
-@Mixin(ItemStack.class)
-public class ItemStackMixin implements CITEnchantment.Cached {
- private WeakReference<List<CITEnchantment>> citresewn_cachedCITEnchantment = new WeakReference<>(null);
- private long citresewn_cacheTimeCITEnchantment = 0;
-
- @Override
- public List<CITEnchantment> citresewn_getCachedCITEnchantment(Supplier<List<CITEnchantment>> realtime) {
- if (System.currentTimeMillis() - citresewn_cacheTimeCITEnchantment >= CITResewnConfig.INSTANCE().cache_ms) {
- citresewn_cachedCITEnchantment = new WeakReference<>(realtime.get());
- citresewn_cacheTimeCITEnchantment = System.currentTimeMillis();
- }
-
- return citresewn_cachedCITEnchantment.get();
- }
-
- @Inject(method = "hasGlint", cancellable = true, at = @At("HEAD"))
- private void disableDefaultGlint(CallbackInfoReturnable<Boolean> cir) {
- if (CITResewn.INSTANCE.activeCITs != null && ((!CITResewn.INSTANCE.activeCITs.effectiveGlobalProperties.useGlint) || (CITEnchantment.appliedContext != null && CITEnchantment.shouldApply && !CITEnchantment.appliedContext.get(0).useGlint)))
- cir.setReturnValue(false);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/MinecraftClientMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/MinecraftClientMixin.java
deleted file mode 100644
index a9bea56..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/MinecraftClientMixin.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.client.MinecraftClient;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import static shcm.shsupercm.fabric.citresewn.pack.cits.CITEnchantment.MergeMethod.ticks;
-
-@Mixin(MinecraftClient.class)
-public class MinecraftClientMixin {
- @Inject(method = "tick", at = @At("HEAD"))
- public void onTick(CallbackInfo ci) {
- ticks++;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/RenderPhaseAccessor.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/RenderPhaseAccessor.java
deleted file mode 100644
index 24e7c3c..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/RenderPhaseAccessor.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.citenchantment;
-
-import net.minecraft.client.render.RenderPhase;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-@Mixin(RenderPhase.class)
-public interface RenderPhaseAccessor {
- @Accessor("ARMOR_GLINT_SHADER") static RenderPhase.Shader ARMOR_GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("ARMOR_ENTITY_GLINT_SHADER") static RenderPhase.Shader ARMOR_ENTITY_GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("TRANSLUCENT_GLINT_SHADER") static RenderPhase.Shader TRANSLUCENT_GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("GLINT_SHADER") static RenderPhase.Shader GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("DIRECT_GLINT_SHADER") static RenderPhase.Shader DIRECT_GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("ENTITY_GLINT_SHADER") static RenderPhase.Shader ENTITY_GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("DIRECT_ENTITY_GLINT_SHADER") static RenderPhase.Shader DIRECT_ENTITY_GLINT_SHADER() { throw new RuntimeException(); }
- @Accessor("DISABLE_CULLING") static RenderPhase.Cull DISABLE_CULLING() { throw new RuntimeException(); }
- @Accessor("EQUAL_DEPTH_TEST") static RenderPhase.DepthTest EQUAL_DEPTH_TEST() { throw new RuntimeException(); }
- @Accessor("COLOR_MASK") static RenderPhase.WriteMaskState COLOR_MASK() { throw new RuntimeException(); }
- @Accessor("VIEW_OFFSET_Z_LAYERING") static RenderPhase.Layering VIEW_OFFSET_Z_LAYERING() { throw new RuntimeException(); }
- @Accessor("ITEM_TARGET") static RenderPhase.Target ITEM_TARGET() { throw new RuntimeException(); }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemRendererMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemRendererMixin.java
deleted file mode 100644
index 2cb5662..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemRendererMixin.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.cititem;
-
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.item.ItemModels;
-import net.minecraft.client.render.item.ItemRenderer;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.json.ModelTransformation;
-import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.world.World;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.ModifyVariable;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITItem;
-
-import java.lang.ref.WeakReference;
-
-@Mixin(ItemRenderer.class)
-public class ItemRendererMixin {
- @Shadow @Final private ItemModels models;
-
- private static WeakReference<BakedModel> mojankCITModel = null;
-
- @Inject(method = "getModel", cancellable = true, at = @At("HEAD"))
- private void getItemModel(ItemStack stack, World world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
- if (!CITResewnConfig.INSTANCE().enabled || CITResewn.INSTANCE.activeCITs == null)
- return;
-
- BakedModel citModel = CITResewn.INSTANCE.activeCITs.getItemModelCached(stack, world == null ? MinecraftClient.getInstance().world : world, entity, seed);
- if (citModel != null)
- cir.setReturnValue(citModel);
- }
-
- @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At("HEAD"))
- private void fixMojankCITsContext(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) {
- mojankCITModel = null;
- if (((CITItem.Cached) (Object) stack).citresewn_isMojankCIT()) {
- boolean bl = renderMode == ModelTransformation.Mode.GUI || renderMode == ModelTransformation.Mode.GROUND || renderMode == ModelTransformation.Mode.FIXED;
- if (bl)
- mojankCITModel = new WeakReference<>(model);
- else { // rendered in hand model of trident/spyglass
- if (stack.isOf(Items.TRIDENT))
- mojankCITModel = new WeakReference<>(this.models.getModelManager().getModel(new ModelIdentifier("minecraft:trident_in_hand#inventory")));
- else if (stack.isOf(Items.SPYGLASS))
- mojankCITModel = new WeakReference<>(this.models.getModelManager().getModel(new ModelIdentifier("minecraft:spyglass_in_hand#inventory")));
- }
- } else
- mojankCITModel = null;
- }
-
- @ModifyVariable(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At(value = "LOAD", ordinal = 0, target = "Lnet/minecraft/client/render/model/BakedModel;getTransformation()Lnet/minecraft/client/render/model/json/ModelTransformation;"), argsOnly = true)
- private BakedModel fixMojankCITs(BakedModel original) {
- if (mojankCITModel != null)
- return mojankCITModel.get();
-
- return original;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemStackMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemStackMixin.java
deleted file mode 100644
index 4c888d8..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ItemStackMixin.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.cititem;
-
-import net.minecraft.item.ItemStack;
-import org.spongepowered.asm.mixin.Mixin;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITItem;
-
-import java.lang.ref.WeakReference;
-import java.util.function.Supplier;
-
-@Mixin(ItemStack.class)
-public class ItemStackMixin implements CITItem.Cached {
- private WeakReference<CITItem> citresewn_cachedCITItem = new WeakReference<>(null);
- private long citresewn_cacheTimeCITItem = 0;
-
- /**
- * Used by tridents and spy glasses to force their unique overrides to be applied correctly
- */
- private boolean citresewn_mojankCIT = false;
-
- @Override
- public CITItem citresewn_getCachedCITItem(Supplier<CITItem> realtime) {
- if (System.currentTimeMillis() - citresewn_cacheTimeCITItem >= CITResewnConfig.INSTANCE().cache_ms) {
- citresewn_cachedCITItem = new WeakReference<>(realtime.get());
- citresewn_cacheTimeCITItem = System.currentTimeMillis();
- }
-
- return citresewn_cachedCITItem.get();
- }
-
- @Override
- public boolean citresewn_isMojankCIT() {
- return citresewn_mojankCIT;
- }
-
- @Override
- public void citresewn_setMojankCIT(boolean mojankCIT) {
- citresewn_mojankCIT = mojankCIT;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/JsonUnbakedModelAccessor.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/JsonUnbakedModelAccessor.java
deleted file mode 100644
index f997d3e..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/JsonUnbakedModelAccessor.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.cititem;
-
-import com.mojang.datafixers.util.Either;
-import net.minecraft.client.render.model.json.JsonUnbakedModel;
-import net.minecraft.client.util.SpriteIdentifier;
-import net.minecraft.util.Identifier;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-import java.util.Map;
-
-@Mixin(JsonUnbakedModel.class)
-public interface JsonUnbakedModelAccessor {
- @Accessor
- Map<String, Either<SpriteIdentifier, String>> getTextureMap();
-
- @Accessor
- Identifier getParentId();
-
- @Accessor
- void setParentId(Identifier parentId);
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ModelLoaderMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ModelLoaderMixin.java
deleted file mode 100644
index ec04910..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/ModelLoaderMixin.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.cititem;
-
-import com.mojang.datafixers.util.Either;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.ModelLoader;
-import net.minecraft.client.render.model.SpriteAtlasManager;
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.render.model.json.JsonUnbakedModel;
-import net.minecraft.client.render.model.json.ModelOverride;
-import net.minecraft.client.texture.TextureManager;
-import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.client.util.SpriteIdentifier;
-import net.minecraft.resource.Resource;
-import net.minecraft.resource.ResourceManager;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.profiler.Profiler;
-import org.apache.commons.io.IOUtils;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.ModifyArg;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.pack.ResewnItemModelIdentifier;
-import shcm.shsupercm.fabric.citresewn.pack.ResewnTextureIdentifier;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CIT;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITItem;
-
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static shcm.shsupercm.fabric.citresewn.CITResewn.info;
-
-@Mixin(ModelLoader.class)
-public class ModelLoaderMixin {
- @Shadow @Final private ResourceManager resourceManager;
- @Shadow @Final private Set<Identifier> modelsToLoad;
- @Shadow @Final private Map<Identifier, UnbakedModel> modelsToBake;
- @Shadow @Final private Map<Identifier, UnbakedModel> unbakedModels;
- @Shadow @Final private Map<Identifier, BakedModel> bakedModels;
-
- @Inject(method = "addModel", at = @At("TAIL"))
- public void addCITItemModels(ModelIdentifier eventModelId, CallbackInfo ci) { if (eventModelId != ModelLoader.MISSING_ID) return;
- if (CITResewn.INSTANCE.activeCITs == null)
- return;
-
- info("Loading CITItem models...");
- CITResewn.INSTANCE.activeCITs.citItems.values().stream()
- .flatMap(Collection::stream)
- .distinct()
- .forEach(citItem -> {
- try {
- citItem.loadUnbakedAssets(resourceManager);
-
- for (JsonUnbakedModel unbakedModel : citItem.unbakedAssets.values()) {
- ResewnItemModelIdentifier id = new ResewnItemModelIdentifier(unbakedModel.id);
- this.unbakedModels.put(id, unbakedModel);
- this.modelsToLoad.addAll(unbakedModel.getModelDependencies());
- this.modelsToBake.put(id, unbakedModel);
- }
- } catch (Exception e) {
- CITResewn.logErrorLoading(e.getMessage());
- }
- });
-
- CITItem.GENERATED_SUB_CITS_SEEN.clear();
- }
-
- @Inject(method = "upload", at = @At("RETURN"))
- public void linkBakedCITItemModels(TextureManager textureManager, Profiler profiler, CallbackInfoReturnable<SpriteAtlasManager> cir) {
- if (CITResewn.INSTANCE.activeCITs == null)
- return;
-
- profiler.push("citresewn_linking");
- info("Linking baked models to CITItems...");
-
- if (CITResewn.INSTANCE.activeCITs != null) {
- for (CITItem citItem : CITResewn.INSTANCE.activeCITs.citItems.values().stream().flatMap(Collection::stream).distinct().collect(Collectors.toList())) {
- for (Map.Entry<List<ModelOverride.Condition>, JsonUnbakedModel> citModelEntry : citItem.unbakedAssets.entrySet()) {
- if (citModelEntry.getKey() == null) {
- citItem.bakedModel = this.bakedModels.get(new ResewnItemModelIdentifier(citModelEntry.getValue().id));
- } else {
- BakedModel bakedModel = bakedModels.get(new ResewnItemModelIdentifier(citModelEntry.getValue().id));
-
- if (bakedModel == null)
- CITResewn.logWarnLoading("Skipping sub cit: Failed loading model for \"" + citModelEntry.getValue().id + "\" in " + citItem.pack.resourcePack.getName() + " -> " + citItem.propertiesIdentifier.getPath());
- else
- citItem.bakedSubModels.override(citModelEntry.getKey(), bakedModel);
- }
- }
- citItem.unbakedAssets = null;
- }
- }
-
- profiler.pop();
- }
-
-
- @Inject(method = "loadModelFromJson", cancellable = true, at = @At("HEAD"))
- public void forceLiteralResewnModelIdentifier(Identifier id, CallbackInfoReturnable<JsonUnbakedModel> cir) {
- if (id instanceof ResewnItemModelIdentifier) {
- InputStream is = null;
- Resource resource = null;
- try {
- JsonUnbakedModel json = JsonUnbakedModel.deserialize(IOUtils.toString(is = (resource = resourceManager.getResource(id)).getInputStream(), StandardCharsets.UTF_8));
- json.id = id.toString();
- json.id = json.id.substring(0, json.id.length() - 5);
-
- ((JsonUnbakedModelAccessor) json).getTextureMap().replaceAll((layer, original) -> {
- Optional<SpriteIdentifier> left = original.left();
- if (left.isPresent()) {
- String originalPath = left.get().getTextureId().getPath();
- String[] split = originalPath.split("/");
- if (originalPath.startsWith("./") || (split.length > 2 && split[1].equals("cit"))) {
- Identifier resolvedIdentifier = CIT.resolvePath(id, originalPath, ".png", identifier -> resourceManager.containsResource(identifier));
- if (resolvedIdentifier != null)
- return Either.left(new SpriteIdentifier(left.get().getAtlasId(), new ResewnTextureIdentifier(resolvedIdentifier)));
- }
- }
- return original;
- });
-
- Identifier parentId = ((JsonUnbakedModelAccessor) json).getParentId();
- if (parentId != null) {
- String[] parentIdPathSplit = parentId.getPath().split("/");
- if (parentId.getPath().startsWith("./") || (parentIdPathSplit.length > 2 && parentIdPathSplit[1].equals("cit"))) {
- parentId = CIT.resolvePath(id, parentId.getPath(), ".json", identifier -> resourceManager.containsResource(identifier));
- if (parentId != null)
- ((JsonUnbakedModelAccessor) json).setParentId(new ResewnItemModelIdentifier(parentId));
- }
- }
-
- json.getOverrides().replaceAll(override -> {
- String[] modelIdPathSplit = override.getModelId().getPath().split("/");
- if (override.getModelId().getPath().startsWith("./") || (modelIdPathSplit.length > 2 && modelIdPathSplit[1].equals("cit"))) {
- Identifier resolvedOverridePath = CIT.resolvePath(id, override.getModelId().getPath(), ".json", identifier -> resourceManager.containsResource(identifier));
- if (resolvedOverridePath != null)
- return new ModelOverride(new ResewnItemModelIdentifier(resolvedOverridePath), override.streamConditions().collect(Collectors.toList()));
- }
-
- return override;
- });
-
- cir.setReturnValue(json);
- } catch (Exception ignored) {
- } finally {
- IOUtils.closeQuietly(is, resource);
- }
- }
- }
-
- @ModifyArg(method = "loadModelFromJson", at =
- @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourceManager;getResource(Lnet/minecraft/util/Identifier;)Lnet/minecraft/resource/Resource;"))
- public Identifier fixDuplicatePrefixSuffix(Identifier original) {
- if (original.getPath().startsWith("models/models/") && original.getPath().endsWith(".json.json") && original.getPath().contains("cit"))
- return new Identifier(original.getNamespace(), original.getPath().substring(7, original.getPath().length() - 5));
-
- return original;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/compat/lambdabettergrass/PackParserMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/compat/lambdabettergrass/PackParserMixin.java
new file mode 100644
index 0000000..69e432c
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/compat/lambdabettergrass/PackParserMixin.java
@@ -0,0 +1,26 @@
+package shcm.shsupercm.fabric.citresewn.mixin.compat.lambdabettergrass;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import shcm.shsupercm.fabric.citresewn.pack.PackParser;
+
+import java.io.IOException;
+
+@SuppressWarnings("UnresolvedMixinReference")
+@Mixin(value = PackParser.class, remap = false)
+public class PackParserMixin {
+ @ModifyVariable(method = "loadGlobalProperties(Lnet/minecraft/resource/ResourceManager;Lshcm/shsupercm/fabric/citresewn/pack/GlobalProperties;)Lshcm/shsupercm/fabric/citresewn/pack/GlobalProperties;", at =
+ @At(value = "INVOKE", target = "shcm/shsupercm/fabric/citresewn/CITResewn.logErrorLoading(Ljava/lang/String;)V"))
+ private static IOException citresewn$compat$lambdabettergrass$muteStacktrace(IOException exception) {
+ if (exception.getMessage().contains("lambdabettergrass"))
+ return new IOException() {
+ @Override
+ public void printStackTrace() {
+ //no
+ }
+ };
+
+ return exception;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ChatScreenMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ChatScreenMixin.java
deleted file mode 100644
index cdd9eb1..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ChatScreenMixin.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.core;
-
-import net.minecraft.client.gui.screen.ChatScreen;
-import net.minecraft.client.gui.screen.Screen;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.ModifyArg;
-import shcm.shsupercm.fabric.citresewn.OptionalCompat;
-
-import static shcm.shsupercm.fabric.citresewn.CITResewnCommand.openConfig;
-
-@Mixin(ChatScreen.class)
-public class ChatScreenMixin {
- @ModifyArg(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setScreen(Lnet/minecraft/client/gui/screen/Screen;)V"))
- public Screen redirectConfigScreen(Screen original) {
- if (original == null && openConfig) {
- openConfig = false;
- return OptionalCompat.getModConfigScreenFactory().apply(null);
- }
-
- return original;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/GroupResourcePackAccessor.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/GroupResourcePackAccessor.java
deleted file mode 100644
index 7ab1c16..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/GroupResourcePackAccessor.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.core;
-
-import net.fabricmc.fabric.api.resource.ModResourcePack;
-import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-import java.util.List;
-
-/* if (FabricLoader.getInstance().isModLoaded("fabric-resource-loader-v0")) */ @Mixin(GroupResourcePack.class)
-public interface GroupResourcePackAccessor {
- @Accessor
- List<ModResourcePack> getPacks();
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ModelLoaderMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ModelLoaderMixin.java
deleted file mode 100644
index d65ffb8..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ModelLoaderMixin.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.core;
-
-import net.minecraft.client.render.model.ModelLoader;
-import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.resource.ResourceManager;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import shcm.shsupercm.fabric.citresewn.ActiveCITs;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-import shcm.shsupercm.fabric.citresewn.pack.CITParser;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CIT;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import static shcm.shsupercm.fabric.citresewn.CITResewn.info;
-
-@Mixin(value = ModelLoader.class, priority = 999)
-public abstract class ModelLoaderMixin {
- @Shadow @Final private ResourceManager resourceManager;
-
- @Inject(method = "addModel", at = @At("TAIL"))
- public void initCITs(ModelIdentifier eventModelId, CallbackInfo ci) { if (eventModelId != ModelLoader.MISSING_ID) return;
- if (CITResewn.INSTANCE.activeCITs != null) {
- info("Clearing active CITs..");
- CITResewn.INSTANCE.activeCITs.dispose();
- CITResewn.INSTANCE.activeCITs = null;
- }
-
- if (!CITResewnConfig.INSTANCE().enabled)
- return;
-
- info("Parsing CITs...");
- List<CITPack> parsedPacks = CITParser.parseCITs(resourceManager.streamResourcePacks().collect(Collectors.toCollection(ArrayList::new)));
- List<CIT> parsed = parsedPacks.stream().flatMap(pack -> pack.cits.stream()).collect(Collectors.toCollection(ArrayList::new));
-
- if (parsed.size() > 0) {
- info("Activating CITs...");
- CITResewn.INSTANCE.activeCITs = new ActiveCITs(parsedPacks, parsed);
- } else
- info("No cit packs found.");
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/NbtCompoundAccessor.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/NbtCompoundAccessor.java
deleted file mode 100644
index 8ced305..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/NbtCompoundAccessor.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.core;
-
-import net.minecraft.nbt.NbtCompound;
-import net.minecraft.nbt.NbtElement;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-import java.util.Map;
-
-@Mixin(NbtCompound.class)
-public interface NbtCompoundAccessor {
- @Accessor
- Map<String, NbtElement> getEntries();
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/SpriteAtlasTextureMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/SpriteAtlasTextureMixin.java
deleted file mode 100644
index 8cc195b..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/SpriteAtlasTextureMixin.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.core;
-
-import net.minecraft.client.texture.SpriteAtlasTexture;
-import net.minecraft.util.Identifier;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import shcm.shsupercm.fabric.citresewn.pack.ResewnTextureIdentifier;
-
-@Mixin(SpriteAtlasTexture.class)
-public class SpriteAtlasTextureMixin {
- @Inject(method = "getTexturePath", cancellable = true, at = @At("HEAD"))
- public void forceLiteralResewnTextureIdentifier(Identifier id, CallbackInfoReturnable<Identifier> cir) {
- if (id instanceof ResewnTextureIdentifier)
- cir.setReturnValue(id);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ZipResourcePackMixin.java b/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ZipResourcePackMixin.java
deleted file mode 100644
index 7acbcb7..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/ZipResourcePackMixin.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.mixin.core;
-
-import com.google.common.collect.Lists;
-import net.minecraft.resource.ResourceType;
-import net.minecraft.resource.ZipResourcePack;
-import net.minecraft.util.Identifier;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-@Mixin(ZipResourcePack.class)
-public abstract class ZipResourcePackMixin {
- @Shadow protected abstract ZipFile getZipFile() throws IOException;
-
- @Inject(method = "findResources", cancellable = true, at = @At("HEAD"))
- public void fixDepthBug(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter, CallbackInfoReturnable<Collection<Identifier>> cir) {
- if (maxDepth != Integer.MAX_VALUE - 53)
- return;
-
- ZipFile zipFile2;
- try {
- zipFile2 = this.getZipFile();
- } catch (IOException var15) {
- cir.setReturnValue(Collections.emptySet()); return;
- }
-
- Enumeration<? extends ZipEntry> enumeration = zipFile2.entries();
- List<Identifier> list = Lists.newArrayList();
- String var10000 = type.getDirectory();
- String string = var10000 + "/" + namespace + "/";
- String string2 = string + prefix + "/";
-
- while(enumeration.hasMoreElements()) {
- ZipEntry zipEntry = enumeration.nextElement();
- if (!zipEntry.isDirectory()) {
- String string3 = zipEntry.getName();
- if (!string3.endsWith(".mcmeta") && string3.startsWith(string2)) {
- String string4 = string3.substring(string.length());
- String[] strings = string4.split("/");
- if (pathFilter.test(strings[strings.length - 1])) {
- list.add(new Identifier(namespace, string4));
- }
- }
- }
- }
-
- cir.setReturnValue(list); return;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITPack.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITPack.java
deleted file mode 100644
index aadf518..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITPack.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack;
-
-import net.minecraft.resource.ResourcePack;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CIT;
-import shcm.shsupercm.fabric.citresewn.pack.cits.CITEnchantment;
-
-import java.util.*;
-
-public class CITPack {
- public final ResourcePack resourcePack;
- public final Collection<CIT> cits = new ArrayList<>();
-
- public CITEnchantment.MergeMethod method = CITEnchantment.MergeMethod.AVERAGE;
- public Integer cap = 8;
- public Float fade = 0.5f;
- public Boolean useGlint = true;
-
- public CITPack(ResourcePack resourcePack) {
- this.resourcePack = resourcePack;
- }
-
- public void loadGlobalProperties(Properties properties) throws Exception {
- try {
- this.method = properties.containsKey("method") ? CITEnchantment.MergeMethod.valueOf(properties.getProperty("method").toUpperCase(Locale.ENGLISH)) : null;
-
- if (properties.containsKey("cap")) {
- this.cap = Integer.parseInt(properties.getProperty("cap"));
- if (this.cap < 0)
- throw new Exception("cap cannot be negative");
- } else
- this.cap = null;
-
- if (properties.containsKey("fade")) {
- this.fade = Float.parseFloat(properties.getProperty("fade"));
- if (this.fade < 0f)
- throw new Exception("fade cannot be negative");
- } else
- this.fade = null;
-
- this.useGlint = properties.containsKey("useGlint") ? switch (properties.getProperty("useGlint").toLowerCase(Locale.ENGLISH)) {
- case "true" -> true;
- case "false" -> false;
- default -> throw new Exception("useGlint is not a boolean");
- } : null;
- } catch (Exception e) {
- this.method = null;
- this.cap = null;
- this.fade = null;
- this.useGlint = null;
- throw e;
- }
- }
-
- public void loadGlobalProperties(CITPack properties) {
- if (properties.method != null)
- this.method = properties.method;
- if (properties.cap != null)
- this.cap = properties.cap;
- if (properties.fade != null)
- this.fade = properties.fade;
- if (properties.useGlint != null)
- this.useGlint = properties.useGlint;
- }
-
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITParser.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITParser.java
deleted file mode 100644
index 974d2ec..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/CITParser.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack;
-
-import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack;
-import net.minecraft.resource.ResourcePack;
-import net.minecraft.resource.ResourceType;
-import net.minecraft.util.Identifier;
-import org.apache.commons.lang3.StringUtils;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.ex.CITParseException;
-import shcm.shsupercm.fabric.citresewn.mixin.core.GroupResourcePackAccessor;
-import shcm.shsupercm.fabric.citresewn.pack.cits.*;
-
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * Parses cits from resourcepacks
- */
-public final class CITParser { private CITParser() {}
- /**
- * CIT type registry.
- */
- public static final Map<String, CITConstructor> REGISTRY = new HashMap<>();
- static {
- REGISTRY.put("item", CITItem::new);
- REGISTRY.put("armor", CITArmor::new);
- REGISTRY.put("elytra", CITElytra::new);
- REGISTRY.put("enchantment", CITEnchantment::new);
- }
-
- /**
- * Parses cit entries from an ordered collection of resourcepacks.
- * @param packs packs to parse
- * @return a collection of parsed CITs
- */
- public static List<CITPack> parseCITs(Collection<ResourcePack> packs) {
- return packs.stream()
- .map(CITParser::parse)
- .flatMap(Collection::stream)
- .collect(Collectors.toList());
- }
-
- /**
- * Parses a resourcepack into a possible collection of citpacks that are contained within.
- * @param resourcePack pack to parse
- * @return a collection of CITPacks or an empty collection if resourcepack contains none
- */
- public static Collection<CITPack> parse(ResourcePack resourcePack) {
- if (resourcePack instanceof GroupResourcePack)
- return ((GroupResourcePackAccessor) resourcePack).getPacks().stream()
- .map(CITParser::parse)
- .flatMap(Collection::stream)
- .collect(Collectors.toList());
-
- final CITPack citPack = new CITPack(resourcePack);
-
- Collection<Identifier> packProperties = new ArrayList<>();
- for (String namespace : resourcePack.getNamespaces(ResourceType.CLIENT_RESOURCES))
- if (Identifier.isValid(namespace))
- for (String citRoot : new String[] { "citresewn", "optifine", "mcpatcher" }) {
- packProperties.addAll(resourcePack.findResources(ResourceType.CLIENT_RESOURCES, namespace, citRoot + "/cit", Integer.MAX_VALUE - 53, s -> s.endsWith(".properties")));
- Identifier global = new Identifier(namespace, citRoot + "/cit.properties");
- if (resourcePack.contains(ResourceType.CLIENT_RESOURCES, global))
- packProperties.add(global);
- }
-
- boolean readGlobalProperties = false;
- for (Iterator<Identifier> iterator = packProperties.iterator(); iterator.hasNext(); ) {
- Identifier propertiesIdentifier = iterator.next();
- try {
- if (StringUtils.countMatches(propertiesIdentifier.getPath(), '/') <= 2 && propertiesIdentifier.getPath().endsWith("cit.properties")) {
- iterator.remove();
- if (!readGlobalProperties)
- try (InputStream is = resourcePack.open(ResourceType.CLIENT_RESOURCES, propertiesIdentifier)) {
- Properties citProperties = new Properties();
- citProperties.load(is);
- citPack.loadGlobalProperties(citProperties);
- readGlobalProperties = true;
- }
- }
- } catch (Exception e) {
- CITResewn.logErrorLoading("Skipped global properties: " + e.getMessage() + " in " + resourcePack.getName() + " -> " + propertiesIdentifier);
- }
- }
-
- packProperties.stream()
- .flatMap(citIdentifier -> {
- try (InputStream is = resourcePack.open(ResourceType.CLIENT_RESOURCES, citIdentifier)) {
- Properties citProperties = new Properties();
- citProperties.load(new InputStreamReader(is, StandardCharsets.UTF_8));
-
- CITConstructor type = REGISTRY.get(citProperties.getProperty("type", "item"));
- if (type == null)
- throw new CITParseException(citPack.resourcePack, citIdentifier, "Unknown cit type \"" + citProperties.getProperty("type") + "\"");
-
- return Stream.of(type.cit(citPack, citIdentifier, citProperties));
- } catch (Exception e) {
- CITResewn.logErrorLoading(e.getMessage());
- return Stream.empty();
- }
- })
- .collect(Collectors.toCollection(() -> citPack.cits));
-
- if (citPack.cits.isEmpty())
- return Collections.emptySet();
- else {
- CITResewn.info("Found " + citPack.cits.size() + " CIT" + (citPack.cits.size() == 1 ? "" : "s") + " in " + resourcePack.getName());
- return Collections.singleton(citPack);
- }
- }
-
- public interface CITConstructor {
- CIT cit(CITPack pack, Identifier identifier, Properties properties) throws CITParseException;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/GlobalProperties.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/GlobalProperties.java
new file mode 100644
index 0000000..3e93b07
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/GlobalProperties.java
@@ -0,0 +1,72 @@
+package shcm.shsupercm.fabric.citresewn.pack;
+
+import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.InvalidIdentifierException;
+import shcm.shsupercm.fabric.citresewn.CITResewn;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+import shcm.shsupercm.fabric.citresewn.api.CITGlobalProperties;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Property group representation of the global cit.properties file.
+ * @see CITGlobalProperties
+ * @see PackParser#loadGlobalProperties(ResourceManager, GlobalProperties)
+ */
+public class GlobalProperties extends PropertyGroup {
+ public GlobalProperties() {
+ super("global_properties", new Identifier("citresewn", "global_properties"));
+ }
+
+ @Override
+ public String getExtension() {
+ return ".properties";
+ }
+
+ @Override
+ public PropertyGroup load(String packName, Identifier identifier, InputStream is) throws IOException, InvalidIdentifierException {
+ PropertyGroup group = PropertyGroup.tryParseGroup(packName, identifier, is);
+ if (group != null)
+ for (Map.Entry<PropertyKey, Set<PropertyValue>> entry : group.properties.entrySet())
+ this.properties.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>()).addAll(entry.getValue());
+
+ return this;
+ }
+
+ /**
+ * Calls all {@link CITGlobalProperties} handler entrypoints for every global property they're associated with.<br>
+ * Global properties are matched to their entrypoints by mod id and it's the handler responsibility to filter the properties.
+ *
+ * @see CITGlobalProperties
+ */
+ public void callHandlers() {
+ for (EntrypointContainer<CITGlobalProperties> container : FabricLoader.getInstance().getEntrypointContainers(CITGlobalProperties.ENTRYPOINT, CITGlobalProperties.class)) {
+ String containerNamespace = container.getProvider().getMetadata().getId();
+ if (containerNamespace.equals("citresewn-defaults"))
+ containerNamespace = "citresewn";
+
+ for (Map.Entry<PropertyKey, Set<PropertyValue>> entry : properties.entrySet())
+ if (entry.getKey().namespace().equals(containerNamespace)) {
+ PropertyValue lastValue = null;
+ for (PropertyValue value : entry.getValue())
+ lastValue = value;
+
+ try {
+ container.getEntrypoint().globalProperty(entry.getKey().path(), lastValue);
+ } catch (Exception e) {
+ CITResewn.logErrorLoading(lastValue == null ? "Errored while disposing global properties" : "Errored while parsing global properties: Line " + lastValue.position() + " of " + lastValue.propertiesIdentifier() + " in " + lastValue.packName());
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/PackParser.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/PackParser.java
new file mode 100644
index 0000000..9f57760
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/PackParser.java
@@ -0,0 +1,128 @@
+package shcm.shsupercm.fabric.citresewn.pack;
+
+import net.minecraft.resource.Resource;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.resource.ResourcePack;
+import net.minecraft.resource.ResourceType;
+import net.minecraft.util.Identifier;
+import shcm.shsupercm.fabric.citresewn.CITResewn;
+import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.WeightCondition;
+import shcm.shsupercm.fabric.citresewn.ex.CITParsingException;
+import shcm.shsupercm.fabric.citresewn.cit.CIT;
+import shcm.shsupercm.fabric.citresewn.cit.CITCondition;
+import shcm.shsupercm.fabric.citresewn.cit.CITRegistry;
+import shcm.shsupercm.fabric.citresewn.cit.CITType;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey;
+import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Utility parsing methods for packs.
+ */
+public final class PackParser { private PackParser() {}
+ /**
+ * Possible CIT roots in resourcepacks ordered in increasing order of priority.
+ */
+ private static final String[] ROOTS = new String[] { "mcpatcher", "optifine", "citresewn" };
+
+ /**
+ * Loads a merged global property group from loaded packs making sure to respect order.
+ *
+ * @see GlobalProperties#callHandlers()
+ * @param resourceManager the manager that contains the packs
+ * @param globalProperties global property group to parse into
+ * @return globalProperties
+ */
+ public static GlobalProperties loadGlobalProperties(ResourceManager resourceManager, GlobalProperties globalProperties) {
+ for (ResourcePack pack : resourceManager.streamResourcePacks().collect(Collectors.toList()))
+ for (String namespace : pack.getNamespaces(ResourceType.CLIENT_RESOURCES))
+ for (String root : ROOTS) {
+ Identifier identifier = new Identifier(namespace, root + "/cit.properties");
+ try {
+ globalProperties.load(pack.getName(), identifier, pack.open(ResourceType.CLIENT_RESOURCES, identifier));
+ } catch (FileNotFoundException ignored) {
+ } catch (IOException e) {
+ CITResewn.logErrorLoading("Errored while loading global properties: " + identifier + " from " + pack.getName());
+ e.printStackTrace();
+ }
+ }
+ return globalProperties;
+ }
+
+ /**
+ * Attempts parsing all CITs out of all loaded packs.
+ * @param resourceManager the manager that contains the packs
+ * @return unordered list of successfully parsed CITs
+ */
+ public static List<CIT<?>> parseCITs(ResourceManager resourceManager) {
+ List<CIT<?>> cits = new ArrayList<>();
+
+ for (String root : ROOTS)
+ for (Identifier identifier : resourceManager.findResources(root + "/cit", s -> s.endsWith(".properties"))) {
+ String packName = null;
+ try (Resource resource = resourceManager.getResource(identifier)) {
+ cits.add(parseCIT(PropertyGroup.tryParseGroup(packName = resource.getResourcePackName(), identifier, resource.getInputStream()), resourceManager));
+ } catch (CITParsingException e) {
+ CITResewn.logErrorLoading(e.getMessage());
+ } catch (Exception e) {
+ CITResewn.logErrorLoading("Errored while loading cit: " + identifier + (packName == null ? "" : " from " + packName));
+ e.printStackTrace();
+ }
+ }
+
+ return cits;
+ }
+
+ /**
+ * Attempts parsing a CIT from a property group.
+ * @param properties property group representation of the CIT
+ * @param resourceManager the manager that contains the the property group, used to resolve relative assets
+ * @return the successfully parsed CIT
+ * @throws CITParsingException if the CIT failed parsing for any reason
+ */
+ public static CIT<?> parseCIT(PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException {
+ CITType citType = CITRegistry.parseType(properties);
+
+ List<CITCondition> conditions = new ArrayList<>();
+
+ Set<PropertyKey> ignoredProperties = citType.typeProperties();
+
+ for (Map.Entry<PropertyKey, Set<PropertyValue>> entry : properties.properties.entrySet()) {
+ if (entry.getKey().path().equals("type") && entry.getKey().namespace().equals("citresewn"))
+ continue;
+ if (ignoredProperties.contains(entry.getKey()))
+ continue;
+
+ for (PropertyValue value : entry.getValue())
+ conditions.add(CITRegistry.parseCondition(entry.getKey(), value, properties));
+ }
+
+ for (CITCondition condition : new ArrayList<>(conditions))
+ if (condition != null)
+ for (Class<? extends CITCondition> siblingConditionType : condition.siblingConditions())
+ conditions.replaceAll(
+ siblingCondition -> siblingCondition != null && siblingConditionType == siblingCondition.getClass() ?
+ condition.modifySibling(siblingCondition) :
+ siblingCondition);
+
+ WeightCondition weight = new WeightCondition();
+
+ conditions.removeIf(condition -> {
+ if (condition instanceof WeightCondition weightCondition) {
+ weight.setWeight(weightCondition.getWeight());
+ return true;
+ }
+
+ return condition == null;
+ });
+
+ citType.load(conditions, properties, resourceManager);
+
+ return new CIT<>(properties.identifier, properties.packName, citType, conditions.toArray(new CITCondition[0]), weight.getWeight());
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnItemModelIdentifier.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnItemModelIdentifier.java
deleted file mode 100644
index c531983..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnItemModelIdentifier.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack;
-
-import net.minecraft.util.Identifier;
-import shcm.shsupercm.fabric.citresewn.mixin.core.ModelLoaderMixin;
-
-/**
- * Marks models as cit item models.
- * @see ModelLoaderMixin
- */
-public class ResewnItemModelIdentifier extends Identifier {
- public ResewnItemModelIdentifier(String id) {
- super(id);
- }
-
- public ResewnItemModelIdentifier(Identifier identifier) {
- super(identifier.getNamespace(), identifier.getPath());
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnTextureIdentifier.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnTextureIdentifier.java
deleted file mode 100644
index 033c567..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnTextureIdentifier.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack;
-
-import net.minecraft.util.Identifier;
-import shcm.shsupercm.fabric.citresewn.mixin.core.SpriteAtlasTextureMixin;
-
-/**
- * Marks path identifiers as forced literal texture paths.
- * @see SpriteAtlasTextureMixin
- */
-public class ResewnTextureIdentifier extends Identifier {
- public ResewnTextureIdentifier(Identifier identifier) {
- super(identifier.getNamespace(), identifier.getPath());
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CIT.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CIT.java
deleted file mode 100644
index a58c946..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CIT.java
+++ /dev/null
@@ -1,435 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack.cits;
-
-import net.minecraft.enchantment.EnchantmentHelper;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.EnchantedBookItem;
-import net.minecraft.item.Item;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.nbt.*;
-import net.minecraft.text.Text;
-import net.minecraft.util.Hand;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.Pair;
-import net.minecraft.util.registry.Registry;
-import net.minecraft.world.World;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.ex.CITParseException;
-import shcm.shsupercm.fabric.citresewn.mixin.core.NbtCompoundAccessor;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-
-import java.util.*;
-import java.util.function.Predicate;
-import java.util.regex.Pattern;
-
-public abstract class CIT {
- public final CITPack pack;
- public final Identifier propertiesIdentifier;
-
- public final Set<Item> items = new HashSet<>();
-
- public final int damageMin, damageMax;
- public final boolean damageAny, damageRange, damagePercentage;
- public final Integer damageMask;
-
- public final int stackMin, stackMax;
- public final boolean stackAny, stackRange;
-
- public final Set<Identifier> enchantments = new HashSet<>();
- public final List<Pair<Integer, Integer>> enchantmentLevels = new ArrayList<>();
- public final boolean enchantmentsAny, enchantmentLevelsAny;
-
- public final Hand hand;
-
- public final Predicate<NbtCompound> nbt;
-
- public final int weight;
-
- public CIT(CITPack pack, Identifier identifier, Properties properties) throws CITParseException {
- this.pack = pack;
- this.propertiesIdentifier = identifier;
- try {
- for (String itemId : (properties.getProperty("items", properties.getProperty("matchItems", " "))).split(" "))
- if (!itemId.isEmpty()) {
- Identifier itemIdentifier = new Identifier(itemId);
- if (!Registry.ITEM.containsId(itemIdentifier))
- throw new Exception("Unknown item " + itemId);
- this.items.add(Registry.ITEM.get(itemIdentifier));
- }
- if (this.items.isEmpty())
- try {
- String id = propertiesIdentifier.getPath().substring(0, propertiesIdentifier.getPath().length() - 11);
- String[] split = id.split("/");
- id = split[split.length - 1];
- Identifier itemId = new Identifier(propertiesIdentifier.getNamespace(), id);
- if (Registry.ITEM.containsId(itemId))
- this.items.add(Registry.ITEM.get(itemId));
- } catch (Exception ignored) { }
-
- String damage = properties.getProperty("damage");
- if (damageAny = damage == null) {
- this.damageRange = false;
- this.damagePercentage = false;
- this.damageMin = 0;
- this.damageMax = 0;
- } else {
- if (this.damagePercentage = damage.contains("%"))
- damage = damage.replace("%", "");
-
- if (damage.contains("-")) {
- String[] split = damage.split("-");
- if (split.length > 2)
- throw new Exception("damage range must have up to 2 numbers");
-
- this.damageMin = split[0].isEmpty() ? Integer.MIN_VALUE : Integer.parseInt(split[0]);
- this.damageMax = split.length == 1 ? Integer.MAX_VALUE : Integer.parseInt(split[1]);
-
- if (this.damageMin > this.damageMax)
- throw new Exception("damage range min is higher than max");
-
- this.damageRange = this.damageMin < this.damageMax;
- } else {
- this.damageRange = false;
- this.damageMin = this.damageMax = Integer.parseInt(damage);
- }
- }
-
- this.damageMask = properties.containsKey("damageMask") ? Integer.parseInt(properties.getProperty("damageMask")) : null;
-
- String stackSize = properties.getProperty("stackSize");
- if (stackAny = stackSize == null) {
- this.stackRange = false;
- this.stackMin = 0;
- this.stackMax = 0;
- } else {
- if (stackSize.contains("-")) {
- String[] split = stackSize.split("-");
- if (split.length > 2)
- throw new Exception("stackSize range must have up to 2 numbers");
-
- this.stackMin = split[0].isEmpty() ? Integer.MIN_VALUE : Integer.parseInt(split[0]);
- this.stackMax = split.length == 1 ? Integer.MAX_VALUE : Integer.parseInt(split[1]);
-
- if (this.stackMin > this.stackMax)
- throw new Exception("stackSize range min is higher than max");
-
- this.stackRange = this.stackMin < this.stackMax;
- } else {
- this.stackRange = false;
- this.stackMin = this.stackMax = Integer.parseInt(stackSize);
- }
- }
-
- String enchantmentIDs = properties.getProperty("enchantments", properties.getProperty("enchantmentIDs"));
- if (!(this.enchantmentsAny = enchantmentIDs == null)) {
- for (String ench : enchantmentIDs.split(" ")) {
- Identifier enchIdentifier = new Identifier(ench);
- if (!Registry.ENCHANTMENT.containsId(enchIdentifier))
- CITResewn.logWarnLoading("CIT Warning: Unknown enchantment " + enchIdentifier);
- this.enchantments.add(enchIdentifier);
- }
- }
-
- String enchantmentLevelsProp = properties.getProperty("enchantmentLevels");
- if (!(this.enchantmentLevelsAny = enchantmentLevelsProp == null)) {
- for (String range : enchantmentLevelsProp.split(" ")) {
- if (range.contains("-")) {
- if (range.startsWith("-")) {
- range = range.substring(1);
- if (range.contains("-"))
- throw new Exception("enchantmentLevels ranges must have up to 2 numbers each");
- this.enchantmentLevels.add(new Pair<>(0, Integer.parseInt(range)));
- } else if (range.endsWith("-")) {
- range = range.substring(0, range.length() - 1);
- if (range.contains("-"))
- throw new Exception("enchantmentLevels ranges must have up to 2 numbers each");
- this.enchantmentLevels.add(new Pair<>(Integer.parseInt(range), Integer.MAX_VALUE));
- } else {
- String[] split = range.split("-");
- if (split.length != 2)
- throw new Exception("enchantmentLevels ranges must have up to 2 numbers each");
- Pair<Integer, Integer> minMaxPair = new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
- if (minMaxPair.getLeft() > minMaxPair.getRight())
- throw new Exception("enchantmentLevels range min is higher than max");
- this.enchantmentLevels.add(minMaxPair);
- }
- } else {
- int level = Integer.parseInt(range);
- this.enchantmentLevels.add(new Pair<>(level, level));
- }
- }
- }
-
- this.hand = switch (properties.getProperty("hand", "any")) {
- case "main" -> Hand.MAIN_HAND;
- case "off" -> Hand.OFF_HAND;
- default -> null;
- };
-
- List<Predicate<NbtCompound>> nbtPredicates = new ArrayList<>();
- for (Object o : properties.keySet())
- if (o instanceof String property && property.startsWith("nbt.")) {
- String matchProperty = properties.getProperty(property);
- final String[] path = property.substring(4).split("\\.");
- final Predicate<String> match;
- final boolean caseSensitive = !matchProperty.startsWith("i");
-
- if (matchProperty.startsWith(caseSensitive ? "pattern:" : "ipattern:")) {
- matchProperty = caseSensitive ? matchProperty.substring(8) : matchProperty.substring(9).toLowerCase(Locale.ENGLISH);
- final String pattern = matchProperty;
- match = s -> matchesPattern(caseSensitive ? s : s.toLowerCase(), pattern, 0, s.length(), 0, pattern.length());
- } else if (matchProperty.startsWith(caseSensitive ? "regex:" : "iregex:")) {
- matchProperty = caseSensitive ? matchProperty.substring(6) : matchProperty.substring(7).toLowerCase(Locale.ENGLISH);
- final Pattern pattern = Pattern.compile(matchProperty);
- match = s -> pattern.matcher(caseSensitive ? s : s.toLowerCase()).matches();
- } else {
- if (property.equals("nbt.display.color") && matchProperty.startsWith("#"))
- try {
- matchProperty = String.valueOf(Integer.parseInt(matchProperty.substring(1).toLowerCase(Locale.ENGLISH), 16));
- } catch (Exception ignored) { }
-
- final String pattern = matchProperty;
- match = s -> s.equals(pattern);
- }
-
- final boolean checkJson = (path[path.length - 1].equals("Name") || (path.length >= 2 && path[path.length - 2].equals("Lore"))) && !((matchProperty.startsWith("{") || matchProperty.startsWith("\\{")) && matchProperty.endsWith("}"));
-
- nbtPredicates.add(new Predicate<NbtCompound>() {
- public boolean test(NbtElement nbtElement, int index) {
- if (index >= path.length) {
- if (nbtElement instanceof NbtString nbtString) {
- String text = nbtString.asString();
- if (checkJson)
- try {
- //noinspection ConstantConditions
- text = Text.Serializer.fromJson(text).getString();
- } catch (Exception ignored) { }
-
- return match.test(text);
- } else if (nbtElement instanceof AbstractNbtNumber nbtNumber)
- return match.test(String.valueOf(nbtNumber.numberValue()));
- } else {
- String name = path[index];
- if (name.equals("*")) {
- if (nbtElement instanceof NbtCompound nbtCompound) {
- for (NbtElement subElement : ((NbtCompoundAccessor) nbtCompound).getEntries().values())
- if (test(subElement, index + 1))
- return true;
- } else if (nbtElement instanceof NbtList nbtList) {
- for (NbtElement subElement : nbtList)
- if (test(subElement, index + 1))
- return true;
- }
- } else {
- if (nbtElement instanceof NbtCompound nbtCompound) {
- NbtElement subElement = nbtCompound.get(name);
- return subElement != null && test(subElement, index + 1);
- } else if (nbtElement instanceof NbtList nbtList) {
- try {
- NbtElement subElement = nbtList.get(Integer.parseInt(name));
- return subElement != null && test(subElement, index + 1);
- } catch (Exception ignored) {
- return false;
- }
- }
- }
- }
- return false;
- }
-
- @Override
- public boolean test(NbtCompound nbtCompound) {
- return test(nbtCompound, 0);
- }
- });
- }
- this.nbt = nbtCompound -> {
- for (Predicate<NbtCompound> predicate : nbtPredicates)
- if(!predicate.test(nbtCompound))
- return false;
- return true;
- };
-
- this.weight = Integer.parseInt(properties.getProperty("weight", "0"));
- } catch (Exception e) {
- throw new CITParseException(pack.resourcePack, identifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage());
- }
- }
-
- public boolean test(ItemStack stack, Hand hand, World world, LivingEntity entity, boolean ignoreItemType) {
- if (!ignoreItemType && !items.isEmpty() && !items.contains(stack.getItem()))
- return false;
-
- if (!damageAny && stack.getItem().isDamageable()) {
- int damage = stack.getDamage();
- if (damageMask != null)
- damage &= damageMask;
- if (damagePercentage)
- damage = Math.round(100f * (float) stack.getDamage() / (float) stack.getMaxDamage());
- if (damageRange ? (damage < damageMin || damage > damageMax) : (damage != damageMin))
- return false;
- }
-
- if (!stackAny) {
- int count = stack.getCount();
- if (stackRange ? (count < stackMin || count > stackMax) : (count != stackMin))
- return false;
- }
-
- if (this.hand != null && this.hand != hand)
- return false;
-
- if (!enchantmentsAny) {
- Map<Identifier, Integer> stackEnchantments = new LinkedHashMap<>();
- for (NbtElement nbtElement : stack.isOf(Items.ENCHANTED_BOOK) ? EnchantedBookItem.getEnchantmentNbt(stack) : stack.getEnchantments())
- stackEnchantments.put(EnchantmentHelper.getIdFromNbt((NbtCompound) nbtElement), EnchantmentHelper.getLevelFromNbt((NbtCompound) nbtElement));
-
- boolean matches = false;
- for (Identifier enchantment : enchantments) {
- Integer level = stackEnchantments.get(enchantment);
- if (level != null)
- if (enchantmentLevelsAny) {
- if (level > 0) {
- matches = true;
- break;
- }
- } else
- for (Pair<Integer, Integer> levelRange : enchantmentLevels)
- if (level >= levelRange.getLeft() && level <= levelRange.getRight()) {
- matches = true;
- break;
- }
- }
-
- if (!matches)
- return false;
- } else if (!enchantmentLevelsAny) {
- Collection<Integer> levels = new ArrayList<>();
- levels.add(0);
- for (NbtElement nbtElement : stack.isOf(Items.ENCHANTED_BOOK) ? EnchantedBookItem.getEnchantmentNbt(stack) : stack.getEnchantments())
- levels.add(EnchantmentHelper.getLevelFromNbt((NbtCompound) nbtElement));
-
- boolean matches = false;
-
- l: for (Integer level : levels) {
- for (Pair<Integer, Integer> levelRange : enchantmentLevels) {
- if (level >= levelRange.getLeft() && level <= levelRange.getRight()) {
- matches = true;
- break l;
- }
- }
- }
-
- if (!matches)
- return false;
- }
-
- return nbt == null || nbt.test(stack.getNbt());
- }
-
- public void dispose() {
- //stub
- }
-
- /**
- * Takes a defined path and resolves it to an identifier pointing to the resourcepack's path of the specified extension(returns null if no path can be resolved).<br>
- * If definedPath is null, will try to resolve a relative file with the same name as the propertyIdentifier with the extension, otherwise: <br>
- * definedPath will be formatted to replace "\\" with "/" the extension will be appended if not there already. <br>
- * It will first try using definedPath as an absolute path, if it cant resolve(or definedPath starts with ./), definedPath will be considered relative. <br>
- * Relative paths support going to parent directories using "..".
- */
- public static Identifier resolvePath(Identifier propertyIdentifier, String path, String extension, Predicate<Identifier> packContains) {
- if (path == null) {
- path = propertyIdentifier.getPath().substring(0, propertyIdentifier.getPath().length() - 11);
- if (!path.endsWith(extension))
- path = path + extension;
- Identifier pathIdentifier = new Identifier(propertyIdentifier.getNamespace(), path);
- return packContains.test(pathIdentifier) ? pathIdentifier : null;
- }
-
- Identifier pathIdentifier = new Identifier(path);
-
- path = pathIdentifier.getPath().replace('\\', '/');
- if (!path.endsWith(extension))
- path = path + extension;
-
- if (path.startsWith("./"))
- path = path.substring(2);
- else if (!path.contains("..")) {
- pathIdentifier = new Identifier(pathIdentifier.getNamespace(), path);
- if (packContains.test(pathIdentifier))
- return pathIdentifier;
- else if (path.startsWith("assets/")) {
- path = path.substring(7);
- int sep = path.indexOf('/');
- pathIdentifier = new Identifier(path.substring(0, sep), path.substring(sep + 1));
- if (packContains.test(pathIdentifier))
- return pathIdentifier;
- }
- pathIdentifier = new Identifier(pathIdentifier.getNamespace(), switch (extension) {
- case ".png" -> "textures/";
- case ".json" -> "models/";
-
- /* UNREACHABLE FAILSAFE */
- default -> "";
- } + path);
- if (packContains.test(pathIdentifier))
- return pathIdentifier;
- }
-
- LinkedList<String> pathParts = new LinkedList<>(Arrays.asList(propertyIdentifier.getPath().split("/")));
- pathParts.removeLast();
-
- if (path.contains("/")) {
- for (String part : path.split("/")) {
- if (part.equals("..")) {
- if (pathParts.size() == 0)
- return null;
- pathParts.removeLast();
- } else
- pathParts.addLast(part);
- }
- } else
- pathParts.addLast(path);
- path = String.join("/", pathParts);
-
- pathIdentifier = new Identifier(propertyIdentifier.getNamespace(), path);
-
- return packContains.test(pathIdentifier) ? pathIdentifier : null;
- }
-
- /**
- * Author: Paul "prupe" Rupe<br>
- * Taken from MCPatcher under public domain licensing.<br>
- * https://bitbucket.org/prupe/mcpatcher/src/1aa45839b2cd029143809edfa60ec59e5ef75f80/newcode/src/com/prupe/mcpatcher/mal/nbt/NBTRule.java#lines-269:301
- */
- public static boolean matchesPattern(String value, String pattern, int curV, int maxV, int curG, int maxG) {
- for (; curG < maxG; curG++, curV++) {
- char g = pattern.charAt(curG);
- if (g == '*') {
- while (true) {
- if (matchesPattern(value, pattern, curV, maxV, curG + 1, maxG)) {
- return true;
- }
- if (curV >= maxV) {
- break;
- }
- curV++;
- }
- return false;
- } else if (curV >= maxV) {
- break;
- } else if (g == '?') {
- continue;
- }
- if (g == '\\' && curG + 1 < maxG) {
- curG++;
- g = pattern.charAt(curG);
- }
-
- if (g != value.charAt(curV))
- return false;
- }
- return curG == maxG && curV == maxV;
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITArmor.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITArmor.java
deleted file mode 100644
index e919a86..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITArmor.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack.cits;
-
-import net.minecraft.item.ArmorItem;
-import net.minecraft.item.Item;
-import net.minecraft.resource.ResourceType;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.registry.Registry;
-import shcm.shsupercm.fabric.citresewn.ex.CITParseException;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-
-import java.util.*;
-import java.util.function.Supplier;
-
-public class CITArmor extends CIT {
- public final Map<String, Identifier> textures = new HashMap<>();
-
- public CITArmor(CITPack pack, Identifier identifier, Properties properties) throws CITParseException {
- super(pack, identifier, properties);
- try {
- if (this.items.size() == 0)
- throw new Exception("CIT must target at least one item type");
- for (Item item : this.items)
- if (!(item instanceof ArmorItem))
- throw new Exception("Armor CIT must target armor items only(" + Registry.ITEM.getId(item) + " is not armor)");
-
- for (Object o : properties.keySet())
- if (o instanceof String property && property.startsWith("texture.")) {
- Identifier textureIdentifier = resolvePath(identifier, properties.getProperty(property), ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (textureIdentifier == null)
- throw new Exception("Cannot resolve path for " + property);
-
- this.textures.put(property.substring(8), textureIdentifier);
- }
- } catch (Exception e) {
- throw new CITParseException(pack.resourcePack, identifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage());
- }
- }
-
- public interface Cached {
- CITArmor citresewn_getCachedCITArmor(Supplier<CITArmor> realtime);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITElytra.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITElytra.java
deleted file mode 100644
index 5c9aa1a..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITElytra.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack.cits;
-
-import net.minecraft.resource.ResourceType;
-import net.minecraft.util.Identifier;
-import shcm.shsupercm.fabric.citresewn.ex.CITParseException;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-
-import java.util.Properties;
-import java.util.function.Supplier;
-
-public class CITElytra extends CIT {
- public final Identifier textureIdentifier;
-
- public CITElytra(CITPack pack, Identifier identifier, Properties properties) throws CITParseException {
- super(pack, identifier, properties);
- try {
- textureIdentifier = resolvePath(identifier, properties.getProperty("texture"), ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (textureIdentifier == null)
- throw new Exception("Cannot resolve texture");
- } catch (Exception e) {
- throw new CITParseException(pack.resourcePack, identifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage());
- }
- }
-
- public interface Cached {
- CITElytra citresewn_getCachedCITElytra(Supplier<CITElytra> realtime);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITEnchantment.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITEnchantment.java
deleted file mode 100644
index 7c4fa94..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITEnchantment.java
+++ /dev/null
@@ -1,371 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack.cits;
-
-import com.mojang.blaze3d.systems.RenderSystem;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.render.*;
-import net.minecraft.enchantment.EnchantmentHelper;
-import net.minecraft.item.EnchantedBookItem;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.nbt.NbtCompound;
-import net.minecraft.nbt.NbtElement;
-import net.minecraft.resource.ResourceType;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.Util;
-import net.minecraft.util.math.Matrix4f;
-import net.minecraft.util.math.Vec3f;
-import org.lwjgl.opengl.GL11;
-import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
-import shcm.shsupercm.fabric.citresewn.ex.CITParseException;
-import shcm.shsupercm.fabric.citresewn.mixin.citenchantment.BufferBuilderStorageAccessor;
-import shcm.shsupercm.fabric.citresewn.mixin.citenchantment.RenderPhaseAccessor;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-
-import java.util.*;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-import static org.lwjgl.opengl.GL11.*;
-import static com.mojang.blaze3d.systems.RenderSystem.*;
-import static shcm.shsupercm.util.logic.Loops.statelessFadingLoop;
-
-public class CITEnchantment extends CIT {
- public static List<CITEnchantment> appliedContext = null;
- public static boolean shouldApply = false;
-
- public final Identifier textureIdentifier;
- public final float speed, rotation, duration, r, g, b, a;
- public final int layer;
- public final boolean useGlint, blur;
- public final Blend blend;
-
- private final WrappedMethodIntensity methodIntensity = new WrappedMethodIntensity();
-
- public final Map<GlintRenderLayer, RenderLayer> renderLayers = new EnumMap<>(GlintRenderLayer.class);
-
- public CITEnchantment(CITPack pack, Identifier identifier, Properties properties) throws CITParseException {
- super(pack, identifier, properties);
- try {
- textureIdentifier = resolvePath(identifier, properties.getProperty("texture"), ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (textureIdentifier == null)
- throw new Exception("Cannot resolve texture");
-
- speed = Float.parseFloat(properties.getProperty("speed", "1"));
-
- rotation = Float.parseFloat(properties.getProperty("rotation", "0"));
-
- duration = Float.max(0f, Float.parseFloat(properties.getProperty("duration", "0")));
-
- layer = Integer.parseInt(properties.getProperty("layer", "0"));
-
- r = Math.max(0f, Float.parseFloat(properties.getProperty("r", "1")));
- g = Math.max(0f, Float.parseFloat(properties.getProperty("g", "1")));
- b = Math.max(0f, Float.parseFloat(properties.getProperty("b", "1")));
- a = Math.max(0f, Float.parseFloat(properties.getProperty("a", "1")));
-
- useGlint = switch (properties.getProperty("useGlint", "false").toLowerCase(Locale.ENGLISH)) {
- case "true" -> true;
- case "false" -> false;
- default -> throw new Exception("useGlint is not a boolean");
- };
-
- blur = switch (properties.getProperty("blur", "true").toLowerCase(Locale.ENGLISH)) {
- case "true" -> true;
- case "false" -> false;
- default -> throw new Exception("blur is not a boolean");
- };
-
- blend = Blend.getBlend(properties.getProperty("blend", "add"));
- } catch (Exception e) {
- throw new CITParseException(pack.resourcePack, identifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage());
- }
- }
-
- public void activate() {
- for (GlintRenderLayer glintLayer : GlintRenderLayer.values()) {
- RenderLayer renderLayer = glintLayer.build(this);
-
- renderLayers.put(glintLayer, renderLayer);
- ((BufferBuilderStorageAccessor) MinecraftClient.getInstance().getBufferBuilders()).entityBuilders().put(renderLayer, new BufferBuilder(renderLayer.getExpectedBufferSize()));
- }
- }
-
- @Override
- public void dispose() {
- appliedContext = null;
- for (RenderLayer renderLayer : renderLayers.values())
- ((BufferBuilderStorageAccessor) MinecraftClient.getInstance().getBufferBuilders()).entityBuilders().remove(renderLayer);
- }
-
- public enum GlintRenderLayer {
- ARMOR_GLINT("armor_glint", 8f, layer -> layer
- .shader(RenderPhaseAccessor.ARMOR_GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())
- .layering(RenderPhaseAccessor.VIEW_OFFSET_Z_LAYERING())),
- ARMOR_ENTITY_GLINT("armor_entity_glint", 0.16f, layer -> layer
- .shader(RenderPhaseAccessor.ARMOR_ENTITY_GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())
- .layering(RenderPhaseAccessor.VIEW_OFFSET_Z_LAYERING())),
- GLINT_TRANSLUCENT("glint_translucent", 8f, layer -> layer
- .shader(RenderPhaseAccessor.TRANSLUCENT_GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())
- .target(RenderPhaseAccessor.ITEM_TARGET())),
- GLINT("glint", 8f, layer -> layer
- .shader(RenderPhaseAccessor.GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())),
- DIRECT_GLINT("glint_direct", 8f, layer -> layer
- .shader(RenderPhaseAccessor.DIRECT_GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())),
- ENTITY_GLINT("entity_glint", 0.16f, layer -> layer
- .shader(RenderPhaseAccessor.ENTITY_GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())
- .target(RenderPhaseAccessor.ITEM_TARGET())),
- DIRECT_ENTITY_GLINT("entity_glint_direct", 0.16f, layer -> layer
- .shader(RenderPhaseAccessor.DIRECT_ENTITY_GLINT_SHADER())
- .writeMaskState(RenderPhaseAccessor.COLOR_MASK())
- .cull(RenderPhaseAccessor.DISABLE_CULLING())
- .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST()));
-
- public final String name;
- private final Consumer<RenderLayer.MultiPhaseParameters.Builder> setup;
- private final float scale;
-
- GlintRenderLayer(String name, float scale, Consumer<RenderLayer.MultiPhaseParameters.Builder> setup) {
- this.name = name;
- this.scale = scale;
- this.setup = setup;
- }
-
- public RenderLayer build(CITEnchantment enchantment) {
- final float speed = enchantment.speed, rotation = enchantment.rotation, r = enchantment.r, g = enchantment.g, b = enchantment.b, a = enchantment.a;
- final WrappedMethodIntensity methodIntensity = enchantment.methodIntensity;
- //noinspection ConstantConditions
- RenderLayer.MultiPhaseParameters.Builder layer = RenderLayer.MultiPhaseParameters.builder()
- .texture(new RenderPhase.Texture(enchantment.textureIdentifier, enchantment.blur, false))
- .texturing(new RenderPhase.Texturing("citresewn_glint_texturing", () -> {
- float l = Util.getMeasuringTimeMs() * CITResewnConfig.INSTANCE().citenchantment_scroll_multiplier * speed;
- float x = (l % 110000f) / 110000f;
- float y = (l % 30000f) / 30000f;
- Matrix4f matrix4f = Matrix4f.translate(-x, y, 0.0f);
- matrix4f.multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(rotation + 10f));
- matrix4f.multiply(Matrix4f.scale(scale, scale, scale));
- setTextureMatrix(matrix4f);
-
- setShaderColor(r, g, b, a * methodIntensity.intensity);
- }, () -> {
- RenderSystem.resetTextureMatrix();
-
- setShaderColor(1f, 1f, 1f, 1f);
- }))
- .transparency(enchantment.blend);
-
- this.setup.accept(layer);
-
- return RenderLayer.of("citresewn:enchantment_" + this.name + ":" + enchantment.propertiesIdentifier.toString(),
- VertexFormats.POSITION_TEXTURE,
- VertexFormat.DrawMode.QUADS,
- 256,
- layer.build(false));
- }
-
- public VertexConsumer tryApply(VertexConsumer base, RenderLayer baseLayer, VertexConsumerProvider provider) {
- if (!shouldApply || appliedContext == null || appliedContext.size() == 0)
- return null;
-
- VertexConsumer[] layers = new VertexConsumer[Math.min(appliedContext.size(), appliedContext.get(0).pack.cap)];
-
- for (int i = 0; i < layers.length; i++)
- layers[i] = provider.getBuffer(appliedContext.get(i).renderLayers.get(GlintRenderLayer.this));
-
- provider.getBuffer(baseLayer); // refresh base layer for armor consumer
-
- return base == null ? VertexConsumers.union(layers) : VertexConsumers.union(VertexConsumers.union(layers), base);
- }
- }
-
- public static class Blend extends RenderPhase.Transparency {
- private final int src, dst, srcAlpha, dstAlpha;
-
- private Blend(String name, int src, int dst, int srcAlpha, int dstAlpha) {
- super(name + "_glint_transparency", null, null);
- this.src = src;
- this.dst = dst;
- this.srcAlpha = srcAlpha;
- this.dstAlpha = dstAlpha;
- }
-
- private Blend(String name, int src, int dst) {
- this(name, src, dst, GL_ZERO, GL_ONE);
- }
-
- @Override
- public void startDrawing() {
- enableBlend();
- blendFuncSeparate(src, dst, srcAlpha, dstAlpha);
- }
-
- @Override
- public void endDrawing() {
- defaultBlendFunc();
- disableBlend();
- }
-
- public static Blend getBlend(String blendString) throws BlendFormatException {
- try { //check named blending function
- return Named.valueOf(blendString.toUpperCase(Locale.ENGLISH)).blend;
- } catch (IllegalArgumentException ignored) { // create custom blending function
- try {
- String[] split = blendString.split(" ");
- int src, dst, srcAlpha, dstAlpha;
- if (split.length == 2) {
- src = parseGLConstant(split[0]);
- dst = parseGLConstant(split[1]);
- srcAlpha = GL_ZERO;
- dstAlpha = GL_ONE;
- } else if (split.length == 4) {
- src = parseGLConstant(split[0]);
- dst = parseGLConstant(split[1]);
- srcAlpha = parseGLConstant(split[2]);
- dstAlpha = parseGLConstant(split[3]);
- } else
- throw new Exception();
-
- return new Blend("custom_" + src + "_" + dst + "_" + srcAlpha + "_" + dstAlpha, src, dst, srcAlpha, dstAlpha);
- } catch (Exception e) {
- throw new BlendFormatException();
- }
- }
- }
-
- private enum Named {
- REPLACE(new Blend("replace", 0, 0) {
- @Override
- public void startDrawing() {
- disableBlend();
- }
- }),
- GLINT(new Blend("glint", GL_SRC_COLOR, GL_ONE)),
- ALPHA(new Blend("alpha", GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)),
- ADD(new Blend("add", GL_SRC_ALPHA, GL_ONE)),
- SUBTRACT(new Blend("subtract", GL_ONE_MINUS_DST_COLOR, GL_ZERO)),
- MULTIPLY(new Blend("multiply", GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA)),
- DODGE(new Blend("dodge", GL_ONE, GL_ONE)),
- BURN(new Blend("burn", GL_ZERO, GL_ONE_MINUS_SRC_COLOR)),
- SCREEN(new Blend("screen", GL_ONE, GL_ONE_MINUS_SRC_COLOR)),
- OVERLAY(new Blend("overlay", GL_DST_COLOR, GL_SRC_COLOR));
-
- public final Blend blend;
-
- Named(Blend blend) {
- this.blend = blend;
- }
- }
-
- private static int parseGLConstant(String s) throws Exception {
- try {
- return GL11.class.getDeclaredField(s).getInt(null);
- } catch (NoSuchFieldException ignored) { }
-
- return s.startsWith("0x") ? Integer.parseInt(s.substring(2), 16) : Integer.parseInt(s);
- }
-
- public static class BlendFormatException extends Exception {
- public BlendFormatException() {
- super("Not a valid blending method");
- }
- }
- }
-
- public enum MergeMethod {
- AVERAGE {
- @Override
- public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CITEnchantment cit) {
- Identifier enchantment = null;
- for (Identifier enchantmentMatch : cit.enchantments)
- if (stackEnchantments.containsKey(enchantmentMatch)) {
- enchantment = enchantmentMatch;
- break;
- }
-
- if (enchantment == null) {
- cit.methodIntensity.intensity = 0f;
- } else {
- float sum = 0f;
- for (Integer value : stackEnchantments.values())
- sum += value;
-
- cit.methodIntensity.intensity = (float) stackEnchantments.get(enchantment) / sum;
- }
- }
- },
- LAYERED {
- @Override
- public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CITEnchantment cit) {
- Identifier enchantment = null;
- for (Identifier enchantmentMatch : cit.enchantments)
- if (stackEnchantments.containsKey(enchantmentMatch)) {
- enchantment = enchantmentMatch;
- break;
- }
- if (enchantment == null) {
- cit.methodIntensity.intensity = 0f;
- return;
- }
-
- float max = 0f;
- for (Integer value : stackEnchantments.values())
- if (value > max)
- max = value;
-
- cit.methodIntensity.intensity = (float) stackEnchantments.get(enchantment) / max;
- }
- },
- CYCLE {
- @Override
- public void applyMethod(List<CITEnchantment> citEnchantments, ItemStack stack) {
- List<Map.Entry<CITEnchantment, Float>> durations = new ArrayList<>();
- for (CITEnchantment cit : citEnchantments)
- durations.add(new HashMap.SimpleEntry<>(cit, cit.duration));
-
- for (Map.Entry<CITEnchantment, Float> intensity : statelessFadingLoop(durations, citEnchantments.get(0).pack.fade, ticks, 20).entrySet())
- intensity.getKey().methodIntensity.intensity = intensity.getValue();
- }
- };
-
- public static int ticks = 0;
-
- public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CITEnchantment cit) {
- cit.methodIntensity.intensity = 1f;
- }
-
- public void applyMethod(List<CITEnchantment> citEnchantments, ItemStack stack) {
- Map<Identifier, Integer> stackEnchantments = new LinkedHashMap<>();
- for (NbtElement nbtElement : stack.isOf(Items.ENCHANTED_BOOK) ? EnchantedBookItem.getEnchantmentNbt(stack) : stack.getEnchantments())
- stackEnchantments.put(EnchantmentHelper.getIdFromNbt((NbtCompound) nbtElement), EnchantmentHelper.getLevelFromNbt((NbtCompound) nbtElement));
-
- for (CITEnchantment cit : citEnchantments)
- if (!cit.enchantmentsAny)
- applyIntensity(stackEnchantments, cit);
- }
- }
-
- private static class WrappedMethodIntensity {
- public float intensity = 1f;
- }
-
- public interface Cached {
- List<CITEnchantment> citresewn_getCachedCITEnchantment(Supplier<List<CITEnchantment>> realtime);
- }
-} \ No newline at end of file
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITItem.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITItem.java
deleted file mode 100644
index 2300f88..0000000
--- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITItem.java
+++ /dev/null
@@ -1,463 +0,0 @@
-package shcm.shsupercm.fabric.citresewn.pack.cits;
-
-import com.google.common.collect.ImmutableMap;
-import com.mojang.datafixers.util.Either;
-import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.json.JsonUnbakedModel;
-import net.minecraft.client.render.model.json.ModelOverride;
-import net.minecraft.client.render.model.json.ModelOverrideList;
-import net.minecraft.client.render.model.json.ModelTransformation;
-import net.minecraft.client.texture.SpriteAtlasTexture;
-import net.minecraft.client.util.SpriteIdentifier;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.Item;
-import net.minecraft.item.ItemStack;
-import net.minecraft.resource.Resource;
-import net.minecraft.resource.ResourceManager;
-import net.minecraft.resource.ResourceType;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.registry.Registry;
-import org.apache.commons.io.IOUtils;
-import shcm.shsupercm.fabric.citresewn.CITResewn;
-import shcm.shsupercm.fabric.citresewn.ex.CITLoadException;
-import shcm.shsupercm.fabric.citresewn.ex.CITParseException;
-import shcm.shsupercm.fabric.citresewn.mixin.cititem.JsonUnbakedModelAccessor;
-import shcm.shsupercm.fabric.citresewn.pack.CITPack;
-import shcm.shsupercm.fabric.citresewn.pack.ResewnItemModelIdentifier;
-import shcm.shsupercm.fabric.citresewn.pack.ResewnTextureIdentifier;
-
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-@SuppressWarnings("deprecation")
-public class CITItem extends CIT {
- private static final String GENERATED_SUB_CITS_PREFIX = "sub_cititem_generated_";
- public static final Set<Identifier> GENERATED_SUB_CITS_SEEN = new HashSet<>();
-
- public Map<Identifier, Identifier> assetIdentifiers = new LinkedHashMap<>();
- public Map<List<ModelOverride.Condition>, JsonUnbakedModel> unbakedAssets = new LinkedHashMap<>();
- private Map<String, Either<SpriteIdentifier, String>> textureOverrideMap = new HashMap<>();
- private boolean isTexture = false;
-
- public BakedModel bakedModel = null;
- public CITOverrideList bakedSubModels = new CITOverrideList();
-
- public CITItem(CITPack pack, Identifier identifier, Properties properties) throws CITParseException {
- super(pack, identifier, properties);
- try {
- if (this.items.size() == 0)
- throw new Exception("CIT must target at least one item type");
-
- Identifier assetIdentifier;
- boolean containsTexture = false;
- String modelProp = properties.getProperty("model");
- if (modelProp == null)
- for (Object o : properties.keySet())
- if (o instanceof String property && (property.startsWith("texture") || property.startsWith("tile"))) {
- containsTexture = true;
- break;
- }
- if (!containsTexture) {
- assetIdentifier = resolvePath(identifier, modelProp, ".json", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (assetIdentifier != null)
- assetIdentifiers.put(null, assetIdentifier);
- else if (modelProp != null) {
- assetIdentifier = resolvePath(identifier, modelProp, ".json", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (assetIdentifier != null)
- assetIdentifiers.put(null, assetIdentifier);
- }
- }
-
- for (Object o : properties.keySet())
- if (o instanceof String property && property.startsWith("model.")) {
- Identifier subIdentifier = resolvePath(identifier, properties.getProperty(property), ".json", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (subIdentifier == null)
- throw new Exception("Cannot resolve path for " + property);
-
- String subItem = property.substring(6);
- Identifier subItemIdentifier = fixDeprecatedSubItem(subItem);
- assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier);
- }
-
- if (assetIdentifiers.size() == 0) { // attempt to load texture
- isTexture = true;
- String textureProp = properties.getProperty("texture");
- if (textureProp == null)
- textureProp = properties.getProperty("tile");
- assetIdentifier = resolvePath(identifier, textureProp, ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (assetIdentifier != null)
- assetIdentifiers.put(null, assetIdentifier);
-
- for (Object o : properties.keySet())
- if (o instanceof String property && property.startsWith("texture.")) {
- Identifier subIdentifier = resolvePath(identifier, properties.getProperty(property), ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (subIdentifier == null)
- throw new Exception("Cannot resolve path for " + property);
-
- String subItem = property.substring(8);
- Identifier subItemIdentifier = fixDeprecatedSubItem(subItem);
- assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier);
- }
- } else { // attempt to load textureOverrideMap from textures
- String textureProp = properties.getProperty("texture");
- if (textureProp == null)
- textureProp = properties.getProperty("tile");
- if (textureProp != null) {
- assetIdentifier = resolvePath(identifier, textureProp, ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (assetIdentifier != null)
- textureOverrideMap.put(null, Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(assetIdentifier))));
- else
- throw new Exception("Cannot resolve path for texture");
- }
-
- for (Object o : properties.keySet())
- if (o instanceof String property && property.startsWith("texture.")) {
- textureProp = properties.getProperty(property);
- Identifier subIdentifier = resolvePath(identifier, textureProp, ".png", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (subIdentifier == null)
- throw new Exception("Cannot resolve path for " + property);
-
- textureOverrideMap.put(property.substring(8), Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(subIdentifier))));
- }
- }
-
- if (assetIdentifiers.size() == 0)
- throw new Exception("Cannot resolve path for model/texture");
- } catch (Exception e) {
- throw new CITParseException(pack.resourcePack, identifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage());
- }
- }
-
- public void loadUnbakedAssets(ResourceManager resourceManager) throws CITLoadException {
- try {
- if (isTexture) {
- JsonUnbakedModel itemJson = getModelForFirstItemType(resourceManager);
- if (((JsonUnbakedModelAccessor) itemJson).getTextureMap().size() > 1) { // use(some/all of) the asset identifiers to build texture override in layered models
- textureOverrideMap = ((JsonUnbakedModelAccessor) itemJson).getTextureMap();
- Identifier defaultAsset = assetIdentifiers.get(null);
- textureOverrideMap.replaceAll((layerName, originalTextureEither) -> {
- Identifier textureIdentifier = assetIdentifiers.remove(originalTextureEither.map(SpriteIdentifier::getTextureId, Identifier::new));
- if (textureIdentifier != null)
- return Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(textureIdentifier)));
- if (defaultAsset != null)
- return Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(defaultAsset)));
- return null;
- });
-
- if (assetIdentifiers.size() == 0 || (assetIdentifiers.size() == 1 && assetIdentifiers.containsKey(null))) {
- unbakedAssets.put(null, itemJson);
- return;
- }
- }
-
- Identifier baseIdentifier = assetIdentifiers.remove(null);
-
- if (baseIdentifier != null)
- unbakedAssets.put(null, loadUnbakedAsset(resourceManager, baseIdentifier));
-
- if (!assetIdentifiers.isEmpty()) { // contains sub models
- LinkedHashMap<Identifier, List<ModelOverride.Condition>> overrideConditions = new LinkedHashMap<>();
- for (Item item : this.items) {
- Identifier itemIdentifier = Registry.ITEM.getId(item);
- overrideConditions.put(new Identifier(itemIdentifier.getNamespace(), "item/" + itemIdentifier.getPath()), Collections.emptyList());
-
- Identifier itemModelIdentifier = new Identifier(itemIdentifier.getNamespace(), "models/item/" + itemIdentifier.getPath() + ".json");
- try (Resource itemModelResource = resourceManager.getResource(itemModelIdentifier); Reader resourceReader = new InputStreamReader(itemModelResource.getInputStream())) {
- JsonUnbakedModel itemModelJson = JsonUnbakedModel.deserialize(resourceReader);
-
- if (itemModelJson.getOverrides() != null && !itemModelJson.getOverrides().isEmpty())
- for (ModelOverride override : itemModelJson.getOverrides())
- overrideConditions.put(override.getModelId(), override.streamConditions().toList());
- }
- }
-
- ArrayList<Identifier> overrideModels = new ArrayList<>(overrideConditions.keySet());
- Collections.reverse(overrideModels);
-
- for (Identifier overrideModel : overrideModels) {
- Identifier replacement = assetIdentifiers.remove(overrideModel);
- if (replacement == null)
- continue;
-
- List<ModelOverride.Condition> conditions = overrideConditions.get(overrideModel);
- unbakedAssets.put(conditions, loadUnbakedAsset(resourceManager, replacement));
- }
- }
- } else { // isModel
- Identifier baseIdentifier = assetIdentifiers.remove(null);
-
- if (baseIdentifier != null) {
- if (!GENERATED_SUB_CITS_SEEN.add(baseIdentifier)) // cit generated duplicate
- baseIdentifier = new Identifier(baseIdentifier.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + baseIdentifier.getPath());
- GENERATED_SUB_CITS_SEEN.add(baseIdentifier);
-
- JsonUnbakedModel model = loadUnbakedAsset(resourceManager, baseIdentifier);
- unbakedAssets.put(null, model);
-
- if (model.getOverrides().size() > 0 && textureOverrideMap.size() > 0) {
- LinkedHashMap<Identifier, List<ModelOverride.Condition>> overrideConditions = new LinkedHashMap<>();
-
- for (ModelOverride override : model.getOverrides())
- overrideConditions.put(override.getModelId(), override.streamConditions().toList());
-
- ArrayList<Identifier> overrideModels = new ArrayList<>(overrideConditions.keySet());
- Collections.reverse(overrideModels);
-
- for (Identifier overrideModel : overrideModels) {
- Identifier replacement = resolvePath(baseIdentifier, overrideModel.toString(), ".json", resourceManager::containsResource);
- if (replacement != null) {
- String subTexturePath = replacement.toString().substring(0, replacement.toString().lastIndexOf('.'));
- final String subTextureName = subTexturePath.substring(subTexturePath.lastIndexOf('/') + 1);
-
- replacement = baseIdentifier;
- if (!GENERATED_SUB_CITS_SEEN.add(replacement)) // cit generated duplicate
- replacement = new Identifier(replacement.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + replacement.getPath());
- GENERATED_SUB_CITS_SEEN.add(replacement);
-
- JsonUnbakedModel jsonModel = loadUnbakedAsset(resourceManager, replacement);
- jsonModel.getOverrides().clear();
-
- ((JsonUnbakedModelAccessor) jsonModel).getTextureMap().replaceAll((layerName, texture) -> {
- if (layerName != null)
- try {
- for (String subTexture : textureOverrideMap.keySet())
- if (subTextureName.equals(subTexture))
- return textureOverrideMap.get(subTexture);
- } catch (Exception ignored) { }
- return texture;
- });
-
- unbakedAssets.put(overrideConditions.get(overrideModel), jsonModel);
- }
- }
- }
- }
-
- if (!assetIdentifiers.isEmpty()) { // contains sub models
- LinkedHashMap<Identifier, List<ModelOverride.Condition>> overrideConditions = new LinkedHashMap<>();
- for (Item item : this.items) {
- Identifier itemIdentifier = Registry.ITEM.getId(item);
- overrideConditions.put(new Identifier(itemIdentifier.getNamespace(), "item/" + itemIdentifier.getPath()), Collections.emptyList());
-
- Identifier itemModelIdentifier = new Identifier(itemIdentifier.getNamespace(), "models/item/" + itemIdentifier.getPath() + ".json");
- try (Resource itemModelResource = resourceManager.getResource(itemModelIdentifier); Reader resourceReader = new InputStreamReader(itemModelResource.getInputStream())) {
- JsonUnbakedModel itemModelJson = JsonUnbakedModel.deserialize(resourceReader);
-
- if (itemModelJson.getOverrides() != null && !itemModelJson.getOverrides().isEmpty())
- for (ModelOverride override : itemModelJson.getOverrides())
- overrideConditions.put(override.getModelId(), override.streamConditions().toList());
- }
- }
-
- ArrayList<Identifier> overrideModels = new ArrayList<>(overrideConditions.keySet());
- Collections.reverse(overrideModels);
-
- for (Identifier overrideModel : overrideModels) {
- Identifier replacement = assetIdentifiers.remove(overrideModel);
- if (replacement == null)
- continue;
-
- if (!GENERATED_SUB_CITS_SEEN.add(replacement)) // cit generated duplicate
- replacement = new Identifier(replacement.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + replacement.getPath());
- GENERATED_SUB_CITS_SEEN.add(replacement);
-
- List<ModelOverride.Condition> conditions = overrideConditions.get(overrideModel);
- unbakedAssets.put(conditions, loadUnbakedAsset(resourceManager, replacement));
- }
- }
- }
- } catch (Exception e) {
- throw new CITLoadException(pack.resourcePack, propertiesIdentifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage());
- } finally {
- assetIdentifiers = null;
- textureOverrideMap = null;
- }
- }
-
- private JsonUnbakedModel loadUnbakedAsset(ResourceManager resourceManager, Identifier assetIdentifier) throws Exception {
- final Identifier identifier;
- {
- Identifier possibleIdentifier = assetIdentifier;
- while (possibleIdentifier.getPath().startsWith(GENERATED_SUB_CITS_PREFIX))
- possibleIdentifier = new Identifier(possibleIdentifier.getNamespace(), possibleIdentifier.getPath().substring(possibleIdentifier.getPath().substring(GENERATED_SUB_CITS_PREFIX.length()).indexOf('_') + GENERATED_SUB_CITS_PREFIX.length() + 1));
- identifier = possibleIdentifier;
- }
- JsonUnbakedModel json;
- if (identifier.getPath().endsWith(".json")) {
- InputStream is = null;
- Resource resource = null;
- try {
- json = JsonUnbakedModel.deserialize(IOUtils.toString(is = (resource = resourceManager.getResource(identifier)).getInputStream(), StandardCharsets.UTF_8));
- json.id = assetIdentifier.toString();
- json.id = json.id.substring(0, json.id.length() - 5);
-
- ((JsonUnbakedModelAccessor) json).getTextureMap().replaceAll((layer, original) -> {
- Optional<SpriteIdentifier> left = original.left();
- if (left.isPresent()) {
- Identifier resolvedIdentifier = resolvePath(identifier, left.get().getTextureId().getPath(), ".png", resourceManager::containsResource);
- if (resolvedIdentifier != null)
- return Either.left(new SpriteIdentifier(left.get().getAtlasId(), new ResewnTextureIdentifier(resolvedIdentifier)));
- }
- return original;
- });
-
- if (textureOverrideMap.size() > 0) {
- Map<String, Either<SpriteIdentifier, String>> jsonTextureMap = ((JsonUnbakedModelAccessor) json).getTextureMap();
- if (jsonTextureMap.size() == 0)
- jsonTextureMap.put("layer0", null);
-
- final Either<SpriteIdentifier, String> defaultTextureOverride = textureOverrideMap.get(null);
- if (defaultTextureOverride != null)
- jsonTextureMap.replaceAll((layerName, spriteIdentifierStringEither) -> defaultTextureOverride);
-
- //jsonTextureMap.putAll(textureOverrideMap);
- jsonTextureMap.replaceAll((layerName, texture) -> {
- if (layerName != null)
- try {
- String[] split = texture.map(id -> id.getTextureId().getPath(), s -> s).split("/");
- String textureName = split[split.length - 1];
- if (textureName.endsWith(".png"))
- textureName = textureName.substring(0, textureName.length() - 4);
- return Objects.requireNonNull(textureOverrideMap.get(textureName));
- } catch (Exception ignored) { }
- return texture;
- });
- jsonTextureMap.values().removeIf(Objects::isNull);
- }
-
- Identifier parentId = ((JsonUnbakedModelAccessor) json).getParentId();
- if (parentId != null) {
- String[] parentIdPathSplit = parentId.getPath().split("/");
- if (parentId.getPath().startsWith("./") || (parentIdPathSplit.length > 2 && parentIdPathSplit[1].equals("cit"))) {
- parentId = resolvePath(identifier, parentId.getPath(), ".json", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (parentId != null)
- ((JsonUnbakedModelAccessor) json).setParentId(new ResewnItemModelIdentifier(parentId));
- }
- }
-
- json.getOverrides().replaceAll(override -> {
- String[] modelIdPathSplit = override.getModelId().getPath().split("/");
- if (override.getModelId().getPath().startsWith("./") || (modelIdPathSplit.length > 2 && modelIdPathSplit[1].equals("cit"))) {
- Identifier resolvedOverridePath = resolvePath(identifier, override.getModelId().getPath(), ".json", id -> pack.resourcePack.contains(ResourceType.CLIENT_RESOURCES, id));
- if (resolvedOverridePath != null)
- return new ModelOverride(new ResewnItemModelIdentifier(resolvedOverridePath), override.streamConditions().collect(Collectors.toList()));
- }
-
- return override;
- });
-
- return json;
- } finally {
- IOUtils.closeQuietly(is, resource);
- }
- } else if (identifier.getPath().endsWith(".png")) {
- json = getModelForFirstItemType(resourceManager);
- if (json == null)
- json = new JsonUnbakedModel(new Identifier("minecraft", "item/generated"), new ArrayList<>(), ImmutableMap.of("layer0", Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(identifier)))), true, JsonUnbakedModel.GuiLight.ITEM, ModelTransformation.NONE, new ArrayList<>());
- json.getOverrides().clear();
- json.id = identifier.toString();
- json.id = json.id.substring(0, json.id.length() - 4);
-
- ((JsonUnbakedModelAccessor) json).getTextureMap().replaceAll((layerName, originalTextureEither) -> {
- if (textureOverrideMap.size() > 0) {
- Either<SpriteIdentifier, String> textureOverride = textureOverrideMap.get(layerName);
- if (textureOverride == null)
- textureOverride = textureOverrideMap.get(null);
- return textureOverride == null ? originalTextureEither : textureOverride;
- } else
- return Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(identifier)));
- });
- return json;
- }
-
- throw new Exception("Unknown asset type");
- }
-
- public Identifier fixDeprecatedSubItem(String subItem) {
- String replacement = switch (subItem) {
- case "bow_pulling_standby" -> "bow";
- case "crossbow_standby" -> "crossbow";
- case "potion_bottle_drinkable" -> "potion";
- case "potion_bottle_splash" -> "splash_potion";
- case "potion_bottle_lingering" -> "lingering_potion";
-
-
- default -> null;
- };
-
- if (replacement != null) {
- CITResewn.logWarnLoading("CIT Warning: Using deprecated sub item id \"" + subItem + "\" instead of \"" + replacement + "\" in " + pack.resourcePack.getName() + " -> " + propertiesIdentifier.toString());
-
- return new Identifier("minecraft", "item/" + replacement);
- }
-
- return null;
- }
-
- private JsonUnbakedModel getModelForFirstItemType(ResourceManager resourceManager) {
- Identifier firstItemIdentifier = Registry.ITEM.getId(this.items.iterator().next()), firstItemModelIdentifier = new Identifier(firstItemIdentifier.getNamespace(), "models/item/" + firstItemIdentifier.getPath() + ".json");
- Resource itemModelResource = null;
- try {
- JsonUnbakedModel json = JsonUnbakedModel.deserialize(IOUtils.toString((itemModelResource = resourceManager.getResource(firstItemModelIdentifier)).getInputStream(), StandardCharsets.UTF_8));
-
- if (!GENERATED_SUB_CITS_SEEN.add(firstItemModelIdentifier)) // cit generated duplicate
- firstItemModelIdentifier = new Identifier(firstItemModelIdentifier.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + firstItemModelIdentifier.getPath());
- GENERATED_SUB_CITS_SEEN.add(firstItemModelIdentifier);
-
- json.id = firstItemModelIdentifier.toString();
- json.id = json.id.substring(0, json.id.length() - 5);
- return json;
- } catch (Exception e) {
- return null;
- } finally {
- IOUtils.closeQuietly(itemModelResource);
- }
- }
-
- public BakedModel getItemModel(ItemStack stack, ClientWorld world, LivingEntity entity, int seed) {
- // get sub items or bakedModel if no sub item matches @Nullable
- BakedModel bakedModel = bakedSubModels.apply(this.bakedModel, stack, world, entity, seed);
-
- // apply model overrides
- if (bakedModel != null && bakedModel.getOverrides() != null)
- bakedModel = bakedModel.getOverrides().apply(bakedModel, stack, world, entity, seed);
-
- return bakedModel;
- }
-
- public static class CITOverrideList extends ModelOverrideList {
- public void override(List<ModelOverride.Condition> key, BakedModel bakedModel) {
- Set<Identifier> conditionTypes = new LinkedHashSet<>(Arrays.asList(this.conditionTypes));
- for (ModelOverride.Condition condition : key)
- conditionTypes.add(condition.getType());
- this.conditionTypes = conditionTypes.toArray(new Identifier[0]);
-
- this.overrides = Arrays.copyOf(this.overrides, this.overrides.length + 1);
-
- Object2IntMap<Identifier> object2IntMap = new Object2IntOpenHashMap<>();
- for(int i = 0; i < this.conditionTypes.length; ++i)
- object2IntMap.put(this.conditionTypes[i], i);
-
- this.overrides[this.overrides.length - 1] = new BakedOverride(
- key.stream()
- .map((condition) -> new InlinedCondition(object2IntMap.getInt(condition.getType()), condition.getThreshold()))
- .toArray(InlinedCondition[]::new)
- , bakedModel);
- }
- }
-
- public interface Cached {
- CITItem citresewn_getCachedCITItem(Supplier<CITItem> realtime);
-
- boolean citresewn_isMojankCIT();
- void citresewn_setMojankCIT(boolean mojankCIT);
- }
-}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertiesGroupAdapter.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertiesGroupAdapter.java
new file mode 100644
index 0000000..0375079
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertiesGroupAdapter.java
@@ -0,0 +1,101 @@
+package shcm.shsupercm.fabric.citresewn.pack.format;
+
+import net.minecraft.util.Identifier;
+import net.minecraft.util.InvalidIdentifierException;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+
+public class PropertiesGroupAdapter extends PropertyGroup {
+ public static final String EXTENSION = ".properties";
+
+ protected PropertiesGroupAdapter(String packName, Identifier identifier) {
+ super(packName, identifier);
+ }
+
+ @Override
+ public String getExtension() {
+ return EXTENSION;
+ }
+
+ @Override
+ public PropertyGroup load(String packName, Identifier identifier, InputStream is) throws IOException, InvalidIdentifierException {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
+ String line;
+ int linePos = 0, multilineSkip = 0;
+ while ((line = reader.readLine()) != null) {
+ linePos++;
+ line = line.stripLeading();
+ if (line.isEmpty() || line.startsWith("#") || line.startsWith("!"))
+ continue;
+
+ while (line.endsWith("\\")) {
+ String nextLine = reader.readLine();
+ linePos++;
+ multilineSkip++;
+ if (nextLine == null)
+ nextLine = "";
+ nextLine = nextLine.stripLeading();
+
+ if (nextLine.startsWith("#") || nextLine.startsWith("!"))
+ continue;
+
+ line = line.substring(0, line.length() - 1) + "\\n" + nextLine;
+ }
+
+ line = line.stripTrailing();
+
+ StringBuilder builder = new StringBuilder();
+
+ String key = null, keyMetadata = null;
+ final boolean checkColonSeparator = !line.contains("=");
+
+ for (int i = 0; i < line.length(); i++) {
+ char c = line.charAt(i);
+
+ if (c == '\\') { // escape
+ c = switch (c = line.charAt(++i)) {
+ case 'n' -> '\n';
+ case 'r' -> '\r';
+ case 'f' -> '\f';
+ case 't' -> '\t';
+ case 'u' -> {
+ try {
+ i += 4;
+ yield (char) Integer.parseInt(line.substring(i - 3, i + 1), 16);
+ } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
+ throw new IOException("Malformatted escaped unicode character");
+ }
+ }
+
+ default -> c;
+ };
+
+ } else if (key == null && (c == '=' || (checkColonSeparator && c == ':'))) {
+ key = builder.toString().stripTrailing();
+ int metadataIndex = key.indexOf('.');
+ if (metadataIndex >= 0) {
+ keyMetadata = key.substring(metadataIndex + 1);
+ key = key.substring(0, metadataIndex);
+ }
+
+ builder = new StringBuilder();
+ for (i++; i < line.length() && Character.isWhitespace(line.charAt(i)); i++);
+ i--;
+ continue;
+ }
+
+ builder.append(c);
+ }
+
+ if (key == null)
+ throw new IOException("Missing separator in line " + linePos);
+
+ int pos = linePos - multilineSkip;
+ multilineSkip = 0;
+ this.put(pos, packName, identifier, key, keyMetadata, PropertySeparator.EQUALS, builder.toString());
+ }
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyGroup.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyGroup.java
new file mode 100644
index 0000000..8d185fd
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyGroup.java
@@ -0,0 +1,157 @@
+package shcm.shsupercm.fabric.citresewn.pack.format;
+
+import net.minecraft.util.Identifier;
+import net.minecraft.util.InvalidIdentifierException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+/**
+ * Storage agnostic map of keys and values.<br>
+ * Keys are stored as {@link PropertyKey}s holding the mod id of the property type.<br>
+ * A key can have multiple values associated with it as they are stored in an ordered set.
+ *
+ * @see PropertyKey
+ * @see PropertyValue
+ * @see PropertiesGroupAdapter
+ */
+public abstract class PropertyGroup {
+ /**
+ * The internal map that backs this property group.
+ */
+ public final Map<PropertyKey, Set<PropertyValue>> properties = new LinkedHashMap<>();
+
+ /**
+ * This group's location in its resourcepack.
+ */
+ public final Identifier identifier;
+
+ /**
+ * The file name of the resourcepack that this property group is in.
+ */
+ public final String packName;
+
+ protected PropertyGroup(String packName, Identifier identifier) {
+ this.packName = packName;
+ this.identifier = identifier;
+ }
+
+ /**
+ * Tries to parse a group out of a stream.
+ * @see #load(String, Identifier, InputStream)
+ * @see #getExtension()
+ * @see PropertiesGroupAdapter
+ * @param packName {@link #packName}
+ * @param identifier {@link #identifier}, needed for extension matching
+ * @param is a stream containing properties as specified by implementation
+ * @return the parsed group or null if could not match an adapter
+ * @throws IOException if errored while parsing the group
+ */
+ public static PropertyGroup tryParseGroup(String packName, Identifier identifier, InputStream is) throws IOException {
+ PropertyGroup group = null;
+ if (identifier.getPath().endsWith(PropertiesGroupAdapter.EXTENSION))
+ group = new PropertiesGroupAdapter(packName, identifier);
+
+ return group == null ? null : group.load(packName, identifier, is);
+ }
+
+ /**
+ * @return file suffix for this property group's implementation
+ */
+ public abstract String getExtension();
+
+ /**
+ * Reads the given input stream into the group.
+ * @param packName {@link #packName}
+ * @param identifier {@link #identifier}
+ * @param is a stream containing properties as specified by implementation
+ * @return this
+ * @throws IOException if errored while reading the stream
+ * @throws InvalidIdentifierException if encountered a malformed {@link Identifier} while reading
+ */
+ public abstract PropertyGroup load(String packName, Identifier identifier, InputStream is) throws IOException, InvalidIdentifierException;
+
+ /**
+ * Adds the given value to the group.
+ * @param position implementation specific interpretation of the value's position in the group, has no effect on internal order
+ * @param packName the value's resourcepack file name
+ * @param propertiesIdentifier the value's property group location identifier
+ * @param key the value's key name
+ * @param keyMetadata nullable, implementation specific metadata for this value's key
+ * @param separator implementation specific connection between the key and the value
+ * @param value string representation of the value to be parsed by the group's user
+ */
+ protected void put(int position, String packName, Identifier propertiesIdentifier, String key, String keyMetadata, PropertySeparator separator, String value) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(value);
+
+ this.properties.computeIfAbsent(PropertyKey.of(key), id -> new LinkedHashSet<>()).add(new PropertyValue(keyMetadata, value, separator, position, propertiesIdentifier, packName));
+ }
+
+ /**
+ * @param namespace the key's namespace(should be the value type's modid by convention)
+ * @param pathAliases all key name aliases to check for
+ * @return all values associated with the given key by alias>insertion order
+ */
+ public Set<PropertyValue> get(String namespace, String... pathAliases) {
+ Set<PropertyValue> values = new LinkedHashSet<>();
+
+ for (String path : pathAliases) {
+ Set<PropertyValue> possibleValues = this.properties.get(new PropertyKey(namespace, path));
+ if (possibleValues != null)
+ values.addAll(possibleValues);
+ }
+
+ return values;
+ }
+
+ /**
+ * @see #getLastWithoutMetadataOrDefault(String, String, String...)
+ * @param namespace the key's namespace(should be the value type's modid by convention)
+ * @param pathAliases all key name aliases to check for
+ * @return the last value associated with the key(by insertion order) that has a null key metadata or null if the key is not present in the group
+ */
+ public PropertyValue getLastWithoutMetadata(String namespace, String... pathAliases) {
+ PropertyValue value = null;
+ for (PropertyValue next : get(namespace, pathAliases))
+ if (next.keyMetadata() == null)
+ value = next;
+
+ return value;
+ }
+
+ /**
+ * @see #getLastWithoutMetadata(String, String...)
+ * @param defaultValue the dummy value to return if not present in the group
+ * @param namespace the key's namespace(should be the value type's modid by convention)
+ * @param pathAliases all key name aliases to check for
+ * @return the last value associated with the key(by insertion order) that has a null key metadata or the wrapped default value if the key is not present in the group
+ */
+ public PropertyValue getLastWithoutMetadataOrDefault(String defaultValue, String namespace, String... pathAliases) {
+ PropertyValue property = getLastWithoutMetadata(namespace, pathAliases);
+ if (property == null)
+ property = new PropertyValue(null, defaultValue, PropertySeparator.EQUALS, -1, this.identifier, this.packName);
+
+ return property;
+ }
+
+ /**
+ * @see #getExtension()
+ * @see #identifier
+ * @return the name of this group without its path or extension
+ */
+ public String stripName() {
+ return identifier.getPath().substring(identifier.getPath().lastIndexOf('/') + 1, identifier.getPath().length() - getExtension().length());
+ }
+
+ /**
+ * Compiles a message with attached info on a value's origin.
+ * @param message message to add descriptor to
+ * @param position implementation specific position of
+ * @return the formatted message
+ */
+ public String messageWithDescriptorOf(String message, int position) {
+ return message + (position != -1 ? " @L" + position : "") + " in " + this.identifier.toString() + " from " + this.packName;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyKey.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyKey.java
new file mode 100644
index 0000000..7bc2cdc
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyKey.java
@@ -0,0 +1,31 @@
+package shcm.shsupercm.fabric.citresewn.pack.format;
+
+import net.minecraft.util.Identifier;
+
+/**
+ * Namespace/path pair of strings. Similar to {@link Identifier} but without validity restrictions.
+ * @see Identifier
+ */
+public record PropertyKey(String namespace, String path) {
+ /**
+ * Attempts to split a given string into a namespace and path by the first occurrence of a colon.<br>
+ * If a namespace cannot be extracted from the given string, "citresewn" is set instead.
+ * @param key key to parse
+ * @return parsed property key
+ */
+ public static PropertyKey of(String key) {
+ String[] split = new String[] {"citresewn", key};
+ int i = key.indexOf(':');
+ if (i >= 0) {
+ split[1] = key.substring(i + 1);
+ if (i >= 1)
+ split[0] = key.substring(0, i);
+ }
+ return new PropertyKey(split[0], split[1]);
+ }
+
+ @Override
+ public String toString() {
+ return namespace + ":" + path;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertySeparator.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertySeparator.java
new file mode 100644
index 0000000..ce4ebab
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertySeparator.java
@@ -0,0 +1,21 @@
+package shcm.shsupercm.fabric.citresewn.pack.format;
+
+/**
+ * Marker for the connection between a {@link PropertyKey} and its {@link PropertyValue}.
+ */
+public enum PropertySeparator {
+ /**
+ * Marks either a check for equality or an action to set a value.
+ */
+ EQUALS("=")
+ ;
+
+ /**
+ * String representation of the separator.
+ */
+ public final String separator;
+
+ PropertySeparator(String separator) {
+ this.separator = separator;
+ }
+}
diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyValue.java b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyValue.java
new file mode 100644
index 0000000..f41119f
--- /dev/null
+++ b/src/main/java/shcm/shsupercm/fabric/citresewn/pack/format/PropertyValue.java
@@ -0,0 +1,20 @@
+package shcm.shsupercm.fabric.citresewn.pack.format;
+
+import net.minecraft.util.Identifier;
+
+/**
+ * Wrapped representation of a property group's value with additional attached metadata.
+ * @param keyMetadata nullable, implementation specific metadata for this value's key
+ * @param value string representation of the value to be parsed by the group's user
+ * @param separator implementation specific connection between the key and the value
+ * @param position implementation specific interpretation of the value's position in the group, has no effect on internal order
+ * @param propertiesIdentifier the value's property group location identifier
+ * @param packName the value's resourcepack file name
+ */
+public record PropertyValue(String keyMetadata,
+ String value,
+ PropertySeparator separator,
+ int position,
+ Identifier propertiesIdentifier,
+ String packName) {
+}
diff --git a/src/main/java/shcm/shsupercm/util/logic/Loops.java b/src/main/java/shcm/shsupercm/util/logic/Loops.java
deleted file mode 100644
index 6c18244..0000000
--- a/src/main/java/shcm/shsupercm/util/logic/Loops.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package shcm.shsupercm.util.logic;
-
-import java.util.*;
-
-/**
- * This class(or class portion) is a part of SHCM's utilities. Feel free to use without credit.
- */
-public class Loops {
- /**
- * Creates a loop of T with linked intensities allowing for fading between the elements.
- * @param items list of items and pause durations(in time units) ordered as they are in the loop
- * @param fade time in units to fade between each item
- * @param ticks positive raising counter
- * @param tpu the amount of ticks per time unit
- * @param <T> element type
- * @return map of elements and their respective intensities(between 0.0f and 1.0f)
- */
- public static <T> Map<T, Float> statelessFadingLoop(List<Map.Entry<T, Float>> items, float fade, int ticks, int tpu) {
- Map<T, Float> itemValues = new HashMap<>();
-
- if (items == null || items.size() == 0)
- return itemValues;
-
- if (items.size() == 1) {
- itemValues.put(items.get(0).getKey(), 1f);
- return itemValues;
- }
-
- float totalUnitsInLoop = 0f;
- for (Map.Entry<T, Float> item : items) {
- itemValues.put(item.getKey(), 0f);
- totalUnitsInLoop += item.getValue() + fade;
- }
-
- float unitInLoop = (ticks % (tpu * totalUnitsInLoop)) / tpu;
-
- for (int i = 0; i < items.size(); i++) {
- Map.Entry<T, Float> item = items.get(i);
- if (unitInLoop < item.getValue()) {
- itemValues.put(item.getKey(), 1f);
- break;
- } else
- unitInLoop -= item.getValue();
-
- if (unitInLoop < fade) {
- Map.Entry<T, Float> nextItem = items.get(i + 1 >= items.size() ? 0 : i + 1);
-
- unitInLoop /= fade;
-
- itemValues.put(item.getKey(), 1f - unitInLoop);
- itemValues.put(nextItem.getKey(), unitInLoop);
-
- break;
- } else
- unitInLoop -= fade;
- }
-
- return itemValues;
- }
-} \ No newline at end of file