diff options
120 files changed, 5172 insertions, 2428 deletions
@@ -20,3 +20,4 @@ build # other eclipse run +srcOld diff --git a/build.gradle b/build.gradle index 7f7f191..aec5b5f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,43 +1,74 @@ plugins { - id 'fabric-loom' version '0.10-SNAPSHOT' + id 'fabric-loom' version '0.11-SNAPSHOT' apply false + id 'io.shcm.shsupercm.fabric.fletchingtable' version '1.4' apply false } version = project.mod_version + "+" + project.minecraft_version group = project.maven_group -repositories { - maven { url "https://maven.shedaniel.me/" } - maven { url "https://maven.terraformersmc.com/releases" } - maven { url "https://cursemaven.com" } -} +allprojects { + apply plugin: "fabric-loom" + apply plugin: "io.shcm.shsupercm.fabric.fletchingtable" -dependencies { - // To change the versions see the gradle.properties file - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + repositories { + maven { url "https://maven.shedaniel.me/" } + maven { url "https://maven.terraformersmc.com/releases" } + maven { url "https://cursemaven.com" } + } + + dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + modImplementation fabricApi.module("fabric-api-base", "${project.fabric_api}") + modImplementation fabricApi.module("fabric-resource-loader-v0", "${project.fabric_api}") + modImplementation fabricApi.module("fabric-command-api-v1", "${project.fabric_api}") - modImplementation fabricApi.module("fabric-api-base", "${project.fabric_api}") - modImplementation fabricApi.module("fabric-resource-loader-v0", "${project.fabric_api}") - modImplementation fabricApi.module("fabric-command-api-v1", "${project.fabric_api}") + modCompileOnly("com.terraformersmc:modmenu:${project.modmenu}") { + exclude(group: "net.fabricmc.fabric-api") + } - modCompileOnly("com.terraformersmc:modmenu:${project.modmenu}") { - exclude(group: "net.fabricmc.fabric-api") + modCompileOnly("me.shedaniel.cloth:cloth-config-fabric:${project.cloth}") { + exclude(group: "net.fabricmc.fabric-api") + } + + //modCompileOnly("curse.maven:cosmeticarmor-436659:${project.cosmetic_armor}") + + + //fletchingTable.shutUpDrasil18() } - modCompileOnly("me.shedaniel.cloth:cloth-config-fabric:${project.cloth}") { - exclude(group: "net.fabricmc.fabric-api") + fletchingTable { + defaultMixinEnvironment = "auto" } - modCompileOnly("curse.maven:cosmeticarmor-436659:${project.cosmetic_armor}") -} + processResources { + inputs.property "version", rootProject.version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": rootProject.version + } + } + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + + java { + archivesBaseName = project.archives_base_name + } + + jar { + archiveClassifier.set "dev" -processResources { - inputs.property "version", project.version - filteringCharset "UTF-8" + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}" } + } + } - filesMatching("fabric.mod.json") { - expand "version": project.version + loom { + shareCaches() } } @@ -45,19 +76,16 @@ loom { accessWidenerPath = file("src/main/resources/citresewn.accesswidener") } -sourceCompatibility = JavaVersion.VERSION_17 -targetCompatibility = JavaVersion.VERSION_17 +dependencies { + afterEvaluate { + include project("defaults:") + } +} java { - archivesBaseName = project.archives_base_name - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. withSourcesJar() } -jar { - from("LICENSE") { - rename { "${it}_${project.archivesBaseName}" } - } +sourcesJar { + from project("defaults:").sourceSets.main.java.srcDirs }
\ No newline at end of file diff --git a/defaults/build.gradle b/defaults/build.gradle new file mode 100644 index 0000000..2e88283 --- /dev/null +++ b/defaults/build.gradle @@ -0,0 +1,13 @@ +archivesBaseName = "citresewn-defaults" + +dependencies { + implementation project(path: ":", configuration: "namedElements") +} + +loom { + accessWidenerPath = file("src/main/resources/citresewn-defaults.accesswidener") + + runConfigs.configureEach { + ideConfigGenerated = true + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamage.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamage.java new file mode 100644 index 0000000..c5c31f9 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamage.java @@ -0,0 +1,46 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; + +import java.util.Set; + +public class ConditionDamage extends IntegerCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionDamage> CONTAINER = new CITConditionContainer<>(ConditionDamage.class, ConditionDamage::new, + "damage"); + + protected Integer mask = null; + + public ConditionDamage() { + super(true, false, true); + } + + @Override + protected int getValue(CITContext context) { + int value = context.stack.isDamageable() ? context.stack.getDamage() : 0; + if (mask != null) + value &= mask; + return value; + } + + @Override + protected int getPercentageTotalValue(CITContext context) { + return context.stack.isDamageable() ? context.stack.getItem().getMaxDamage() : 0; + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionDamageMask.class); + } + + @Override + public <T extends CITCondition> T modifySibling(T sibling) { + if (sibling instanceof ConditionDamageMask damageMask) + this.mask = damageMask.getMask(); + return null; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamageMask.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamageMask.java new file mode 100644 index 0000000..6adde08 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamageMask.java @@ -0,0 +1,38 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; + +import java.util.Set; + +public class ConditionDamageMask extends IntegerCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionDamageMask> CONTAINER = new CITConditionContainer<>(ConditionDamageMask.class, ConditionDamageMask::new, + "damage_mask", "damageMask"); + + public ConditionDamageMask() { + super(false, false, false); + } + + @Override + protected int getValue(CITContext context) { + return 0; + } + + @Override + public boolean test(CITContext context) { + return true; + } + + public int getMask() { + return this.min; + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionDamage.class); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantmentLevels.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantmentLevels.java new file mode 100644 index 0000000..da6b4f4 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantmentLevels.java @@ -0,0 +1,62 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.util.Identifier; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ListCondition; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class ConditionEnchantmentLevels extends ListCondition<ConditionEnchantmentLevels.EnchantmentLevelCondition> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionEnchantmentLevels> CONTAINER = new CITConditionContainer<>(ConditionEnchantmentLevels.class, ConditionEnchantmentLevels::new, + "enchantment_levels", "enchantmentLevels"); + + protected Set<Identifier> enchantments = null; + + public ConditionEnchantmentLevels() { + super(EnchantmentLevelCondition.class, EnchantmentLevelCondition::new); + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionEnchantments.class); + } + + @Override + public <T extends CITCondition> T modifySibling(T sibling) { + if (sibling instanceof ConditionEnchantments conditionEnchantments) { + if (enchantments == null) { + enchantments = new HashSet<>(); + for (EnchantmentLevelCondition subCondition : this.conditions) + subCondition.enchantments = enchantments; + } + enchantments.addAll(Arrays.asList(conditionEnchantments.getEnchantments())); + } + + return sibling; + } + + protected static class EnchantmentLevelCondition extends IntegerCondition { + protected Set<Identifier> enchantments = null; + + protected EnchantmentLevelCondition() { + super(true, false, false); + } + + @Override + public boolean test(CITContext context) { + for (Map.Entry<Identifier, Integer> entry : context.enchantments().entrySet()) + if ((enchantments == null || enchantments.contains(entry.getKey())) && entry.getValue() != null && (range ? min <= entry.getValue() && entry.getValue() <= max : entry.getValue() == min)) + return true; + + return false; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantments.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantments.java new file mode 100644 index 0000000..301f9c1 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantments.java @@ -0,0 +1,59 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IdentifierCondition; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ListCondition; +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.Set; + +public class ConditionEnchantments extends ListCondition<ConditionEnchantments.EnchantmentCondition> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionEnchantments> CONTAINER = new CITConditionContainer<>(ConditionEnchantments.class, ConditionEnchantments::new, + "enchantments", "enchantmentIDs"); + + public ConditionEnchantments() { + super(EnchantmentCondition.class, EnchantmentCondition::new); + } + + public Identifier[] getEnchantments() { + Identifier[] enchantments = new Identifier[this.conditions.length]; + + for (int i = 0; i < this.conditions.length; i++) + enchantments[i] = this.conditions[i].getValue(null); + + return enchantments; + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionEnchantmentLevels.class); + } + + protected static class EnchantmentCondition extends IdentifierCondition { + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + super.load(value, properties); + + if (!Registry.ENCHANTMENT.containsId(this.value)) + warn(this.value + " is not in the enchantment registry", value, properties); + } + + @Override + public boolean test(CITContext context) { + return context.enchantments().containsKey(this.value); + } + + @Override + protected Identifier getValue(CITContext context) { + return this.value; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionHand.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionHand.java new file mode 100644 index 0000000..0405cbc --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionHand.java @@ -0,0 +1,49 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.EnumCondition; + +public class ConditionHand extends EnumCondition<ConditionHand.Hand> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionHand> CONTAINER = new CITConditionContainer<>(ConditionHand.class, ConditionHand::new, + "hand"); + + public ConditionHand() { + super(ConditionHand.Hand::values); + } + + @Override + protected Hand getValue(CITContext context) { + if (context.entity.getMainHandStack() == context.stack) + return Hand.MAINHAND; + if (context.entity.getOffHandStack() == context.stack) + return Hand.OFFHAND; + + return null; + } + + @Override + public boolean test(CITContext context) { + Hand hand = getValue(context); + return this.value == hand || (this.value == Hand.ANY && hand != null); + } + + protected enum Hand implements EnumCondition.Aliased { + MAINHAND("main", "mainhand", "main_hand"), + OFFHAND("off", "offhand", "off_hand"), + ANY("any", "either", "*"); + + private final String[] aliases; + + Hand(String... aliases) { + this.aliases = aliases; + } + + @Override + public String[] getAliases() { + return this.aliases; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionItems.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionItems.java new file mode 100644 index 0000000..117aa47 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionItems.java @@ -0,0 +1,75 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IdentifierCondition; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ListCondition; +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.LinkedHashSet; +import java.util.Set; + +public class ConditionItems extends ListCondition<ConditionItems.ItemCondition> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionItems> CONTAINER = new CITConditionContainer<>(ConditionItems.class, ConditionItems::new, + "items", "matchItems"); + + public Item[] items = new Item[0]; + + public ConditionItems() { + super(ItemCondition.class, ItemCondition::new); + } + + public ConditionItems(Item... items) { + this(); + this.items = items; + } + + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + super.load(value, properties); + + Set<Item> items = new LinkedHashSet<>(); + + for (ItemCondition itemCondition : this.conditions) + items.add(itemCondition.item); + + this.items = items.toArray(new Item[0]); + } + + @Override + public boolean test(CITContext context) { + for (Item item : this.items) + if (context.stack.getItem() == item) + return true; + + return false; + } + + protected static class ItemCondition extends IdentifierCondition { + public Item item = null; + + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + super.load(value, properties); + + if (Registry.ITEM.containsId(this.value)) + this.item = Registry.ITEM.get(this.value); + else { + this.item = null; + warn(this.value + " is not in the item registry", value, properties); + } + } + + @Override + protected Identifier getValue(CITContext context) { + return this.value; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT.java new file mode 100644 index 0000000..09ae08e --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT.java @@ -0,0 +1,244 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.nbt.*; +import net.minecraft.text.Text; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +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.Locale; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class ConditionNBT extends CITCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionNBT> CONTAINER = new CITConditionContainer<>(ConditionNBT.class, ConditionNBT::new, + "nbt"); + + protected String[] path; + + protected StringMatcher matchString = null; + protected NbtInt matchInteger = null; + protected NbtByte matchByte = null; + protected NbtFloat matchFloat = null; + protected NbtDouble matchDouble = null; + protected NbtLong matchLong = null; + protected NbtShort matchShort = null; + protected NbtCompound matchCompound = null; + + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + if (value.keyMetadata() == null || value.keyMetadata().isEmpty()) + throw new CITParsingException("Missing nbt path", properties, value.position()); + + path = value.keyMetadata().split("\\."); + for (String s : path) + if (s.isEmpty()) + throw new CITParsingException("Path segment cannot be empty", properties, value.position()); + + try { + if (value.value().startsWith("regex:")) + matchString = new StringMatcher.RegexMatcher(value.value().substring(6)); + else if (value.value().startsWith("iregex:")) + matchString = new StringMatcher.IRegexMatcher(value.value().substring(7)); + else if (value.value().startsWith("pattern:")) + matchString = new StringMatcher.PatternMatcher(value.value().substring(8)); + else if (value.value().startsWith("ipattern:")) + matchString = new StringMatcher.IPatternMatcher(value.value().substring(9)); + else + matchString = new StringMatcher.DirectMatcher(value.value()); + } catch (PatternSyntaxException e) { + throw new CITParsingException("Malformatted regex expression", properties, value.position(), e); + } catch (Exception ignored) { } + try { + if (value.value().startsWith("#")) + matchInteger = NbtInt.of(Integer.parseInt(value.value().substring(1).toLowerCase(Locale.ENGLISH), 16)); + else if (value.value().startsWith("0x")) + matchInteger = NbtInt.of(Integer.parseInt(value.value().substring(2).toLowerCase(Locale.ENGLISH), 16)); + else + matchInteger = NbtInt.of(Integer.parseInt(value.value())); + } catch (Exception ignored) { } + try { + matchByte = NbtByte.of(Byte.parseByte(value.value())); + } catch (Exception ignored) { } + try { + matchFloat = NbtFloat.of(Float.parseFloat(value.value())); + } catch (Exception ignored) { } + try { + matchDouble = NbtDouble.of(Double.parseDouble(value.value())); + } catch (Exception ignored) { } + try { + matchLong = NbtLong.of(Long.parseLong(value.value())); + } catch (Exception ignored) { } + try { + matchShort = NbtShort.of(Short.parseShort(value.value())); + } catch (Exception ignored) { } + try { + matchCompound = StringNbtReader.parse(value.value()); + } catch (Exception ignored) { } + } + + @Override + public boolean test(CITContext context) { + return testPath(context.stack.getNbt(), 0); + } + + protected boolean testPath(NbtElement element, int pathIndex) { + if (element == null) + return false; + + if (pathIndex >= path.length) + return testValue(element); + + final String path = this.path[pathIndex]; + if (path.equals("*")) { + if (element instanceof NbtCompound compound) { + for (NbtElement subElement : compound.entries.values()) + if (testPath(subElement, pathIndex + 1)) + return true; + } else if (element instanceof NbtList list) { + for (NbtElement subElement : list) + if (testPath(subElement, pathIndex + 1)) + return true; + } + } else { + if (element instanceof NbtCompound compound) + return testPath(compound.get(path), pathIndex + 1); + else if (element instanceof NbtList list) + try { + return testPath(list.get(Integer.parseInt(path)), pathIndex + 1); + } catch (NumberFormatException | IndexOutOfBoundsException ignored) { } + } + + return false; + } + + private boolean testValue(NbtElement element) { + try { + if (element instanceof NbtString nbtString) //noinspection ConstantConditions + return matchString.matches(nbtString.asString()) || matchString.matches(Text.Serializer.fromJson(nbtString.asString()).getString()); + else if (element instanceof NbtInt nbtInt) + return nbtInt.equals(matchInteger); + else if (element instanceof NbtByte nbtByte) + return nbtByte.equals(matchByte); + else if (element instanceof NbtFloat nbtFloat) + return nbtFloat.equals(matchFloat); + else if (element instanceof NbtDouble nbtDouble) + return nbtDouble.equals(matchDouble); + else if (element instanceof NbtLong nbtLong) + return nbtLong.equals(matchLong); + else if (element instanceof NbtShort nbtShort) + return nbtShort.equals(matchShort); + else if (element instanceof NbtCompound nbtCompound) + return NbtHelper.matches(matchCompound, nbtCompound, true); + } catch (Exception ignored) { } + return false; + } + + protected static abstract class StringMatcher { + public abstract boolean matches(String value); + + public static class DirectMatcher extends StringMatcher { + protected final String pattern; + + public DirectMatcher(String pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(String value) { + return pattern.equals(value); + } + } + + public static class RegexMatcher extends StringMatcher { + protected final Pattern pattern; + + public RegexMatcher(String pattern) { + this(Pattern.compile(pattern)); + } + + protected RegexMatcher(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(String value) { + return this.pattern.matcher(value).matches(); + } + } + + public static class PatternMatcher extends StringMatcher { + protected final String pattern; + + public PatternMatcher(String pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(String value) { + return matchesPattern(value, this.pattern, 0, value.length(), 0, pattern.length()); + } + + /** + * Author: Paul "prupe" Rupe<br> + * Taken and modified 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 + */ + protected 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 (!charsEqual(g, value.charAt(curV))) + return false; + } + return curG == maxG && curV == maxV; + } + + protected boolean charsEqual(char p, char v) { + return p == v; + } + } + + public static class IRegexMatcher extends RegexMatcher { + public IRegexMatcher(String pattern) { + super(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)); + } + } + + public static class IPatternMatcher extends PatternMatcher { + public IPatternMatcher(String pattern) { + super(pattern.toLowerCase(Locale.ROOT)); + } + + @Override + protected boolean charsEqual(char p, char v) { + return p == v || p == Character.toLowerCase(v); + } + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionStackSize.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionStackSize.java new file mode 100644 index 0000000..1d22a53 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionStackSize.java @@ -0,0 +1,21 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; + +public class ConditionStackSize extends IntegerCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionStackSize> CONTAINER = new CITConditionContainer<>(ConditionStackSize.class, ConditionStackSize::new, + "stack_size", "stackSize", "amount"); + + public ConditionStackSize() { + super(true, false, false); + } + + @Override + protected int getValue(CITContext context) { + return context.stack.getCount(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeArmor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeArmor.java new file mode 100644 index 0000000..d97bf67 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeArmor.java @@ -0,0 +1,111 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.Item; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionItems; +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.*; + +public class TypeArmor extends CITType { + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + public final Map<String, Identifier> textures = new HashMap<>(); + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of(PropertyKey.of("texture")); + } + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + boolean itemsConditionPresent = false; + for (CITCondition condition : conditions) + if (condition instanceof ConditionItems conditionItems) + for (Item item : conditionItems.items) + if (item instanceof ArmorItem) + itemsConditionPresent = true; + else + throw new CITParsingException("This type only accepts armor items for the items condition", properties, -1); + + if (!itemsConditionPresent) + try { + Identifier propertiesName = new Identifier(properties.stripName()); + if (!Registry.ITEM.containsId(propertiesName)) + throw new Exception(); + Item item = Registry.ITEM.get(propertiesName); + if (!(item instanceof ArmorItem)) + throw new Exception(); + conditions.add(new ConditionItems(item)); + } catch (Exception ignored) { + throw new CITParsingException("Not targeting any item type", properties, -1); + } + + for (PropertyValue propertyValue : properties.get("citresewn", "texture")) { + Identifier identifier = resolveAsset(properties.identifier, propertyValue, "textures", ".png", resourceManager); + if (identifier == null) + throw new CITParsingException("Could not resolve texture", properties, propertyValue.position()); + + textures.put(propertyValue.keyMetadata(), identifier); + } + if (textures.size() == 0) + throw new CITParsingException("Texture not specified", properties, -1); + } + + public static class Container extends CITTypeContainer<TypeArmor> { + public Container() { + super(TypeArmor.class, TypeArmor::new, "armor"); + } + + public Set<CIT<TypeArmor>> loaded = new HashSet<>(); + public Map<ArmorItem, Set<CIT<TypeArmor>>> loadedTyped = new IdentityHashMap<>(); + + @Override + public void load(List<CIT<TypeArmor>> parsedCITs) { + loaded.addAll(parsedCITs); + for (CIT<TypeArmor> cit : parsedCITs) + for (CITCondition condition : cit.conditions) + if (condition instanceof ConditionItems items) + for (Item item : items.items) + if (item instanceof ArmorItem armorItem) + loadedTyped.computeIfAbsent(armorItem, i -> new LinkedHashSet<>()).add(cit); + } + + @Override + public void dispose() { + loaded.clear(); + loadedTyped.clear(); + } + + public CIT<TypeArmor> getCIT(CITContext context) { + return ((CITCacheArmor) (Object) context.stack).citresewn$getCacheTypeArmor().get(context).get(); + } + + public CIT<TypeArmor> getRealTimeCIT(CITContext context) { + if (!(context.stack.getItem() instanceof ArmorItem)) + return null; + + Set<CIT<TypeArmor>> loadedForItemType = loadedTyped.get(context.stack.getItem()); + if (loadedForItemType != null) + for (CIT<TypeArmor> cit : loadedForItemType) + if (cit.test(context)) + return cit; + + return null; + } + } + + public interface CITCacheArmor { + CITCache.Single<TypeArmor> citresewn$getCacheTypeArmor(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeElytra.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeElytra.java new file mode 100644 index 0000000..40e6fbe --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeElytra.java @@ -0,0 +1,76 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.item.ElytraItem; +import net.minecraft.item.Item; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionItems; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class TypeElytra extends CITType { + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + public Identifier texture; + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of(PropertyKey.of("texture")); + } + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + for (CITCondition condition : conditions) + if (condition instanceof ConditionItems items) + for (Item item : items.items) + if (!(item instanceof ElytraItem)) + warn("Non elytra item type condition", null, properties); + + texture = resolveAsset(properties.identifier, properties.getLastWithoutMetadata("citresewn", "texture"), "textures", ".png", resourceManager); + if (texture == null) + throw new CITParsingException("Texture not specified", properties, -1); + } + + public static class Container extends CITTypeContainer<TypeElytra> { + public Container() { + super(TypeElytra.class, TypeElytra::new, "elytra"); + } + + public Set<CIT<TypeElytra>> loaded = new HashSet<>(); + + @Override + public void load(List<CIT<TypeElytra>> parsedCITs) { + loaded.addAll(parsedCITs); + } + + @Override + public void dispose() { + loaded.clear(); + } + + public CIT<TypeElytra> getCIT(CITContext context) { + return ((CITCacheElytra) (Object) context.stack).citresewn$getCacheTypeElytra().get(context).get(); + } + + public CIT<TypeElytra> getRealTimeCIT(CITContext context) { + for (CIT<TypeElytra> cit : loaded) + if (cit.test(context)) + return cit; + + return null; + } + } + + public interface CITCacheElytra { + CITCache.Single<TypeElytra> citresewn$getCacheTypeElytra(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeEnchantment.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeEnchantment.java new file mode 100644 index 0000000..0bd29e7 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeEnchantment.java @@ -0,0 +1,528 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import com.mojang.blaze3d.systems.RenderSystem; +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.*; +import net.minecraft.resource.ResourceManager; +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.api.CITGlobalProperties; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionEnchantments; +import shcm.shsupercm.fabric.citresewn.defaults.config.CITResewnDefaultsConfig; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment.BufferBuilderStorageAccessor; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment.RenderPhaseAccessor; +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 shcm.shsupercm.util.logic.Loops; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.function.Consumer; + +import static com.mojang.blaze3d.systems.RenderSystem.*; +import static org.lwjgl.opengl.GL11.*; + +public class TypeEnchantment extends CITType { + @Entrypoint(CITGlobalProperties.ENTRYPOINT) + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of( + PropertyKey.of("texture"), + PropertyKey.of("layer"), + PropertyKey.of("speed"), + PropertyKey.of("rotation"), + PropertyKey.of("duration"), + PropertyKey.of("r"), + PropertyKey.of("g"), + PropertyKey.of("b"), + PropertyKey.of("a"), + PropertyKey.of("useGlint"), + PropertyKey.of("blur"), + PropertyKey.of("blend")); + } + + public Identifier texture; + public int layer; + public float speed, rotation, duration, r, g, b, a; + public boolean useGlint, blur; + public Blend blend; + + public final Map<GlintRenderLayer, RenderLayer> renderLayers = new EnumMap<>(GlintRenderLayer.class); + private final MergeMethodIntensity methodIntensity = new MergeMethodIntensity(); + private Set<Identifier> enchantmentChecks = null; + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + PropertyValue textureProp = properties.getLastWithoutMetadata("citresewn", "texture"); + this.texture = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (this.texture == null) + throw textureProp == null ? new CITParsingException("No texture specified", properties, -1) : new CITParsingException("Could not resolve texture", properties, textureProp.position()); + + PropertyValue layerProp = properties.getLastWithoutMetadataOrDefault("0", "citresewn", "layer"); + try { + this.layer = Integer.parseInt(layerProp.value()); + } catch (Exception e) { + throw new CITParsingException("Could not parse integer", properties, layerProp.position(), e); + } + + this.speed = parseFloatOrDefault(1f, "speed", properties); + this.rotation = parseFloatOrDefault(10f, "rotation", properties); + this.duration = Math.max(0f, parseFloatOrDefault(0f, "duration", properties)); + this.r = Math.max(0f, parseFloatOrDefault(1f, "r", properties)); + this.g = Math.max(0f, parseFloatOrDefault(1f, "g", properties)); + this.b = Math.max(0f, parseFloatOrDefault(1f, "b", properties)); + this.a = Math.max(0f, parseFloatOrDefault(1f, "a", properties)); + + this.useGlint = Boolean.parseBoolean(properties.getLastWithoutMetadataOrDefault("false", "citresewn", "useGlint").value()); + this.blur = Boolean.parseBoolean(properties.getLastWithoutMetadataOrDefault("true", "citresewn", "blur").value()); + + PropertyValue blendProp = properties.getLastWithoutMetadataOrDefault("add", "citresewn", "blend"); + try { + this.blend = Blend.getBlend(blendProp.value()); + } catch (Exception e) { + throw new CITParsingException("Could not parse blending method", properties, blendProp.position(), e); + } + + for (CITCondition condition : conditions) + if (condition instanceof ConditionEnchantments enchantments) { + if (enchantmentChecks == null && enchantments.getEnchantments().length > 0) + enchantmentChecks = new HashSet<>(); + + enchantmentChecks.addAll(Arrays.asList(enchantments.getEnchantments())); + } + } + + private float parseFloatOrDefault(float defaultValue, String propertyName, PropertyGroup properties) throws CITParsingException { + PropertyValue property = properties.getLastWithoutMetadata("citresewn", propertyName); + if (property == null) + return defaultValue; + try { + return Float.parseFloat(property.value()); + } catch (Exception e) { + throw new CITParsingException("Could not parse float", properties, property.position(), e); + } + } + + public static class Container extends CITTypeContainer<TypeEnchantment> implements CITGlobalProperties { + public Container() { + super(TypeEnchantment.class, TypeEnchantment::new, "enchantment"); + } + + public boolean globalUseGlint = true; + public int globalCap = Integer.MAX_VALUE; + public MergeMethodIntensity.MergeMethod globalMergeMethod = MergeMethodIntensity.MergeMethod.AVERAGE; + public float globalFade = 0.5f; + + public List<CIT<TypeEnchantment>> loaded = new ArrayList<>(); + public List<List<CIT<TypeEnchantment>>> loadedLayered = new ArrayList<>(); + + private List<CIT<TypeEnchantment>> appliedContext = null; + private boolean apply = false, defaultGlint = false; + + @Override + public void load(List<CIT<TypeEnchantment>> parsedCITs) { + loaded.addAll(parsedCITs); + + Map<Integer, List<CIT<TypeEnchantment>>> layers = new HashMap<>(); + for (CIT<TypeEnchantment> cit : loaded) + layers.computeIfAbsent(cit.type.layer, i -> new ArrayList<>()).add(cit); + loadedLayered.clear(); + layers.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEachOrdered(layer -> loadedLayered.add(layer.getValue())); + + for (CIT<TypeEnchantment> cit : loaded) + for (GlintRenderLayer glintLayer : GlintRenderLayer.values()) { + RenderLayer renderLayer = glintLayer.build(cit.type, cit.propertiesIdentifier); + + cit.type.renderLayers.put(glintLayer, renderLayer); + ((BufferBuilderStorageAccessor) MinecraftClient.getInstance().getBufferBuilders()).getEntityBuilders().put(renderLayer, new BufferBuilder(renderLayer.getExpectedBufferSize())); + } + } + + @Override + public void globalProperty(String key, @Nullable PropertyValue value) throws Exception { + switch (key) { + case "useGlint" -> { + globalUseGlint = value == null ? true : Boolean.parseBoolean(value.value()); + if (!globalUseGlint && !"false".equalsIgnoreCase(value.value())) + throw new Exception("Could not parse boolean"); + } + case "cap" -> { + globalCap = value == null ? Integer.MAX_VALUE : Integer.parseInt(value.value()); + } + case "method" -> { + globalMergeMethod = value == null ? MergeMethodIntensity.MergeMethod.AVERAGE : MergeMethodIntensity.MergeMethod.parse(value.value()); + } + case "fade" -> { + globalFade = value == null ? 0.5f : Float.parseFloat(value.value()); + } + } + } + + @Override + public void dispose() { + appliedContext = null; + apply = false; + + for (CIT<TypeEnchantment> cit : loaded) + for (RenderLayer renderLayer : cit.type.renderLayers.values()) + ((BufferBuilderStorageAccessor) MinecraftClient.getInstance().getBufferBuilders()).getEntityBuilders().remove(renderLayer); + + loaded.clear(); + loadedLayered.clear(); + } + + public void apply() { + if (appliedContext != null) + apply = true; + } + + public boolean shouldApply() { + return apply && active(); + } + + public boolean shouldNotApplyDefaultGlint() { + return !globalUseGlint || (apply && !defaultGlint); + } + + public Container setContext(CITContext context) { + apply = false; + defaultGlint = false; + appliedContext = null; + if (context == null) + return this; + + List<WeakReference<CIT<TypeEnchantment>>> cits = ((CITCacheEnchantment) (Object) context.stack).citresewn$getCacheTypeEnchantment().get(context); + + appliedContext = new ArrayList<>(); + if (cits != null) + for (WeakReference<CIT<TypeEnchantment>> citRef : cits) + if (citRef != null) { + CIT<TypeEnchantment> cit = citRef.get(); + if (cit != null) { + appliedContext.add(cit); + if (cit.type.useGlint) + defaultGlint = true; + } + } + + if (appliedContext.isEmpty()) + appliedContext = null; + else + globalMergeMethod.applyMethod(appliedContext, context); + + return this; + } + + public List<CIT<TypeEnchantment>> getRealTimeCIT(CITContext context) { + List<CIT<TypeEnchantment>> cits = new ArrayList<>(); + for (List<CIT<TypeEnchantment>> layer : loadedLayered) + for (CIT<TypeEnchantment> cit : layer) + if (cit.test(context)) { + cits.add(cit); + break; + } + + return cits; + } + } + + 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(TypeEnchantment enchantment, Identifier propertiesIdentifier) { + class Texturing implements Runnable { + private final float speed, rotation, r, g, b, a; + private final MergeMethodIntensity methodIntensity; + + Texturing(float speed, float rotation, float r, float g, float b, float a, MergeMethodIntensity methodIntensity) { + this.speed = speed; + this.rotation = rotation; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + this.methodIntensity = methodIntensity; + } + + @Override + public void run() { + float l = Util.getMeasuringTimeMs() * CITResewnDefaultsConfig.INSTANCE.type_enchantment_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)); + matrix4f.multiply(Matrix4f.scale(scale, scale, scale)); + setTextureMatrix(matrix4f); + + setShaderColor(r, g, b, a * methodIntensity.intensity); + } + } + + RenderLayer.MultiPhaseParameters.Builder layer = RenderLayer.MultiPhaseParameters.builder() + .texture(new RenderPhase.Texture(enchantment.texture, enchantment.blur, false)) + .texturing(new RenderPhase.Texturing("citresewn_glint_texturing", new Texturing(enchantment.speed, enchantment.rotation, enchantment.r, enchantment.g, enchantment.b, enchantment.a, enchantment.methodIntensity), () -> { + RenderSystem.resetTextureMatrix(); + + setShaderColor(1f, 1f, 1f, 1f); + })) + .transparency(enchantment.blend); + + this.setup.accept(layer); + + return RenderLayer.of("citresewn:enchantment_" + this.name + ":" + propertiesIdentifier.toString(), + VertexFormats.POSITION_TEXTURE, + VertexFormat.DrawMode.QUADS, + 256, + layer.build(false)); + } + + public VertexConsumer tryApply(VertexConsumer base, RenderLayer baseLayer, VertexConsumerProvider provider) { + if (!CONTAINER.apply || CONTAINER.appliedContext == null || CONTAINER.appliedContext.size() == 0) + return null; + + VertexConsumer[] layers = new VertexConsumer[Math.min(CONTAINER.appliedContext.size(), CONTAINER.globalCap)]; + + for (int i = 0; i < layers.length; i++) + layers[i] = provider.getBuffer(CONTAINER.appliedContext.get(i).type.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 MergeMethodIntensity { + public float intensity = 1f; + + public enum MergeMethod { + NONE, + AVERAGE { + @Override + public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CIT<TypeEnchantment> cit) { + Identifier enchantment = null; + for (Identifier enchantmentMatch : cit.type.enchantmentChecks) + if (stackEnchantments.containsKey(enchantmentMatch)) { + enchantment = enchantmentMatch; + break; + } + + if (enchantment == null) { + cit.type.methodIntensity.intensity = 0f; + } else { + float sum = 0f; + for (Integer value : stackEnchantments.values()) + sum += value; + + cit.type.methodIntensity.intensity = (float) stackEnchantments.get(enchantment) / sum; + } + } + }, + LAYERED { + @Override + public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CIT<TypeEnchantment> cit) { + Identifier enchantment = null; + for (Identifier enchantmentMatch : cit.type.enchantmentChecks) + if (stackEnchantments.containsKey(enchantmentMatch)) { + enchantment = enchantmentMatch; + break; + } + if (enchantment == null) { + cit.type.methodIntensity.intensity = 0f; + return; + } + + float max = 0f; + for (Integer value : stackEnchantments.values()) + if (value > max) + max = value; + + cit.type.methodIntensity.intensity = (float) stackEnchantments.get(enchantment) / max; + } + }, + CYCLE { + @Override + public void applyMethod(List<CIT<TypeEnchantment>> citEnchantments, CITContext context) { + List<Map.Entry<CIT<TypeEnchantment>, Float>> durations = new ArrayList<>(); + for (CIT<TypeEnchantment> cit : citEnchantments) + durations.add(new HashMap.SimpleEntry<>(cit, cit.type.duration)); + + for (Map.Entry<CIT<TypeEnchantment>, Float> intensity : Loops.statelessFadingLoop(durations, CONTAINER.globalFade, ticks, 20).entrySet()) + intensity.getKey().type.methodIntensity.intensity = intensity.getValue(); + } + }; + + public static int ticks = 0; + + public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CIT<TypeEnchantment> cit) { + cit.type.methodIntensity.intensity = 1f; + } + + public void applyMethod(List<CIT<TypeEnchantment>> citEnchantments, CITContext context) { + Map<Identifier, Integer> stackEnchantments = context.enchantments(); + + for (CIT<TypeEnchantment> cit : citEnchantments) + if (cit.type.enchantmentChecks != null) + applyIntensity(stackEnchantments, cit); + } + + public static MergeMethod parse(String value) { + return switch (value.toLowerCase(Locale.ROOT)) { + case "none" -> NONE; + case "average" -> AVERAGE; + case "layered" -> LAYERED; + case "cycle" -> CYCLE; + default -> throw new IllegalArgumentException("Unknown merge method"); + }; + } + } + } + + 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 Exception { + try { //check named blending function + return Named.valueOf(blendString.toUpperCase(Locale.ENGLISH)).blend; + } catch (IllegalArgumentException ignored) { // create custom blending function + String[] split = blendString.split("\\p{Zs}+"); + 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); + } + } + + 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 interface CITCacheEnchantment { + CITCache.MultiList<TypeEnchantment> citresewn$getCacheTypeEnchantment(); + } +} diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITItem.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeItem.java index 2300f88..fe0bafb 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/cits/CITItem.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeItem.java @@ -1,7 +1,8 @@ -package shcm.shsupercm.fabric.citresewn.pack.cits; +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; import com.google.common.collect.ImmutableMap; import com.mojang.datafixers.util.Either; +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.client.render.model.BakedModel; @@ -12,36 +13,48 @@ 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.item.Items; 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 shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionItems; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnItemModelIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnTextureIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item.JsonUnbakedModelAccessor; +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.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 { +/** + * ///// PORTED FROM BETA \\\\\ + * This shit was ported from the + * beta and will be rewritten at + * some point! + * \\\\\ ///// + */ +public class TypeItem extends CITType { + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + private static final String GENERATED_SUB_CITS_PREFIX = "sub_cititem_generated_"; public static final Set<Identifier> GENERATED_SUB_CITS_SEEN = new HashSet<>(); + private final List<Item> items = new ArrayList<>(); + 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<>(); @@ -50,93 +63,95 @@ public class CITItem extends CIT { 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); - } - } + @Override + public Set<PropertyKey> typeProperties() { + return Set.of(PropertyKey.of("model"), PropertyKey.of("texture"), PropertyKey.of("tile")); + } - 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); + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + for (CITCondition condition : conditions) + if (condition instanceof ConditionItems conditionItems) + items.addAll(Arrays.asList(conditionItems.items)); - String subItem = property.substring(6); - Identifier subItemIdentifier = fixDeprecatedSubItem(subItem); - assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier); - } + if (this.items.size() == 0) + try { + Identifier propertiesName = new Identifier(properties.stripName()); + if (!Registry.ITEM.containsId(propertiesName)) + throw new Exception(); + Item item = Registry.ITEM.get(propertiesName); + conditions.add(new ConditionItems(item)); + this.items.add(item); + } catch (Exception ignored) { + throw new CITParsingException("Not targeting any item type", properties, -1); + } + + Identifier assetIdentifier; + PropertyValue modelProp = properties.getLastWithoutMetadata("citresewn", "model"); + boolean containsTexture = modelProp == null && !properties.get("citresewn", "texture", "tile").isEmpty(); - 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 (!containsTexture) { + assetIdentifier = resolveAsset(properties.identifier, modelProp, "models", ".json", resourceManager); + if (assetIdentifier != null) + assetIdentifiers.put(null, assetIdentifier); + else if (modelProp != null) { + assetIdentifier = resolveAsset(properties.identifier, modelProp, "models", ".json", resourceManager); 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 (PropertyValue property : properties.get("citresewn", "model")) { + Identifier subIdentifier = resolveAsset(properties.identifier, property, "models", ".json", resourceManager); + if (subIdentifier == null) + throw new CITParsingException("Cannot resolve path", properties, property.position()); - 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); + String subItem = property.keyMetadata(); + Identifier subItemIdentifier = fixDeprecatedSubItem(subItem, properties, property.position()); + assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier); + } - textureOverrideMap.put(property.substring(8), Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(subIdentifier)))); - } + if (assetIdentifiers.size() == 0) { // attempt to load texture + isTexture = true; + PropertyValue textureProp = properties.getLastWithoutMetadata("citresewn", "texture", "tile"); + assetIdentifier = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (assetIdentifier != null) + assetIdentifiers.put(null, assetIdentifier); + + for (PropertyValue property : properties.get("citresewn", "texture", "tile")) { + Identifier subIdentifier = resolveAsset(properties.identifier, property, "textures", ".png", resourceManager); + if (subIdentifier == null) + throw new CITParsingException("Cannot resolve path", properties, property.position()); + + String subItem = property.keyMetadata(); + Identifier subItemIdentifier = fixDeprecatedSubItem(subItem, properties, property.position()); + assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier); + } + } else { // attempt to load textureOverrideMap from textures + PropertyValue textureProp = properties.getLastWithoutMetadata("citresewn", "texture", "tile"); + if (textureProp != null) { + assetIdentifier = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (assetIdentifier != null) + textureOverrideMap.put(null, Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(assetIdentifier)))); + else + throw new CITParsingException("Cannot resolve path", properties, textureProp.position()); } - 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()); + for (PropertyValue property : properties.get("citresewn", "texture", "tile")) { + textureProp = property; + Identifier subIdentifier = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (subIdentifier == null) + throw new CITParsingException("Cannot resolve path", properties, property.position()); + + textureOverrideMap.put(property.keyMetadata(), Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(subIdentifier)))); + } } + + if (assetIdentifiers.size() == 0) + throw new CITParsingException("Could not resolve a replacement model/texture", properties, -1); } - public void loadUnbakedAssets(ResourceManager resourceManager) throws CITLoadException { + public void loadUnbakedAssets(ResourceManager resourceManager) throws Exception { try { if (isTexture) { JsonUnbakedModel itemJson = getModelForFirstItemType(resourceManager); @@ -212,7 +227,7 @@ public class CITItem extends CIT { Collections.reverse(overrideModels); for (Identifier overrideModel : overrideModels) { - Identifier replacement = resolvePath(baseIdentifier, overrideModel.toString(), ".json", resourceManager::containsResource); + Identifier replacement = resolveAsset(baseIdentifier, overrideModel.toString(), "models", ".json", resourceManager); if (replacement != null) { String subTexturePath = replacement.toString().substring(0, replacement.toString().lastIndexOf('.')); final String subTextureName = subTexturePath.substring(subTexturePath.lastIndexOf('/') + 1); @@ -274,8 +289,6 @@ public class CITItem extends CIT { } } } - } catch (Exception e) { - throw new CITLoadException(pack.resourcePack, propertiesIdentifier, (e.getClass() == Exception.class ? "" : e.getClass().getSimpleName() + ": ") + e.getMessage()); } finally { assetIdentifiers = null; textureOverrideMap = null; @@ -302,7 +315,7 @@ public class CITItem extends CIT { ((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); + Identifier resolvedIdentifier = resolveAsset(identifier, left.get().getTextureId().getPath(), "textures", ".png", resourceManager); if (resolvedIdentifier != null) return Either.left(new SpriteIdentifier(left.get().getAtlasId(), new ResewnTextureIdentifier(resolvedIdentifier))); } @@ -337,7 +350,7 @@ public class CITItem extends CIT { 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)); + parentId = resolveAsset(identifier, parentId.getPath(), "models", ".json", resourceManager); if (parentId != null) ((JsonUnbakedModelAccessor) json).setParentId(new ResewnItemModelIdentifier(parentId)); } @@ -346,7 +359,7 @@ public class CITItem extends CIT { 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)); + Identifier resolvedOverridePath = resolveAsset(identifier, override.getModelId().getPath(), "models", ".json", resourceManager); if (resolvedOverridePath != null) return new ModelOverride(new ResewnItemModelIdentifier(resolvedOverridePath), override.streamConditions().collect(Collectors.toList())); } @@ -381,7 +394,9 @@ public class CITItem extends CIT { throw new Exception("Unknown asset type"); } - public Identifier fixDeprecatedSubItem(String subItem) { + public Identifier fixDeprecatedSubItem(String subItem, PropertyGroup properties, int position) { + if (subItem == null) + return null; String replacement = switch (subItem) { case "bow_pulling_standby" -> "bow"; case "crossbow_standby" -> "crossbow"; @@ -394,7 +409,7 @@ public class CITItem extends CIT { }; if (replacement != null) { - CITResewn.logWarnLoading("CIT Warning: Using deprecated sub item id \"" + subItem + "\" instead of \"" + replacement + "\" in " + pack.resourcePack.getName() + " -> " + propertiesIdentifier.toString()); + CITResewn.logWarnLoading(properties.messageWithDescriptorOf("Warning: Using deprecated sub item id \"" + subItem + "\" instead of \"" + replacement + "\"", position)); return new Identifier("minecraft", "item/" + replacement); } @@ -422,13 +437,14 @@ public class CITItem extends CIT { } } - public BakedModel getItemModel(ItemStack stack, ClientWorld world, LivingEntity entity, int seed) { + public BakedModel getItemModel(CITContext context, int seed) { + ClientWorld world = context.world instanceof ClientWorld clientWorld ? clientWorld : null; // get sub items or bakedModel if no sub item matches @Nullable - BakedModel bakedModel = bakedSubModels.apply(this.bakedModel, stack, world, entity, seed); + BakedModel bakedModel = bakedSubModels.apply(this.bakedModel, context.stack, world, context.entity, seed); // apply model overrides if (bakedModel != null && bakedModel.getOverrides() != null) - bakedModel = bakedModel.getOverrides().apply(bakedModel, stack, world, entity, seed); + bakedModel = bakedModel.getOverrides().apply(bakedModel, context.stack, world, context.entity, seed); return bakedModel; } @@ -448,16 +464,57 @@ public class CITItem extends CIT { this.overrides[this.overrides.length - 1] = new BakedOverride( key.stream() - .map((condition) -> new InlinedCondition(object2IntMap.getInt(condition.getType()), condition.getThreshold())) - .toArray(InlinedCondition[]::new) + .map((condition) -> new InlinedCondition(object2IntMap.getInt(condition.getType()), condition.getThreshold())) + .toArray(InlinedCondition[]::new) , bakedModel); } } - public interface Cached { - CITItem citresewn_getCachedCITItem(Supplier<CITItem> realtime); + public static class Container extends CITTypeContainer<TypeItem> { + public Container() { + super(TypeItem.class, TypeItem::new, "item"); + } + + public Set<CIT<TypeItem>> loaded = new HashSet<>(); + public Map<Item, Set<CIT<TypeItem>>> loadedTyped = new IdentityHashMap<>(); + + @Override + public void load(List<CIT<TypeItem>> parsedCITs) { + loaded.addAll(parsedCITs); + for (CIT<TypeItem> cit : parsedCITs) + for (CITCondition condition : cit.conditions) + if (condition instanceof ConditionItems items) + for (Item item : items.items) + if (item != null) + loadedTyped.computeIfAbsent(item, i -> new LinkedHashSet<>()).add(cit); + } + + @Override + public void dispose() { + loaded.clear(); + loadedTyped.clear(); + } + + public CIT<TypeItem> getCIT(CITContext context, int seed) { + return ((CITCacheItem) (Object) context.stack).citresewn$getCacheTypeItem().get(context).get(); + } + + public CIT<TypeItem> getRealTimeCIT(CITContext context) { + Set<CIT<TypeItem>> loadedForItemType = loadedTyped.get(context.stack.getItem()); + if (loadedForItemType != null) + for (CIT<TypeItem> cit : loadedForItemType) + if (cit.test(context)) + return cit; + + return null; + } + } + + public interface CITCacheItem { + CITCache.Single<TypeItem> citresewn$getCacheTypeItem(); + } - boolean citresewn_isMojankCIT(); - void citresewn_setMojankCIT(boolean mojankCIT); + public interface BakedModelManagerMixinAccess { + void citresewn$forceMojankModel(BakedModel model); } -} +}
\ No newline at end of file diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnItemModelIdentifier.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnItemModelIdentifier.java index c531983..498f61f 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnItemModelIdentifier.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnItemModelIdentifier.java @@ -1,11 +1,9 @@ -package shcm.shsupercm.fabric.citresewn.pack; +package shcm.shsupercm.fabric.citresewn.defaults.common; import net.minecraft.util.Identifier; -import shcm.shsupercm.fabric.citresewn.mixin.core.ModelLoaderMixin; /** - * Marks models as cit item models. - * @see ModelLoaderMixin + * Marks models as cit models. */ public class ResewnItemModelIdentifier extends Identifier { public ResewnItemModelIdentifier(String id) { diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnTextureIdentifier.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnTextureIdentifier.java index 033c567..a6dd2ef 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/pack/ResewnTextureIdentifier.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnTextureIdentifier.java @@ -1,11 +1,9 @@ -package shcm.shsupercm.fabric.citresewn.pack; +package shcm.shsupercm.fabric.citresewn.defaults.common; 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) { diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfig.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfig.java new file mode 100644 index 0000000..41126d2 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfig.java @@ -0,0 +1,50 @@ +package shcm.shsupercm.fabric.citresewn.defaults.config; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonWriter; +import org.apache.commons.io.IOUtils; +import shcm.shsupercm.fabric.citresewn.CITResewn; + +import java.io.*; + +public class CITResewnDefaultsConfig { + public float type_enchantment_scroll_multiplier = 1f; + + private static final File FILE = new File("config/citresewn-defaults.json"); + + public static final CITResewnDefaultsConfig INSTANCE = read(); + + public static CITResewnDefaultsConfig read() { + if (!FILE.exists()) + return new CITResewnDefaultsConfig().write(); + + Reader reader = null; + try { + return new Gson().fromJson(reader = new FileReader(FILE), CITResewnDefaultsConfig.class); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(reader); + } + } + + public CITResewnDefaultsConfig write() { + Gson gson = new Gson(); + JsonWriter writer = null; + try { + FILE.getParentFile().mkdirs(); + writer = gson.newJsonWriter(new FileWriter(FILE)); + writer.setIndent(" "); + + gson.toJson(gson.toJsonTree(this, CITResewnDefaultsConfig.class), writer); + } catch (Exception e) { + CITResewn.LOG.error("Couldn't save defaults config"); + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(writer); + } + return this; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfigScreenFactory.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfigScreenFactory.java new file mode 100644 index 0000000..95b0579 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfigScreenFactory.java @@ -0,0 +1,33 @@ +package shcm.shsupercm.fabric.citresewn.defaults.config; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.LiteralText; +import net.minecraft.text.TranslatableText; +import shcm.shsupercm.fabric.citresewn.config.CITResewnConfigScreenFactory; + +public class CITResewnDefaultsConfigScreenFactory { + @Entrypoint(CITResewnConfigScreenFactory.DEFAULTS_CONFIG_ENTRYPOINT) + public static Screen create(Screen parent) { + CITResewnDefaultsConfig currentConfig = CITResewnDefaultsConfig.INSTANCE, defaultConfig = new CITResewnDefaultsConfig(); + + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(new TranslatableText("config.citresewn-defaults.title")) + .setSavingRunnable(currentConfig::write); + + ConfigCategory category = builder.getOrCreateCategory(new LiteralText("")); + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + + category.addEntry(entryBuilder.startFloatField(new TranslatableText("config.citresewn-defaults.type_enchantment_scroll_multiplier.title"), currentConfig.type_enchantment_scroll_multiplier) + .setTooltip(new TranslatableText("config.citresewn-defaults.type_enchantment_scroll_multiplier.tooltip")) + .setSaveConsumer(newConfig -> currentConfig.type_enchantment_scroll_multiplier = newConfig) + .setDefaultValue(defaultConfig.type_enchantment_scroll_multiplier) + .build()); + + return builder.build(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsModMenu.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsModMenu.java new file mode 100644 index 0000000..48edf33 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsModMenu.java @@ -0,0 +1,27 @@ +package shcm.shsupercm.fabric.citresewn.defaults.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 net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.NoticeScreen; +import net.minecraft.text.Text; + +@Entrypoint("modmenu") +public class CITResewnDefaultsModMenu implements ModMenuApi { + @Override + public ConfigScreenFactory<?> getModConfigScreenFactory() { + if (FabricLoader.getInstance().isModLoaded("cloth-config2")) + return new ClothConfigOpenImpl().getModConfigScreenFactory(); + + return parent -> new NoticeScreen(() -> MinecraftClient.getInstance().setScreen(parent), Text.of("CIT Resewn: Defaults"), Text.of("CIT Resewn requires Cloth Config to be able to show the config.")); + } + + private static class ClothConfigOpenImpl implements ModMenuApi { + @Override + public ConfigScreenFactory<?> getModConfigScreenFactory() { + return CITResewnDefaultsConfigScreenFactory::create; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/ModelLoaderMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/ModelLoaderMixin.java new file mode 100644 index 0000000..8ed0e20 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/ModelLoaderMixin.java @@ -0,0 +1,87 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.common; + +import com.mojang.datafixers.util.Either; +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.render.model.json.ModelOverride; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +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.callback.CallbackInfoReturnable; +import shcm.shsupercm.fabric.citresewn.cit.CITType; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnItemModelIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnTextureIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item.JsonUnbakedModelAccessor; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Will be rewritten at some point. + */ +@Mixin(ModelLoader.class) +public class ModelLoaderMixin { + @Shadow @Final private ResourceManager resourceManager; + + @Inject(method = "loadModelFromJson", cancellable = true, at = @At("HEAD")) + public void citresewn$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 = CITType.resolveAsset(id, originalPath, "textures", ".png", resourceManager); + 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 = CITType.resolveAsset(id, parentId.getPath(), "models", ".json", resourceManager); + 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 = CITType.resolveAsset(id, override.getModelId().getPath(), "models", ".json", resourceManager); + 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); + } + } + } +} diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/SpriteAtlasTextureMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/SpriteAtlasTextureMixin.java index 8cc195b..61eff5c 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/core/SpriteAtlasTextureMixin.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/SpriteAtlasTextureMixin.java @@ -1,4 +1,4 @@ -package shcm.shsupercm.fabric.citresewn.mixin.core; +package shcm.shsupercm.fabric.citresewn.defaults.mixin.common; import net.minecraft.client.texture.SpriteAtlasTexture; import net.minecraft.util.Identifier; @@ -6,7 +6,7 @@ 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; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnTextureIdentifier; @Mixin(SpriteAtlasTexture.class) public class SpriteAtlasTextureMixin { diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ArmorFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ArmorFeatureRendererMixin.java new file mode 100644 index 0000000..d61af4c --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ArmorFeatureRendererMixin.java @@ -0,0 +1,51 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.armor; + +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.cit.CIT; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeArmor; + +import java.util.Map; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeArmor.CONTAINER; + +@Mixin(ArmorFeatureRenderer.class) +public class ArmorFeatureRendererMixin<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> { + private Map<String, Identifier> citresewn$cachedTextures = null; + + @Inject(method = "renderArmor", at = @At("HEAD")) + public void citresewn$renderArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { + citresewn$cachedTextures = null; + if (!CONTAINER.active()) + return; + + ItemStack equippedStack = entity.getEquippedStack(armorSlot); + + CIT<TypeArmor> cit = CONTAINER.getCIT(new CITContext(equippedStack, entity.getWorld(), entity)); + if (cit != null) + citresewn$cachedTextures = cit.type.textures; + } + + @Inject(method = "getArmorTexture", cancellable = true, at = @At("HEAD")) + private void citresewn$getArmorTexture(ArmorItem item, boolean legs, String overlay, CallbackInfoReturnable<Identifier> cir) { + if (citresewn$cachedTextures == null) + return; + + Identifier identifier = citresewn$cachedTextures.get(item.getMaterial().getName() + "_layer_" + (legs ? "2" : "1") + (overlay == null ? "" : "_" + overlay)); + if (identifier != null) + cir.setReturnValue(identifier); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ItemStackMixin.java new file mode 100644 index 0000000..85b0d22 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ItemStackMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.armor; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeArmor; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeArmor.CITCacheArmor { + private final CITCache.Single<TypeArmor> citresewn$cacheTypeArmor = new CITCache.Single<>(TypeArmor.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.Single<TypeArmor> citresewn$getCacheTypeArmor() { + return this.citresewn$cacheTypeArmor; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ElytraFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ElytraFeatureRendererMixin.java new file mode 100644 index 0000000..0772adf --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ElytraFeatureRendererMixin.java @@ -0,0 +1,42 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.elytra; + +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.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +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.cit.CIT; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeElytra; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeElytra.CONTAINER; + +@Mixin(ElytraFeatureRenderer.class) +public class ElytraFeatureRendererMixin { + @Shadow @Mutable @Final private static Identifier SKIN; + + private final static Identifier citresewn$ORIGINAL_SKIN = SKIN; + + @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At("HEAD")) + public void citresewn$render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { + if (!CONTAINER.active()) + return; + + ItemStack equippedStack = livingEntity.getEquippedStack(EquipmentSlot.CHEST); + if (!equippedStack.isOf(Items.ELYTRA)) + return; + + CIT<TypeElytra> cit = CONTAINER.getCIT(new CITContext(equippedStack, livingEntity.getWorld(), livingEntity)); + SKIN = cit == null ? citresewn$ORIGINAL_SKIN : cit.type.texture; + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ItemStackMixin.java new file mode 100644 index 0000000..a751fb9 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ItemStackMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.elytra; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeElytra; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeElytra.CITCacheElytra { + private final CITCache.Single<TypeElytra> citresewn$cacheTypeElytra = new CITCache.Single<>(TypeElytra.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.Single<TypeElytra> citresewn$getCacheTypeElytra() { + return this.citresewn$cacheTypeElytra; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ArmorFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ArmorFeatureRendererMixin.java new file mode 100644 index 0000000..4a852ca --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ArmorFeatureRendererMixin.java @@ -0,0 +1,30 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +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.cit.CITContext; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment.CONTAINER; + +@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 citresewn$enchantment$setAppliedContextAndStartApplyingArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T livingEntity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(new CITContext(livingEntity.getEquippedStack(armorSlot), livingEntity.world, livingEntity)).apply(); + } + + @Inject(method = "renderArmor", at = @At("RETURN")) + private void citresewn$enchantment$stopApplyingArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T livingEntity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(null); + } +} diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/BufferBuilderStorageAccessor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/BufferBuilderStorageAccessor.java index 619a82d..e23e091 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/BufferBuilderStorageAccessor.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/BufferBuilderStorageAccessor.java @@ -1,4 +1,4 @@ -package shcm.shsupercm.fabric.citresewn.mixin.citenchantment; +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilderStorage; @@ -10,6 +10,6 @@ import java.util.SortedMap; @Mixin(BufferBuilderStorage.class) public interface BufferBuilderStorageAccessor { - @Accessor("entityBuilders") - SortedMap<RenderLayer, BufferBuilder> entityBuilders(); + @Accessor + SortedMap<RenderLayer, BufferBuilder> getEntityBuilders(); } diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ElytraFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ElytraFeatureRendererMixin.java new file mode 100644 index 0000000..c64fb77 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ElytraFeatureRendererMixin.java @@ -0,0 +1,29 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +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 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.CITContext; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment.CONTAINER; + +@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 citresewn$enchantment$setAppliedContextAndStartApplyingElytra(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(new CITContext(livingEntity.getEquippedStack(EquipmentSlot.CHEST), livingEntity.world, livingEntity)).apply(); + } + + @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 citresewn$enchantment$stopApplyingElytra(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(null); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemRendererMixin.java new file mode 100644 index 0000000..b4bd37d --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemRendererMixin.java @@ -0,0 +1,86 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +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.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment.CONTAINER; + +@Mixin(value = ItemRenderer.class, priority = 200) +public class ItemRendererMixin { + @Inject(method = "getModel", at = @At("HEAD")) + private void citresewn$enchantment$setAppliedContext(ItemStack stack, World world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) { + if (CONTAINER.active()) + CONTAINER.setContext(new CITContext(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 citresewn$enchantment$startApplyingItem(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.apply(); + } + + @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("RETURN")) + private void citresewn$enchantment$stopApplyingItem(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(null); + } + + @Inject(method = "getArmorGlintConsumer", cancellable = true, at = @At("RETURN")) + private static void citresewn$enchantment$getArmorGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = solid ? TypeEnchantment.GlintRenderLayer.ARMOR_GLINT.tryApply(cir.getReturnValue(), layer, provider) : TypeEnchantment.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 citresewn$enchantment$getCompassGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, MatrixStack.Entry entry, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = TypeEnchantment.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 citresewn$enchantment$getDirectCompassGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, MatrixStack.Entry entry, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = TypeEnchantment.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 citresewn$enchantment$getItemGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = MinecraftClient.isFabulousGraphicsOrBetter() && layer == TexturedRenderLayers.getItemEntityTranslucentCull() ? TypeEnchantment.GlintRenderLayer.GLINT_TRANSLUCENT.tryApply(cir.getReturnValue(), layer, provider) : (solid ? TypeEnchantment.GlintRenderLayer.GLINT.tryApply(cir.getReturnValue(), layer, provider) : TypeEnchantment.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 citresewn$enchantment$getDirectItemGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = solid ? TypeEnchantment.GlintRenderLayer.DIRECT_GLINT.tryApply(cir.getReturnValue(), layer, provider) : TypeEnchantment.GlintRenderLayer.DIRECT_ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider); + if (vertexConsumer != null) + cir.setReturnValue(vertexConsumer); + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemStackMixin.java new file mode 100644 index 0000000..d49e116 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemStackMixin.java @@ -0,0 +1,25 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +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.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeEnchantment.CITCacheEnchantment { + private final CITCache.MultiList<TypeEnchantment> citresewn$cacheTypeEnchantment = new CITCache.MultiList<>(TypeEnchantment.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.MultiList<TypeEnchantment> citresewn$getCacheTypeEnchantment() { + return this.citresewn$cacheTypeEnchantment; + } + + @Inject(method = "hasGlint", cancellable = true, at = @At("HEAD")) + private void citresewn$enchantment$disableDefaultGlint(CallbackInfoReturnable<Boolean> cir) { + if (TypeEnchantment.CONTAINER.shouldNotApplyDefaultGlint()) + cir.setReturnValue(false); + } +} diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/MinecraftClientMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/MinecraftClientMixin.java index a9bea56..e8b346b 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/MinecraftClientMixin.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/MinecraftClientMixin.java @@ -1,17 +1,16 @@ -package shcm.shsupercm.fabric.citresewn.mixin.citenchantment; +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; 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; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment; @Mixin(MinecraftClient.class) public class MinecraftClientMixin { @Inject(method = "tick", at = @At("HEAD")) public void onTick(CallbackInfo ci) { - ticks++; + TypeEnchantment.MergeMethodIntensity.MergeMethod.ticks++; } } diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/RenderPhaseAccessor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/RenderPhaseAccessor.java index 24e7c3c..b2a59b4 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/citenchantment/RenderPhaseAccessor.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/RenderPhaseAccessor.java @@ -1,4 +1,4 @@ -package shcm.shsupercm.fabric.citresewn.mixin.citenchantment; +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; import net.minecraft.client.render.RenderPhase; import org.spongepowered.asm.mixin.Mixin; diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/BakedModelManagerMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/BakedModelManagerMixin.java new file mode 100644 index 0000000..f7f6b48 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/BakedModelManagerMixin.java @@ -0,0 +1,29 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.client.util.ModelIdentifier; +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.defaults.cit.types.TypeItem; + +@Mixin(BakedModelManager.class) +public class BakedModelManagerMixin implements TypeItem.BakedModelManagerMixinAccess { + private BakedModel citresewn$forcedMojankModel = null; + + @Inject(method = "getModel", cancellable = true, at = + @At("HEAD")) + private void citresewn$getCITMojankModel(ModelIdentifier id, CallbackInfoReturnable<BakedModel> cir) { + if (citresewn$forcedMojankModel != null) { + cir.setReturnValue(citresewn$forcedMojankModel); + citresewn$forcedMojankModel = null; + } + } + + @Override + public void citresewn$forceMojankModel(BakedModel model) { + this.citresewn$forcedMojankModel = model; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemRendererMixin.java new file mode 100644 index 0000000..5f59329 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemRendererMixin.java @@ -0,0 +1,68 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +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.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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import shcm.shsupercm.fabric.citresewn.cit.CIT; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; + +import java.lang.ref.WeakReference; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem.CONTAINER; + +/** + * Do not go through this class, it looks awful because it was ported from a "proof of concept".<br> + * The whole type will be rewritten at some point. + */ +@Mixin(ItemRenderer.class) +public class ItemRendererMixin { + @Shadow @Final private ItemModels models; + + private WeakReference<BakedModel> citresewn$mojankCITModel = null; + + @Inject(method = "getModel", cancellable = true, at = @At("HEAD")) + private void citresewn$getItemModel(ItemStack stack, World world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) { + if (!CONTAINER.active()) + return; + + CITContext context = new CITContext(stack, world, entity); + CIT<TypeItem> cit = CONTAINER.getCIT(context, seed); + citresewn$mojankCITModel = null; + if (cit != null) { + BakedModel citModel = cit.type.getItemModel(context, seed); + + if (citModel != null) { + if (stack.isOf(Items.TRIDENT) || stack.isOf(Items.SPYGLASS)) { + citresewn$mojankCITModel = new WeakReference<>(citModel); + } else + 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 citresewn$fixMojankCITsContext(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (!CONTAINER.active() || citresewn$mojankCITModel == null) + return; + + if (renderMode == ModelTransformation.Mode.GUI || renderMode == ModelTransformation.Mode.GROUND || renderMode == ModelTransformation.Mode.FIXED) + ((TypeItem.BakedModelManagerMixinAccess) this.models.getModelManager()).citresewn$forceMojankModel(citresewn$mojankCITModel.get()); + + citresewn$mojankCITModel = null; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemStackMixin.java new file mode 100644 index 0000000..450d8a3 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemStackMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeItem.CITCacheItem { + private final CITCache.Single<TypeItem> citresewn$cacheTypeItem = new CITCache.Single<>(TypeItem.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.Single<TypeItem> citresewn$getCacheTypeItem() { + return this.citresewn$cacheTypeItem; + } +} diff --git a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/JsonUnbakedModelAccessor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/JsonUnbakedModelAccessor.java index f997d3e..350d01a 100644 --- a/src/main/java/shcm/shsupercm/fabric/citresewn/mixin/cititem/JsonUnbakedModelAccessor.java +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/JsonUnbakedModelAccessor.java @@ -1,4 +1,4 @@ -package shcm.shsupercm.fabric.citresewn.mixin.cititem; +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; import com.mojang.datafixers.util.Either; import net.minecraft.client.render.model.json.JsonUnbakedModel; diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ModelLoaderMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ModelLoaderMixin.java new file mode 100644 index 0000000..93bab6a --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ModelLoaderMixin.java @@ -0,0 +1,99 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.client.color.block.BlockColors; +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.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; +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.cit.CIT; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnItemModelIdentifier; + +import java.util.*; + +import static shcm.shsupercm.fabric.citresewn.CITResewn.info; +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem.CONTAINER; + +@Mixin(ModelLoader.class) +public class ModelLoaderMixin { + @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 = "<init>", at = + @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V")) + public void citresewn$addTypeItemModels(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int i, CallbackInfo ci) { + profiler.swap("citresewn:type_item_models"); + if (!CONTAINER.active()) + return; + + info("Loading item CIT models..."); + for (CIT<TypeItem> cit : CONTAINER.loaded) + try { + cit.type.loadUnbakedAssets(resourceManager); + + for (JsonUnbakedModel unbakedModel : cit.type.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("Errored loading model in " + cit.propertiesIdentifier + " from " + cit.packName); + e.printStackTrace(); + } + + TypeItem.GENERATED_SUB_CITS_SEEN.clear(); + } + + @Inject(method = "upload", at = @At("RETURN")) + public void citresewn$linkTypeItemModels(TextureManager textureManager, Profiler profiler, CallbackInfoReturnable<SpriteAtlasManager> cir) { + if (!CONTAINER.active()) + return; + + profiler.push("citresewn:type_item_linking"); + info("Linking baked models to item CITs..."); + + for (CIT<TypeItem> cit : CONTAINER.loaded) { + for (Map.Entry<List<ModelOverride.Condition>, JsonUnbakedModel> citModelEntry : cit.type.unbakedAssets.entrySet()) { + if (citModelEntry.getKey() == null) { + cit.type.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 " + cit.propertiesIdentifier + " from " + cit.packName); + else + cit.type.bakedSubModels.override(citModelEntry.getKey(), bakedModel); + } + } + cit.type.unbakedAssets = null; + } + + profiler.pop(); + } + + @ModifyArg(method = "loadModelFromJson", at = + @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourceManager;getResource(Lnet/minecraft/util/Identifier;)Lnet/minecraft/resource/Resource;")) + public Identifier citresewn$fixDuplicatePrefixSuffix(Identifier original) { + if (CONTAINER.active() && 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/util/logic/Loops.java b/defaults/src/main/java/shcm/shsupercm/util/logic/Loops.java index 6c18244..6c18244 100644 --- a/src/main/java/shcm/shsupercm/util/logic/Loops.java +++ b/defaults/src/main/java/shcm/shsupercm/util/logic/Loops.java diff --git a/defaults/src/main/resources/assets/citresewn-defaults/lang/en_us.json b/defaults/src/main/resources/assets/citresewn-defaults/lang/en_us.json new file mode 100644 index 0000000..5f68e45 --- /dev/null +++ b/defaults/src/main/resources/assets/citresewn-defaults/lang/en_us.json @@ -0,0 +1,7 @@ +{ + "config.citresewn-defaults.title": "CIT Resewn: Defaults", + "config.citresewn-defaults.tooltip": "Go to the defaults' config menu", + + "config.citresewn-defaults.type_enchantment_scroll_multiplier.title": "Type Enchantment: Scroll multiplier", + "config.citresewn-defaults.type_enchantment_scroll_multiplier.tooltip": "A general multiplier for how fast cit enchantment glints scroll" +}
\ No newline at end of file diff --git a/defaults/src/main/resources/assets/citresewn-defaults/logo.png b/defaults/src/main/resources/assets/citresewn-defaults/logo.png Binary files differnew file mode 100644 index 0000000..35859ac --- /dev/null +++ b/defaults/src/main/resources/assets/citresewn-defaults/logo.png diff --git a/defaults/src/main/resources/citresewn-defaults.accesswidener b/defaults/src/main/resources/citresewn-defaults.accesswidener new file mode 100644 index 0000000..739fc5e --- /dev/null +++ b/defaults/src/main/resources/citresewn-defaults.accesswidener @@ -0,0 +1,30 @@ +accessWidener v1 named + +# ConditionNBT +accessible field net/minecraft/nbt/NbtCompound entries Ljava/util/Map; + +# TypeItem +accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride +accessible class net/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition +accessible method net/minecraft/client/render/model/json/ModelOverrideList <init> ()V +accessible method net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride <init> ([Lnet/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition;Lnet/minecraft/client/render/model/BakedModel;)V +accessible method net/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition <init> (IF)V +accessible field net/minecraft/client/render/model/json/ModelOverrideList overrides [Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride; +mutable field net/minecraft/client/render/model/json/ModelOverrideList overrides [Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride; +accessible field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier; +mutable field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier; + +# TypeEnchantment +accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters +accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;ILnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; +accessible class net/minecraft/client/render/RenderPhase$Shader +accessible class net/minecraft/client/render/RenderPhase$Texturing +accessible class net/minecraft/client/render/RenderPhase$Transparency +accessible class net/minecraft/client/render/RenderPhase$Texture +accessible class net/minecraft/client/render/RenderPhase$Lightmap +accessible class net/minecraft/client/render/RenderPhase$Overlay +accessible class net/minecraft/client/render/RenderPhase$Cull +accessible class net/minecraft/client/render/RenderPhase$DepthTest +accessible class net/minecraft/client/render/RenderPhase$WriteMaskState +accessible class net/minecraft/client/render/RenderPhase$Layering +accessible class net/minecraft/client/render/RenderPhase$Target
\ No newline at end of file diff --git a/defaults/src/main/resources/citresewn-defaults.mixins.json b/defaults/src/main/resources/citresewn-defaults.mixins.json new file mode 100644 index 0000000..536380b --- /dev/null +++ b/defaults/src/main/resources/citresewn-defaults.mixins.json @@ -0,0 +1,9 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "shcm.shsupercm.fabric.citresewn.defaults.mixin", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + } +} diff --git a/defaults/src/main/resources/fabric.mod.json b/defaults/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..f35e7bb --- /dev/null +++ b/defaults/src/main/resources/fabric.mod.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": 1, + "id": "citresewn-defaults", + "version": "${version}", + "name": "CIT Resewn: Defaults", + "description": "Default types and conditions for CIT Resewn", + "authors": [ + "SHsuperCM" + ], + "license": "MIT", + "icon": "assets/citresewn-defaults/logo.png", + "contact": { + "homepage": "https://citresewn.shcm.io/", + "sources": "https://github.com/SHsuperCM/CITResewn", + "issues": "https://github.com/SHsuperCM/CITResewn/issues" + }, + + "environment": "client", + "accessWidener" : "citresewn-defaults.accesswidener", + "mixins": [ + "citresewn-defaults.mixins.json" + ], + "custom": { + "modmenu": { + "parent": { + "id": "citresewn" + } + } + }, + "depends": { + "citresewn": "${version}" + }, + "conflicts": { + "eatinganimationid": "*" + } +} diff --git a/gradle.properties b/gradle.properties index d24d6cd..425af40 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,18 +3,18 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.18.1 +minecraft_version=1.18.2 minecraft_version_compat=1.18.x -yarn_mappings=1.18.1+build.18 -loader_version=0.12.12 +yarn_mappings=1.18.2+build.1 +loader_version=0.13.3 -fabric_api=0.45.0+1.18 +fabric_api=0.47.9+1.18.2 modmenu=3.0.1 cloth=6.1.48 cosmetic_armor=3544655 # Mod Properties -mod_version=0.9.1 +mod_version=rewrite-SNAPSHOT maven_group=shcm.shsupercm.fabric archives_base_name=CITResewn diff --git a/settings.gradle b/settings.gradle index f91a4fe..4ab5170 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,3 +7,5 @@ pluginManagement { gradlePluginPortal() } } + +include 'defaults'
\ No newline at end of file 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 <pack> - 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/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/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/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/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/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/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/resources/assets/citresewn/lang/en_us.json b/src/main/resources/assets/citresewn/lang/en_us.json index 2f90a92..3164cb5 100644 --- a/src/main/resources/assets/citresewn/lang/en_us.json +++ b/src/main/resources/assets/citresewn/lang/en_us.json @@ -1,5 +1,6 @@ { "config.citresewn.title": "CIT Resewn Configuration", + "config.citresewn.configure": "Configure", "config.citresewn.enabled.title": "Enabled", "config.citresewn.enabled.tooltip": "Should CIT Resewn load and inject cits from resourcepacks", @@ -10,9 +11,6 @@ "config.citresewn.mute_warns.title": "Mute loading warnings", "config.citresewn.mute_warns.tooltip": "Should CIT Resewn not log cit warnings", - "config.citresewn.citenchantment_scroll_multiplier.title": "CIT Enchantment: Scroll Multiplier", - "config.citresewn.citenchantment_scroll_multiplier.tooltip": "General multiplier for the scroll speed of the glint textures added by enchantment CITs.", - "config.citresewn.broken_paths.title": "Allow broken paths in resourcepacks", "config.citresewn.broken_paths.tooltip": "Allows packs with illegal resource paths to load.\nMay cause issues!", diff --git a/src/main/resources/citresewn.accesswidener b/src/main/resources/citresewn.accesswidener index 33ad46d..dfde67a 100644 --- a/src/main/resources/citresewn.accesswidener +++ b/src/main/resources/citresewn.accesswidener @@ -1,29 +1 @@ -accessWidener v1 named - -# net.minecraft.client.render.model.json.ModelOverrideList -accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride -accessible class net/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition -accessible method net/minecraft/client/render/model/json/ModelOverrideList <init> ()V -accessible method net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride <init> ([Lnet/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition;Lnet/minecraft/client/render/model/BakedModel;)V -accessible method net/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition <init> (IF)V -accessible field net/minecraft/client/render/model/json/ModelOverrideList overrides [Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride; -mutable field net/minecraft/client/render/model/json/ModelOverrideList overrides [Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride; -accessible field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier; -mutable field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier; - -# net.minecraft.client.render.RenderLayer -accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters -accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;ILnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; - -# net.minecraft.client.render.RenderPhase -accessible class net/minecraft/client/render/RenderPhase$Shader -accessible class net/minecraft/client/render/RenderPhase$Texturing -accessible class net/minecraft/client/render/RenderPhase$Transparency -accessible class net/minecraft/client/render/RenderPhase$Texture -accessible class net/minecraft/client/render/RenderPhase$Lightmap -accessible class net/minecraft/client/render/RenderPhase$Overlay -accessible class net/minecraft/client/render/RenderPhase$Cull -accessible class net/minecraft/client/render/RenderPhase$DepthTest -accessible class net/minecraft/client/render/RenderPhase$WriteMaskState -accessible class net/minecraft/client/render/RenderPhase$Layering -accessible class net/minecraft/client/render/RenderPhase$Target
\ No newline at end of file +accessWidener v1 named
\ No newline at end of file diff --git a/src/main/resources/citresewn.mixins.json b/src/main/resources/citresewn.mixins.json index e32c847..31b8216 100644 --- a/src/main/resources/citresewn.mixins.json +++ b/src/main/resources/citresewn.mixins.json @@ -2,35 +2,8 @@ "required": true, "minVersion": "0.8", "package": "shcm.shsupercm.fabric.citresewn.mixin", - "compatibilityLevel": "JAVA_16", "plugin": "shcm.shsupercm.fabric.citresewn.config.CITResewnMixinConfiguration", - "mixins": [ - "broken_paths.AbstractFileResourcePackMixin", - "broken_paths.IdentifierMixin", - "broken_paths.ReloadableResourceManagerImplMixin", - "broken_paths.ResourcePackCompatibilityMixin", - "citarmor.ArmorFeatureRendererMixin", - "citarmor.ItemStackMixin", - "citelytra.ElytraFeatureRendererMixin", - "citelytra.ItemStackMixin", - "citenchantment.ArmorFeatureRendererMixin", - "citenchantment.BufferBuilderStorageAccessor", - "citenchantment.ElytraFeatureRendererMixin", - "citenchantment.ItemRendererMixin", - "citenchantment.ItemStackMixin", - "citenchantment.RenderPhaseAccessor", - "citenchantment.MinecraftClientMixin", - "cititem.ItemRendererMixin", - "cititem.ItemStackMixin", - "cititem.JsonUnbakedModelAccessor", - "cititem.ModelLoaderMixin", - "core.ChatScreenMixin", - "core.GroupResourcePackAccessor", - "core.ModelLoaderMixin", - "core.NbtCompoundAccessor", - "core.SpriteAtlasTextureMixin", - "core.ZipResourcePackMixin" - ], + "compatibilityLevel": "JAVA_17", "injectors": { "defaultRequire": 1 } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 3ab2b9b..a8e2234 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -2,23 +2,20 @@ "schemaVersion": 1, "id": "citresewn", "version": "${version}", - "name": "CITResewn", + "name": "CIT Resewn", "description": "Re-implements MCPatcher's CIT", "authors": [ "SHsuperCM" ], "license": "MIT", "icon": "assets/citresewn/logo.png", + "contact": { + "homepage": "https://citresewn.shcm.io/", + "sources": "https://github.com/SHsuperCM/CITResewn", + "issues": "https://github.com/SHsuperCM/CITResewn/issues" + }, "environment": "client", - "entrypoints": { - "client": [ - "shcm.shsupercm.fabric.citresewn.CITResewn" - ], - "modmenu": [ - "shcm.shsupercm.fabric.citresewn.config.CITResewnModMenu" - ] - }, "accessWidener" : "citresewn.accesswidener", "mixins": [ "citresewn.mixins.json" @@ -27,5 +24,12 @@ "fabric": "*", "fabricloader": "*", "minecraft": "1.18.x" + }, + "recommends": { + "modmenu": "*", + "cloth-config2": "*" + }, + "breaks": { + "canvas": "*" } } |