From 828323688914a98dd4be809b6385ee490d66bf70 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 23 Apr 2023 15:29:46 +0200 Subject: [PATCH 01/51] Add option to adjust thread priorities (#162) Client thread given default priority + 2 Server thread given min priority + 2 Chunk builder threads given min priority + 1 Number of chunk builders is also adjusted to match Sodium defaults on modern versions (cherry picked from commit f73ce5bdc3178f7bf0cb9d9b62d0315a09cc63ad) --- .../mixins/MinecraftServerMixin.java | 15 ++++++ .../client/ChunkRenderDispatcherMixin.java | 47 +++++++++++++++++++ .../mirror/normalasm/config/NormalConfig.java | 3 +- .../normalasm/core/NormalLoadingPlugin.java | 2 + .../mirror/normalasm/proxy/CommonProxy.java | 2 + src/main/resources/mixins.priorities.json | 17 +++++++ 6 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java create mode 100644 src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java create mode 100644 src/main/resources/mixins.priorities.json diff --git a/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java b/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java new file mode 100644 index 00000000..bc68f462 --- /dev/null +++ b/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java @@ -0,0 +1,15 @@ +package mirror.normalasm.common.priorities.mixins; + +import net.minecraft.server.MinecraftServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + @Redirect(method = "startServerThread", at = @At(value = "INVOKE", target = "Ljava/lang/Thread;start()V")) + private void setPriorityAndStart(Thread serverThread) { + serverThread.setPriority(Thread.MIN_PRIORITY + 2); + serverThread.start(); + } +} \ No newline at end of file diff --git a/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java b/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java new file mode 100644 index 00000000..91212f51 --- /dev/null +++ b/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java @@ -0,0 +1,47 @@ +package mirror.normalasm.common.priorities.mixins.client; + +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; +import net.minecraft.util.math.MathHelper; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.Opcodes; +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.ModifyArg; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ChunkRenderDispatcher.class) +public class ChunkRenderDispatcherMixin { + @Shadow @Final @Mutable + private int countRenderBuilders; + + @Shadow @Final private static Logger LOGGER; + + private int getRenderBuilderCount() { + int processors = Runtime.getRuntime().availableProcessors(); + boolean allowSingleThread = Runtime.getRuntime().availableProcessors() < 2; + return MathHelper.clamp(Math.max(processors / 3, processors - 6), allowSingleThread ? 1 : 2, 10); + } + + @ModifyVariable(method = "(I)V", at = @At("STORE"), index = 3, ordinal = 2) + private int setBuilders(int original) { + return getRenderBuilderCount(); + } + + @Redirect(method = "(I)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher;countRenderBuilders:I")) + private void setBuilders(ChunkRenderDispatcher dispatcher, int original) { + int nThreads = getRenderBuilderCount(); + /* we need more builder objects in the queue than number of threads, because threads don't free them immediately */ + this.countRenderBuilders = nThreads * 10; + LOGGER.info("Creating {} chunk builders", nThreads); + } + + @Redirect(method = "(I)V", at = @At(value = "INVOKE", target = "Ljava/lang/Thread;start()V")) + private void setPriorityAndStart(Thread workerThread) { + workerThread.setPriority(Thread.MIN_PRIORITY + 1); + workerThread.start(); + } +} \ No newline at end of file diff --git a/src/main/java/mirror/normalasm/config/NormalConfig.java b/src/main/java/mirror/normalasm/config/NormalConfig.java index da799483..380233ac 100644 --- a/src/main/java/mirror/normalasm/config/NormalConfig.java +++ b/src/main/java/mirror/normalasm/config/NormalConfig.java @@ -69,7 +69,7 @@ public boolean shouldSkipClass(Class clazz) { public boolean optimizeRegistries, optimizeNBTTagCompoundBackingMap, optimizeFurnaceRecipeStore, stripNearUselessItemStackFields, moreModelManagerCleanup, efficientHashing, replaceSearchTreeWithJEISearching; public boolean releaseSpriteFramesCache, onDemandAnimatedTextures; public boolean optimizeSomeRendering, stripUnnecessaryLocalsInRenderHelper; - public boolean quickerEnableUniversalBucketCheck, stripInstancedRandomFromSoundEventAccessor, classCaching, copyScreenshotToClipboard, releaseScreenshotCache, asyncScreenshot, removeExcessiveGCCalls, smoothDimensionChange; + public boolean quickerEnableUniversalBucketCheck, stripInstancedRandomFromSoundEventAccessor, classCaching, copyScreenshotToClipboard, releaseScreenshotCache, asyncScreenshot, removeExcessiveGCCalls, smoothDimensionChange, threadPriorityFix; public boolean fixBlockIEBaseArrayIndexOutOfBoundsException, cleanupChickenASMClassHierarchyManager, optimizeAmuletRelatedFunctions, labelCanonicalization, skipCraftTweakerRecalculatingSearchTrees, bwmBlastingOilOptimization, optimizeQMDBeamRenderer, repairEvilCraftEIOCompat, optimizeArcaneLockRendering, fixXU2CrafterCrash, disableXU2CrafterRendering, fixTFCFallingBlockFalseStartingTEPos; public boolean fixAmuletHolderCapability, delayItemStackCapabilityInit; public boolean fixFillBucketEventNullPointerException, fixTileEntityOnLoadCME, removeForgeSecurityManager, fasterEntitySpawnPreparation, fixDimensionTypesInliningCrash; @@ -137,6 +137,7 @@ public void load() { asyncScreenshot = getBoolean("asyncScreenshot", "misc", "Process screenshots and print to chat asynchronously", true); removeExcessiveGCCalls = getBoolean("removeExcessiveGCCalls", "misc", "Removes forced garbage collection calls, inspired by VanillaFix, can make world loading faster", true); smoothDimensionChange = getBoolean("smoothDimensionChange", "misc", "Allows changing of dimensions to be smooth and nearly instantaneous, inspired by VanillaFix", true); + threadPriorityFix = getBoolean("threadPriorityFix", "misc", "Adjust thread priorities to improve performance on systems with few cores", true); fixBlockIEBaseArrayIndexOutOfBoundsException = getBoolean("fixBlockIEBaseArrayIndexOutOfBoundsException", "modfixes", "When Immersive Engineering is installed, sometimes it or it's addons can induce an ArrayIndexOutOfBoundsException in BlockIEBase#getPushReaction. This option will be ignored when IE isn't installed", true); cleanupChickenASMClassHierarchyManager = getBoolean("cleanupChickenASMClassHierarchyManager", "modfixes", "EXPERIMENTAL: When ChickenASM (Library of CodeChickenLib and co.) is installed, ClassHierarchyManager can cache a lot of Strings and seem to be unused in any transformation purposes. This clears ClassHierarchyManager of those redundant strings. This option will be ignored when ChickenASM isn't installed", true); diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 17971fdd..53af8388 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -158,6 +158,7 @@ public List getMixinConfigs() { "mixins.crashes.json", "mixins.fix_mc129057.json", "mixins.bucket.json", + "mixins.priorities.json", "mixins.rendering.json", "mixins.datastructures_modelmanager.json", "mixins.screenshot.json", @@ -179,6 +180,7 @@ public List getMixinConfigs() { "mixins.capability.json", "mixins.singletonevents.json", "mixins.efficienthashing.json", + "mixins.priorities.json", "mixins.crashes.json", "mixins.fix_mc129057.json"); } diff --git a/src/main/java/mirror/normalasm/proxy/CommonProxy.java b/src/main/java/mirror/normalasm/proxy/CommonProxy.java index 2a14535f..b829fae9 100644 --- a/src/main/java/mirror/normalasm/proxy/CommonProxy.java +++ b/src/main/java/mirror/normalasm/proxy/CommonProxy.java @@ -56,6 +56,8 @@ public void construct(FMLConstructionEvent event) { if (NormalConfig.instance.cleanupLaunchClassLoaderEarly) { cleanupLaunchClassLoader(); } + if (NormalConfig.instance.threadPriorityFix) + Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2); } public void preInit(FMLPreInitializationEvent event) { } diff --git a/src/main/resources/mixins.priorities.json b/src/main/resources/mixins.priorities.json new file mode 100644 index 00000000..a775d374 --- /dev/null +++ b/src/main/resources/mixins.priorities.json @@ -0,0 +1,17 @@ +{ + "package": "mirror.normalasm.common.priorities.mixins", + "plugin": "mirror.normalasm.core.NormalMixinPlugin", + "refmap": "mixins.normalasm.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MinecraftServerMixin" + ], + "client": [ + "client.ChunkRenderDispatcherMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file From 3496e032b641051aa9369b12eb55ea76f426fff8 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 24 Apr 2023 14:59:17 +0200 Subject: [PATCH 02/51] Update to use RetroFuturaGradle instead of ForgeGradle 2.3 (cherry picked from commit 5e6e40aaf0f17a5997a73158a492550a90417043) --- build.gradle | 214 ++++++++++-------- gradle.properties | 9 +- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 24 ++ .../modfixes/xu2/mixins/TileCrafterMixin.java | 11 +- src/main/resources/META-INF/normalasm_at.cfg | 0 6 files changed, 159 insertions(+), 101 deletions(-) create mode 100644 settings.gradle delete mode 100644 src/main/resources/META-INF/normalasm_at.cfg diff --git a/build.gradle b/build.gradle index 58fcd863..b3ebec05 100644 --- a/build.gradle +++ b/build.gradle @@ -1,40 +1,37 @@ -buildscript { - repositories { - maven { url = 'https://files.minecraftforge.net/maven' } - maven { url = 'https://repo.spongepowered.org/maven' } - } - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' - classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' - } +import org.jetbrains.gradle.ext.Gradle + +plugins { + id 'java' + id 'java-library' + id 'org.jetbrains.gradle.plugin.idea-ext' version '1.1.7' + id 'com.gtnewhorizons.retrofuturagradle' version '1.3.3' + id 'com.matthewprenger.cursegradle' version '1.4.0' } -apply plugin: 'net.minecraftforge.gradle.forge' -apply plugin: 'org.spongepowered.mixin' - -version = "5.6" -group = "mirror.normalasm" -archivesBaseName = "normalasm" +version = project.mod_version +group = project.maven_group +archivesBaseName = project.archives_base_name -sourceCompatibility = targetCompatibility = '1.8' -compileJava { - sourceCompatibility = targetCompatibility = '1.8' +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + vendor.set(org.gradle.jvm.toolchain.JvmVendorSpec.AZUL) + } + // Generate sources and javadocs jars when building and publishing + withSourcesJar() + withJavadocJar() } minecraft { - version = "1.12.2-14.23.5.2847" - runDir = "run" - mappings = "stable_39" + mcVersion = '1.12.2' def args = [ + "-ea:${project.group}", '-Dfml.coreMods.load=mirror.normalasm.core.NormalLoadingPlugin', '-Dmixin.hotSwap=true', '-Dmixin.checks.interfaces=true', - '-Dmixin.debug.export=true' + '-Dmixin.debug.export=true' ] - clientJvmArgs.addAll(args) - serverJvmArgs.addAll(args) - useDepAts = true - makeObfSourceJar = false + extraRunJvmArguments.addAll(args) } configurations { @@ -45,111 +42,144 @@ configurations { repositories { jcenter() maven { - url "https://maven.cleanroommc.com" + url 'https://maven.cleanroommc.com' } maven { - url "http://chickenbones.net/maven" + url 'http://chickenbones.net/maven' + allowInsecureProtocol = true } maven { - url "http://maven.covers1624.net" + url 'http://maven.covers1624.net' + allowInsecureProtocol = true } maven { - url "https://www.cursemaven.com" + url 'https://www.cursemaven.com' } maven { - url "https://dvs1.progwml6.com/files/maven/" + url 'https://dvs1.progwml6.com/files/maven/' } maven { - url "https://modmaven.k-4u.nl/" + url 'https://modmaven.k-4u.nl/' } maven { - url "https://maven.blamejared.com" + url 'https://maven.blamejared.com' } maven { - url "https://maven.thiakil.com" + url 'https://maven.thiakil.com' } maven { - url "https://repo.codemc.io/repository/maven-public/" + url 'https://repo.codemc.io/repository/maven-public/' } maven { - url "http://maven.tterrag.com" + url 'http://maven.tterrag.com' + allowInsecureProtocol = true } // maven { - // url = "http://maven.bluexin.be/repository/snapshots/" + // url = 'http://maven.bluexin.be/repository/snapshots/' // } } dependencies { - embed "me.nallar.whocalled:WhoCalled:1.1" - deobfProvided "com.enderio.core:EnderCore:1.12.2-+" - deobfProvided ("com.enderio:EnderIO:1.12.2-+") { + embed 'me.nallar.whocalled:WhoCalled:1.1' + implementation 'zone.rong:mixinbooter:7.1' + implementation files('./etc/spark-forge-deobf.jar') + + compileOnly 'com.enderio.core:EnderCore:1.12.2-+' + compileOnly ('com.enderio:EnderIO:1.12.2-+') { transitive = false } - compile "codechicken:ChickenASM:1.12-1.0.2.9" - provided "epicsquid.mysticallib:mysticallib:1.12.2-+" - compile "mezz.jei:jei_1.12.2:4.15.0.293" - compile "zone.rong:mixinbooter:4.2" - // compile files("./etc/spark-forge-deobf.jar", "./etc/preview_OptiFine_1.12.2_HD_U_G6_pre1-deobf.jar") - compile files("./etc/spark-forge-deobf.jar") - - deobfCompile 'slimeknights.mantle:Mantle:1.12-1.3.3.55+' - deobfCompile 'slimeknights:TConstruct:1.12.2-2.13.0.183+' - - deobfProvided "betterwithmods:BetterWithMods:1.12-2.3.20-1030" - - deobfCompile "codechicken:CodeChickenLib:1.12.2-3.2.3.358:universal" - deobfProvided "com.azanor.baubles:Baubles:1.12-1.5.2" - deobfProvided "blusunrize:ImmersiveEngineering:0.12-92-+" - deobfProvided "curse.maven:astral-sorcery-241721:3044416" - deobfProvided "curse.maven:tfcraft-302973:3268988" - deobfProvided "curse.maven:foamfix-optimization-mod-278494:3327893" - deobfProvided "curse.maven:electroblobs-wizardry-265642:3189062" - // deobfCompile "curse.maven:qmd-362056:3474533" - // deobfCompile "cofh:CoFHCore:1.12.2-+:universal" - // compile "com.teamwizardry.librarianlib:librarianlib-1.12:4.0-SNAPSHOT:deobf" - deobfProvided "curse.maven:extra-utilities-225561:2678374" - deobfProvided "curse.maven:gottschcore-272450:3748293" - deobfProvided "curse.maven:treasure2-289760:3758107" - deobfProvided "curse.maven:time-speed-mod-221053:2991593" - deobfCompile "curse.maven:gregtech-ce-unofficial-557242:3745499" + compileOnly 'codechicken:ChickenASM:1.12-1.0.2.9' + compileOnly 'epicsquid.mysticallib:mysticallib:1.12.2-+' + compileOnly 'mezz.jei:jei_1.12.2:4.15.0.293' + compileOnly 'slimeknights.mantle:Mantle:1.12-1.3.3.55+' + compileOnly 'slimeknights:TConstruct:1.12.2-2.13.0.183+' + compileOnly 'betterwithmods:BetterWithMods:1.12-2.3.20-1030' + compileOnly rfg.deobf('codechicken:CodeChickenLib:1.12.2-3.2.3.358:universal') + compileOnly 'com.azanor.baubles:Baubles:1.12-1.5.2' + compileOnly 'blusunrize:ImmersiveEngineering:0.12-92-+' + + compileOnly rfg.deobf('curse.maven:astral-sorcery-241721:3044416') + compileOnly rfg.deobf('curse.maven:tfcraft-302973:3268988') + compileOnly rfg.deobf('curse.maven:foamfix-optimization-mod-278494:3327893') + compileOnly rfg.deobf('curse.maven:electroblobs-wizardry-265642:3189062') + // deobfCompile 'curse.maven:qmd-362056:3474533' + // deobfCompile 'cofh:CoFHCore:1.12.2-+:universal' + // compile 'com.teamwizardry.librarianlib:librarianlib-1.12:4.0-SNAPSHOT:deobf' + compileOnly rfg.deobf('curse.maven:extra-utilities-225561:2678374') + compileOnly rfg.deobf('curse.maven:gottschcore-272450:3748293') + compileOnly rfg.deobf('curse.maven:treasure2-289760:3758107') + compileOnly rfg.deobf('curse.maven:time-speed-mod-221053:2991593') + compileOnly rfg.deobf('curse.maven:gregtech-ce-unofficial-557242:3745499') } -processResources { - // this will ensure that this task is redone when the versions change. - inputs.property "version", project.version - inputs.property "mcversion", project.minecraft.version - - // replace stuff in mcmod.info, nothing else - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' +def mixinConfigRefMap = 'mixins.' + project.archives_base_name + '.refmap.json' +def mixinTmpDir = buildDir.path + File.separator + 'tmp' + File.separator + 'mixins' +def refMap = "${mixinTmpDir}" + File.separator + mixinConfigRefMap +def mixinSrg = "${mixinTmpDir}" + File.separator + 'mixins.srg' - // replace version and mcversion - expand 'version':project.version, 'mcversion':project.minecraft.version - } +tasks.named('reobfJar', com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar).configure { + extraSrgFiles.from(mixinSrg) +} - // copy everything else except the mcmod.info - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' +tasks.named('compileJava', JavaCompile).configure { + doFirst { + new File(mixinTmpDir).mkdirs() } + options.compilerArgs += ["-AreobfSrgFile=${tasks.reobfJar.srg.get().asFile}", "-AoutSrgFile=${mixinSrg}", "-AoutRefMapFile=${refMap}",] } -sourceSets { - main { - ext.refMap = "mixins.normalasm.refmap.json" +processResources { + inputs.property 'version', project.version + inputs.property 'mcversion', project.minecraft.version + + filesMatching('mcmod.info') { fcd -> + include 'mcmod.info' + // replace version and mcversion + fcd.expand ('version': project.version, 'mcversion': project.minecraft.version) } + + from refMap + dependsOn 'compileJava' } jar { - from (configurations.embed.collect { it.isDirectory() ? it : zipTree(it) }) { - exclude 'LICENSE.txt', 'META-INF/MANIFSET.MF', 'META-INF/maven/**', 'META-INF/*.RSA', 'META-INF/*.SF' - } manifest { attributes([ - "FMLCorePluginContainsFMLMod": true, - "FMLCorePlugin": 'mirror.normalasm.core.NormalLoadingPlugin', - "ForceLoadAsMod": project.gradle.startParameter.taskNames[0] == "build", - "TweakClass": 'org.spongepowered.asm.launch.MixinTweaker', - "FMLAT": "normalasm_at.cfg" + 'FMLCorePluginContainsFMLMod': true, + 'FMLCorePlugin': 'mirror.normalasm.core.NormalLoadingPlugin', + 'ForceLoadAsMod': project.gradle.startParameter.taskNames[0] == 'build', + 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker', + 'FMLAT': 'normalasm_at.cfg' ]) } } + +idea { + module { + inheritOutputDirs = true + } + project { + settings { + runConfigurations { + '1. Run Client'(Gradle) { + taskNames = ['runClient'] + } + '2. Run Server'(Gradle) { + taskNames = ['runServer'] + } + '3. Run Obfuscated Client'(Gradle) { + taskNames = ['runObfClient'] + } + '4. Run Obfuscated Server'(Gradle) { + taskNames = ['runObfServer'] + } + } + compiler.javac { + afterEvaluate { + javacAdditionalOptions = '-encoding utf8' + moduleJavacAdditionalOptions = [(project.name + '.main'): tasks.compileJava.options.compilerArgs.collect { '"' + it + '"' }.join(' ')] + } + } + } + } +} diff --git a/gradle.properties b/gradle.properties index e9b9fd5a..eaa49830 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,6 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -# This is required to provide enough memory for the Minecraft decompilation process. -org.gradle.jvmargs=-Xmx3G +org.gradle.jvmargs = -Xmx3G + +# Mod Information +mod_version = 5.6 +maven_group = mirror.normalasm +archives_base_name = NormalASM diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6b071a78..d8d24753 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..655f4bbf --- /dev/null +++ b/settings.gradle @@ -0,0 +1,24 @@ +pluginManagement { + repositories { + maven { + // RetroFuturaGradle + name = 'GTNH Maven' + url = uri 'http://jenkins.usrv.eu:8081/nexus/content/groups/public/' + allowInsecureProtocol = true + mavenContent { + includeGroup 'com.gtnewhorizons' + includeGroup 'com.gtnewhorizons.retrofuturagradle' + } + } + gradlePluginPortal() + mavenCentral() + mavenLocal() + } +} + +plugins { + // Automatic toolchain provisioning + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' +} + +rootProject.name = archives_base_name diff --git a/src/main/java/mirror/normalasm/common/modfixes/xu2/mixins/TileCrafterMixin.java b/src/main/java/mirror/normalasm/common/modfixes/xu2/mixins/TileCrafterMixin.java index 86413715..a33f296a 100644 --- a/src/main/java/mirror/normalasm/common/modfixes/xu2/mixins/TileCrafterMixin.java +++ b/src/main/java/mirror/normalasm/common/modfixes/xu2/mixins/TileCrafterMixin.java @@ -73,14 +73,15 @@ public void renderAlt(double x, double y, double z) { GlStateManager.disableCull(); GlStateManager.pushAttrib(); GlStateManager.pushMatrix(); - RenderItem renderItem = Minecraft.getMinecraft().getRenderItem(); + Minecraft mc = Minecraft.getMinecraft(); + RenderItem renderItem = mc.getRenderItem(); IBakedModel model = renderItem.getItemModelWithOverrides(stack, null, null); GlStateManager.translate(0.5F, model.isGui3d() ? 1.05F : 1.15F, 0.5F); GlStateManager.translate(x, y, z); GlStateManager.scale(0.9F, 0.9F, 0.9F); GlStateManager.rotate((MCTimer.renderTimer / 64) * (180F / (float)Math.PI), 0.0F, 1.0F, 0.0F); - renderItem.textureManager.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); - renderItem.textureManager.getTexture(TextureMap.LOCATION_BLOCKS_TEXTURE).setBlurMipmap(false, false); + mc.getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + mc.getTextureManager().getTexture(TextureMap.LOCATION_BLOCKS_TEXTURE).setBlurMipmap(false, false); GlStateManager.pushMatrix(); GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); GlStateManager.color(1, 1, 1, 0.4F); @@ -105,8 +106,8 @@ public void renderAlt(double x, double y, double z) { GlStateManager.popAttrib(); GlStateManager.color(1, 1, 1, 1); GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - renderItem.textureManager.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); - renderItem.textureManager.getTexture(TextureMap.LOCATION_BLOCKS_TEXTURE).restoreLastBlurMipmap(); + mc.getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + mc.getTextureManager().getTexture(TextureMap.LOCATION_BLOCKS_TEXTURE).restoreLastBlurMipmap(); RenderHelper.enableStandardItemLighting(); } diff --git a/src/main/resources/META-INF/normalasm_at.cfg b/src/main/resources/META-INF/normalasm_at.cfg deleted file mode 100644 index e69de29b..00000000 From f2205fdddc927e9f57ff3f74b7245a3f824a280e Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 24 Apr 2023 14:49:11 +0100 Subject: [PATCH 03/51] Deobf baubles + IE for dev (cherry picked from commit 12628bf2af2ef8800806687806a6190e7e5d98a6) --- build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b3ebec05..9e004a6e 100644 --- a/build.gradle +++ b/build.gradle @@ -94,9 +94,10 @@ dependencies { compileOnly 'slimeknights.mantle:Mantle:1.12-1.3.3.55+' compileOnly 'slimeknights:TConstruct:1.12.2-2.13.0.183+' compileOnly 'betterwithmods:BetterWithMods:1.12-2.3.20-1030' + compileOnly rfg.deobf('codechicken:CodeChickenLib:1.12.2-3.2.3.358:universal') - compileOnly 'com.azanor.baubles:Baubles:1.12-1.5.2' - compileOnly 'blusunrize:ImmersiveEngineering:0.12-92-+' + compileOnly rfg.deobf('com.azanor.baubles:Baubles:1.12-1.5.2') + compileOnly rfg.deobf('blusunrize:ImmersiveEngineering:0.12-92-+') compileOnly rfg.deobf('curse.maven:astral-sorcery-241721:3044416') compileOnly rfg.deobf('curse.maven:tfcraft-302973:3268988') From 711f2192dffdfe315877c672e52ac4f4dd85a165 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 24 Apr 2023 16:05:01 +0100 Subject: [PATCH 04/51] Fixed import + embed configuration not working (cherry picked from commit c0f3dbbe6e712db3371cb0e5d79abc7e081d23d1) --- build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9e004a6e..d130badd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar import org.jetbrains.gradle.ext.Gradle plugins { @@ -118,7 +119,7 @@ def mixinTmpDir = buildDir.path + File.separator + 'tmp' + File.separator + 'mix def refMap = "${mixinTmpDir}" + File.separator + mixinConfigRefMap def mixinSrg = "${mixinTmpDir}" + File.separator + 'mixins.srg' -tasks.named('reobfJar', com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar).configure { +tasks.named('reobfJar', ReobfuscatedJar).configure { extraSrgFiles.from(mixinSrg) } @@ -144,6 +145,9 @@ processResources { } jar { + from provider { + configurations.embed.collect {it.isDirectory() ? it : zipTree(it)} + } manifest { attributes([ 'FMLCorePluginContainsFMLMod': true, From 543e1199f078149cc0e0863e3a08c015aff17394 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 17 May 2023 13:17:04 +0100 Subject: [PATCH 05/51] Mixin AP + RFG update (cherry picked from commit 810bf5b7f36c502dc3c540daa87182e757c3f047) --- build.gradle | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d130badd..b6667485 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'java' id 'java-library' id 'org.jetbrains.gradle.plugin.idea-ext' version '1.1.7' - id 'com.gtnewhorizons.retrofuturagradle' version '1.3.3' + id 'com.gtnewhorizons.retrofuturagradle' version '1.3.9' id 'com.matthewprenger.cursegradle' version '1.4.0' } @@ -20,7 +20,7 @@ java { } // Generate sources and javadocs jars when building and publishing withSourcesJar() - withJavadocJar() + // withJavadocJar() } minecraft { @@ -42,6 +42,9 @@ configurations { repositories { jcenter() + maven { + url 'https://repo.spongepowered.org/maven' + } maven { url 'https://maven.cleanroommc.com' } @@ -112,6 +115,16 @@ dependencies { compileOnly rfg.deobf('curse.maven:treasure2-289760:3758107') compileOnly rfg.deobf('curse.maven:time-speed-mod-221053:2991593') compileOnly rfg.deobf('curse.maven:gregtech-ce-unofficial-557242:3745499') + + api ('org.spongepowered:mixin:0.8.3') { + transitive = false + } + annotationProcessor 'org.ow2.asm:asm-debug-all:5.2' + annotationProcessor 'com.google.guava:guava:24.1.1-jre' + annotationProcessor 'com.google.code.gson:gson:2.8.6' + annotationProcessor ('org.spongepowered:mixin:0.8.3') { + transitive = false + } } def mixinConfigRefMap = 'mixins.' + project.archives_base_name + '.refmap.json' From 3895bc2e24e449fe0fa0833c1f960146a14814cf Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 17 May 2023 13:51:57 +0100 Subject: [PATCH 06/51] IntelliJ moment (cherry picked from commit 2dfea742faf2003ebd60fe57159c8229dc71ac3a) --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 655f4bbf..3f05c79d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,4 +21,4 @@ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' } -rootProject.name = archives_base_name +rootProject.name = 'NormalASM' From 6dd8d190078260bd9c84b5ed44d5cc67554fcce3 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 17 May 2023 15:05:14 +0200 Subject: [PATCH 07/51] Fixes #125 (cherry picked from commit c17983c1366e08cd9c57be2e4e040dbaec89be5a) --- .../mc186052/MinecraftMixin.java} | 29 ++++++++++++------- .../mc186052/TextureManagerExpansion.java | 17 +++++++++++ .../normalasm/core/NormalLoadingPlugin.java | 3 +- .../mirror/normalasm/proxy/ClientProxy.java | 16 ++++------ src/main/resources/mixins.fix_mc186052.json | 11 +++++++ 5 files changed, 54 insertions(+), 22 deletions(-) rename src/main/java/mirror/normalasm/client/mcfixes/{SkinDataReleaser.java => mixins/mc186052/MinecraftMixin.java} (55%) create mode 100644 src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/TextureManagerExpansion.java create mode 100644 src/main/resources/mixins.fix_mc186052.json diff --git a/src/main/java/mirror/normalasm/client/mcfixes/SkinDataReleaser.java b/src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/MinecraftMixin.java similarity index 55% rename from src/main/java/mirror/normalasm/client/mcfixes/SkinDataReleaser.java rename to src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/MinecraftMixin.java index b712ed9c..1ef6ee23 100644 --- a/src/main/java/mirror/normalasm/client/mcfixes/SkinDataReleaser.java +++ b/src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/MinecraftMixin.java @@ -1,24 +1,31 @@ -package mirror.normalasm.client.mcfixes; +package mirror.normalasm.client.mcfixes.mixins.mc186052; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.renderer.ThreadDownloadImageData; import net.minecraft.client.renderer.texture.ITextureObject; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.network.FMLNetworkEvent; +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 mirror.normalasm.NormalLogger; -import mirror.normalasm.common.internal.mixins.TextureManagerAccessor; +import mirror.normalasm.config.NormalConfig; +import javax.annotation.Nullable; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -public class SkinDataReleaser { +@Mixin(Minecraft.class) +public class MinecraftMixin { - @SubscribeEvent - public static void onClientDisconnect(FMLNetworkEvent.ClientDisconnectionFromServerEvent event) { - Minecraft.getMinecraft().addScheduledTask(() -> { - Map textureObjects = ((TextureManagerAccessor) Minecraft.getMinecraft().getTextureManager()).getMapTextureObjects(); + @Inject(method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Ljava/lang/String;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/FMLClientHandler;handleClientWorldClosing(Lnet/minecraft/client/multiplayer/WorldClient;)V", + remap = false)) + private void injectLoadWorld(@Nullable WorldClient worldClientIn, String loadingMessage, CallbackInfo ci) { + if (NormalConfig.instance.fixMC186052) { + Map textureObjects = ((TextureManagerExpansion) Minecraft.getMinecraft().getTextureManager()).getMapTextureObjects(); if (textureObjects != null) { int count = 0; Iterator> entryIter = textureObjects.entrySet().iterator(); @@ -35,7 +42,7 @@ public static void onClientDisconnect(FMLNetworkEvent.ClientDisconnectionFromSer } NormalLogger.instance.info("Released {} skin textures", count); } - }); + } } -} +} \ No newline at end of file diff --git a/src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/TextureManagerExpansion.java b/src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/TextureManagerExpansion.java new file mode 100644 index 00000000..706cbb8c --- /dev/null +++ b/src/main/java/mirror/normalasm/client/mcfixes/mixins/mc186052/TextureManagerExpansion.java @@ -0,0 +1,17 @@ +package mirror.normalasm.client.mcfixes.mixins.mc186052; + +import net.minecraft.client.renderer.texture.ITextureObject; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(TextureManager.class) +public interface TextureManagerExpansion { + + @Accessor(value = "mapTextureObjects") + Map getMapTextureObjects(); + +} diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 53af8388..5a983b30 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -165,7 +165,8 @@ public List getMixinConfigs() { "mixins.ondemand_sprites.json", "mixins.searchtree_vanilla.json", "mixins.resolve_mc2071.json", - "mixins.fix_mc_skindownloading.json") : + "mixins.fix_mc_skindownloading.json", + "mixins.fix_mc186052.json") : Arrays.asList( "mixins.devenv.json", "mixins.vfix_bugfixes.json", diff --git a/src/main/java/mirror/normalasm/proxy/ClientProxy.java b/src/main/java/mirror/normalasm/proxy/ClientProxy.java index 085da25c..4339c29e 100644 --- a/src/main/java/mirror/normalasm/proxy/ClientProxy.java +++ b/src/main/java/mirror/normalasm/proxy/ClientProxy.java @@ -1,7 +1,5 @@ package mirror.normalasm.proxy; -import mirror.normalasm.bakedquad.NormalVertexDataPool; -import mirror.normalasm.core.NormalTransformer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; @@ -17,14 +15,15 @@ import net.minecraftforge.fml.relauncher.Side; import pl.asie.foamfix.shared.FoamFixShared; import slimeknights.tconstruct.library.client.texture.AbstractColoredTexture; -import mirror.normalasm.NormalLogger; -import mirror.normalasm.NormalReflector; -import mirror.normalasm.client.mcfixes.SkinDataReleaser; -import mirror.normalasm.client.models.bucket.NormalBakedDynBucket; +import mirror.normalasm.LoliLogger; +import mirror.normalasm.LoliReflector; +import mirror.normalasm.bakedquad.LoliVertexDataPool; +import mirror.normalasm.client.models.bucket.LoliBakedDynBucket; import mirror.normalasm.client.screenshot.ScreenshotListener; import mirror.normalasm.client.sprite.FramesTextureData; import mirror.normalasm.common.modfixes.qmd.QMDEventHandler; -import mirror.normalasm.config.NormalConfig; +import mirror.normalasm.config.LoliConfig; +import mirror.normalasm.core.LoliTransformer; import java.util.ArrayList; import java.util.List; @@ -60,9 +59,6 @@ public void preInit(FMLPreInitializationEvent event) { if (NormalConfig.instance.copyScreenshotToClipboard) { MinecraftForge.EVENT_BUS.register(ScreenshotListener.class); } - if (NormalConfig.instance.fixMC186052) { - MinecraftForge.EVENT_BUS.register(SkinDataReleaser.class); - } } @Override diff --git a/src/main/resources/mixins.fix_mc186052.json b/src/main/resources/mixins.fix_mc186052.json new file mode 100644 index 00000000..1d5d195b --- /dev/null +++ b/src/main/resources/mixins.fix_mc186052.json @@ -0,0 +1,11 @@ +{ + "package": "mirror.normalasm.client.mcfixes.mixins.mc186052", + "refmap": "mixins.normalasm.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "client": [ + "MinecraftMixin", + "TextureManagerExpansion" + ] +} \ No newline at end of file From 25fbb7488a661ceaa75852e4c4245435ce9bef64 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 17 May 2023 15:13:35 +0200 Subject: [PATCH 08/51] Fixes #135 (cherry picked from commit bbb4a199368d06ef61f007760a611aaf923ce94c) --- .../mixins/vanilla/MinecraftMixin.java | 58 ++++++++++--------- .../normalasm/core/NormalLoadingPlugin.java | 2 - 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/java/mirror/normalasm/client/searchtree/mixins/vanilla/MinecraftMixin.java b/src/main/java/mirror/normalasm/client/searchtree/mixins/vanilla/MinecraftMixin.java index 2fd8d5c0..c671dab5 100644 --- a/src/main/java/mirror/normalasm/client/searchtree/mixins/vanilla/MinecraftMixin.java +++ b/src/main/java/mirror/normalasm/client/searchtree/mixins/vanilla/MinecraftMixin.java @@ -9,9 +9,12 @@ import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.common.Loader; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; 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 mirror.normalasm.client.searchtree.JEIRedirectSearchTree; +import mirror.normalasm.config.NormalConfig; import java.util.Collections; import java.util.Objects; @@ -22,36 +25,35 @@ public class MinecraftMixin { @Shadow private SearchTreeManager searchTreeManager; - /** - * @author Rongmario - * @reason Use JEIRedirectSearchTree - */ - @Overwrite - public void populateSearchTreeManager() { - final SearchTree recipeSearchTree = new SearchTree<>( - rl -> () -> rl.getRecipes().stream() - .flatMap((r) -> r.getRecipeOutput().getTooltip(null, TooltipFlags.NORMAL).stream()) - .map(TextFormatting::getTextWithoutFormattingCodes) - .filter(Objects::nonNull) - .map(String::trim) - .filter(tooltip -> !tooltip.isEmpty()) - .iterator(), - rl -> () -> rl.getRecipes().stream() - .map((r) -> r.getRecipeOutput().getItem().getRegistryName()) - .iterator() - ); - final SearchTree itemSearchTree = Loader.isModLoaded("jei") ? - new JEIRedirectSearchTree() : - new SearchTree<>(stack -> - stack.getTooltip(null, TooltipFlags.NORMAL) - .stream() + @Inject(method = "populateSearchTreeManager", at = @At("HEAD"), cancellable = true) + private void redirectToJeiSearchTree(CallbackInfo ci) { + if (NormalConfig.instance.replaceSearchTreeWithJEISearching && Loader.isModLoaded("jei")) { + final SearchTree recipeSearchTree = new SearchTree<>( + rl -> () -> rl.getRecipes().stream() + .flatMap((r) -> r.getRecipeOutput().getTooltip(null, TooltipFlags.NORMAL).stream()) .map(TextFormatting::getTextWithoutFormattingCodes) + .filter(Objects::nonNull) .map(String::trim) .filter(tooltip -> !tooltip.isEmpty()) - .collect(Collectors.toList()), - stack -> Collections.singleton(stack.getItem().getRegistryName())); - this.searchTreeManager.register(SearchTreeManager.RECIPES, recipeSearchTree); - this.searchTreeManager.register(SearchTreeManager.ITEMS, itemSearchTree); + .iterator(), + rl -> () -> rl.getRecipes().stream() + .map((r) -> r.getRecipeOutput().getItem().getRegistryName()) + .iterator() + ); + final SearchTree itemSearchTree = Loader.isModLoaded("jei") ? + new JEIRedirectSearchTree() : + new SearchTree<>(stack -> + stack.getTooltip(null, TooltipFlags.NORMAL) + .stream() + .map(TextFormatting::getTextWithoutFormattingCodes) + .map(String::trim) + .filter(tooltip -> !tooltip.isEmpty()) + .collect(Collectors.toList()), + stack -> Collections.singleton(stack.getItem().getRegistryName())); + this.searchTreeManager.register(SearchTreeManager.RECIPES, recipeSearchTree); + this.searchTreeManager.register(SearchTreeManager.ITEMS, itemSearchTree); + ci.cancel(); + } } } diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 5a983b30..d026029c 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -203,8 +203,6 @@ public boolean shouldMixinConfigQueue(String mixinConfig) { return NormalConfig.instance.releaseScreenshotCache || NormalConfig.instance.asyncScreenshot; case "mixins.ondemand_sprites.json": return NormalConfig.instance.onDemandAnimatedTextures; - case "mixins.searchtree_vanilla.json": - return NormalConfig.instance.replaceSearchTreeWithJEISearching; case "mixins.resolve_mc2071.json": return NormalConfig.instance.resolveMC2071; case "mixins.fix_mc_skindownloading.json": From b0e4b68ac6667d047f7e76e47d49b24797f9d2c6 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 17 May 2023 15:15:38 +0200 Subject: [PATCH 09/51] Bump to 5.7 (cherry picked from commit db6cce7ee39c4043ad6f4846aaa1de7308107696) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index eaa49830..d2b26dc0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.6 +mod_version = 5.7 maven_group = mirror.normalasm archives_base_name = NormalASM diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index d026029c..f9350f7f 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -29,7 +29,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.6"; + public static final String VERSION = "5.7"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 3c0e588903c6fde30987cc5a03caf635775991ba Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 17 May 2023 21:59:43 -0400 Subject: [PATCH 10/51] Re-add clamping logic from vanilla for render builder count (#167) Fixes #166 (cherry picked from commit ce40e7390319b32ff3b7fbd197aabc6c71d54bbd) --- .../priorities/mixins/client/ChunkRenderDispatcherMixin.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java b/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java index 91212f51..9598ce2c 100644 --- a/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java +++ b/src/main/java/mirror/normalasm/common/priorities/mixins/client/ChunkRenderDispatcherMixin.java @@ -33,9 +33,10 @@ private int setBuilders(int original) { @Redirect(method = "(I)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher;countRenderBuilders:I")) private void setBuilders(ChunkRenderDispatcher dispatcher, int original) { - int nThreads = getRenderBuilderCount(); + int memoryClampMagicValue = Math.max(1, (int)((double)Runtime.getRuntime().maxMemory() * 0.3D) / 10485760); + int nThreads = MathHelper.clamp(getRenderBuilderCount(), 1, memoryClampMagicValue / 5); /* we need more builder objects in the queue than number of threads, because threads don't free them immediately */ - this.countRenderBuilders = nThreads * 10; + this.countRenderBuilders = MathHelper.clamp(nThreads * 10, 1, memoryClampMagicValue); LOGGER.info("Creating {} chunk builders", nThreads); } From c09f8c38efc3ba69036180df7ee431d16a76aee5 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Thu, 18 May 2023 04:51:00 +0200 Subject: [PATCH 11/51] Bump to 5.8 (cherry picked from commit cacff27fdacf8bd23df3dc18e64b48e4b7318e53) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index d2b26dc0..d8600815 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.7 +mod_version = 5.8 maven_group = mirror.normalasm archives_base_name = NormalASM diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index f9350f7f..02cba10a 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -29,7 +29,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.7"; + public static final String VERSION = "5.8"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 168616a1fa7ec64c0c9634abbd860875d2210e7d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 22 May 2023 13:29:07 +0200 Subject: [PATCH 12/51] Revamp releaseSpriteFramesCache (#168) * Now loads/unloads frame data dynamically for ALL `TextureAtlasSprites` * Data not referenced for 20 consecutive client ticks is unloaded * Synchronize some methods in `FramesTextureData` to help prevent crashes (cherry picked from commit 8b423982e45039d9cc51ffa7ce2114d054d5dd18) --- build.gradle | 4 +- gradle.properties | 2 +- .../client/sprite/FramesTextureData.java | 132 +++++++++++++----- .../mixins/TextureAtlasSpriteAccessor.java | 13 ++ .../internal/mixins/TextureMapAccessor.java | 14 ++ .../mirror/normalasm/proxy/ClientProxy.java | 45 +----- src/main/resources/mixins.internal.json | 2 + 7 files changed, 135 insertions(+), 77 deletions(-) create mode 100644 src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java create mode 100644 src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java diff --git a/build.gradle b/build.gradle index b6667485..fdfb9431 100644 --- a/build.gradle +++ b/build.gradle @@ -95,8 +95,8 @@ dependencies { compileOnly 'codechicken:ChickenASM:1.12-1.0.2.9' compileOnly 'epicsquid.mysticallib:mysticallib:1.12.2-+' compileOnly 'mezz.jei:jei_1.12.2:4.15.0.293' - compileOnly 'slimeknights.mantle:Mantle:1.12-1.3.3.55+' - compileOnly 'slimeknights:TConstruct:1.12.2-2.13.0.183+' + compileOnly 'slimeknights.mantle:Mantle:1.12-1.3.3.55' + compileOnly 'slimeknights:TConstruct:1.12.2-2.13.0.183' compileOnly 'betterwithmods:BetterWithMods:1.12-2.3.20-1030' compileOnly rfg.deobf('codechicken:CodeChickenLib:1.12.2-3.2.3.358:universal') diff --git a/gradle.properties b/gradle.properties index d8600815..5f0c3e9e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xmx3G # Mod Information mod_version = 5.8 maven_group = mirror.normalasm -archives_base_name = NormalASM +archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java index 7d1fbcfe..4a6e4f20 100644 --- a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java +++ b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java @@ -3,90 +3,152 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.resources.IReloadableResourceManager; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.ColorHandlerEvent; +import net.minecraftforge.client.resource.ISelectiveResourceReloadListener; +import net.minecraftforge.client.resource.VanillaResourceType; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import mirror.normalasm.NormalLogger; -import mirror.normalasm.proxy.ClientProxy; +import mirror.normalasm.common.internal.mixins.TextureAtlasSpriteAccessor; +import mirror.normalasm.common.internal.mixins.TextureMapAccessor; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; public class FramesTextureData extends ArrayList { + private static boolean canReload = true; - private static final Set scheduledToReleaseCache = Collections.newSetFromMap(new WeakHashMap<>()); + private static final Class FOAMFIX_SPRITE; + + static { + Class ffSprite; + try { + ffSprite = Class.forName("pl.asie.foamfix.client.FastTextureAtlasSprite"); + } catch(ClassNotFoundException e) { + ffSprite = null; + } + FOAMFIX_SPRITE = ffSprite; + } + + @SubscribeEvent + public static void registerEvictionListener(ColorHandlerEvent.Block event) { + ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener((ISelectiveResourceReloadListener) (manager, predicate) -> { + if (predicate.test(VanillaResourceType.MODELS)) { + canReload = false; + Set> skippedSpriteClasses = new HashSet<>(); + try { + for (TextureAtlasSprite sprite : ((TextureMapAccessor)Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { + if (sprite.getClass() == TextureAtlasSprite.class || sprite.getClass() == FOAMFIX_SPRITE) { + sprite.setFramesTextureData(new FramesTextureData(sprite)); + } else + skippedSpriteClasses.add(sprite.getClass()); + } + } catch (Throwable e) { + e.printStackTrace(); + } + NormalLogger.instance.debug("Evicted most sprites' frame texture data, skipped classes: [{}]", skippedSpriteClasses.stream().map(Class::getName).collect(Collectors.joining(", "))); + canReload = true; + } + }); + } @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { - if (event.phase == TickEvent.Phase.END && !FramesTextureData.scheduledToReleaseCache.isEmpty()) { - for (Iterator iter = scheduledToReleaseCache.iterator(); iter.hasNext();) { - TextureAtlasSprite sprite = iter.next(); + if (event.phase == TickEvent.Phase.END) { + for(TextureAtlasSprite sprite : ((TextureMapAccessor)Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { if (sprite != null) { - try { - sprite.clearFramesTextureData(); - } catch (NullPointerException e) { - NormalLogger.instance.error("NullPointerException: Trying to clear {}'s FramesTextureData but unable to!", sprite.getIconName()); - } + List data = ((TextureAtlasSpriteAccessor)sprite).normal$getTextureData(); + if(data instanceof FramesTextureData) + ((FramesTextureData)data).tick(); } - iter.remove(); } } } private final TextureAtlasSprite sprite; + private int ticksInactive; + + private static final int INACTIVITY_THRESHOLD = 20; + public FramesTextureData(TextureAtlasSprite sprite) { super(); this.sprite = sprite; + this.ticksInactive = INACTIVITY_THRESHOLD + 1; + } + + public void tick() { + this.ticksInactive++; + if(this.ticksInactive == INACTIVITY_THRESHOLD) { + this.clear(); + } } @Override public int[][] get(int index) { - if (ClientProxy.canReload && super.isEmpty()) { - load(); - Minecraft.getMinecraft().addScheduledTask(() -> scheduledToReleaseCache.add(sprite)); + synchronized (this) { + if (canReload && super.isEmpty()) { + load(); + } + this.ticksInactive = 0; + return super.get(index); } - return super.get(index); } @Override public int size() { - if (ClientProxy.canReload && super.isEmpty()) { - load(); - Minecraft.getMinecraft().addScheduledTask(() -> scheduledToReleaseCache.add(sprite)); + synchronized (this) { + if (canReload && super.isEmpty()) { + load(); + } + this.ticksInactive = 0; + return super.size(); } - return super.size(); } @Override public boolean isEmpty() { - if (ClientProxy.canReload && super.isEmpty()) { - load(); - Minecraft.getMinecraft().addScheduledTask(() -> scheduledToReleaseCache.add(sprite)); + synchronized (this) { + if (canReload && super.isEmpty()) { + load(); + } + this.ticksInactive = 0; + return super.isEmpty(); } - return super.isEmpty(); } @Override public void clear() { - super.clear(); - trimToSize(); + synchronized (this) { + super.clear(); + trimToSize(); + } } private void load() { - ResourceLocation location = getLocation(); - IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager(); - TextureMap textureMap = Minecraft.getMinecraft().getTextureMapBlocks(); - if (sprite.hasCustomLoader(resourceManager, location)) { - sprite.load(resourceManager, location, rl -> textureMap.getAtlasSprite(rl.toString())); - } else { - try (IResource resource = resourceManager.getResource(location)) { - sprite.loadSpriteFrames(resource, 1); - } catch (IOException e) { - e.printStackTrace(); + // prevent recursive loads + boolean oldReload = canReload; + canReload = false; + try { + ResourceLocation location = getLocation(); + IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager(); + TextureMap textureMap = Minecraft.getMinecraft().getTextureMapBlocks(); + if (sprite.hasCustomLoader(resourceManager, location)) { + sprite.load(resourceManager, location, rl -> textureMap.getAtlasSprite(rl.toString())); + } else { + try (IResource resource = resourceManager.getResource(location)) { + sprite.loadSpriteFrames(resource, 1); + } catch (IOException e) { + e.printStackTrace(); + } } + } finally { + canReload = oldReload; } } diff --git a/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java b/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java new file mode 100644 index 00000000..dcde5f60 --- /dev/null +++ b/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java @@ -0,0 +1,13 @@ +package mirror.normalasm.common.internal.mixins; + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(TextureAtlasSprite.class) +public interface TextureAtlasSpriteAccessor { + @Accessor("framesTextureData") + List normal$getTextureData(); +} diff --git a/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java b/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java new file mode 100644 index 00000000..5fe2221f --- /dev/null +++ b/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java @@ -0,0 +1,14 @@ +package mirror.normalasm.common.internal.mixins; + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(TextureMap.class) +public interface TextureMapAccessor { + @Accessor("mapRegisteredSprites") + Map getMapRegisteredSprites(); +} diff --git a/src/main/java/mirror/normalasm/proxy/ClientProxy.java b/src/main/java/mirror/normalasm/proxy/ClientProxy.java index 4339c29e..c4b76a18 100644 --- a/src/main/java/mirror/normalasm/proxy/ClientProxy.java +++ b/src/main/java/mirror/normalasm/proxy/ClientProxy.java @@ -1,8 +1,6 @@ package mirror.normalasm.proxy; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.resources.IReloadableResourceManager; import net.minecraftforge.client.resource.ISelectiveResourceReloadListener; import net.minecraftforge.client.resource.VanillaResourceType; @@ -13,17 +11,15 @@ import net.minecraftforge.fml.common.event.FMLLoadCompleteEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.relauncher.Side; -import pl.asie.foamfix.shared.FoamFixShared; -import slimeknights.tconstruct.library.client.texture.AbstractColoredTexture; -import mirror.normalasm.LoliLogger; -import mirror.normalasm.LoliReflector; -import mirror.normalasm.bakedquad.LoliVertexDataPool; -import mirror.normalasm.client.models.bucket.LoliBakedDynBucket; +import mirror.normalasm.NormalLogger; +import mirror.normalasm.NormalReflector; +import mirror.normalasm.bakedquad.NormalVertexDataPool; +import mirror.normalasm.client.models.bucket.NormalBakedDynBucket; import mirror.normalasm.client.screenshot.ScreenshotListener; import mirror.normalasm.client.sprite.FramesTextureData; import mirror.normalasm.common.modfixes.qmd.QMDEventHandler; -import mirror.normalasm.config.LoliConfig; -import mirror.normalasm.core.LoliTransformer; +import mirror.normalasm.config.NormalConfig; +import mirror.normalasm.core.NormalTransformer; import java.util.ArrayList; import java.util.List; @@ -33,19 +29,6 @@ public class ClientProxy extends CommonProxy { public static final List refreshAfterModels = new ArrayList<>(); - public static final boolean flushTinkerSpriteFrameTextureData; - - static { - boolean static$flushTinkerSpriteFrameTextureData = true; - if (Loader.isModLoaded("tconstruct") && Loader.isModLoaded("foamfix")) { - if (FoamFixShared.config.clDynamicItemModels) { - static$flushTinkerSpriteFrameTextureData = false; - } - } - flushTinkerSpriteFrameTextureData = static$flushTinkerSpriteFrameTextureData; - } - - public static boolean canReload = true; @Override public void preInit(FMLPreInitializationEvent event) { @@ -91,22 +74,6 @@ private void releaseSpriteFramesCache() { NormalBakedDynBucket.coverQuads.clear(); NormalBakedDynBucket.flippedCoverQuads.clear(); } - if (NormalConfig.instance.releaseSpriteFramesCache) { - canReload = false; - try { - for (TextureAtlasSprite sprite : ((Map) NormalReflector.resolveFieldGetter(TextureMap.class, "mapRegisteredSprites", "field_110574_e").invoke(Minecraft.getMinecraft().getTextureMapBlocks())).values()) { - if (!sprite.hasAnimationMetadata()) { - if (!flushTinkerSpriteFrameTextureData && sprite instanceof AbstractColoredTexture) { - continue; - } - sprite.setFramesTextureData(new FramesTextureData(sprite)); - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - canReload = true; - } if (!NormalTransformer.isOptifineInstalled && NormalConfig.instance.vertexDataCanonicalization) { NormalVertexDataPool.invalidate(); } diff --git a/src/main/resources/mixins.internal.json b/src/main/resources/mixins.internal.json index 42ad670a..9b7a0aa2 100644 --- a/src/main/resources/mixins.internal.json +++ b/src/main/resources/mixins.internal.json @@ -10,6 +10,8 @@ "LoadControllerMixin" ], "client": [ + "TextureAtlasSpriteAccessor", + "TextureMapAccessor", "TextureManagerAccessor", "TileEntityMixin" ] From 81cb55df03e5c11996fa382c74a91c5ad753d209 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 24 May 2023 14:42:34 +0200 Subject: [PATCH 13/51] Bump to 5.9 (cherry picked from commit 4021bf0649e910f5c38e7cf29cecb6daa319f1fa) --- gradle.properties | 2 +- .../mirror/normalasm/NormalReflector.java | 7 +++ .../client/sprite/FramesTextureData.java | 58 ++++++++++--------- .../mixins/TextureAtlasSpriteAccessor.java | 1 + .../internal/mixins/TextureMapAccessor.java | 2 + .../normalasm/core/NormalLoadingPlugin.java | 2 +- 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/gradle.properties b/gradle.properties index 5f0c3e9e..a9cbdf2b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.8 +mod_version = 5.9 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/NormalReflector.java b/src/main/java/mirror/normalasm/NormalReflector.java index 3a7e06e9..803dbc1d 100644 --- a/src/main/java/mirror/normalasm/NormalReflector.java +++ b/src/main/java/mirror/normalasm/NormalReflector.java @@ -214,6 +214,13 @@ public static boolean doesTweakExist(String tweakName) { return ((List) Launch.blackboard.get("TweakClasses")).contains(tweakName); } + public static Class getNullableClass(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException ignored) { } + return null; + } + public static Optional> getClass(String className) { try { return Optional.of(Class.forName(className)); diff --git a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java index 4a6e4f20..9b874ba6 100644 --- a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java +++ b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java @@ -1,5 +1,6 @@ package mirror.normalasm.client.sprite; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; @@ -13,6 +14,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import mirror.normalasm.NormalLogger; +import mirror.normalasm.NormalReflector; import mirror.normalasm.common.internal.mixins.TextureAtlasSpriteAccessor; import mirror.normalasm.common.internal.mixins.TextureMapAccessor; @@ -21,35 +23,38 @@ import java.util.stream.Collectors; public class FramesTextureData extends ArrayList { - private static boolean canReload = true; - private static final Class FOAMFIX_SPRITE; + private static final Class FOAMFIX_SPRITE = NormalReflector.getNullableClass("pl.asie.foamfix.client.FastTextureAtlasSprite"); + private static final int INACTIVITY_THRESHOLD = 20; - static { - Class ffSprite; - try { - ffSprite = Class.forName("pl.asie.foamfix.client.FastTextureAtlasSprite"); - } catch(ClassNotFoundException e) { - ffSprite = null; - } - FOAMFIX_SPRITE = ffSprite; - } + private static boolean canReload = true; @SubscribeEvent public static void registerEvictionListener(ColorHandlerEvent.Block event) { ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener((ISelectiveResourceReloadListener) (manager, predicate) -> { if (predicate.test(VanillaResourceType.MODELS)) { canReload = false; - Set> skippedSpriteClasses = new HashSet<>(); + Set> skippedSpriteClasses = new ObjectOpenHashSet<>(); try { - for (TextureAtlasSprite sprite : ((TextureMapAccessor)Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { - if (sprite.getClass() == TextureAtlasSprite.class || sprite.getClass() == FOAMFIX_SPRITE) { - sprite.setFramesTextureData(new FramesTextureData(sprite)); - } else - skippedSpriteClasses.add(sprite.getClass()); + if (FOAMFIX_SPRITE == null) { + for (TextureAtlasSprite sprite : ((TextureMapAccessor) Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { + if (sprite.getClass() == TextureAtlasSprite.class) { + sprite.setFramesTextureData(new FramesTextureData(sprite)); + } else { + skippedSpriteClasses.add(sprite.getClass()); + } + } + } else { + for (TextureAtlasSprite sprite : ((TextureMapAccessor) Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { + if (sprite.getClass() == FOAMFIX_SPRITE || sprite.getClass() == TextureAtlasSprite.class) { + sprite.setFramesTextureData(new FramesTextureData(sprite)); + } else { + skippedSpriteClasses.add(sprite.getClass()); + } + } } - } catch (Throwable e) { - e.printStackTrace(); + } catch (Throwable t) { + t.printStackTrace(); } NormalLogger.instance.debug("Evicted most sprites' frame texture data, skipped classes: [{}]", skippedSpriteClasses.stream().map(Class::getName).collect(Collectors.joining(", "))); canReload = true; @@ -60,11 +65,12 @@ public static void registerEvictionListener(ColorHandlerEvent.Block event) { @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { if (event.phase == TickEvent.Phase.END) { - for(TextureAtlasSprite sprite : ((TextureMapAccessor)Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { + for (TextureAtlasSprite sprite : ((TextureMapAccessor)Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { if (sprite != null) { - List data = ((TextureAtlasSpriteAccessor)sprite).normal$getTextureData(); - if(data instanceof FramesTextureData) - ((FramesTextureData)data).tick(); + List data = ((TextureAtlasSpriteAccessor) sprite).normal$getTextureData(); + if (data instanceof FramesTextureData) { + ((FramesTextureData) data).tick(); + } } } } @@ -74,8 +80,6 @@ public static void onClientTick(TickEvent.ClientTickEvent event) { private int ticksInactive; - private static final int INACTIVITY_THRESHOLD = 20; - public FramesTextureData(TextureAtlasSprite sprite) { super(); this.sprite = sprite; @@ -84,7 +88,7 @@ public FramesTextureData(TextureAtlasSprite sprite) { public void tick() { this.ticksInactive++; - if(this.ticksInactive == INACTIVITY_THRESHOLD) { + if (this.ticksInactive == INACTIVITY_THRESHOLD) { this.clear(); } } @@ -131,7 +135,7 @@ public void clear() { } private void load() { - // prevent recursive loads + // Prevent recursive loads boolean oldReload = canReload; canReload = false; try { diff --git a/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java b/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java index dcde5f60..ef96dc41 100644 --- a/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java +++ b/src/main/java/mirror/normalasm/common/internal/mixins/TextureAtlasSpriteAccessor.java @@ -8,6 +8,7 @@ @Mixin(TextureAtlasSprite.class) public interface TextureAtlasSpriteAccessor { + @Accessor("framesTextureData") List normal$getTextureData(); } diff --git a/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java b/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java index 5fe2221f..fff0c2ed 100644 --- a/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java +++ b/src/main/java/mirror/normalasm/common/internal/mixins/TextureMapAccessor.java @@ -9,6 +9,8 @@ @Mixin(TextureMap.class) public interface TextureMapAccessor { + @Accessor("mapRegisteredSprites") Map getMapRegisteredSprites(); + } diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 02cba10a..ccfd41f5 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -29,7 +29,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.8"; + public static final String VERSION = "5.9"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 4559857c1da8e655c03ce1ab8a097bc93a6486e0 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 24 May 2023 19:54:38 -0400 Subject: [PATCH 14/51] Fix FramesTextureData not actually generating mipmaps (#170) Should be the actual fix for #60, #169 (cherry picked from commit 1a3a56c22b07bc7bd6ae8fe0f055758f13de0671) --- .../normalasm/client/sprite/FramesTextureData.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java index 9b874ba6..57915809 100644 --- a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java +++ b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java @@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.texture.TextureUtil; import net.minecraft.client.resources.IReloadableResourceManager; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; @@ -145,11 +146,19 @@ private void load() { if (sprite.hasCustomLoader(resourceManager, location)) { sprite.load(resourceManager, location, rl -> textureMap.getAtlasSprite(rl.toString())); } else { + int mipLevels = Minecraft.getMinecraft().gameSettings.mipmapLevels; try (IResource resource = resourceManager.getResource(location)) { - sprite.loadSpriteFrames(resource, 1); + sprite.loadSpriteFrames(resource, mipLevels + 1); } catch (IOException e) { e.printStackTrace(); } + /* generate mipmaps, as loadSpriteFrames doesn't actually fill them in */ + for(int i = 0; i < this.size(); i++) { + int[][] aint = this.get(i); + if(aint != null) { + this.set(i, TextureUtil.generateMipmapData(mipLevels, sprite.getIconWidth(), aint)); + } + } } } finally { canReload = oldReload; From dca225f1d5606973c55c2e919390b0d052ff446e Mon Sep 17 00:00:00 2001 From: Rongmario Date: Fri, 26 May 2023 18:05:23 +0200 Subject: [PATCH 15/51] Bump to 5.10 (cherry picked from commit 726645a17065f9db27a48d138cc552308bf9c1fd) --- gradle.properties | 2 +- .../mirror/normalasm/client/sprite/FramesTextureData.java | 4 ++-- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index a9cbdf2b..3c0111de 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.9 +mod_version = 5.10 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java index 57915809..322e3320 100644 --- a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java +++ b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java @@ -153,9 +153,9 @@ private void load() { e.printStackTrace(); } /* generate mipmaps, as loadSpriteFrames doesn't actually fill them in */ - for(int i = 0; i < this.size(); i++) { + for (int i = 0; i < this.size(); i++) { int[][] aint = this.get(i); - if(aint != null) { + if (aint != null) { this.set(i, TextureUtil.generateMipmapData(mipLevels, sprite.getIconWidth(), aint)); } } diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index ccfd41f5..69b6c05e 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -29,7 +29,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.9"; + public static final String VERSION = "5.10"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 880c8d7edd89ba846329f92aee754b670b0263ce Mon Sep 17 00:00:00 2001 From: Surreal <50203885+ISurrealI@users.noreply.github.com> Date: Mon, 29 May 2023 05:38:18 +0300 Subject: [PATCH 16/51] Fix Thermal Expansion machine item texture bug (#172) A weird bug that happens because of face being null. Returning down or any other facing doesn't change the rendering. So i guess that's a fix? (cherry picked from commit a1e25e1b141778f53185d6ea37d54ac64a8be5b7) --- .../java/mirror/normalasm/bakedquad/SupportingBakedQuad.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mirror/normalasm/bakedquad/SupportingBakedQuad.java b/src/main/java/mirror/normalasm/bakedquad/SupportingBakedQuad.java index 4febebcf..3a3db9a0 100644 --- a/src/main/java/mirror/normalasm/bakedquad/SupportingBakedQuad.java +++ b/src/main/java/mirror/normalasm/bakedquad/SupportingBakedQuad.java @@ -33,7 +33,7 @@ public SupportingBakedQuad(int[] vertexDataIn, int tintIndexIn, EnumFacing faceI @Override public EnumFacing getFace() { - return face; + return face != null ? face : EnumFacing.DOWN; } @Override From 30236487495046775cf36344dc766fef50db4414 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Fri, 2 Jun 2023 23:28:11 +0100 Subject: [PATCH 17/51] Fixed incompatibility with MixinBooter's crash section for mixins (cherry picked from commit 2241c2b28f2c41143960c251b904897a01552697) --- .../common/crashes/mixins/CrashReportMixin.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/crashes/mixins/CrashReportMixin.java b/src/main/java/mirror/normalasm/common/crashes/mixins/CrashReportMixin.java index 9e8f7ca8..07c14e75 100644 --- a/src/main/java/mirror/normalasm/common/crashes/mixins/CrashReportMixin.java +++ b/src/main/java/mirror/normalasm/common/crashes/mixins/CrashReportMixin.java @@ -33,6 +33,8 @@ public abstract class CrashReportMixin implements ICrashReportSuspectGetter { throw new AssertionError(); } + @Shadow public abstract String getCauseStackTraceOrString(); + @Unique private Set suspectedMods; @Override @@ -84,7 +86,7 @@ public String getCompleteReport() { .append("Time: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(new Date())).append("\n") .append("Description: ").append(description) .append("\n\n") - .append(stacktraceToString(cause).replace("\t", " ")) + .append(this.getCauseStackTraceOrString()) .append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n"); for (int i = 0; i < 87; i++) { builder.append("-"); @@ -94,13 +96,6 @@ public String getCompleteReport() { return builder.toString().replace("\t", " "); } - private static String stacktraceToString(Throwable cause) { - StringWriter writer = new StringWriter(); - cause.printStackTrace(new PrintWriter(writer)); - return writer.toString(); - } - - /** * @author VanillFix * @reason Improve report formatting, add blame comment From 75a4dc302369ba0b14710786e38fbeeed365243f Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 3 Jun 2023 00:31:52 +0200 Subject: [PATCH 18/51] Bump to 5.11 (cherry picked from commit aceebf51799493f1f75a81637c2d36612cc75805) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 3c0111de..19d5dbd2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.10 +mod_version = 5.11 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 69b6c05e..a2af6cd1 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -29,7 +29,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.10"; + public static final String VERSION = "5.11"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 184502fafdcfa7195a13dacbf38639569e20559e Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 3 Jun 2023 02:07:50 +0200 Subject: [PATCH 19/51] Alleviate some misery 8u51 users are having by updating CA Certs (cherry picked from commit d44e45fd386b84c76e29caa78ea6aac7413abbd2) --- .../mirror/normalasm/config/NormalConfig.java | 3 ++- .../normalasm/core/NormalLoadingPlugin.java | 15 ++++++++++++++- src/main/resources/cacerts | Bin 0 -> 158913 bytes 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/cacerts diff --git a/src/main/java/mirror/normalasm/config/NormalConfig.java b/src/main/java/mirror/normalasm/config/NormalConfig.java index 380233ac..f2eb7b30 100644 --- a/src/main/java/mirror/normalasm/config/NormalConfig.java +++ b/src/main/java/mirror/normalasm/config/NormalConfig.java @@ -69,7 +69,7 @@ public boolean shouldSkipClass(Class clazz) { public boolean optimizeRegistries, optimizeNBTTagCompoundBackingMap, optimizeFurnaceRecipeStore, stripNearUselessItemStackFields, moreModelManagerCleanup, efficientHashing, replaceSearchTreeWithJEISearching; public boolean releaseSpriteFramesCache, onDemandAnimatedTextures; public boolean optimizeSomeRendering, stripUnnecessaryLocalsInRenderHelper; - public boolean quickerEnableUniversalBucketCheck, stripInstancedRandomFromSoundEventAccessor, classCaching, copyScreenshotToClipboard, releaseScreenshotCache, asyncScreenshot, removeExcessiveGCCalls, smoothDimensionChange, threadPriorityFix; + public boolean quickerEnableUniversalBucketCheck, stripInstancedRandomFromSoundEventAccessor, classCaching, copyScreenshotToClipboard, releaseScreenshotCache, asyncScreenshot, removeExcessiveGCCalls, smoothDimensionChange, threadPriorityFix, outdatedCaCertsFix; public boolean fixBlockIEBaseArrayIndexOutOfBoundsException, cleanupChickenASMClassHierarchyManager, optimizeAmuletRelatedFunctions, labelCanonicalization, skipCraftTweakerRecalculatingSearchTrees, bwmBlastingOilOptimization, optimizeQMDBeamRenderer, repairEvilCraftEIOCompat, optimizeArcaneLockRendering, fixXU2CrafterCrash, disableXU2CrafterRendering, fixTFCFallingBlockFalseStartingTEPos; public boolean fixAmuletHolderCapability, delayItemStackCapabilityInit; public boolean fixFillBucketEventNullPointerException, fixTileEntityOnLoadCME, removeForgeSecurityManager, fasterEntitySpawnPreparation, fixDimensionTypesInliningCrash; @@ -138,6 +138,7 @@ public void load() { removeExcessiveGCCalls = getBoolean("removeExcessiveGCCalls", "misc", "Removes forced garbage collection calls, inspired by VanillaFix, can make world loading faster", true); smoothDimensionChange = getBoolean("smoothDimensionChange", "misc", "Allows changing of dimensions to be smooth and nearly instantaneous, inspired by VanillaFix", true); threadPriorityFix = getBoolean("threadPriorityFix", "misc", "Adjust thread priorities to improve performance on systems with few cores", true); + outdatedCaCertsFix = getBoolean("outdatedCaCertsFix", "misc", "Use updated CA Certs that was included in 8u311. This most notably fixes 8u51 certs issues", true); fixBlockIEBaseArrayIndexOutOfBoundsException = getBoolean("fixBlockIEBaseArrayIndexOutOfBoundsException", "modfixes", "When Immersive Engineering is installed, sometimes it or it's addons can induce an ArrayIndexOutOfBoundsException in BlockIEBase#getPushReaction. This option will be ignored when IE isn't installed", true); cleanupChickenASMClassHierarchyManager = getBoolean("cleanupChickenASMClassHierarchyManager", "modfixes", "EXPERIMENTAL: When ChickenASM (Library of CodeChickenLib and co.) is installed, ClassHierarchyManager can cache a lot of Strings and seem to be unused in any transformation purposes. This clears ClassHierarchyManager of those redundant strings. This option will be ignored when ChickenASM isn't installed", true); diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index a2af6cd1..21d90e02 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -5,6 +5,7 @@ import net.minecraftforge.fml.relauncher.FMLLaunchHandler; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; import net.minecraftforge.fml.relauncher.Side; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import mirror.normalasm.UnsafeNormal; import mirror.normalasm.config.NormalConfig; @@ -16,6 +17,7 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.lang.management.ManagementFactory; import java.net.MalformedURLException; import java.util.Arrays; @@ -29,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.11"; + public static final String VERSION = "5.12"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); @@ -72,6 +74,17 @@ public NormalLoadingPlugin() { if (NormalConfig.instance.sparkProfileEntireGameLoad) { NormalSparker.start("game"); } + if (NormalConfig.instance.outdatedCaCertsFix) { + try (InputStream is = this.getClass().getResource("/cacerts").openStream()) { + File cacertsCopy = File.createTempFile("cacerts", ""); + cacertsCopy.deleteOnExit(); + FileUtils.copyInputStreamToFile(is, cacertsCopy); + System.setProperty("javax.net.ssl.trustStore", cacertsCopy.getAbsolutePath()); + NormalLogger.instance.warn("Replacing CA Certs with an updated one..."); + } catch (IOException e) { + NormalLogger.instance.warn("Unable to replace CA Certs", e); + } + } if (NormalConfig.instance.removeForgeSecurityManager) { UnsafeNormal.removeFMLSecurityManager(); } diff --git a/src/main/resources/cacerts b/src/main/resources/cacerts new file mode 100644 index 0000000000000000000000000000000000000000..f6bd60c6fb47e22c4d009b074f8fb2d0801d0087 GIT binary patch literal 158913 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

5evCeod}mwzx_ zZNn2LiGbiwoO+jN_*EWxRSYD(u})XrJzd3n61;*%gk$qf9u2d%9&>M1eBaxMoGz?x#e9pxNLO-sM)We=IvVP01&hh{eValV+xzP3W~n zcjCD1kF=Ph>x19LcRVA=s?oP#E8O99bQtQwdN#$pXYu9$1*f(Y`&an=7v{i|46eja zHkqXImKHk5-jcOy>8SlPM*Y#4fctHefQ8gYxfsVGyXkCnA})@(DZZ2uODZdUZnG|{ z?wi0=2wQI{BfYF50gJSafH~<-n5{In8NF9?;~nGat}?1&TBU&}@*IMLmK`mGusW)m zq4C>%0Sr?s% zksce34}4idEHzSqK>CC&piuKcLFOMh!?9^HMRJGay zyyIZm9y671RC*#^?aA;QP6M>hjqo{i%bd^`w^Sh%Jcw{uLs6ccWaiahjSDaSD z9&D~$zx|&_uz!tQb#C|x4KVzG*}?!LjSQKjOjRY7K#*BB>x>lSC5$V6sUtv{Zc)XcNbl!Pu@=s(*;554<+|NflP3FO4rIjz z*=QU_%x4enlp34DOZHViQ^h9T8*AI_kE3v1>~3liYW0tDfHS<0k(7gEBFbqxHH?AJ z|1}7{{)_y}c%DOC+GT@WX+}v++x7C*rE2CHe82p7xs~!aW%5N#aPrA=~a9zF>(b4zF=Mius>+nuazEQ-+f;y#{O8A*H**b@4mVYXHh7{&S+Q9gAtFMo)AlRup5UAvYTq0_7XU+(Te@p z+CK3e`QqBI0 zbQYH8bo7z;LKZjcfI%96#I1k5N8krVG$!uP9)YW=8vo(`fd7Ui{DZ3PnXHLw-wNbY z=>2s&&b&Fqp z%xBD*H9G*Q|D&%fFmP%>VKMk)?H<$T+JT zhQ8Nbgcvkq^!~)~Z88*yU01D1JD?&_b~<}REOo1WdO++gE@VZAXOOhN_k--%9N*Rf zh}v%o3$aUTuS=+K!5h~8_YDh99vc`L8JGaPpI7-~Eq%cG(Yx;ACkLx!TYzC2$SWIZ z1C}oEu4^R^*22H8UvJ`H{?jE*({G)d5MDnwHS8>qM4kkcaMRVMMuGi`<~b>LAk0Ev zRs0*`fPSBScAWq~KtT|@Ui*z)vcS~?{3Mw|!Be>O^1u%v`tu>c%9{c75Uf{GsDHiX zdar{s3t&I+4|G#tLmwcR211MMOaQMa5PbmB$-fT&H!bz=lFF6O<)|gN*g^+fdu#91 z_RLhg??d95f4(&R4zW@hif36%V{RYaRVzR3Yp&(M>y;AZ0t5&|mA5@K_r1fsaPgeC zyo*ae+#;bHeW{p=`Fx;l15U}u`#hI1;p|y6Y`e*}r%#nPbbkAW-rq8sBOn-f{x3j<22Be&SHGNuaWXs*t?v znEOdPNTj5br{dMZ%BqXm3w}Qt%8YZ4CQ_5?Z#aQHC^@BsMmD$RpV|xaDD*mKD&MW5}m@XB1CZO2v2YK7%WHyc~MIB=GK#CZw_+ zFGkqQcLP;op!Im|x(8reyiVEa-uKq1Iy9(8@kh2+hN+Ogk44XoPDIR@smZ#nyFgw6 z%d2&Z{DA3>m4z?u7j~NDp%p@Bce+;YMX91Q0@ErTc+RJr{6dtc+so-L?C@uz>D5O%o~_nds#7emwYIa>G;F-E@Xg^*l0ItB>=8bg7Dl=S z#c(FBj|#6HyiEM%eoA&V*Yo2v1Y``9y~lL7!cET?n7!Oa`<4h zvSwj_K*;1*Q2r33x`1pSJf?hGjs@SFPoH9#4c8s}6w+ckV)h9oX*A`6%@-=u69`Um zjS18)xv=|ZIB=0!cVq90p3BvKXelgG=$$p<%fr!oGX+cps(d6}+^F7_fIIgz(LuAA zGRP_-?z7sRLG+nuqBxbxyF(1gnHP^{`bcHgo+@$ZH4hxc#3WR)esF&1Eh6JtFUaBi znT;s3YCS3%qu*UqM4R~CHg7$1QGAf}{C=nmN#H?Z)a*f~{VN}H83`&}(euqiS#%jO zr*5IeS_j@%zG)35Dma5B3qF^j50E~|4>>IC&pAN#e4Fi>JRuV86AU)|$6vX$HYxor zQJe_wtY^RW%5I{1J+{IPbSi^Pm3CY zaqg#c-{P$c($sRxiFb9PgIaR~z3!swJR{bHD5M`Edl<-sV-&r0D;s{aJFIVNz<}uU z$Iee5;@5i3&1a1sj&qFTVDuQks%=je8E@fJoN@>Z}vHS zLbx23-@_3Y_T|#8zCgSuSfv^JW;8AP%_jwGQrKMC@^~!*Bhd6D34YNS@$A{yr><}} zw){;J0qGe>+QC}-jEA3dD)KmnW70oRti>5ZsE4Xw$dHNQebozouK^byv|qJWx)#c4 zSazVi`kb)G|IYgem#E5mcDa&LZ&)k-7)wpFKwdheFSz=*v3)?h%Vfm0T zOBJ9Cp=64sR8_~f7BRWec>ELm`=iHnv)faeT!9oXVtLVI8U5s>^7a-SZV>x#+Y9U< zsq0aPo1%$oZ7|HN>*0;~IB~W5iN9T|n|gpdSNPJ>_C6yfyNe#b``vvZJ(f-H`fovn z8Ys-^`J|HWZByS^=4~eCGWo091ra>ovRd6!oiswlQemmx7n}J&(piEka-TcqVfC}> zvSHHsn3wXx*i-s(y9b~V9)^Vg_ryit$;n6_7R-#7yT@j-W+HqT%J|X^LHTek5tz6S z2`Q4S_lp8!QcN+6_{`6}?{~=DB2^qaz!F;eBzU;)6~P#Wb}lb`LYr{mM_ws0fhd$d zn_dz7bcWFBNm8JSfVWF}BISr_-VACRo6{C_DB+UrOPg)3fR^VtCM7IvON<}D z^8e(fz4^-QD)mJEO2bUUyU2V!e!FkDrgn}M`{3BNfY z{FjV!idVw_=P{yBZ+JxFvJ4MEG~fs?Cb>9xA%C>=fSDcF3jte*L?D8z$P1=Ccr9R> zoy+_JFliKAcMc?^fQT=MnT3`4iY@ELA?UyE&VMJ{XSVM$h=O4`yCmb7{8X`o(pQ3U z2IiCp$!?|Np3UWsl4O0Jdi`~Ct^#^8{OQ?qi`V{-Uqb3xVMi`Pad}PUcR>a=9q6`s z2pvMTag=Em7o5b`r!NmXgQ5>mKZFJ443D$!7k>BsNSlJq6u~grK)AoH8RMz-xPB(= zzFW3UB_>IEuQb0$5cvu{!F~i{F>INjv~3D$ZO* zojc-$Cw-iS46!UG9e!{_5Fz{F2s)BQ%y1A6J`20;&?u zd&@DnG}Ynn@;u-*33o;`{!n6SYs!KkdkW>%%H#7(wtvMo)V@i{6lZu2a}4fj#U|Gw z9x0d4{>ha2lPPq=w)s&WlAQ7gN0&ZJiG(a3?&Qu^C3)*3%5iC#&9t}nv%+L4UbaE- z2(iWKtp(_e(a5iLX+Fd`Q2;y{?7RB7R zmE-giizs(_)Qo;cJUJT`EF<)d&6m#p)&g$L9?=()iFBx+ZQa78%Oc_Zs4?UG@dNm% z#CW%7m+uO)Pp6QNRzdHBWgB!;wl4+ua#|3;r)OfObIqX#b`Y=5To&wJrDz^u|CC(V zFMtcU2T1S4wc3U#ELX)5eOW)>^~G-tf7$6bTm^{fkU@x-;s}QcC3G`YBc>`OEh|I{ zro+3Qiw%u}BnDp0<*mUvV>W>Qm+7)$m+^9v4ioqgH~^xfs{a??{C7@P=a9okbmH7B z&s7|qS4B;PLX74S6X&>-1Enm_InJMbB>g3IYf9&MVX@{pzch+EO=;H5-M07-0AhnaUPx9r}RM%K zdo?acDu#Xd@@oOuUE*Cirh-d^e0}qtSs(jfCDljDa)M#T%jy+Y7Z=^zwXI;ibK7oK2B*+$$h*IRd#HfLscJ z&3gv}#w8o%lHr1GV4`cv`%6C3T|z)Efea!bA+UNv;kmJ^f!u(Am@p|Hz+cV828<`9 zdjd?8(xO+oWT`UHpcBym=)MgcNEI%*bx4I-8A;_IF$jS8F24v7g@eyVTLY-gG9s1I z(KY~*FBTdmI;NyTz-KiLv~@H{8JL0Z2L>H7fJlG)#=lRL{ess!L^^2M`xK6)A$^~V zik52?t&CuQlVETgi(MuPj(C!!H%CJkS_Y0&Ql;oiydYFf)}tPL!`$~HMANC})=&fjQfD=2SrtEcreZx_yfuvKM{4=p;3CUfnp~ z!4UCX8_l-9m>$Uao&y68yV{#QBKjF4#JqimMpx;|>f^fl`yP)Qc+)skzN<)ZQQw;? z#kS5z^{%3~Y^Z(ewZy-_@nV}8L>|+{Hqb(5WA@IXozH225bb1q#T#BCjTN5MP+E!oo|EVgG#3Z^jyWXs$zjS5W<8*f4cNL8 z?Owr_wldiHY&v9l%vU;c&DIgyyZ0uUp{X>n&nQeKJE~u} z**?KSRx2}F4X#$yc%0qMY?WmfcHmvj;#3FJ`s*{-$tJZYEcwwKc~vZ7ce6-DXIO-z zi*MJWe<8XygSQ za zxE=%8wI-L7)pRTXonxb6rEg6K6#6}4W@i5}V^oLE(!oH_#Pm8nc*&r}08nl~zOgVp zi}~70B=?2>jP;X84gH zL_wAMv2aOX;S9JS>`O7!KtWUDQxH}BxzHQsjyiPmSBt$Wdb;G#0{UGphu&UB8X1A~ zA5hEuBhvWe@PA@SDj-Za`1_usz1&cca0uW(pdsO*`5|B;r@4K{3mr|{sOkiRi%OKR z9RmgKIT5(4Y-{Nkx4rP{#}*~d4$iBUPo13Q2+mZ0sn1yE$v5YvM(fym{@P(O?Q-DP zpKPx`x?ylPstdg^un`8{HQJ#R74)`s2Jh5^U>g3w1NfJ3_{FFI1uX^v5{4p!C{vyp z^HoGx(-4&}rRrvtG(58+c1sM(OR7m9o{oH(}ooSb|KYRbQ9Z zNEd&%c&DavyfO{ABoI&#uWoK=Jxy9taA;l6Lg%uOh}2laL`xso**ez~7A-Z5fb_QZ z)kX&l6jN=}%ThZd17iT6fzx2qNkc>9hL;3bRoAY`l_8}7@pBu)umTBfV59&v!9Q|= z9B*u25q^g2dFco!w?(cODD`uJHzMisSF2hUD6mii;g4vm7x z$Hzzd%a_vmIoBC%d@+O73ot$EdI0;)+T8!q#r>TlRmYofXfnTU<3%JTBp6sH+~Ee7 z(0KnGk0kIj6u=1lT-Fzd0XY_~ztxPqM~qHEtNVkAzA&=y+}V9b!-!K?v-h2J%V`Jo%x{#OH3ce zW_5Y{`6d*)`e=YGo(!cl%S=+4tKO?J}x|KI*V<)yrYU503Vl>RT$U zJU`Gm8FmjI2+urh`}C^0nHieaDRM@&2`vjLrV-ZfJ<7v_XB>S-Y>J3pv+}$~Pj|k5 zc&}pub3{1J^+^@GEpcK`MHED)18uaqMrOGg2c7bXEiV}xFX*HSsyB-^=N2aI@Dc*$ zd-5ptZ^19DhErqZYxi^XKc$*9jTsfCp(oaF=&va4=kG5*c*KF|QfBBeR!6t>n4r8^ zYvGF~t?TeB2tL_8cDJO6HP*;zlRs89)AsK00qQ9hBQq0-ysbXLqJAKMo{Lm z#P|oKT?dGuG0Cs5U3QZG+KzwD2LgqEKy}X#aQlr5>3_*O|ISYhSyc!fL{cisMZGuE z+9#-I74v{C&FQWggn5npT+7M*+d&d-cSd4_rtZ*($O=z56t=D4mU^P$Y!n73u$^#m+x zQ6NTrHVkQpwt}LGu-?B}S~U4Sp8YxW-lTw`W`*L}dSi{z2fqWffY{G*Mg}q8>_kCA zBYE?vTte#K86)MWYV?{v(tj{q7jADSO)etQnH5E-N|(;rcN1oF-&pN-5f#vrwzwbn ztX7hc+V>f`W9j?oiS18vZq10o(#AY7ClR=lubGtjxB}$1kUu;tpw%LnQ|@?Nv-Z3t zu0SYeAx-sOX;qFAcc1CI3$dqlyV)0q6?toHr&Se)$Q;M30{p~2GIOv}d^I^DX_2QWuKG@9kEoXSf@k-=Vv%ShF zDM7*htLS$YHOk_H5((9}z86aJEp6T5enFiO-(6oKH<=}}|wSc{@sd_f}(mekl%oZAMf{_|rz=>)3}p%3#l?%X20 zpYG?-dJda8-!R&7o|qr>po%-@+qfm-Q(=?L_`@-(p@>N~Hw&U6%TK8yG0=~mkBHer z&=9q6#*q@i-!o9QD`x-B;htO;|2m+aK1`FUoP81(FTa)xQUAg{j%#ET8G2DU5$o%~ zhD=AT*WL0DVgmLa&#E>;kyF}M^|K{$iU?wmR^@3!t5Xp1tWk8b9lP$96EsnKG!tI* zKhWGscSH<{*7L`L?9qpNrA1~9?d?pfnwa%ONf=9WW++baa?)3yH(njIks-o8*mv#6rH`h zH+_`cwrF;StEji{ zcT-nf)^f_-P!%_L#4!JYN9-4;(67ipm`Chp36uaI*g0ba`b{sJOdtIPbJ%~uE&mR| z3t^`^HvC&me93kKrQ*$zNS>H&8N0?uwT?+j3yF2{@|WFaHo|YRN@sZK0Ts0=z=R|} z81ElgUSU7=VC2<)pl-|d14#JL;gWu+IoTt{0$sGOPa^6MA271BJmH{r`{sz#hF76E zDnb#`yNiEYDo^Xn&|~YDG`lGvqWj6wnb@`4XZWnU3n2nQCm%;|QHe?J%rx$Kn8kfu z&sG)qZtQ7lxyB}xv&-7v?QKn5Z3K4_XUK!QsqxPIb=#fPuVQQ1Wn7~v8ecHJ!kXSW z!>H>A!8g$qB1%(EFQ#UPydr4|5%PYv6kTDSfsNn!+8U$V%u0qX?oNo{nG$vKd9~-O zj+1A|Pp8`?Y&zbPPgtU79I8V3JeEl$cim34lE2R_Pi1h&D3Fm>8-h|xgH<|BxcsqG z30(IxI-C#P47!K(+#!T4Fo3UFFr#BKE!kOi67eSe>itz1$RVf4v z*IWfw;ObmXfRSD()18L$!OhT)I>dTf8dy^(!^EAM`ujC%4YEOeX69bSQspo??rDq# z`P+uLe#J%SM)Se`-q}&2y&Y0r2XK8aUm@`q5fj@!3`|}QMT7eeOFl?B&miZ-IE{XV z;CDe}{}Mz4@&ShEm&`6UJCS!rmdYlewA^tT^OMcwvc5`+uyi1gQFHs4PrPH|TgM_a zB7UlJWS~aMKbJZ<+44F{BA{?Jw_`iQ044@0H1jj4w6KqszF zl!`=d#CDle+HYYte6vqJ%_|QkK-btNo+r`(EGly9B(syq#dOx?0-|9nNA_**&z)7& z!@t`_2XTeTZSe-f&uMGj>ykEyO&JR(@Mhd2w<6Ea$Q-|IAgP`Pwf$goSSu9yMcqRi z!?X{SH9^qhNc)o7!nN$0OOYGQS{T&b5qo(%JJZfjMb{)M)^&2%zB`wdbqF2jHYQC} zu4{S+w(Lo`-OgL~b01A?+Hk{5dR0w$Po2rEd-E&f!Ltc_r=7&~^Uk}?_qMS^i?~B% zC_QU+E$?Bjr{`-m<~g}TbKSD@B(z^_IoOAZAkDw)+ET&+p;^DodT(C2s+~C4dgSHf z2#p7goIOh=`WpShlsRNi4CprK+c#``GdaqjnPhARR-P$9KJT(S>_n;WyB&y2cUM1r z<^6e1@dLPim>w`h2i%0{{}hisVD8iZs`|SQWa#S{8R?i9Xpw4YX=v*JaXnHE6JR8^ zj-`%h;sl#xS;W`;~Wq-k^Zuo z$0gzh)B_avf*^iy>lH2va$KX-m*6FlnTd+mo zn1Fj+lJH0tJyX>_#JPYiKXZuTw2TrlflGBquJyrOSU08Q;?m z5-g@z?|p8CqQ&?uz-#FWb6tEuE}nmmULs$HJJ2tu3_#utU1o;xpJ8f<-%UhFr&$`u zgt@!TM*$MKd@nH!z(SJD1%h>jrq{Cozvx(l;9Ozob+oTufgm^t*5&Fd@N#+8<@ze{ zdU^GR_5CAVO|oOpH}7cjxY*y5jbB`vh>bWvx9Xltr_611vIgiVPYUDbL*z8sMyaSd z8{BYeF&ypP&9TgH-=lR%6V0D|p6)@dtG{<{v|`wV+5wj+9iTy-`RZ)xNOK*JOFCIT zp_n6nBO^Wp=Yy2%ClpxG9HTGY-?|dSN_U|$&di{voO-0R^iB?-Z%5j}1r+IR1wO7zl7aeCg81-2(rfO1SHx?`?p#)f`czAYuR)Dv zu_;)^K}<=gJLJ>4j_$QWxNzKDA1iH{gFdeJJ!arp^j>rpqv(h#ONfn;D&+bm4iTq4 zlf*7DKs8nYhBp42(DtLd5S&N2EDpW~s-MTe&@5wPAj4*$Ve~VDaE(V!9zXF2RvR!q z)d4&by$)I+LGn8Ts3ml_LlE54c=Ok|G(^R3pcBw*_=C_E6HyFEXn;$Q8K^)Mmxu+8 zN&NF1FbC`BVt!-@u4WzrQB?+3V2~9fBbdAP=Hb8fUVmqLX365uK91oYW)j{rmD6x7 z&XEby^Lg$*AwPpcJu$X>ksl#o5p!Jj{Izc*|2%`N)qo4?>z+8W_W}#Kc`rOo4iJ4w zR2geiH^PrspCN5tL=P62mz2sUNqg;t*3@pwFoL`#a9=35$TuB9MFmV~KB&J(q8TvP zTtOotX2%t#?oIwZW|Fm|DRr1u0YZ<(#Ihdh>2CVWQei5k!^cmo8TdF~&dPW4AM<`O zR)sn1W&U_?+UhRhb0*B&&s!iVjzBX|&yzllh}4#D5T;a%iH#?zK^^#hz8CyiQ%0t- zzz@v|H+#Us`;`NvSa+$geH(7kWHd3n%850Zht5o}Qt#>e4E2PTgyScLezji5K@aXC z-L>KT$~4~zabR#Pk@`ux=1my8JN#Ecvp47Ps!K(=19;v0EK6cy2p^R?xlhsYocuT& zF`J}Ke&j>?kR5ddPK#m>9I7*vnPk# zU!S<{t*7Oli7^vUAMkl|U~aF@Uc}saS=F^lyk9A+-oP+qxwKQ4yv2lURYywNqGz;= z0PUdY{8m^xGV73-XZ~m$ah^&0fz}w~i=^WQM|3;WsG6|YAAb!Kp zE}Y+Xx?>&x3Z+6vAgj%90t2>8QS*Ztk1{1*3)zH2%zu(1(P0&sa$hu1X9}}rfdbcQqs~hWmsCM6Ok5rsdGq;4 zrY+4gq4tMr(@&F~*_Tho&M~nvx46T_`K^YVZB;CLf`sc7AMKp1hA^&2J%u5kebn4t zW8BHa@e0Y{adOAHMUcOi=iORkuE@lQPhyhqQY-Hh#y}bDLNUb`*=cBlb;S1ygxp}JxYj3EsCSt^-zhyX2QJ@oPY5I0D z=^1j}a%;<*&F;Yo4A_NdGf&!o|DEqkqu(Y3+jGPvY3H4jqGylbG6==zrytRAO}{4z zCKwTq-ySeC>fSz7{)SiOZsG-o?~=cQ?|(zzDFZOw;zvK{WnarbZo>Q*`%ZPVuJwhh z;ZJ=5bq1uHkAZ*ASNZ}KBGb>F26#RQ+5V8@8~J8vkW2j^RmG3&^1rYB6nOIgX!|KJ zO6N)^-Tx1^p9%?F=_epp4QMA8Mi!>a^fkET3OHoBJpBLQ3I9P4nmcA^f0Z$xcjN$K5r6hkF5y97xbov-ZwQhy7 zV;YHguC;0QoHb9+Hor6BwW1(Ls+JtdmqpFt91(b)Zqk31Iiy@`gV~riCFQ;X5 z-RII5=Q+z+P$0FKF%LGlGDn274JtqJ@Z(TM-N^uyi)6Vd?g8v`RV z12Apo%H9HIQe7T`Seb!->VMO1{?63uR_4c1h+mE#8j{*R-)c`1U>DaZS;g^Yo(u)7lUqPTfBd&yrSUQ(RrXY zNjv#G(1a_EXY$l_X=eXfF)Wj*FKcfER5ntQFX$p5V1D)tRkd8 zlucPz%42?mnuoRe0ys)O_t`W;&NB0*8@jM`!r3H3;dWs9JL0{kkR4qLin#B$=y2G5 z*q(MS8XSs==h!w>H7p4zAL)&6s>_ggh*OlP&k1Lm@09dycC(x2|4{c9U{$SK+c4cA zA>G}u=#r97>F(}smF|#kBt<}JloSwY>29Q35R_KRfBi zwZ@!0&&Ye+qaY=ZQiOSLU!Q6*RPIF2Zer@b9WI+?0bgKPe4DZ=MvkI~&`E7~?(XAy zrH28T_di1`X=$nzB!7P{h(J3z`zRI#iW`e=WY=Aa+A)DK zs-zn2i@aHMa~~ID7N(qE+xXUQAezKyXGG#&Rt+QV#8QQXSLH4`L^B|)_Ssq}ZchtB z`$Vv=Vdz+2=nE~8b-j6G@@TyNsiZ)mCDKGguw%D#tsviXN}1_Pq3s)2%O5=PY9iA= z#+-hOSAir6l;0G{Zye+|0t0_dnbo8L2H2E#kY79u9Pk1(G%N%J5VPX>1qj=D-cT!; zk+@g@JMed%(-a7TZZk8cz#L~|V^aqg03`yjFkGE#O7f3;;fSDNt9gxWQ;9fe3?Luzk~wEBE7?}^K<{-Q~<7l0mcr_*DP><5mz6)$5`+e z5jfvvHfK%Z(3z?2ffxkYT|EwRD07gh@BP0zAxG*30J!|OXb69Zf6zVG{_CTXYzEo0 z_XqaJW~#5r*>~4zr(3nF%LAqc2bW0CzZw+q=^7|g=cXUQk_w~WHFZImfOHt%3Emcj#F=Qpl_R_2aX5aQLRs|09WTuwI z%YE`tMeeKvGc~vMdy2{VN=R~2iiw>3&ABp2sD#0(vah~6p~bIk9i>kf>K4e=WI~iW zgLO@lLvEaI;kp=sCG?PyG$j;hzV)|iO7-+AI*_JNI(GR{FTYku2w_)&>?_(*IzrB+ z-w^bKK8|U_J3nGBb+MV+o>BMJKK97e!;g!UhHRCZ_)B&ZT)*3?{(c-z8qP-qe8VQo0q`_S4Rrv?F$@yDjX9zsVI>gMbRaqJV_A1|gaGo`dL*4Wl4l8y!I zwdo1GI6jPJ?Xzh%D7sVO-)#qde4Wli?-L0jx2ExkEt4J(h33{zg zttEjgTGG*&YzNyEHZff0Q*?(G!fN@lzMeJOYoyblZPCpWDCdLC3!Rk6L+3K_UGr0+D^g*hg=WiT4Tq;LkDAaW^L!gKA1WtMIb}| z$Y|EaDvq1ky6}8bvR!tJ)icvaNxw>bo|d(w*vqSoB)Nvt*!{*0MrjQVBgn}Z5^4d8 zlb!?@V#B3?xVl@-?29Yqta%HHp+RVsz)<1$ylKMnp$c-9Poi$NpM@t|D#@yq2|jTU z&gQt&v8*s2uCC5>EnKepP9Pd*U$W)gt7pK(+)Az*&VAc-5Sc`T7fopM!jP*j*qLo2 zIVqF;ss9UEzond^dUO3m*gR^qT`bhc@5H2KE1`y8?~VOHQtVf-f@3+2okof_(SdB` zjUW-Ui!$fu9lv<09onsT-fK7;!R9_9%E|$61QmycI+;l=an47&e6mfbeVb@42lDO0vsPC|~ z)B18u=9PgzrbNHHEBLKFdDD%I=%jq&1`8`Jk%V-<;7=8=8hJrRY+(9Cf1Rr8m^CIv zVU){J^(cX?@yDoKwXp)`cWT4~ zJS|J-2!)soPvyLfJ8Y6g9}L^KN@F9O#0;zF;wt7dCJRr)J}Od}j)_VTUljO~@Be+=92yIUZ0n2;COs^&lr$vwoKJ#p1k%R4QU@;oU+sYA15*v(@2aDY%Cq&xVKJFKx#QRoZzP3DF^IIw~?K?Pk}<>aeY_%+Ou5n z_HnajPs6Vd2o~%7b#${^Hzy?J0Cb z&edZ-^iOv5Q)Vg3I(llmzqgCh-DO;#;+mukDO~?FEw?44+-LUJ*-;INeZ^tzN z+#7a=Bxb;0cGs!re}bO7%~l(m0U^=t42PRqSs=o13lR%gh$z59xZYR@C>-hDKTog& zsoimWMC4gOAf~H09|4IP;LrdB5&tE~Z?EtV=(nGAo{&gLBH{po?rJ2%qem*Y=m>70 zFOm&_uCoBKKR}EJP-fg7{>Q%e?}DcZUF$wnL1JjlF=#oi3Y47``e&GgXYn%F{-lnm zN)w7E4p<*lE#;nJ+GaH);eS}jGapAP9v~|%b6260kquW?@@m~>ny+%CY}wT1-_2pp zJEnWkO-^X_nlPte|LacnnCJlJfD<#Jaq>p$>5upqD!#Hxqe`!tq&~&;)#^71&gXoT zBN?UK2&7f6sR$*$Je!WmLMUPs7OdFEq20yOWMBs-W5G-mh7LnId9f~SS;*NjB$+<` zsCjm2{o zON_6Rb8*jnh-V~O+t~!$_!Qoz^(_H=|j~2#@BI z4ewf=?!-s>1&nnh)&tgahsfXjYra*QF`?2NC2KuS!iomRWwf#L;n9FG2>=fWXqAhb zW9D-rt{_bD98@_SwSfFU)j?&?AnWhM_+bAEpxm%;PJYG{KxhnZ)Hrx+Uc zjs~bosC6yE@Ow_g<}4nUNzk{WAg@{t?(z;VUoifl8AN+jUzenKoLJL;{sLwe@@d6M|Qh!$7}Iz!Gsr+-vW;`7dpE(0%f3t zMneO6xuH6gaMxNG?dgIoJ>+G2##J*2E+azg9?6p&?XPvBxEhv?vu@#|i@ui4gDttp(Mo^2~@krgd-fB2E_5x!6vcP5&Gr!zWw-s?%^@#J(3=w;%Xh@(zp$JRjm z7DuGUJzdY8ghk|}UYGPV2ydOh&^d7u`Xt>mkP3zXhKQCpttJK;ijZYKIObVV7$szj z{J~4;?5n!t#>tNpq~rKYZ`n z2RpFFu>JT(@9W;x`#0Q?^y6UKLJ08o=8z1|v72pBjjfJ_qhB%;?cN!-YEmo*6y zgXY)UH$=^>e~R^gAt-KJK(59!Tmj~;>Dk$@r#0Lhg8nN)T#*ys*_Pl}$R}`Q;NgJ7 zLHGk~;q%WYw$yQ?a6i!#Cf09D9pv}9^SDf^jr+a$!BHy==|Am&~Vi{ z`KH`ZVyQ%V1_mze!ud`{_(tu-_WF1an;;ZT3ropytiF3d7RmYeww}G#`ko{7;v+8B zMP=8Xo-^{Udt=&>XUe+@So2&=pZ(K~WrBcHivv%%cgi6O!{hI`46 zfO{I^i)LrJl7tPqWREp=;@<5g0M8>#pn1V^!0h!y|Z#0u2=KQuLy;_@*6>y;G@3?yFF~wsQ_zODBDf zVB?Qcc=7@143)?xX)gr1?Ve#o$!-9B;>okPKqsDUlC6Nx0e#7KaPPL|sj8jnS4NIx zFQ^=s4AESqFV*ov?zu`b4JcY-XG5SHCXNjWRieu7wiTm_yv)x;7)vxtLmmkAZ)ZhX zq0bUqL(ZbfsHmgUQZM1US4QYF)0lOgf?)Y8&0}b5=yf$L?55%D`rsGff&Ip*OfLMT zO~8+U&2j+zc-&|c3>cfNiIxUbSz3`*$H$Kuy6{?1?v@rnJbzR!R~-IV4kiYS@by>y zOvXcEpx(T2%WEo3!hY>Ta)3C1X6K)c73`ophyOPp{Z9%v!>_}+9_1+`U$lfjmMMHJ zE@ND7bi{~11LsIO47Oq1m_~j6g~v{Ji$A~H?^T{QNs5>**36F$RNLi@h2=*5K%=h? zCT!U~6wD6wp*eKft|xw_MlfxDGdy;*3wp&B|IVpxyL1;$!imdcZ(r9snwY3zwGF!>SN)yHRw+L>d z`R`D#*N?~0jd^D7ti0YzFRYP!vf*#WPY=*JZ{UP(RRO96|_AN zs#pV|it^2>LId+h9Vj0bTGkXbUT84(kNAVly28;s`s1>q1ySEDD-x-n;wb+VN9iZZ z@Y;g?3Rp0Zq?DB;ZkC(IO|g;#bTw~=1;oyF%@1++@ZbN1e;28gY*09VyU+DCJ(FIY zh9IPAw&oLeOr+g|HT7(dW7iM47l&4MM_`X=X67w|ZA9a%D~x~KN4IV!9^PU}C-oj; zdr&WVIOqAPs`e6npEOOTB|O;CQrgVSCffUfLvsl&8O+y}_iMhTOES?lson{YStAn3 zPf{hxl{dIcA0gNWFbzqmNBX){Pcsj4M?HpgAjNk*06jR1E}fe;e!Y$L=)*HK8Seqa zjmx+0Oy4RWOE1WxQzAJ;0T}#6Wr`h)sa@P=)GFyn!LU3-#rfh8*o1G!jyp_C_Sz|a zi(xwKo;GfKd@)9zYcns>n5o(aQ5R9>r%<0^o!1-}9(S%nz@|iV!X>URaU~&hx@MlE zI8_j)zk@uLf}Zr7gMLFdF1%R`?v{vfZ}QVvBRVk$GYz)Qa7s%dyotM=1Xq23v8JBI zb04Siuyz<>S^-UgIjTd7ZnOfeJXs-P@toAR1-y!O!F-udoeI#QJv2WHD0*NbLPkdo zGDBAiJeDSqafU|!-m$fyfbm2m-lgiJ%lb27aJWIxsJ0{L8`%055J*R|#-A_cDE!<2Na37u=zaVT)n5vq1oMd(O45bSx}~`_6IIu!qDMxc zxjdd(!hASNHb||z=y`#&?hslczcv-Y+c8J2Xm|O}D&e zF0>0z<7hM8?ykr4GZ0ZF)+l;cS*S3GR56KXNkUBvS8V926K;3nziP=mcBCd#SL=+b zIYOH3y)Nr`UO8+~!c&O2g-xm!w6B1ox!u2@xmn+hxflFAI?(rMWivKr7CYStU+jxn z;_wsu4_kbD!~;F!rPTXHx z4G(1tlVuEi;QwhiNgcYLq2ai#rc<8Jzlo)b%-K0^=!5SI9v)OSa(~$eS;3X6DTGb5 z!I5O?nM>Au>b(9Aaf&${>eUw1aVrI!~QmmZ|*U3K~hXO*A0jG1%% z;&V5He*XDoF%1M$)5PP3Wyi-cSR=4{;>u6BU>`<~v?XPXrM{(GNW}A%ES2Sf?eCR% zp_E8pUwd9M%%=Qwl~B%hET^LLq5YZ=HHS_fO$}_+dK6pCI(gQ)LWZY}_}ClFsw6w_ zbfhHSS|vuoaqY!(xDSCB6oq+V)5TVcICK?-B8J){#It-+ZU>ATQLdk`*=+cg&mZ>) z>tD|14jOVVCe;?}MsQ8Ijiwe<^P8dcYN^27a`Ey8~k`$(&|_e28gN8<}p92hw*8 z7C>72N4@>OcL%P^?=1fjcYqD_­gbsQ+=*E3e4Zwxa{oOQQP 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%0A_C?&)>VWuh9 zv+}{n>01@<=&rId0Ta=1qP=od#3*UH5}j8`_ zQbb0JCkvKQljhINPa#zEYhTus)^f(Fd$a5btYd3*fn}&4uv@ zD$f8Ax_?ygx@^!zYV7i7`L5qYSfoIGuZwVGA>}pTLnQ&WVjS>~;F8y+r%BVoTZ6qB z&4pt2`rTwi12>m|K|q031`YJegoHH&p3jEKs;g#k%xWcDCXO1$WO4i^rYT9YStu=0Dmv!|b(&YY5?P+!Ce*f0*Q~bG* z!APNI)FCx-LGB+@uWkzD|ME9}2fM%z zED5OM;ij<3E=NNIqg@ZVIT70VW?Tqy~pNKtkrL z(-#74upf1Giqy>9)QnW$5h!G$Zv(X8zFsN%7YpL{lj{Wm4b;+sZUYuX>7@k(^-`PC z0{KyDB(mG~D^Uv9n@|o?S6ltQHgEx9EZ71<5e9|{icWwWqbuPFGB(DaLuH|3rnv~j zlVAUaIB2*6*TcPDAp&3-^?h(1)N5Y}RIU5Ma{{`r{@)$V-}yo9pT0XT91qriXJBA` zG49+kJ&Rz&tD;>wf!Btzl5 zFPX1|#kIa8VQnAnX;@S8La!yy|3J<_q4A(?b(||^4(>ZbG)gPAfXZ>VW(cmQ{Srr$ zi~}NnBM+CnmMaV4^U98UA}Iwgts!tUVQSxEuP7iT2?&tO8tVu72*|&1(+>HDFR3=f zxS&I%MDLmOnE(zi&&;ux9R^!F+kQaPLuFjoXofS0Srz6xW)+P$)|MN0a1Iov2bD=} zaP5=rE~G-P5+tJ!O(r_={_nFXh=X(pJhDCQ%X~Ky4Tr(Y5@>;r!(VH?mf)a=eKM;1VZ>(=z#jP2dpJ$i~UU4ron_ zn{KnZJi<_WAG+f{ifJ6dag=c)Qk^j>%_3D7y(B~*QTgP{_?(iP{nzhgagbvwK`Hl) zeukQS`j*D|xsbiwJoRp8#uIWnJE1uhF&2IN4fVDFCRyg6SuA*WI~Qk-@z}bX2ne(I z54v>2Si&A7gfO7MX)D&Y9wH_6;p$~#HG14P4 zt+Fj{KGHx7rG+j$NXjz4`__(9C+cxVgVZfVrmAjYA+wM0RPXz>_kWPRF~dsZOKZwF z@zzc?{JbhwOhILu#km9F`%HPb=wO)XyeM4@k!N-B^`q5Gd6UQs+!HT*AVM5;d+}!$pIOgrzfeNC>Ra{3< z;a>(OMd808Jy_B4==3-)qf^R@F)&x4fYm1u_6esi5=yu`^^(UY1&yYSsAyS-xF#cm z8HxMLR0f+Hw)SGqy)fYm{Qkl{Co#1U_<&oZVdg~&Akq+ zR{@CMC*l3#V>M&t*MgAaBW`Ch7izaiOxK4)xFgHIfz#wgoq;Zv%s<{+26R!J$QCjxDkLJ zqCK!(@>}mKP*(B3mo|J!yf9Ax0tg_f3mQo3g1SuVf`VSUdGkSr#?7u*K$7tPZt^df zWC0R?IRNb*2MZeuC>0B+oC7*!=K!*SHUIlQ=25eL;iCtz`^`%_#xupWOOxDTJLks8I>0Kz&A9MALd50w#=caNxenL8B_~+ zmE%>(HAtwZNJ#mubM80Z%__I4E~k3Pt%Ufa#A3;*;)AhvM3YC$6aUW{cqe;|a-_5f znXWr+#H}fcCdA^pa~N=ur{lkixwiQcwuq@SvwVW?=%zM zcG+03i-y@O(lRDg;9UeL!sNll6>a%U;-|$RfVC!X zDuB&TRuUIlEbH;U{J7w2H5F50iOuw#**+qA65-jC#^~&{Y}z4sE`?;L%D{DR_E{Y7QAN9Ff3*|Hi8M7mixKFO|v1PZV z%jDS##B+IlB=%qF%aDi(iVH&F36M0&OxK8&Nz{i6l>7?;`oARE_!nrzQ-8pBkqJvu zezW=3@7J5Dar19+<)?l($NFe9-p=wCQD7$V;P^CU_8Hd4H4meKZdi&i)zPd)T^(6u zzceiunk+<0G7AGJX8Zo?NbMiIGfxl$3Y}wkJGD>6H&C6(3lU~*+9bYopfnWdn{Vy& z&d_!r((RZ;ae|-0ysKx>5b#g0==i^Gc?I(SE@^sj5jjIUAR`!@6nx2&0Ie?!+yHI5 ziLrw*;H&~qElA8q4%Cq@GjoEhO=N85KW4}zB(L-@XLzO7L&nCt zp5*WEeOcHO$Thw^|ED4Q8)J0kB(rh?<(b&nK~V%&z-I;>a)1v16Q}fdTWL*vjga6` zyEUz?_*9%Uw;JqviyNkZ+5NTEvlPh4Y%c1PeGE|(Mj2v)ast`*zUKJgvXAF1VQkRj zwSG?@koxPcRj5)I98AA?WY5{wErymzYt~F2Z-6^4HoDhKckV0ovGl<=>G3FU`G<(1 z{t!S}fBxH_k56eeQtrmzq8G^^u@Q2!M$t|>6EA4HE2gb`YO_dO_0vqGFVIgbC-iyw z9F6xGg?(^oV?*Ri#RREhh3E6wEci>ZGMVEqD=mf^xRvd@+}*vRCZFB$9`&i$p!|S& zwh%kJ?>%pP%+85M<83GNnk$eo!%OXT#;sQ?MBUV6dNo8Ni#aglhXZ3Hm!7sy8M9C6 z9~H>>UgkKy&I1G?4-7V}YLy@T1}bocR4=xyhE9Ma`S$y-@P*2sFvt+LsbK z_J3!6BX#bdyo^X2C?5Fp8N2`l4B*ufBB*jQvhc7oGDAbbME$Yaz`J_vAamI;G%0QG zq2W?A3M^@n9)8zT2id{$@!o`EWlel~6ombXhJeC!FKb@KO4{+oLH}^WpXl9`kkRs1 zyLvdYu};m{8O%KLb5)Pqh&IWEpWBPQ>?HWOoSFjh82jALQx8){g0K=-@##n9v8MF+ z*On1!x{nMvDw?&UT1>__<;GsS!V zG4_6~Cw{7{Y4=+wXEA@8O5BMl2|r#rcV7>XERgr!EYOyWW7LC9=2H}y{#<3)11a=2 z&2J*|G4GCMRn^m1W5`il&Ko{=O#1iL?Cw`Kolw8FrwGu(J|p(htl|SBYRyeVHFLfo z!wsB`^zDp6@lBAu4J0C4*&14sTADarOP=*@v<=y`4fGvB-}H@~T)^L1Y}_o(^^L$U zu~`D;I2*OArA{s_*%uRTsRSnbOCb%K>{Sg18o=HMm+gc` zp}%@RptLe*!b0E)i-9LB+7=B=_Qupfk^Mk#BYmh9cR#3-A7W)Wh(b<5T0_M%_J;WiIlZp)y=tGvdpt z`^aW^h8J~SR(CpF*xVc^1Xx!ZFEUhn?~&NKy&mUQMm2UG#TAHye673s^ZC|!snHo} z%CPpz*$WZ`quekTl~Q6}>c@;h6idagN!pD>No(!A_`ZpcCn(i&yUr8LcHsHDBx*6$ z)7_x?j+xnGgyZ zqA)C1gjwh>+!L68K-pge=BFdm*v*={-u!0b9i`K7r76GRgqt{b#Uzw?trOuDrSU01 zWHnDUZAJw86`L<`bz&L*;sqYxJLev@eu7z!YA+z`9 zPzDJN(FdzCc@3Q?E#5*@=ANc!USGD-{oZ{PwIO|RsITyrjOl4(KCWT*V0_S4#+#jx z9sYp2y79*9H#CQ1oUjlQanSz4i%mi-JlrUvR>jG+Y6{B;!w}OQf^NTWr?AWc;kuR-=&JQ)j%E34%eiP>`>@JWE z&}jgHF|c(7wh=E(K;W!1(9^&T2mxAK16|`Tt1N&dVut#cltS>>HS$2~0+t<6LKsT$ z0(pMt96^kN{0+>Jl)nzzVWTUF2+7Hig5?{a&%e&Qz$OM18y8~``fUs#1&5Hp6}7+( zxGO+CF?Q|?YT?)6|A9~ZyG2}CdFC}>UA}?mp_Y$g?Gnl))ns%my&k%`L*ub`zD_&6 zy!=+nh&(05mY#>wTmd$ja$<;?%92kE=aL6GV{WgCgiR83xg7SQRel%Z7-Eg=~ro_)kW z{`uLP81FRBNt6Vm^a=8Pqp&IcZ@6)axLMB>KS~vneQYmZ>u!mE+=sqdVs|jrE#tZR z^{8a@otN@<{<7QA=vS9})Q%S%1E|;YuLYXrD3NS75ujx}mrBi~midmY0bcxO>6X7R znf@ZYVD_k}uzp@n$E^~!`c~y*?3MNyo$0;-i?id#RO_Y=SZfW^R-(eeaWQlhZ%&WVwQ$q5X4p#A3jNYE%WMNNUkR(i55eETd{R=G!<Cw+mt^?nF6DGi)VZEXk0?I{>52=07uJou>Qy3j;Z^ADp=!3ndGr5o} zqZKkVc==xIu>K?;K&ui+r2-s&2cQwr?^*Bvgm@_Gzh1krL4Xd+4_LpJ7i$+yWT76~ zkF6!ZSbX7JMByfyf~uoPUzPkr#=iB7Qn=Pt0i?tID}Ld>NIC$?P}f1li`=N|!~f<_ z`#TL*Jp7Vlm&xHOPS;bueN=?4DiLIQ-zxF;6)nD3esuydY&dzgUdyzf+RK71+iF7k z+AGd+z9`_3tx8T3YPNq3T9@I$g0k zwTvP`D1mn^z7LvbYiNU!G#QbQ*)Ts}Xj_HJP*h4S3tU^{tTij#3*y5!E5*5WO^v>W z^1Yk4L#F1@-S&RukkIzqF+N>vh^@C)nicm5$AITLH>q&d_niI_&R1NO_j4y+5)0n? zG`u3Dr#DOAwNu@xZ1676z79`U&UiZFHyvSys4bt*YB5Ki4e~IC>{FJpw;L_*S8&h2 zym!BLbEAd`TP?QcTgAa_K&@j=9Y?Xq_H_FFY+L2fEeBN_#^5#uL1Emu_1reDgj88s zO=Phhxcd^W%nkOO-(RefAvm8b=;J+f9U6^J^hdH9gpzS;##uY%KK9$z#mR;#LtZ{x zkDReVu0W{NH$k*0hvP9$&}iP|IQ=+A{hR%HSBk?4*bFKS zX%}v-ABXwbBYx<8=Tpp>hXFlo;(J}zMmY5D>T&~82oi1aMg)dvZv1LLu<&{M@1wqX!A7nmCDB;YV-``KYB| zUjP_L=kRCj;va$=zYy+~6>M%S_H54L>@d;Sj!mylQ>ComVyJ(4!3~hRDq`YCv9{EA z8n5^{qZuYE+I&H~*Zriz4ksH5KBp~(4MfjP#nKT$|SX%D|L_un^9TX+gHWdmnk^9>16Tq9`Z zDb8beqhfQRjpz>P)!UO+8%hk}$uqj!4i9>iJKveE=AlcAoDaNVz6&oMiP038Fq*7c zm8?dT_o*H?MCmO(Ec$$F27dW%_95x?w`=<(ukXdfJSl{(U-$VwrsokRnH}XsQBe6n zs*_&zT}S5z*Q|ETrrUm+|63;peMnMI&%SLrcn!TU1|h{2QIxouJrglE*CAyslG1ye z-6EQ@S;FF-BW1~Lxc-Pgze-3n8`mum*xO_kZuu^WJh1u-HH>8H2dnup4+9MHzeRft zZ7$#(Fu*S9FR;_-3}C(S6DMOEBY?ki(YG=;x~PE#+6TZ+q3v4AFL(z`Gf;O2=u6WK zL$04La?hxfEkll7-Q=#_#$V(B?^J3G-Rj!^EBzy#l?lAoDd;zHby(BJ3`xZD;4s+}tYO<=358Sqj zX?U%U4kE2(Iyt6ZtxViocM~ZHsDTas7OX?zk!WO!INGF%3mdqDNW7}f%7(e)YEgOJYEdrPmLr?*>LL0b5%;WZ=O+)2XV ze~2UN5Zjl{(pWrwo=&>2Ed+siE>8sWx&|vo85bXe;0x(HBy)`;d$)sU-m!>Ln=#5! zEw)*5CFf2?7xpQ~!mwFyVwd?-KBltrIF>L>nD@EG(y(|h_JY2gW4XCvpkN-%;yi1Y zHTASp3VvPLyw83AZVXQc@=I;Y^~IHm(8$TV?Inb5wRDd$aGh+&?&M=Kg;0;p*f|yhCFi{{_Z@DIhvXvPZh|v~N>psDPc|1FNF^RFrQr=mrbcc`Z#X}gh@gA)+KGKS0+GCg{+%8|69hCEvV;=Fa~H`ibTNXOf)b>WAQ(Vh)SAC$|ni&9o0c9V6yKaa7{JMVQGrWqZC9`AY;JlTg^%ou&2)Lhu zAHoQ*$Mt{%_C~KqLblL_yGN%b_%ME6`j zt+%R_*5nk4SLqg`=r_yVAwxV`I2f0k2*dpv8QY*I3Y@_>y?CBImdU?!ne8b{_XRv} z{=KpsQIPPTrPH-iihAYAhlYfNg#o)|cIODgl*U+(%4|62$)F|IRAq(Be7*fYhm-^Z zi9wLTt}0KXWNK8Gqh~yRRd;DVN8NUuSr4}_HpDOwM~=h=LB>b}nF-x$IeMgZY&;lZ zU6aNx1#qdpigYDd986nq^1N1Bg#I}FuWZn32+8<*9{*J!`4zbH0EJTIN6U%0@-nI}}VW9P7^qfjlzvd%4x-b#3&2B^U@mzs_Q@&JB261a! zRFSY?!?P`Vg11p)ki?KX*2tzrYVTmmUiTe-L#Z&KmV!z^)Xr(b>4H-jl{m)I+UwH+ zPqw}Y9SkY#v}F;oQWP{B*vDs|Z+?uY>POQ(GSgu@KbQG37AMmPT~@4x;hagS zORjy+Cg(FV57*qPPbkd(9*eaOAv@WY;k&3G#vk2la%d5wN?UTQ`C6gIEJBlBKTh^oN;;Wnxv4&{Hb-izZ0vPc3f&c>GOzM#93a>zcSD+Xx)2+#S6`IMcr2G#E zfWiJS5wE>!pe+TN1$ACU2hH029Amuv=7)T)>*=4zGqgU68+#(Z)?BoEQ2h)Vt6aJ_ zQWzG)@tF@2CHX}Qny((3hQnbJO6g{!@2rQd_BheOWhk_{ND;JsUwtXf8q;KX;71y;3{`6%!iW+;3Jm`GVb(qvU^h1xXH!i=EBgB z)EOyq>gt(JrM-f7Li}V(IReM`yw%n-&@;3`Vju4@{xm=nV;Mp(c-e4TfzYVpQ%BVS4$rOljs)s{F4JnRDtQPoU+(22B5z|qOS>l zfRthmR^R|?fnOuhx10ad-T$2!8NudJtWvCLdi5xvTU)W5SJc=~ws$X~0IBkfJ{TH%s6jXo|JZ6|`9?3sL>M=a>#PH_Tiey>iRB)?uMv3OfM zEgXZ^IXNlv}4O{}?s#F83FNP5$A(2YqgVHa{Fo za=bZWrH$fMy>UK$hzKI{MxTkcA6daQJ<+vzEffFTBO3PAw75fnJqqvGpl!j6EXw91 zs*C~LZxQ)JFULL%MxNWBL(+FzX|w4X)fw|WvBh(7JDkauBE`QK6}R!_6nFcL{1Oy~ z%H+!O?3p06*CMe_PeDtff*J4OIun9uU4@#KftgqRMNU{yW~o8j9r(xWyB6Thsr0J+ z-@v$rDXqT97uPvSU|7X&vrlZM#C{TMi(zq?kcK#dSUDiw@R~|cDZ1PYJ4u$jfECU*-*_edFVXM*6kbn}Nk%4=nbK zABw@Jk1M$ECB8+k=$#Yp0_*VBKj}&_Fut|+0p0dr&zbp77Y2z*_%mnbI$Z`Br?l7) z-1|oy&`;zV0J48emib>A#^1?;G8F8gWl=w^s>l2FdMEZ6dF3-S&q9k2h7QSi=+yO) zj{T;Ku;W?v`D?4_g2KXPG7yjgaXq4AO2a<6D`~L=F&v>Lv2wZ$&du@{X75;>k?$$$ z&u5UmBVDYT`i5sThM}o@qK?PWBKanZ^Tg8kZ6&v0Ct+6v+thnzyl-@zC11vC@rp@$YD-UDEV2n$GrsOy{UiDye~Zev(*NgyJW2bHFQtD>RwVTCI*qN z8}B^+INxb1U8B8G4bQMav*p(2eeceGJq?Go?i27jY0V69=+qJc>dwJB-l_UUDdT_;`159{y1!VNL4 zKBz7@MG$As-rc*_3;x*{TJ{@D`NsUcVru>et}M6QpTYrr1&E&oSV_Pi-v|eE2JT#< z1O%u#k!rd;#5MLLh}$;MKN-=={x7<-{{0x*f2GHNY;!jBxQ|C$QKMuq@!%6d=9Z_d z@W8_3yf&r@*OId#nXYAkX|(g{{=xPs91BpYBgUvk6%EZhchOAueCQpnx#ZqI0+}!3L zw|QM?<r{y}5`8&f)PP~FTwB)Hb#M5|=lTt72OzFXe~Yd8Dv=I=tQxUD0@$_5P&z z+r6Y9ZsW{<8IMsZ3VJfu$=ZrHV3iLm-}foKEg!0~tYpxWJz#ieva>?hBUT z^E%8PJcxtDXcHS#5YCNAODD}adinH|6xKKS)Vx@=@*XaSW(bfeH9{4V&UTUEQ`?mo z#BU}PuO%$USUlHe+Y+!07Ay22+7 z8~`tpqm{l3z$SHt-x!$#LYGXR$q@*6ymmVo0u|4%&t2TeQ@o{=Darm2umB5`$j?FiI0*z1i2WSNkKATROfgxL$k_yh1!64SUA|eBZj}4|08|=o#!b#ryC4@GFY3@8tdp~m*aDem-Z?? z{%0i;4?cE!NxQ9alw zCdbYoUkDrbqm`pT8}|5Fn1e3HwqtgXY3DQI9S;_$r>$`2gt^&2ho6tGL1F1L3j37y zy2=kcR6f&ribn{gVb;^%eOiy1nN*K{r+!jx(Cazw=m7P%5P2#lRV26j&%rVB?-YO= z+jtoe2L;W4?y#)QV~7vzQbI>^Bu1>3h#3{(9TKvMFT3n{R%VkJ6y+Sr^g~e3!Mm&^423?C%q1^dHAUFN?vkbxt?J z8NV&B=0x6T;%-7(NqxQ%X)>EUJo!ZU2x4mXogMarP%sv9(~^A9m%2*o@3|~is2QBH zIHxhud%`PbBD>70?6qu_R)Zpdn_bP0G3irXDBV2jK_}FfjYB%W%teascou_TESin% zbqoWp!scCx+;DJMo+qN_#fcu_snZRa9^sz;%k=zH0KxCBrQ6w&waLd7mCp{B$R-iW zhU8UqnQTP1{;+_qNnL1QbnHGEv-JhoOr_!S5_*_agCDu^nd>lrzLYq=tKmwQ2l z5$0`;j`cOI_VsGZbJ{35{Jx@AkB)boDAvIaHibRe)<*zywr`7#I~-AZSO!6uQvUnq zDGrq?^jAfYHL84#L2(p1KAlGxis7H{nznr7lsjw~9<7mn$~R7rl1!NJRlk9+`%ayk z)eB17-HY*R9cR+y_`=eo>4jG;o@b*HHDfKNFAS8M;J+GH#$~6RtSd5w>tU3fAL^;@ zplOA76Ntd2WD4X6{$KnyAZ@)3onjV`*FChLOr5So}OT zhry#xaOsB<;JjMj4{L?8bIJWB$-cYZ7X^ES+Psw_{mvHjk%VOFR>$sJzQxV$1kD&s zc@kCLjmn*b<^)RgrajRRC!_d2I_G&}j;1lLKyD>&PQGEQ^%F~!ku#_@1=n}R;BFyE z9)jcsUwjSik@_>lJ`~bsJGa^R3%={vAWTBF(_*g<8|(E{bYG7m zaTzNMHdWrGBg*?Cm%3MS>yp&5bg(OL-4-D#)%fLu;M$x_C5%Cp$NKyJ2eW*B2iC|Gu1t?zcurNoIVzllE(Vf$qW>jM;m{4^q;jfd>WACh5SNbxVUhG>D zI!olN$l$K$zC*kg<-bAi2H&dz+B@62=mWBv%*x!_+{xJJy8!=r`nz#W#zJPwc4J{1 zzWyb}0lyJ6;5UMlx{=~oZtA*BWq0vqm$+a#*fHs96W$uf{b4@-28xT^{~@M<+9wa4 zZB?&_B6l;CA0@INF|mJMx|ZO8JZB-3yeSt8RI>nL!njzu*sn510WIWL^FP?r-wE%` zwg+fY>w8v0FQC$b8(Ch@FfOAPLGcr+bbe9?5mUYB+6kVExVz9PFdwk0P$v1CqZ$L`OrT}EJBKR1`^U~>ErG69sx1|u^(^TVpg%A=IxkO9Hj_NM;M<_AK zwY$TErzbsfD_sEnNhGch$HuGqW|Xv!$j3{e+`*=&g1sdlG1H*&l7{0BtDe3%ryL)i z=%$ta_?$l#E+<@@f&Ak+PqEUpx6d#Eyi$ucG}LgGOmw(Q0CLvxSTB~VW(+5+f2ttN zsiKbIt9-C;dFzUVinUL993bHhS}XN%Z1QKRUqYJ9?n8|VEy{<79N4Y|X3_@fKy@C* zw1Nb>cM8p0xKnd;6GjcD%{#yo$_Kh1vVTS@z9f%&6fL8*te;Xaw$9)d`A~otdprA4 zRFT%)tCJXtl~8f}+1O+&j|>5UBrlz#HXO4|opMCVa}CsD3Kz!yEHvWfaWTr6(2O32 z)pv&}HR>eHpeJyI%GtF^l$&s_MDg1CqXSRUbhBk*Q+CLUrE%a2TlMK!`kl&xkEUK# zg^#YFvEJ7WC*$RpCS?nLg#2Lu-dWV#@yycRdw0HD&budvsW0Arz%e^Y{yx>2&N5V+ z17SWbqqIh{$2%=}vU=ZVjS63oiF86m2+`|5#eR5hbzm_c0h0t-Hb}RVC%Lcm-xA(A ziIe*}*=P$T;u-w7M*Q30c4>ouSS;7V8}C%p`CQPWSJ$=o_584I7e=>`_FLBC28(wS zK+jF zDLDT6S-T4YHFA+B1X0llpDtpDKd*q%LVE$DeCz%0_w+p!cq6Y$9Uubk74rhc>JwRJ z;S9T+u^RR07&Xm4;uM(#P(D1|PbYK@F1rzmGkl`W9VQKm-ukkEf8a{Mf_L9k z*^1fL_~e7YCi$k$!9n!U=v5@kI86LE$rDtghC{evKSM69~UnXkI?a zv`sJ?xx>(h?S+)Sc(9d-Tj4ZWM&tT28(JB2H5I0iHKeoF+U92FB|5yDUgj;me8KY1 zu^V4tD|E$ssK4vCTDNN4r#IAjad($dU`xaJW09uNOQ~dk>*u5}4Ez``!ckMRsdn=t zCp|vO))^%j#s|HWc|*iR^_Wd>rR!RF^ZXTVs&8Uq?qGfGH~lWRZ=E5ta{x*TIa}XI zZDG-0Qd@#K+qWm*Rs62-E!7*qL$Pk^7$P7c3Sako?I`_O zpb81|)>*O#H+^6G=0PA9z%2?eW3h2wiD;k&;GH+k|3nvmC!4!5=8JT{3D3?RW`r#r zl`inRrL?@?_4UYPW0;8~(=2DS{S-IFX=hoPwnl%WY zM|5S~Fc_psNo_7R2$XvP;{ocUZ{LEspV&y$&2nc#w?zp9H!WF>-o#a(oCTJNS~?wX zZ+riaeMD^&53IE7`3}?580bhwW>?Q0(RPmJl=ml&1$XyG)VAs5X>_Aw%;1?Dj%Q%j znNkEF7K~DKBNT8;S=F3edY6Q#Ej3LBySX-rewOk*df!HAP0~KfX{E09WjhyNncxs| z%w=^UqLsg9TuOGa@7VWgp^HSCh_PTUNhd6%h6H0n;(q-^wF>!8(~KNg;u%&vEnEAV zsHDUgTr!RxUy9Gz>K6CsO>h!^DOG9sUO6tX=bkg72L9`V_$o>=&FsoP3 zeDu4+=6wSQ5Fd3vVs)))(KA=&xp)ENd3Vx?bE;KpD~Qx5k0P3(LF6Ey{;g7sn>-`I zg}<%XJ3ery1d)$mi%gcEUu`>-Hn)HKTwHiOb5!jfR%iN4HvfWY(>qM_ktn8U4#i9I zjTO;p-;0+T{u$Q&quzZBru`9!TcdU#%|M=ax%6`bQ3WeyUyZcJ!!#GVNcUnj3EYW8 z#iM$$H8`<$yr3~ezc;TiF2Gcv^&bn*)KWd??|$ERX7`-gEg_JMbIuFZ95Xb}nKKD4 zzh}O5Xi0stUU05ye}i4nTLxMkmjcUP(%PQ?P)MAjJ9zp6$U z+-nl4OheN0VKS!CQc?h`cJs+XPG*d3AdbrJuDsA20p&*ZK^YWvZ*k0dByOQ_>pW$S zv&$<=vn}w(B=+fnld94&-FOH`y}n9qsYoB2Hpb%!r#6=i^FlKn4QAmbSYZQ^3RqUja)mUDn zZObG=#kJMpVcUd(R8foUfgBqgotjcTEby0*lMBUoae$JEl?wt34IKH>=*4jFgddT ztCWCX^tb2qAL|<6&G3ovM6YGD{GVj=Rcbs?{02ax$?Po6Z$J`bYiA=f2mM=hw!h!; zJ0XPOjhbzHMD|l)LjV$}&`a3y<=#8$;4R9NBvv6OrK3oZMwp-2r z(Z2rya(9uZU+zhRM!a560V;<%@UN*7v5)6z8CP7Ys5se6}7S29cM6v`C$ zkAt0OoiFEI`j))|jr6+uw)w-VfF6)x9jb976tX=0_su=lpXQ#)uM+Rje>C^R)Yz#s z{hy?A>$G7twdbmcekUh{@dmr>+j|sHwVu(3-fn#@22@5?CI}RimY<# zYH++6wa|MK8z&Wu8-tib^^lKu}#>_hL_aOot@3;K7*UQFDFcaT2Wd%RRM?VaUq zS62OiAA>e>2XeISy^VpWj-RcBK0R6@QHy24u7N^zTJg| za}DhwKYQzA=@LP{riOQZ;AeRonl-O<^cYw&E4bFkcryxF?;;Jt6OTWS(r%GU+I~e~ z=DDeqzmCO`Gyldf14-(P=-^AcOvC9!guP5JDX`(B)C6kp1*z`jsfiBJmqnDQw0ECr zg3nTtXQ_ilb&XhHZecEA-0_<+U5&4PLqcK0a4h_G?===|WS9y@kNIIkh#9kzPUToJ zd1ulpXvtKiH`S&TzfdJ#9A<=)_rr#jU;~`{he8jU%mcO1)smFUx3E z)a4UTn=w0)KJrYz;8CkB;V5<>=L_Wc#4_(eZqvXD=S|ESi!Ec|FudfN)iQxj=mKsE z_fZ(JYXjTnI2V5^yQE@~R#Ej1MA%&{s*`pRh<2L3p6HJ@+}=uzD_co5@rGu;CmaX! zp|A8fo_2Vd6X^p^;Eg%N3PC6|abxud+!a!LkLqyt_>p5EdhD3M%lNV1{Zc9Z1*~rC zldX$JyWXa>W>21LvjdKOYPY~27R$A{7d!8FPZZwA{^i$=n$YNlht(|t&L?!*eJ#nU zp4!DLuNM%$u2qj`%$lVb5YiXPRYyT%`fN_tO;o^ zk2K8_Jjswmg;O+-9^r@1sf2PpM0C?aAjuKA=uHnr?bvgJpi+*jc=9;3U&fF4^N5Y0 zJ`-m%2MTS%g$X;KzJucz94$#0Woa-Eb&N`co#2bA1~zAlI3kQyJsn$O-+ofcfgt@1 zk^r$2-7*6esuabft%skod=hzAnMu*7mHh4oKQG}x`#_;<@k%FAX^#~G9+acLiwr*D z&{O4Q?%8$7DL$xgqa*k1l}`B@icWa@*Eb)xvQP-www3X|p|OCw$~!?Otw+xBu2%P3HK48fHA11g819v#I}Rea!^ z;QU!;)K8|0HqFezUWYP{pI!-ew31^$-#f2|l!nmkqh4K9=00?P96_v9Eg$R&!`*xL z__)#`l6u8e@c@!YD*u6QC>h9RFF>?JATj8Z(iwTD0xCsHNW&wBBS``LP{y_3h3A{W za28co?ES=>Qg`^=woJOC+|2{$d?T)fH}4OX;>x{3Vd!oEP($3X+5jOkKiGACo&H|C z_&OBk#@?fQ_fvg<%mZw_D|h3Q8}$JZ3g`k195uL@QR?;tyb6KgyOnM&daKtfCE-W) zI!v5jSIE8xX;8l^ip`0*NqvBc6w6?MtTPJv}tBIQRIwWMl52ed} zB*I3VBD0VnhPKZ?MK@TQ?E8v=wVP05-I+xL5yK|jyMJWa5MA$yl*hd>C;4GQo98^V zVE-M1)0Z`kY`%;KP5BYx94{Xj$IloB?98^i-cK{=H*~}qp+}W2lG?2)uD5zs0OAqS zF7IB{rcv4*BBF%TFMNjI1Z5z!Ny=^KiUDI^0K(Izrp>#NN&a~t|LnhyPWz?R)WLZo zdkmJ6$%Ux0>I|3I1T)&Hf$jl%BM|S(4dMi`XR>C3LYT~)oa}&jS0E~jmC4T8z{=c^ z@p?X9N4x@2u*`-|!0#1U%mlQ4i+K6V9&VXZz_h!Lcv0O!pv+X3k5BABeljiI`8EfP z`hXO(q&_ED-TWn}>^*iE<5K?{e~n{DN{s?9==}={K9o_I?hl-Dj!;o6l@!ab_L=gg zD3==KWylI_AgD4Z^h`a&i5=iyv1qNkXZT49lw~2pVhKoILYfmGA&Px6q%QES?r(4j z+M)z#a7x5|#^-C=*Td@G6lR;=QJknZ-sO=q%#3x9Q$$ss4Q$RBjV@4f>>9WPZF)g9 zycZ*l)3t}EsJ9XPOav*PPC%xA=FhdMbB-Wh^6nF^%1GvCz0~}pr3g*>moR!*>5PeT zMRC~a`rY8VQj_Yfsw^1t=zZiLYtUnjKfK5fc^+!%0g{iZ?jVYOt zE%5M*Bjz@{>#eZZYyYdMk)i$#QG>p-lNmrVcS|y)#s8EH&=CL<6beWOyYEPlH+lM8y@Ar6r_Ag_Xt0gjJNK6cnYE zACdhGk^Lb(kZ>f$0093QJi59KV4fpWR8Ua9hKkq$dBJ(j|H$zRD&pYa;Q;s_|2M|) zcZ%Os4$f|$lap)e4jVEbjE~BBtzNWC2!@Eas)=A|z?Op{%JcA9_PY=dA9mqiJW>wo z|6*AqSGm3>q0QZsUBHB{))au9ofd6fnJj~_xADZzP~N?}Ub8h@yJ}Au;!ar+Z_cJW z!lo|o(#4J+e;Io!K}jf$r)qXU27Z~B92U}trguh~SQWkZUrHG5wRKL6XriXs<0b04%KtL2O9;bKW zYG_L3eRlYRyNwzq43)U4&q-X;WlUIv=kJ}pg4rRg^C6Jn{AyR@-`N`B7Yr30`Rw+ro+iS- zk@Uvv*l&K0IvHLLTc?3Kgoq?rup-#d>fM{lk*S}^E9{IrcqhS?;VC)ERJ;qBso5}c z>-;1syhY05prd^*vp0rcy%I=5M^-r*)wDmaZ;z}qU^5+k{w2g20pI8rS;~&F(Ni>J zhQ&F%FLHe zUy9!^3A)%XlyQG(EhWoq80DVvEBa6i{D*&sCH-Z3;Hb3P$AUpK^(JuCJNR!REA_=o z(YFjAm>-k+w&6ryq;DV=vr4**nNn@Fda}bNV__4%-MAo4InqfquP30+ zMW~6rr^{IyzNuCy_{l#EPmL$oi9n=xT@zC``UGzMsZ->kh+zNA^J|c z1Lkz!y%>^7jrn?s^VN{kfsbS^9!>cJhhB3Ac~#-ta{(q{^1L9@dwv6Dv`t7l??$Rr zU%T2@*E(lCC=wFIeMvVptQfA@XZTh`Fq6$gg6S&)nS`>Bg#ybLe;*MiZevH%roh!W%35@%!Zl3;<#a}*%NLaN(Sve zXW{ag@TDqSZn?M^vq#`I_BZDR~2XoAC&4T8rN$b;&Y zzKV96gQ=x2MpDc^h7khuhaVau3~Zl&!)GtCIBC)gUE@NF6Ycq&B;I?wUMdG}da}X; zi3i5mS4Nw(1^2k5ZJX|$ldp$c@}pog8!bv!*>?GkH4W<}yLMN!_H*!~;Hg3}G^8|oT8o1ewqt2Hz3{iXFL;C6vx;Tpyak69bgf);GG)19pU+UNS`T3nL|F`c zdsBmDgxjl0|D3gbZ~Jr{QRHn&y)f%E(%YS5|B2>E^?S91YtnStcGE0SvtO%m_T&)c zVGFlp1axYBb7HzGXm8}-zZeq@Iro4=byTj|WH)D#kgzMUT3}S=Hx2PGZ}{zDPP*&i%$2U8ys;@2!{ug{o?Z|a zWN7468s>cf^Ob3>TTR+ZpB!%js?3f5F0Y?u!A1o(*82M=osHa9i?LQ=mqv}Q)M}s( zLaLmFne}-|ui6(>aL}Ml##Y33@%9IeMf)2`;233np*>0Lx{(O{J?j(k@z!(K0^;9_ z;$Zs;>zRf8lm&l+^_UJ*A?OM|cwA286m7RcW)5Rm5Z{&u|1nsP?KfEOisJGXtoOb7 zpMmwRd-^-=u4VuQ%{1yWR$>7w1A;22ubEtLjV&)FR_rCdZ{F+D?HZSKkM(fE5=)j3 zr-({&6|eFL-1)=gE=%XB=j5jXSP{kMUVn%v&9T7=e& zoL=gYnXPL+lYblRecOtmZgI?5EIXKJ(v0KB=2a*=T~F{gJ`?Gmdsv0dOhO_)r)k2N zeXGno+D#^L<6dAupmFU`R=8j1=(`ldX zf)`L?8WyEKc0KP@b=c8&2^lc*Ntw@3@=I-a!n+q)PZxrchW#8ML-REy${pOj z;2_D3%j*}2@~RlJS>{ZjzIHgR+0R`HJQ_@(!3YnIS0gB~bVd{tK&RvP)URKRHdR>J zwKXk5%r^=5Vk` zH0Q!PwCL&^tmT~{a#iuy)tu6&`$8P%_7}$0d)U*NM>{Sk#!OGN@vhXnSg87u&ug!) z^FCh@JjyojWk9IJlbj*5^H$6eeB{;H)Zd3Kr2pDs(ynx@ewS!DgnoBu$INh~mMjcg zKgA~`=|%rbm$Shp5YAwZevcF8iseoY_Gid%nBhjtf5($bwt*B_qEjPBk=T9@$2IZPM~g zA-`8?Vd69aCJ!Z2;60Z3zp5?3hiczTdh)K-Muxkld-}!eTmnPBZBr%zGRxE zQjqx5q@qE^+IPT^&SZ`^sunCIG5Q9k&qv%zV~t{cwhPWr!H^;PlBp|+2bSCOTn9{q zbf1;RJuv8KWw-MoMh7AZ6eW^!o*c9EE1A(c8$D#pbIb5xDx>-*C?(XAn*75)(yhJg z2tCtZeNb@W#-$QR?4RzU@(8k95P84fqbP4SKVW;2FQqbyibAutS%=725X|xB;!$75 z=o>)|diF+vprI<`w~u0DRh`K@U7w$LEa;_=mqFnjcx~v!MJvfr&>u_P5xgH#s_8ob zll(~Uy}EIHq}b?JUmsdu?{dFGSh+gEG<)S_DA^13lcbyam=d?OJDcwW4+hTec2``k z`i_h|+(0R8pu!~A_i&CMmu|>j*?v!Tdoce?glhwa{3Af66TVUB!2eVcQ~s9zCa=Ik z3?jVp4I&_6iG1%}Sm{^qKmCD7xIZr6u&=V+PGVyLO5+07IyWmf$5rY(5ZLjn3Hnd> z_jj^8ZyF&U=fP6^o=&<83k<7A>K8PZ2C$S`Mb0-=l9d^FMlC5vrxWh)V+`adnCG|! zCO;Og6W#sbhQ1zU_7K~lcc1O?a;7$-)<-p12GHS~ZZ224`*^Xd?GuU@_KbFdpL)8( zmUih74_uP6!XK+t^J1RY3(|5$Q7jcIFzo0x3)J&;p@2&!Yhx&U5It4P+B!*ZLzkq{ z#7rvD#tEzBB2r5izYI;_Y;S%I>uLMg@ZAWj#iX_s(w7<=o<&O*CLSv9^e@?)XS$LZOIuiymVr@6Du|12B2e z)YsoL!1iE#Q2#K-r-CL4jcsb+gJ+m8aSt9vnN_D9*}#K#$8YV!lq`$}mj?9+9ER&# zTlfcw0)50?^AX}8)t+&q5|jN7orJWm?wu|#%Wz(I-x}#*fyE~onIPHV#QnAJm7FOJS?fzAW!Zxgyy2N_k#@hEnHnsy^(Au8+Y z{|nju%T$+acyqa8yM0`}$t;g@LR}ffZ`t)Hu=JMV2`D<(vKx$yQkVlO=B1E<-p(S2 z+`D_Fi|jECS+*{xI5@ddTzA)FUhV4rlZ{78xh(ewEC zaB3ve&&T}8y9Y)j?S!*l2jauswcgWqO$)=qe9KAnbaw@YYbJj+CY3u{%aTwcL{vmq z5FA!XS=-qAYnWqJin0bNy(kv#%V5;nP)`EBXZ6H-1+{6P=*`tMQsX>CL4=U+4Eluf zi_eXgW!}Xqfftg6+N9}<6@959}1`)2-_?SWVUVe8@{m~Tt4P3 zHcQ`X)lRe%L#{*Nm{~y5Zq9u8Y`_O+1D!eNsd%59{ZvbTvZ9&<^Mm154)X&6>>-%E zyQqRk{!P!CyPDgL-Nr)PFQxei0+Dv-+&266?#mI(1<)UNYD{l!uhQzQiI1ccJuEaU z5nCDww^%ynYo59P&S>vp+iG!%#yZP3Op9YXrU61mZ)4)M;w^GpcK@)dS7a#Vh&@! zf@wxoqwszZC=2&RkNe3J$o1CGIuc6oGg^llYhT3~AU0U^-_f(l4fT2qIO}-6b=~1nZt6$CBg9CSW)79TcwjK<_$k^$^hSL7|NHzU6=F~? zg?*#~KgjbyY;Fd5tPb_({)_x2lK%>Si6H@;kaw<*60+-ik8)CE#D_SLJ9u7wOBELC zaaek>)j>0%@@+g}y!)#GPqe|gYD%bbu~D9KXvm1^$g7@aM^D#X{?g%_f=vW!?6Co?q2?fH^UFz-W0dFeqxLt{LC284yZYSJwhDi2Mu zW!gAkmnWQ)xZFWlAL!GqUYQR{s$_${4r=+n>`EfCl*jSg@NhF0&jur?KtfhAVwY-o z+W6(g1NuD$O44boQc|&nSxUEun!PyJqQU64XfSoSiT5%ywz4t?=&;H34fTzHNL=8F z4M0|4>}ag-U}y$}fjK&vJ2^W6%v_EDBky;G&rII|p#CCrvU0p;@%j$m0TotQ0j3mi z9|t>}Z$E_xggPKNl!36Y58p97Q>XMu*Y|uDA1DqkA1$^I{T3W{y9B4C;tzg)1SBS@ zALA6hJx;|Rb=KM8KKzzdM?L5J1Ka_pK^3Va%hnLYjA^<6{>eUGWzOYhy6U(4g}a z8p~qYCMzg}P#xSomB;6xO7=x|bv1$@rFGC$(O!a6*F#cuX_mnxc|G*U&f^ZxViZ!* zs2K{~7~5im)nl+73bN|21IYelO8!zu`U#c%F}^`f;EWcxq}bMH#^c+({)0MlyUH!( z_smc9x$@%~`OwOuU5c`o4~2I0>>0PCA-c68?WA*`etv}&YL`EbTv`13u29<9dVoy| z1n7=r3T)5lOMkD|hzWSYggFNw_~*5hbjTT4b^J=%axmuJr&h5&4ASDOoAsCryjItf zn)BzRwgG&R`i6!s4*KREEWqTxif#nL8m~^>O!b>*S1wCq$D3)Y@vB-D>^o2Z>^NYb zG~Y~9IGwKgd{N)rkAK|TL|8CDXxEz+61=desH&o{vNE@TH7Ph? zyFgtx5kc@*FM`2@5Ldc(BC-J7IvgM#HZCB}1K`%UjyM0_1pO!a_&ZZ1wWy?BUna4F zIngA=Nq1x_;bM4k*<^>5RFdtbSk8RP9BHlcEVh?92fA%@A5xSB9?ARYDV!EGDNe0j z!`1>5-=tnTu5kED6n=QX4RVYasaX| zLJ$)yXkJFDV~GM`ZJnzCPp%3cnNrmk>i!gN5MEy2w9qrhjUf~@en@}bbc8H5QS(wY zYxfwpmD(44?_1;|>9PuP3Ys9~sYe;gx5^FD&K?vIGsdA(mh^O@KD`{t#y(VnwLdRmGnPxx`d1NYwt)f-rwGoHET@|?P94B`m#2B z#NC{>JnH*)MLpH)^zA-Ms&njelOhwo&*erLfK@f+2fNJ;SX};4W8^i zraBE2>3n=T!AQ@bVeesN@s3u{5c$K9^kF1v%oA4JV@J`3F^#wo{mBJqgwO6KE};tc z8&K4f2pS%7D~+5*Pyyf;z}{m590o8@zzHzm8L%UPQ~Veb1O(8+A_)LT z0}}>w26F=JE4h zE1*pVyq75OqY6Bu1D=}#ueZ5=&DFJ=_f!VX83Jc*!K{G86nKxTYwp1DU-c#q+jDA%R${*=!mI-~ImVc1d*H&>N z3q;1J3sF-kF|1|!$6IoACL_@t)A#l3WfJe#eu()9&4kk9KC+i+j@k+(Io0-|mVP>Q zs=O$9Vw@iocNm#fcn%XOcJLY}-ivLBYe&lNALq56vkH|2skxz9-UaCLq+-49<%11((>tPb&V}F$rf=hi5l&QY zMG2%abRfSMFj?Y9Zk>3hwYXToK?ntQbWbDlvuLaC6XK%I%E9Et`(Oyu{j{2&Fk`fq z581L+tY<#L-)F9((WG?-gYPzQrH9jf{;0Ihrz}GmL;=}JVsG^Agc~6NZx#ZplyU%u zh$PoL5_yhRc&%(2yS|cg_D*z?-j=-w7b?yoo^qEaM-<5JrwpKR`?}x9)0O;12PAxy_YmVcOspnE^`QG^@Lt*hRzavg4ZzYa12s63fieo=Ue zDJlN|G0D`g^BRCqMW7bUb*SPsAs?Wi0!jk_XR-hkp8s|Se`op5SF9&KfD2T6F=$|X z6ce!7JAT&^^jdRss>{1qs1pw>{Noy%i!{U1J#y)|QhAgLL8$@b?elSRRO2y*Dc0zS zm9MUq7@oE99ZzzKnzkI7Jdj{m1G9a?ut0CnGZz@R8{H9DE*`Y{8PC^0Gz{!p4AuWE ztM9+5XxW&(C`&+>A(itSX&OFC5SNK$mTX?Lhas$lsnxFR6(Rw3jFZDFPm2=vNzjwa z6*EjvWyHS8@=cAjrz8SKH4#1Y?ft@bn>Y>=G@INiJfb7#7kFAiUlKPaQc@#{o2IE7 zd*07?VngtjOxt`BWIEJ>OXHsmG+#zb#b>CaS6f;UNX^)V(7xF%MLjgjgt#;Xun8F-_yQ#QfT16z=bS7N9?SZ}bWV_%q&}4q(dGkclnFt`DAj=Y6nP`L=rTQGT`_jJMWK zx?*o-1LDglL^%GyGhQ93Zaflod|eUMbWfgz2uvzi@nImHJ8t?>gagdv5krP zj$9Kg5F%X>-^U_CMZtccuS3{Q58&XqsS{xNV~PH#0t&1_0Wv3JH~sH)(`L5Lj!w4U z3kWzmTKyH0iK=`{#K{%D#FnAx&RcA$aJIq=Z^kd(GWl(Pl!n1!1}KJJFm_U zbed`2&Rl{3dHoJ+p@5LCRC+ihXyvO>{;&aHkzl26hWax~83|QMN$xkY-XG=Vk?4Rc z*JHjhOn)2mPx9z1%k|o5-~lh&ldrw`^NPK#J^tk@eqYkJty_ri)~)TQjYsIYdc*pa37%WfC(M|!Gg~X?4=m9o zBx;Dr!3>()x<0MFflWR^M$~o?>@J1?k+w7;E`q4eysZ6H5RwJ`kl9vDa%ys#^CrTh z;qT^a`z-Fl!kJl*d70bj&aYS)U+-Hoe_RyTR_#BXv-bm-VI(uW0cLFXC4SEND~4G} zM_|rt-pqMKu<~KBrtV?ZW6|o{8`xDGh3fA!|6j=2BLWdzEfPpf%-zkj#g4Q~`Reo2NNd{0L%v+v0MFh~{cFjL_!#?mQf3hl`7i93OlF0nh$ zx#qE5)W@faUze#*lmn-T$NMyb@9Ff`!EOt745d_*s){i1P}@zm*Lrf6l^!|Hym?0) z7MOQ-CkC6#l88ofhL2t~&Z4H%f;U~@urrZc9Xgx{eQEhcgLW`+;5Rp2L~%1bsXPfs zcD`?@eJ;(tc>B_9S`jV(inn^79pil7k3c&2ZBhK!=TCI9pOiiuC^Sp02s5UA^JIHA zqSlh$PBhaTak&xo*LL@7ZGFr7Xh*^T$)l(t(xw#~?15K&zxEF}(0^U~?AlKS4i5HG zM)ee%+>=5>?Dbi2qnvqeTk3|L$*oTnUIvVWM8wSh&OA$u_SL_-}nCU9mn^6 z^VhT7&wbp_F~@UW=Y8JieO)9|>f!<2O}JHH-IGsPee)G-xVE-V&rbD?qg=UQ(_XBQN73gY?eI03g8BBF<@5{b(^via`3#)tK&dt)r@+$sh zH+|XuF~l05H+o;c+S|%t?doxuX4+E_VuOTu2s-0=>mD#1wm6)NdmZuLABeYyjgZ!s z0tb5SL)0NG;4AyyB7xwcfnpyPjuG6x#c#o4)ur$|Cu*3Y{QS|z-^U~c>I5Q-r4D#+u&J!Q4 zeT~y)tyT@7s(Ri6So1WZzZ{?x!)_qmauq@HBsD)ZPcxh$_-QtYxi0E18x1#zsUL9j zrU*iDZm_t7WhtnOzd4yBMQ-t&j1JH8LZ`bWnH7h=R$zUad?4f=s-15j!3q~F7IU*C z>oU-2R;FW_yR^q&XyHQ3Luop{nF=$B)ipH@NoCs}Y@%`fK1BUJ{3FuURy(e$^<h6Q&%F^NhM2cBFb@Z`UQ;UF8^EM$xaWdBHd z@E07G(#zGsI#e$`nusWAnv}nW@yxg3Ut=NkKWnBOQWrAA9ToqPVMR8B*`=gyf|c)+ zru7DwrB&aDfC?7pRU+0Nz9EC?c|MS;tf0A9=`Co^O+9wy1z60@87Td1oxS8h%U5j_ zLUeuX?OWC-!;cG6Ns5ag)HaD1a-8baj2}2$_(*>5B06g%11NX*%ClRMPbjpklt^#$FqnSS^0Eu%TfV(`2OT#CXlgXL%bvDFl7U2oC8q zTw12zCa_wC9@C#iGH5AQk7}zVJOSgXT&&C;FcyP+L;+jQZ7Yfci(Ru@XD!thONdXh zo}fO@H2Pd?Ar$#Ta>d+^h%znIz;fG9YH??riJ;Q;p^lZemu%(+jyv|5iyyT0JKMvF z(xDiU0yrzlj~oq>)1N{fiNUwW`wYbWnI37_x0XKcu55dV4x6t}sJXY!`W||l%d3?{ z&M}`C-7#NfGPYyE{Y2atSI?=R22KPuN~H$Kg*xW|6+-=qzL^qfpK5>Vo&l7`iS2;6 zCFZuU%~#~!`!n{I=68Gh5_>?~q#N0n_@@N(H`4hlE~;!76Zg$sv7ZZ}mvjpG#8y`} z)Wr1>#TTS_HBKHk`YK3~AlYH4U>S&n%97|`px9LSe z7I+}D$Eg{D^0!_s{gW9*$3>2;tY>#@jGi_w%?>4!)A#s-uV;A4v`!q&sz1n zsjEj58~<4f#Z8ra>3^PM{qc!^0$CzL0th20j(3wMjfIWP&GKu!2il#h$R2HcU$hup z=0?254fhI(mh*M|R(&@oL8hEB<~dv5x`Ii}xq{aHP3be6GsjZJ(D{JF1bFy zn(Bs_h!|>Gk7tjNQaVc*lB1d@r&3xo1-1X{M(O~+{BaJPfLLK|b5N3qSG~7;^GnPZ zPUUZDPFqaYY1uU-EsT`h&-KEAWhk9t<h{ zJMI_Gjs|66&7UpMjiQGr*aOB4k|;Tb(doxGr?b^xNDA-FTr17=PAC@$3S&S*{`wG2 zknu{lL$po8e^m}R0PkPraehT6{(yeY?Gm%-_3M-sVSzwhHMv^nfh>gJTdhhD!7*V1ov`XtTtu3Rm2KJ}D z94lf6GaiOQE08eig^G-GS0?Qm@I^K#o4W5>f@?O|W8nP=a&s(i;rr%KOL}UT=TToz z8FRcYAv4&7oz;tW%O_zof>rMY8HuAr`R88KKh%#r*h*w4`fC%Z>w?&?aPeC@u@hz^ zm=Bk;7tLN?MnaIp(M!LF4Y?!a_Rqx}!NN>?Q(FMdZq>+xZ9=M|d%Ep`TC?jU*!%4} zPmk1!{9ktePr%(g4R`fl;vt1)2XTFHZ6{n$O3oK;fZnlGjFD+?v(g$le($8z;*GfWcLy+>K}6$UG7cue?qpq}n`_pWv{Sd(EzVZI_G8~}yIJvuuMFT#@auQ(q8kq``u9|iA z)s(+Nnk6gRYrf;o^iEokzo64k@M>#Y)W%|Md@_QnZY!&6M!IIrV>;e8#(h9EOpnho z&StU0NU{A>efh74mV6Q;38-I#H;60mql~FxXLUE@vf9j%uljC(sFrYF;i=QUHXQ_e zcTFbwr0ex>Yh7dAao8@Si!WfLRQ2098sX2bksvrlT2D_cO8I1R7~+HQ+_Sui&r2q^ zdgc3yd}9YqWmg7@HmaNgnrt4>sQ*UEzZ z@Jq2)29rQklcpvn_jW|1Ox3a1Cxftz6Z%GyJA*?0st={baO=i02yILoQ8>wiQ})g0 zt9Zhn_lXJV^zI~wV;Mzb^;=9v)7H(WsGuopAI5PUrB)nILKR8{ZMrRR69FJQWp>h? ziL9lv$7L%wqrL;!q3i-~&#DN;eS`_7IDPXB`*qkOHpx~Sn7(xF;j;fxTt?zG`rV~9 zTbHX|0v^`8S$lWwH+UIh?~aF^r3>B)52LfSRA`yb8 z8WV7ZMlaBEd=Wk8uoi%M+AN35+zD}GOKM!ZJyn?jqgSdk>IwKbU$cBdV zl`zkh&sB+5`JM2F_Yt{nBCPnt<(l)_Efa57tny@kpascI)d}q!Xox&&XM~zg(H2B* S6)t?A2q3OFC*;%)llTujW+?gq literal 0 HcmV?d00001 From 4c01d0f3047bf4ec4a4dcaf2930635fcbc6229f8 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 3 Jun 2023 18:32:51 +0200 Subject: [PATCH 20/51] Bump to 5.12 (cherry picked from commit b4fbed33fbb6ca6dbf541ea58a59def3046add85) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 19d5dbd2..d98815b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.11 +mod_version = 5.12 maven_group = mirror.normalasm archives_base_name = normalasm From 63a8001212b1a47fc7dd8c5bef066033f383999d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 7 Jun 2023 18:13:58 +0200 Subject: [PATCH 21/51] Optimize iteration over ticking FramesTextureData instances (#175) (cherry picked from commit 431a044875cd86898cb8f4ee63fcb0c559f64999) --- .../client/sprite/FramesTextureData.java | 66 +++++++++++-------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java index 322e3320..05e2b4ce 100644 --- a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java +++ b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java @@ -1,6 +1,7 @@ package mirror.normalasm.client.sprite; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; @@ -16,7 +17,6 @@ import net.minecraftforge.fml.common.gameevent.TickEvent; import mirror.normalasm.NormalLogger; import mirror.normalasm.NormalReflector; -import mirror.normalasm.common.internal.mixins.TextureAtlasSpriteAccessor; import mirror.normalasm.common.internal.mixins.TextureMapAccessor; import java.io.IOException; @@ -30,6 +30,8 @@ public class FramesTextureData extends ArrayList { private static boolean canReload = true; + private static final Set tickingSpritesSet = new ReferenceLinkedOpenHashSet<>(); + @SubscribeEvent public static void registerEvictionListener(ColorHandlerEvent.Block event) { ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener((ISelectiveResourceReloadListener) (manager, predicate) -> { @@ -37,21 +39,15 @@ public static void registerEvictionListener(ColorHandlerEvent.Block event) { canReload = false; Set> skippedSpriteClasses = new ObjectOpenHashSet<>(); try { - if (FOAMFIX_SPRITE == null) { - for (TextureAtlasSprite sprite : ((TextureMapAccessor) Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { - if (sprite.getClass() == TextureAtlasSprite.class) { - sprite.setFramesTextureData(new FramesTextureData(sprite)); - } else { - skippedSpriteClasses.add(sprite.getClass()); - } - } - } else { - for (TextureAtlasSprite sprite : ((TextureMapAccessor) Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { - if (sprite.getClass() == FOAMFIX_SPRITE || sprite.getClass() == TextureAtlasSprite.class) { - sprite.setFramesTextureData(new FramesTextureData(sprite)); - } else { - skippedSpriteClasses.add(sprite.getClass()); - } + synchronized (tickingSpritesSet) { + tickingSpritesSet.clear(); + } + for (TextureAtlasSprite sprite : ((TextureMapAccessor) Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { + if (sprite.getClass() == FOAMFIX_SPRITE || sprite.getClass() == TextureAtlasSprite.class) { + FramesTextureData ftd = new FramesTextureData(sprite); + sprite.setFramesTextureData(ftd); + } else { + skippedSpriteClasses.add(sprite.getClass()); } } } catch (Throwable t) { @@ -66,11 +62,14 @@ public static void registerEvictionListener(ColorHandlerEvent.Block event) { @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { if (event.phase == TickEvent.Phase.END) { - for (TextureAtlasSprite sprite : ((TextureMapAccessor)Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { - if (sprite != null) { - List data = ((TextureAtlasSpriteAccessor) sprite).normal$getTextureData(); - if (data instanceof FramesTextureData) { - ((FramesTextureData) data).tick(); + List toTick; + synchronized (tickingSpritesSet) { + toTick = new ArrayList<>(tickingSpritesSet); + } + for(FramesTextureData data : toTick) { + if(data.tick()) { + synchronized (tickingSpritesSet) { + tickingSpritesSet.remove(data); } } } @@ -87,10 +86,21 @@ public FramesTextureData(TextureAtlasSprite sprite) { this.ticksInactive = INACTIVITY_THRESHOLD + 1; } - public void tick() { - this.ticksInactive++; - if (this.ticksInactive == INACTIVITY_THRESHOLD) { - this.clear(); + public boolean tick() { + synchronized (this) { + this.ticksInactive++; + if (this.ticksInactive == INACTIVITY_THRESHOLD) { + this.clear(); + return true; + } + return false; + } + } + + private void markActive() { + this.ticksInactive = 0; + synchronized (tickingSpritesSet) { + tickingSpritesSet.add(this); } } @@ -100,7 +110,7 @@ public int[][] get(int index) { if (canReload && super.isEmpty()) { load(); } - this.ticksInactive = 0; + markActive(); return super.get(index); } } @@ -111,7 +121,7 @@ public int size() { if (canReload && super.isEmpty()) { load(); } - this.ticksInactive = 0; + markActive(); return super.size(); } } @@ -122,7 +132,7 @@ public boolean isEmpty() { if (canReload && super.isEmpty()) { load(); } - this.ticksInactive = 0; + markActive(); return super.isEmpty(); } } From 32baf245aa7726469d77cef9ea9ebcab250ca4de Mon Sep 17 00:00:00 2001 From: Desoroxxx Date: Mon, 12 Jun 2023 17:14:49 +0200 Subject: [PATCH 22/51] Fix `player.setInvulnerable(true);` not working (#173) * Fix `player.setInvulnerable(true);` not working properly (cherry picked from commit 4ed45d34a0ecfb2a7fa7bec64ce3753930e8a47a) --- .../vanillafix/bugfixes/mixins/MixinEntityPlayerMP.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/mirror/normalasm/vanillafix/bugfixes/mixins/MixinEntityPlayerMP.java b/src/main/java/mirror/normalasm/vanillafix/bugfixes/mixins/MixinEntityPlayerMP.java index 401212d9..77ea0a10 100644 --- a/src/main/java/mirror/normalasm/vanillafix/bugfixes/mixins/MixinEntityPlayerMP.java +++ b/src/main/java/mirror/normalasm/vanillafix/bugfixes/mixins/MixinEntityPlayerMP.java @@ -12,8 +12,8 @@ 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import zone.rong.mixinextras.injector.ModifyReturnValue; @Mixin(EntityPlayerMP.class) public abstract class MixinEntityPlayerMP extends EntityPlayer { @@ -27,9 +27,9 @@ public abstract class MixinEntityPlayerMP extends EntityPlayer { * teleporting the player again (in the other nether portal before the client had the * time to confirm the teleport). */ - @Redirect(method = "isEntityInvulnerable", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/EntityPlayer;isEntityInvulnerable(Lnet/minecraft/util/DamageSource;)Z")) - private boolean isEntityInvulnerable(EntityPlayer entityPlayer, DamageSource source) { - return false; + @ModifyReturnValue(method = "isEntityInvulnerable", at = @At(value = "RETURN")) + private boolean isEntityInvulnerable(boolean original, DamageSource source) { + return super.isEntityInvulnerable(source); } /** From 583c8c45a1b019c3b86cf0c14c92c164319603b3 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 12 Jun 2023 17:17:52 +0200 Subject: [PATCH 23/51] Bump to 5.13 (cherry picked from commit 2b9d89b5816c0ecd9c22413a448c79946d0101d8) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index d98815b0..29f99551 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.12 +mod_version = 5.13 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 21d90e02..2c504ac4 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.12"; + public static final String VERSION = "5.13"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From c43b448884c8230d45314c09531f988056bbf8e7 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 15 Jun 2023 23:03:34 -0400 Subject: [PATCH 24/51] Optimize updateAnimations iteration (#182) * Use LinkedHashSet so iteration time is dependent on size instead of capacity * Clear whole set at once instead of removing items one by one * Iterate listAnimatedSprites instead of mapUploadedSprites Fixes #180 (cherry picked from commit dd6e234f7b98e30d8c7973d3ac5ab308f8fc0544) --- .../sprite/ondemand/mixins/TextureMapMixin.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/TextureMapMixin.java b/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/TextureMapMixin.java index f3ca2271..fb9453b8 100644 --- a/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/TextureMapMixin.java +++ b/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/TextureMapMixin.java @@ -1,6 +1,6 @@ package mirror.normalasm.client.sprite.ondemand.mixins; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; @@ -18,10 +18,10 @@ @Mixin(TextureMap.class) public abstract class TextureMapMixin extends AbstractTexture implements IAnimatedSpritePrimer { - @Shadow @Final public Map mapUploadedSprites; + @Shadow @Final private List listAnimatedSprites; @Unique private final FloorUVTree animatedSpritesUVRanges = new FloorUVTree(); - @Unique private final Set queuedUVCoords = new ObjectOpenHashSet<>(8); + @Unique private final Set queuedUVCoords = new ObjectLinkedOpenHashSet<>(8); @Override public void registerUVRanges(float minU, float minV, TextureAtlasSprite sprite) { @@ -49,7 +49,7 @@ private void afterInit(String basePathIn, ITextureMapPopulator iconCreatorIn, bo private boolean populateUVRanges(List list, Object object) { TextureAtlasSprite sprite = (TextureAtlasSprite) object; registerUVRanges(sprite.getMinU(), sprite.getMinV(), sprite); - return false; + return listAnimatedSprites.add(sprite); } /** @@ -68,18 +68,17 @@ public void updateAnimations() { } // Mark all captured sprites - Iterator iter = this.queuedUVCoords.iterator(); - while (iter.hasNext()) { - TextureAtlasSprite sprite = this.animatedSpritesUVRanges.getNearestFloorSprite(iter.next()); + for (FloorUV queuedUVCoord : this.queuedUVCoords) { + TextureAtlasSprite sprite = this.animatedSpritesUVRanges.getNearestFloorSprite(queuedUVCoord); if (sprite != null && sprite.hasAnimationMetadata()) { // Only activate animated sprites ((IAnimatedSpriteActivator) sprite).setActive(true); } - iter.remove(); // Pop off queue } + this.queuedUVCoords.clear(); GlStateManager.bindTexture(this.getGlTextureId()); // Bind TextureMap texture - for (TextureAtlasSprite sprite : this.mapUploadedSprites.values()) { + for (TextureAtlasSprite sprite : this.listAnimatedSprites) { if (((IAnimatedSpriteActivator) sprite).isActive()) { sprite.updateAnimation(); // Update Animation ((IAnimatedSpriteActivator) sprite).setActive(false); // Unactivated From 8d4d59474d6e8fd3f5840d1ca1f1b5e4f9626253 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 3 Jul 2023 02:54:09 +0200 Subject: [PATCH 25/51] Magma compatibility (#184) * Magma compatibility + add logging (cherry picked from commit 2df63fb3f92a1712c5432d2256db90aca8e42d83) --- .../common/priorities/mixins/MinecraftServerMixin.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java b/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java index bc68f462..a56fb929 100644 --- a/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java +++ b/src/main/java/mirror/normalasm/common/priorities/mixins/MinecraftServerMixin.java @@ -1,15 +1,22 @@ package mirror.normalasm.common.priorities.mixins; import net.minecraft.server.MinecraftServer; +import org.apache.logging.log4j.Logger; +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.Redirect; @Mixin(MinecraftServer.class) public class MinecraftServerMixin { - @Redirect(method = "startServerThread", at = @At(value = "INVOKE", target = "Ljava/lang/Thread;start()V")) + + @Shadow @Final private static Logger LOGGER; + + @Redirect(method = "startServerThread", at = @At(value = "INVOKE", target = "Ljava/lang/Thread;start()V"), require = 0) private void setPriorityAndStart(Thread serverThread) { serverThread.setPriority(Thread.MIN_PRIORITY + 2); serverThread.start(); + LOGGER.debug("NormalASM: Started server thread, with {} priority", serverThread.getPriority()); } } \ No newline at end of file From f165b635cf45b5fd5133cafdaa9261647fd37d36 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Tue, 4 Jul 2023 05:35:01 +0200 Subject: [PATCH 26/51] Bump to 5.14 (cherry picked from commit 16a9d12b277063605e49c5a0bc7ac833ec4122f7) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 29f99551..fa099f5e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.13 +mod_version = 5.14 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 2c504ac4..ee11ff26 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.13"; + public static final String VERSION = "5.14"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 44cb4dfe084044f9061a468f8f363576b9caef99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Ro=C3=9Fmann?= Date: Thu, 17 Aug 2023 18:59:08 +0200 Subject: [PATCH 27/51] Config "threadPriorityFix" now disables mixin as well (#199) (cherry picked from commit b41880b5b3441d67790b410fc1b5b3355c5218db) --- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index ee11ff26..fe0e937a 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -245,6 +245,8 @@ public boolean shouldMixinConfigQueue(String mixinConfig) { return NormalConfig.instance.crashReportImprovements; case "mixins.fix_mc129057.json": return NormalConfig.instance.fixMC129057; + case "mixins.priorities.json": + return NormalConfig.instance.threadPriorityFix; } return true; } From 57f99c7f57b14088a0b277974aa97955af223085 Mon Sep 17 00:00:00 2001 From: The_Computerizer <41523721+TheComputerizer@users.noreply.github.com> Date: Sat, 23 Sep 2023 05:43:33 -0500 Subject: [PATCH 28/51] Fix a crash with SGCraft passing a null TextureAtlasSprite through its renderer #137 (#211) (cherry picked from commit 5575cee2607430f5f43668ac1ecabae7810d5fbb) --- .../client/sprite/ondemand/mixins/VertexLighterFlatMixin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/VertexLighterFlatMixin.java b/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/VertexLighterFlatMixin.java index e9a39322..727ed29c 100644 --- a/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/VertexLighterFlatMixin.java +++ b/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/VertexLighterFlatMixin.java @@ -10,6 +10,8 @@ import mirror.normalasm.client.sprite.ondemand.ICompiledChunkExpander; import mirror.normalasm.client.sprite.ondemand.IVertexLighterExpander; +import java.util.Objects; + @Mixin(VertexLighterFlat.class) public abstract class VertexLighterFlatMixin extends QuadGatheringTransformer implements IVertexLighterExpander { @@ -23,7 +25,7 @@ public Object primeForDispatch() { @Override public void setTexture(TextureAtlasSprite texture) { - if (this.primedForDispatch && texture.hasAnimationMetadata()) { + if (this.primedForDispatch && Objects.nonNull(texture) && texture.hasAnimationMetadata()) { CompiledChunk chunk = IAnimatedSpritePrimer.CURRENT_COMPILED_CHUNK.get(); if (chunk != null) { ((ICompiledChunkExpander) chunk).resolve(texture); From 389c9ad07adc075d91fb0b3e7a0c4095abf3ee8f Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 24 Sep 2023 00:18:50 +0100 Subject: [PATCH 29/51] Fixes #189 (cherry picked from commit 36ed50ee72e2b5215cd860051fe8208b937b93e4) --- .../java/mirror/normalasm/common/crashes/CrashUtils.java | 2 ++ .../normalasm/common/crashes/mixins/EntityMixin.java | 7 ++++++- .../normalasm/common/crashes/mixins/TileEntityMixin.java | 8 ++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/crashes/CrashUtils.java b/src/main/java/mirror/normalasm/common/crashes/CrashUtils.java index 3180ef98..7d8543e6 100644 --- a/src/main/java/mirror/normalasm/common/crashes/CrashUtils.java +++ b/src/main/java/mirror/normalasm/common/crashes/CrashUtils.java @@ -12,6 +12,8 @@ public final class CrashUtils { + public static final ThreadLocal WRITING_DETAIL = ThreadLocal.withInitial(() -> false); + public static void crash(CrashReport report) { throw new ReportedException(report); } diff --git a/src/main/java/mirror/normalasm/common/crashes/mixins/EntityMixin.java b/src/main/java/mirror/normalasm/common/crashes/mixins/EntityMixin.java index 44e3b939..57b83b61 100644 --- a/src/main/java/mirror/normalasm/common/crashes/mixins/EntityMixin.java +++ b/src/main/java/mirror/normalasm/common/crashes/mixins/EntityMixin.java @@ -7,13 +7,18 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import mirror.normalasm.common.crashes.CrashUtils; @Mixin(Entity.class) public class EntityMixin { @Inject(method = "addEntityCrashInfo", at = @At("TAIL")) private void onAddEntityCrashInfo(CrashReportCategory category, CallbackInfo ci) { - category.addDetail("Entity NBT", () -> ((Entity) (Object) this).writeToNBT(new NBTTagCompound()).toString()); + if (!CrashUtils.WRITING_DETAIL.get()) { + CrashUtils.WRITING_DETAIL.set(true); + category.addDetail("Entity NBT", () -> ((Entity) (Object) this).writeToNBT(new NBTTagCompound()).toString()); + CrashUtils.WRITING_DETAIL.set(false); + } } } diff --git a/src/main/java/mirror/normalasm/common/crashes/mixins/TileEntityMixin.java b/src/main/java/mirror/normalasm/common/crashes/mixins/TileEntityMixin.java index 20a3ba17..e364098e 100644 --- a/src/main/java/mirror/normalasm/common/crashes/mixins/TileEntityMixin.java +++ b/src/main/java/mirror/normalasm/common/crashes/mixins/TileEntityMixin.java @@ -7,14 +7,18 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import mirror.normalasm.common.crashes.CrashUtils; @Mixin(TileEntity.class) public class TileEntityMixin { - // "Block Entity" to stay consistent with vanilla strings @Inject(method = "addInfoToCrashReport", at = @At("TAIL")) private void onAddEntityCrashInfo(CrashReportCategory category, CallbackInfo ci) { - category.addDetail("Block Entity NBT", () -> ((TileEntity) (Object) this).writeToNBT(new NBTTagCompound()).toString()); + if (!CrashUtils.WRITING_DETAIL.get()) { + CrashUtils.WRITING_DETAIL.set(true); + category.addDetail("NBT", () -> ((TileEntity) (Object) this).writeToNBT(new NBTTagCompound()).toString()); + CrashUtils.WRITING_DETAIL.set(false); + } } } From b5b4a0298f3e290b46b6e18b2fb9b91671bc3c95 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 24 Sep 2023 01:22:52 +0200 Subject: [PATCH 30/51] Bump to 5.15 (cherry picked from commit 6e6c4184d510785c3ab36ce76c105a18b2e67200) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index fa099f5e..b1519d16 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.14 +mod_version = 5.15 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index fe0e937a..e1753d25 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.14"; + public static final String VERSION = "5.15"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 687adb1e2525e305f74325b356ece786ecc2d4e3 Mon Sep 17 00:00:00 2001 From: The_Computerizer <41523721+TheComputerizer@users.noreply.github.com> Date: Sun, 24 Sep 2023 15:41:12 -0500 Subject: [PATCH 31/51] Fix makeEventsSingletons breaking capabilities (#213) * Fix capability maps being cleared before dispatchers were made (cherry picked from commit 7c4f2200f0eb7fd463c1521283725af8180bcfd0) --- .../mixins/ForgeEventFactoryMixin.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java index 09f21fa9..8f2c8621 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java @@ -5,6 +5,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; @@ -22,6 +23,7 @@ import javax.annotation.Nullable; import java.util.EnumSet; +import java.util.Map; @Mixin(value = ForgeEventFactory.class, remap = false) public abstract class ForgeEventFactoryMixin { @@ -59,8 +61,10 @@ private static CapabilityDispatcher gatherCapabilities(AttachCapabilitiesEvent caps = TE_ATTACH_CAPABILITIES_EVENT.getCapabilities(); + CapabilityDispatcher dispatcher = !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; TE_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return !TE_ATTACH_CAPABILITIES_EVENT.getCapabilities().isEmpty() ? new CapabilityDispatcher(TE_ATTACH_CAPABILITIES_EVENT.getCapabilities(), null) : null; + return dispatcher; } /** @@ -72,8 +76,10 @@ public static CapabilityDispatcher gatherCapabilities(TileEntity tileEntity) { public static CapabilityDispatcher gatherCapabilities(Entity entity) { ENTITY_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(entity); MinecraftForge.EVENT_BUS.post(ENTITY_ATTACH_CAPABILITIES_EVENT); + Map caps = ENTITY_ATTACH_CAPABILITIES_EVENT.getCapabilities(); + CapabilityDispatcher dispatcher = !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; ENTITY_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return !ENTITY_ATTACH_CAPABILITIES_EVENT.getCapabilities().isEmpty() ? new CapabilityDispatcher(ENTITY_ATTACH_CAPABILITIES_EVENT.getCapabilities(), null) : null; + return dispatcher; } /** @@ -85,8 +91,10 @@ public static CapabilityDispatcher gatherCapabilities(Entity entity) { public static CapabilityDispatcher gatherCapabilities(ItemStack stack, ICapabilityProvider parent) { ITEM_STACK_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(stack); MinecraftForge.EVENT_BUS.post(ITEM_STACK_ATTACH_CAPABILITIES_EVENT); + Map caps = ITEM_STACK_ATTACH_CAPABILITIES_EVENT.getCapabilities(); + CapabilityDispatcher dispatcher = parent != null || !caps.isEmpty() ? new CapabilityDispatcher(caps, parent) : null; ITEM_STACK_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return parent != null || !ITEM_STACK_ATTACH_CAPABILITIES_EVENT.getCapabilities().isEmpty() ? new CapabilityDispatcher(ITEM_STACK_ATTACH_CAPABILITIES_EVENT.getCapabilities(), parent) : null; + return dispatcher; } /** @@ -98,8 +106,10 @@ public static CapabilityDispatcher gatherCapabilities(ItemStack stack, ICapabili public static CapabilityDispatcher gatherCapabilities(Chunk chunk) { CHUNK_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(chunk); MinecraftForge.EVENT_BUS.post(CHUNK_ATTACH_CAPABILITIES_EVENT); + Map caps = CHUNK_ATTACH_CAPABILITIES_EVENT.getCapabilities(); + CapabilityDispatcher dispatcher = !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; CHUNK_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return !CHUNK_ATTACH_CAPABILITIES_EVENT.getCapabilities().isEmpty() ? new CapabilityDispatcher(CHUNK_ATTACH_CAPABILITIES_EVENT.getCapabilities(), null) : null; + return dispatcher; } /** From c2b3af0ae9ec1fad48bcf51e23a7dd3a1f68bd1e Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 24 Sep 2023 21:43:46 +0100 Subject: [PATCH 32/51] Typo, use the uncasted NeighborNotifyEvent (cherry picked from commit 1aeee85c73e8b7c198b0c4e3f90f1ff80a2e8bda) --- .../common/singletonevents/mixins/ForgeEventFactoryMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java index 8f2c8621..8cf8816c 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java @@ -44,7 +44,7 @@ private static CapabilityDispatcher gatherCapabilities(AttachCapabilitiesEvent Date: Sun, 24 Sep 2023 22:02:50 +0100 Subject: [PATCH 33/51] Refactored some of `IRefreshEvent` + avoid World objs from leaking (cherry picked from commit e6bc5591e036e33295630a595bcac7de64cdd3b0) --- .../common/singletonevents/IRefreshEvent.java | 14 ++------------ .../mixins/ForgeEventFactoryMixin.java | 18 ++++-------------- .../mixins/blocks/BlockEventMixin.java | 17 ++++++++++------- .../blocks/NeighborNotifyEventMixin.java | 5 ----- 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java b/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java index 3f60e37b..ad1b0612 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java @@ -35,26 +35,14 @@ default void beforeAttachCapabilities(Object data) { } - default void afterAttachCapabilities() { - - } - default void beforeBlockEvent(World world, BlockPos pos, IBlockState state) { } - default void afterBlockEvent() { - - } - default void beforeNeighborNotify(EnumSet notifiedSides, boolean forceRedstoneUpdate) { } - default void afterNeighborNotify() { - - } - // Special-case for PlayerTickEvents default IRefreshEvent setTickSide(Side side) { return this; @@ -64,6 +52,7 @@ default void beforeWorldTick(World world) { } + // TODO: determine if this avoiding this will memory leak default void afterWorldTick() { } @@ -72,6 +61,7 @@ default void beforePlayerTick(EntityPlayer player) { } + // TODO: determine if this avoiding this will memory leak default void afterPlayerTick() { } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java index 8cf8816c..db933943 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java @@ -62,9 +62,7 @@ public static CapabilityDispatcher gatherCapabilities(TileEntity tileEntity) { TE_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(tileEntity); MinecraftForge.EVENT_BUS.post(TE_ATTACH_CAPABILITIES_EVENT); Map caps = TE_ATTACH_CAPABILITIES_EVENT.getCapabilities(); - CapabilityDispatcher dispatcher = !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; - TE_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return dispatcher; + return !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; } /** @@ -77,9 +75,7 @@ public static CapabilityDispatcher gatherCapabilities(Entity entity) { ENTITY_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(entity); MinecraftForge.EVENT_BUS.post(ENTITY_ATTACH_CAPABILITIES_EVENT); Map caps = ENTITY_ATTACH_CAPABILITIES_EVENT.getCapabilities(); - CapabilityDispatcher dispatcher = !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; - ENTITY_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return dispatcher; + return !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; } /** @@ -92,9 +88,7 @@ public static CapabilityDispatcher gatherCapabilities(ItemStack stack, ICapabili ITEM_STACK_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(stack); MinecraftForge.EVENT_BUS.post(ITEM_STACK_ATTACH_CAPABILITIES_EVENT); Map caps = ITEM_STACK_ATTACH_CAPABILITIES_EVENT.getCapabilities(); - CapabilityDispatcher dispatcher = parent != null || !caps.isEmpty() ? new CapabilityDispatcher(caps, parent) : null; - ITEM_STACK_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return dispatcher; + return parent != null || !caps.isEmpty() ? new CapabilityDispatcher(caps, parent) : null; } /** @@ -107,9 +101,7 @@ public static CapabilityDispatcher gatherCapabilities(Chunk chunk) { CHUNK_ATTACH_CAPABILITIES_EVENT_CASTED.beforeAttachCapabilities(chunk); MinecraftForge.EVENT_BUS.post(CHUNK_ATTACH_CAPABILITIES_EVENT); Map caps = CHUNK_ATTACH_CAPABILITIES_EVENT.getCapabilities(); - CapabilityDispatcher dispatcher = !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; - CHUNK_ATTACH_CAPABILITIES_EVENT_CASTED.afterAttachCapabilities(); - return dispatcher; + return !caps.isEmpty() ? new CapabilityDispatcher(caps, null) : null; } /** @@ -122,8 +114,6 @@ public static NeighborNotifyEvent onNeighborNotify(World world, BlockPos pos, IB NEIGHBOR_NOTIFY_EVENT_CASTED.beforeBlockEvent(world, pos, state); NEIGHBOR_NOTIFY_EVENT_CASTED.beforeNeighborNotify(notifiedSides, forceRedstoneUpdate); MinecraftForge.EVENT_BUS.post(NEIGHBOR_NOTIFY_EVENT); - NEIGHBOR_NOTIFY_EVENT_CASTED.afterBlockEvent(); - NEIGHBOR_NOTIFY_EVENT_CASTED.afterNeighborNotify(); return NEIGHBOR_NOTIFY_EVENT; } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java index ab815434..8fc0008a 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java @@ -8,25 +8,28 @@ import org.spongepowered.asm.mixin.*; import mirror.normalasm.common.singletonevents.IRefreshEvent; +import java.lang.ref.WeakReference; + @Mixin(value = BlockEvent.class, remap = false) public class BlockEventMixin extends Event implements IRefreshEvent { - @Shadow @Final @Mutable private World world; + // @Shadow @Final @Mutable private World world; @Shadow @Final @Mutable private BlockPos pos; @Shadow @Final @Mutable private IBlockState state; + @Unique private WeakReference normalWorldRef; + @Override public void beforeBlockEvent(World world, BlockPos pos, IBlockState state) { - this.world = world; + // this.world = world; + this.normalWorldRef = new WeakReference<>(world); this.pos = pos; this.state = state; } - @Override - public void afterBlockEvent() { - this.world = null; - this.pos = null; - this.state = null; + @Overwrite + public World getWorld() { + return this.normalWorldRef.get(); } } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java index ce127fb2..79f89583 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java @@ -25,11 +25,6 @@ public void beforeNeighborNotify(EnumSet notifiedSides, boolean forc this.forceRedstoneUpdate = forceRedstoneUpdate; } - @Override - public void afterNeighborNotify() { - this.notifiedSides = null; - } - @Nullable @Override public EventPriority getPhase() { From 3d951e6991133a767a2e73ab08f47558750157e4 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 25 Sep 2023 00:46:25 +0200 Subject: [PATCH 34/51] Implement BlockEvent stuff in NeighborNotifyEvent for now (cherry picked from commit ff110d56c8d9bba57e370ce874aaa83b2cfdc3bc) --- .../common/singletonevents/IRefreshEvent.java | 6 +---- .../mixins/ForgeEventFactoryMixin.java | 3 +-- .../blocks/NeighborNotifyEventMixin.java | 24 +++++++++++++++---- .../AttachCapabilitiesEventMixin.java | 6 ----- .../resources/mixins.singletonevents.json | 1 - 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java b/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java index ad1b0612..cc899dbf 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java @@ -35,11 +35,7 @@ default void beforeAttachCapabilities(Object data) { } - default void beforeBlockEvent(World world, BlockPos pos, IBlockState state) { - - } - - default void beforeNeighborNotify(EnumSet notifiedSides, boolean forceRedstoneUpdate) { + default void beforeNeighborNotify(World world, EnumSet notifiedSides, boolean forceRedstoneUpdate) { } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java index db933943..bbf18c65 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java @@ -111,8 +111,7 @@ public static CapabilityDispatcher gatherCapabilities(Chunk chunk) { @Nullable @Overwrite public static NeighborNotifyEvent onNeighborNotify(World world, BlockPos pos, IBlockState state, EnumSet notifiedSides, boolean forceRedstoneUpdate) { - NEIGHBOR_NOTIFY_EVENT_CASTED.beforeBlockEvent(world, pos, state); - NEIGHBOR_NOTIFY_EVENT_CASTED.beforeNeighborNotify(notifiedSides, forceRedstoneUpdate); + NEIGHBOR_NOTIFY_EVENT_CASTED.beforeNeighborNotify(world, notifiedSides, forceRedstoneUpdate); MinecraftForge.EVENT_BUS.post(NEIGHBOR_NOTIFY_EVENT); return NEIGHBOR_NOTIFY_EVENT; } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java index 79f89583..8058ce39 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java @@ -1,26 +1,42 @@ package mirror.normalasm.common.singletonevents.mixins.blocks; +import net.minecraft.block.state.IBlockState; import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.BlockEvent.NeighborNotifyEvent; -import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.eventhandler.EventPriority; import org.spongepowered.asm.mixin.*; import mirror.normalasm.common.singletonevents.IRefreshEvent; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.util.EnumSet; @Mixin(NeighborNotifyEvent.class) -public class NeighborNotifyEventMixin extends Event implements IRefreshEvent { +public class NeighborNotifyEventMixin extends BlockEvent implements IRefreshEvent { @Shadow @Final @Mutable private EnumSet notifiedSides; @Shadow @Final @Mutable private boolean forceRedstoneUpdate; - @Unique private EventPriority normalPriority = null; + @Unique private EventPriority normalPriority; + @Unique private WeakReference normalWorldRef; + + NeighborNotifyEventMixin(World world, BlockPos pos, IBlockState state) { + super(world, pos, state); + throw new AssertionError(); + } + + @Override + public World getWorld() { + return this.normalWorldRef.get(); + } @Override - public void beforeNeighborNotify(EnumSet notifiedSides, boolean forceRedstoneUpdate) { + public void beforeNeighborNotify(World world, EnumSet notifiedSides, boolean forceRedstoneUpdate) { + this.normalWorldRef = new WeakReference<>(world); this.notifiedSides = notifiedSides; this.forceRedstoneUpdate = forceRedstoneUpdate; } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/capabilities/AttachCapabilitiesEventMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/capabilities/AttachCapabilitiesEventMixin.java index 443b9ef6..a2ba7707 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/capabilities/AttachCapabilitiesEventMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/capabilities/AttachCapabilitiesEventMixin.java @@ -24,13 +24,7 @@ public class AttachCapabilitiesEventMixin extends Event implements IRefreshEv @Override public void beforeAttachCapabilities(Object data) { this.obj = (T) data; - } - - @Override - public void afterAttachCapabilities() { - this.obj = null; this.caps.clear(); - this.normalPriority = null; } @Nullable diff --git a/src/main/resources/mixins.singletonevents.json b/src/main/resources/mixins.singletonevents.json index 2bafc154..a1e5f03a 100644 --- a/src/main/resources/mixins.singletonevents.json +++ b/src/main/resources/mixins.singletonevents.json @@ -7,7 +7,6 @@ "mixins": [ "FMLCommonHandlerMixin", "ForgeEventFactoryMixin", - "blocks.BlockEventMixin", "blocks.NeighborNotifyEventMixin", "capabilities.AttachCapabilitiesEventMixin", "gameevent.PlayerTickEventMixin", From 49b7f0db7180d9182d3eef4a34eb11e7d0322389 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Tue, 26 Sep 2023 03:19:24 +0200 Subject: [PATCH 35/51] Bump to 5.16 (cherry picked from commit 00622a1b29bb16b3ad8012d602418e5133a44111) --- gradle.properties | 2 +- .../mixins/blocks/BlockEventMixin.java | 35 ------------------- .../normalasm/core/NormalLoadingPlugin.java | 2 +- 3 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java diff --git a/gradle.properties b/gradle.properties index b1519d16..e7b5c2d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.15 +mod_version = 5.16 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java deleted file mode 100644 index 8fc0008a..00000000 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/BlockEventMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package mirror.normalasm.common.singletonevents.mixins.blocks; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.fml.common.eventhandler.Event; -import org.spongepowered.asm.mixin.*; -import mirror.normalasm.common.singletonevents.IRefreshEvent; - -import java.lang.ref.WeakReference; - -@Mixin(value = BlockEvent.class, remap = false) -public class BlockEventMixin extends Event implements IRefreshEvent { - - // @Shadow @Final @Mutable private World world; - @Shadow @Final @Mutable private BlockPos pos; - @Shadow @Final @Mutable private IBlockState state; - - @Unique private WeakReference normalWorldRef; - - @Override - public void beforeBlockEvent(World world, BlockPos pos, IBlockState state) { - // this.world = world; - this.normalWorldRef = new WeakReference<>(world); - this.pos = pos; - this.state = state; - } - - @Overwrite - public World getWorld() { - return this.normalWorldRef.get(); - } - -} diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index e1753d25..839cce50 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.15"; + public static final String VERSION = "5.16"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From bffa5670eb185d15e399468829c769b2cf41a846 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 4 Oct 2023 17:09:09 +0200 Subject: [PATCH 36/51] Fixes #218 (cherry picked from commit beedb68e2dcc143664bf20c17fb35998875ce380) --- .../common/singletonevents/IRefreshEvent.java | 2 +- .../mixins/ForgeEventFactoryMixin.java | 2 +- .../mixins/blocks/NeighborNotifyEventMixin.java | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java b/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java index cc899dbf..1c6757df 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/IRefreshEvent.java @@ -35,7 +35,7 @@ default void beforeAttachCapabilities(Object data) { } - default void beforeNeighborNotify(World world, EnumSet notifiedSides, boolean forceRedstoneUpdate) { + default void beforeNeighborNotify(World world, BlockPos pos, IBlockState state, EnumSet notifiedSides, boolean forceRedstoneUpdate) { } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java index bbf18c65..d3a3529c 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/ForgeEventFactoryMixin.java @@ -111,7 +111,7 @@ public static CapabilityDispatcher gatherCapabilities(Chunk chunk) { @Nullable @Overwrite public static NeighborNotifyEvent onNeighborNotify(World world, BlockPos pos, IBlockState state, EnumSet notifiedSides, boolean forceRedstoneUpdate) { - NEIGHBOR_NOTIFY_EVENT_CASTED.beforeNeighborNotify(world, notifiedSides, forceRedstoneUpdate); + NEIGHBOR_NOTIFY_EVENT_CASTED.beforeNeighborNotify(world, pos, state, notifiedSides, forceRedstoneUpdate); MinecraftForge.EVENT_BUS.post(NEIGHBOR_NOTIFY_EVENT); return NEIGHBOR_NOTIFY_EVENT; } diff --git a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java index 8058ce39..1de901cd 100644 --- a/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java +++ b/src/main/java/mirror/normalasm/common/singletonevents/mixins/blocks/NeighborNotifyEventMixin.java @@ -23,6 +23,8 @@ public class NeighborNotifyEventMixin extends BlockEvent implements IRefreshEven @Unique private EventPriority normalPriority; @Unique private WeakReference normalWorldRef; + @Unique private BlockPos normalPos; + @Unique private IBlockState normalState; NeighborNotifyEventMixin(World world, BlockPos pos, IBlockState state) { super(world, pos, state); @@ -35,8 +37,20 @@ public World getWorld() { } @Override - public void beforeNeighborNotify(World world, EnumSet notifiedSides, boolean forceRedstoneUpdate) { + public BlockPos getPos() { + return normalPos; + } + + @Override + public IBlockState getState() { + return normalState; + } + + @Override + public void beforeNeighborNotify(World world, BlockPos pos, IBlockState state, EnumSet notifiedSides, boolean forceRedstoneUpdate) { this.normalWorldRef = new WeakReference<>(world); + this.normalPos = pos; + this.normalState = state; this.notifiedSides = notifiedSides; this.forceRedstoneUpdate = forceRedstoneUpdate; } From a8f243f55f59e6b64436ca54bb1f59eed1a5b94d Mon Sep 17 00:00:00 2001 From: Rongmario Date: Wed, 4 Oct 2023 17:21:02 +0200 Subject: [PATCH 37/51] Bump to 5.17 (cherry picked from commit 000f6362b3a185b6a931ec8d0afc1102cf55c628) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index e7b5c2d8..9ebd08a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.16 +mod_version = 5.17 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 839cce50..bea88103 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.16"; + public static final String VERSION = "5.17"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 1f4f25c16eac7d93e5988d7b1afdcebf114a4f55 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:22:53 -0400 Subject: [PATCH 38/51] Move BufferBuilder texture detection logic to a public method (#219) This makes it possible to call that logic from another mixin without reimplementing the whole thing (cherry picked from commit f12b0dab638d16c9f7c0e89156b37162f6850728) --- .../ondemand/IBufferPrimerConfigurator.java | 2 ++ .../ondemand/mixins/BufferBuilderMixin.java | 32 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/java/mirror/normalasm/client/sprite/ondemand/IBufferPrimerConfigurator.java b/src/main/java/mirror/normalasm/client/sprite/ondemand/IBufferPrimerConfigurator.java index 40c871d2..5ef89f55 100644 --- a/src/main/java/mirror/normalasm/client/sprite/ondemand/IBufferPrimerConfigurator.java +++ b/src/main/java/mirror/normalasm/client/sprite/ondemand/IBufferPrimerConfigurator.java @@ -4,4 +4,6 @@ public interface IBufferPrimerConfigurator { void setPrimer(IAnimatedSpritePrimer primer); + void hookTexture(float u, float v); + } diff --git a/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/BufferBuilderMixin.java b/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/BufferBuilderMixin.java index 7254da45..321ad58b 100644 --- a/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/BufferBuilderMixin.java +++ b/src/main/java/mirror/normalasm/client/sprite/ondemand/mixins/BufferBuilderMixin.java @@ -51,20 +51,7 @@ public BufferBuilder tex(double u, double v) { float fv = (float) v; this.byteBuffer.putFloat(i, fu); this.byteBuffer.putFloat(i + 4, fv); - if (primer != null && drawMode == GL11.GL_QUADS && IAnimatedSpritePrimer.PRIMED.get()) { - if (++vertexCountOfQuad == 4) { - vertexCountOfQuad = 0; - primer.addAnimatedSprite(minUOfQuad, minVOfQuad); - minUOfQuad = Float.MAX_VALUE; - minVOfQuad = Float.MAX_VALUE; - } - if (minUOfQuad > fu) { - minUOfQuad = fu; - } - if (minVOfQuad > fv) { - minVOfQuad = fv; - } - } + hookTexture(fu, fv); break; case UINT: case INT: @@ -103,4 +90,21 @@ public void finishDrawing() { } } + @Override + public void hookTexture(float fu, float fv) { + if (primer != null && drawMode == GL11.GL_QUADS && IAnimatedSpritePrimer.PRIMED.get()) { + if (++vertexCountOfQuad == 4) { + vertexCountOfQuad = 0; + primer.addAnimatedSprite(minUOfQuad, minVOfQuad); + minUOfQuad = Float.MAX_VALUE; + minVOfQuad = Float.MAX_VALUE; + } + if (minUOfQuad > fu) { + minUOfQuad = fu; + } + if (minVOfQuad > fv) { + minVOfQuad = fv; + } + } + } } From a81de58c8b2ccbe4a06aa6bd9b64df2334c5b9ea Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 16 Oct 2023 01:52:57 +0200 Subject: [PATCH 39/51] Bump to 5.18 (cherry picked from commit 77716031cc5f8f35ce2c03c4db1e552d4624bd3e) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9ebd08a6..b2197288 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.17 +mod_version = 5.18 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index bea88103..b9874eb0 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.17"; + public static final String VERSION = "5.18"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 049e4788e462a34b627955edf21a449154680c2c Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 19 Feb 2024 12:03:40 +0100 Subject: [PATCH 40/51] Fixes #239 that was introduced in embeddedt@8b42398 (cherry picked from commit 260bbd6203569da608fba257beb5866e74783cfe) --- .../client/sprite/FramesTextureData.java | 16 ++++++++++------ .../java/mirror/normalasm/proxy/ClientProxy.java | 3 +-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java index 05e2b4ce..03cab184 100644 --- a/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java +++ b/src/main/java/mirror/normalasm/client/sprite/FramesTextureData.java @@ -37,23 +37,27 @@ public static void registerEvictionListener(ColorHandlerEvent.Block event) { ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener((ISelectiveResourceReloadListener) (manager, predicate) -> { if (predicate.test(VanillaResourceType.MODELS)) { canReload = false; + int count = 0; Set> skippedSpriteClasses = new ObjectOpenHashSet<>(); try { synchronized (tickingSpritesSet) { tickingSpritesSet.clear(); } for (TextureAtlasSprite sprite : ((TextureMapAccessor) Minecraft.getMinecraft().getTextureMapBlocks()).getMapRegisteredSprites().values()) { - if (sprite.getClass() == FOAMFIX_SPRITE || sprite.getClass() == TextureAtlasSprite.class) { - FramesTextureData ftd = new FramesTextureData(sprite); - sprite.setFramesTextureData(ftd); - } else { - skippedSpriteClasses.add(sprite.getClass()); + if (!sprite.hasAnimationMetadata()) { + if (sprite.getClass() == FOAMFIX_SPRITE || sprite.getClass() == TextureAtlasSprite.class) { + count++; + sprite.setFramesTextureData(new FramesTextureData(sprite)); + } else { + skippedSpriteClasses.add(sprite.getClass()); + } } } } catch (Throwable t) { t.printStackTrace(); } - NormalLogger.instance.debug("Evicted most sprites' frame texture data, skipped classes: [{}]", skippedSpriteClasses.stream().map(Class::getName).collect(Collectors.joining(", "))); + NormalLogger.instance.info("Evicted {} sprites' frame texture data", count); + NormalLogger.instance.debug("While evicting sprites' frame texture data, the following classes were skipped: [{}]", skippedSpriteClasses.stream().map(Class::getName).collect(Collectors.joining(", "))); canReload = true; } }); diff --git a/src/main/java/mirror/normalasm/proxy/ClientProxy.java b/src/main/java/mirror/normalasm/proxy/ClientProxy.java index c4b76a18..e37ed309 100644 --- a/src/main/java/mirror/normalasm/proxy/ClientProxy.java +++ b/src/main/java/mirror/normalasm/proxy/ClientProxy.java @@ -12,7 +12,6 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.relauncher.Side; import mirror.normalasm.NormalLogger; -import mirror.normalasm.NormalReflector; import mirror.normalasm.bakedquad.NormalVertexDataPool; import mirror.normalasm.client.models.bucket.NormalBakedDynBucket; import mirror.normalasm.client.screenshot.ScreenshotListener; @@ -23,7 +22,7 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; + @Mod.EventBusSubscriber(modid = "normalasm", value = Side.CLIENT) public class ClientProxy extends CommonProxy { From 1fb96d5a26cfe4278a36e17620c31458e60698a4 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 19 Feb 2024 12:06:35 +0100 Subject: [PATCH 41/51] Bump to 5.19 (cherry picked from commit bce972984659ed9135d5b7e40a71e543f4671725) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index b2197288..76842b86 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.18 +mod_version = 5.19 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index b9874eb0..dc1dc219 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.18"; + public static final String VERSION = "5.19"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From cbe40344d0bcd6913a30dfa3b603aa317266bc8f Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 13 Apr 2024 01:00:37 +0200 Subject: [PATCH 42/51] Set squashBakedQuads to false if any sodium ports are installed (cherry picked from commit 7f64ad7768b14ee66cbe749fba1717e2fd269f67) --- .../mirror/normalasm/core/NormalTransformer.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/mirror/normalasm/core/NormalTransformer.java b/src/main/java/mirror/normalasm/core/NormalTransformer.java index f6b90558..b4bd6943 100644 --- a/src/main/java/mirror/normalasm/core/NormalTransformer.java +++ b/src/main/java/mirror/normalasm/core/NormalTransformer.java @@ -22,7 +22,7 @@ public class NormalTransformer implements IClassTransformer { - public static boolean isOptifineInstalled; + public static boolean isOptifineInstalled, isSodiumPortInstalled; public static boolean squashBakedQuads = NormalConfig.instance.squashBakedQuads; Multimap> transformations; @@ -30,9 +30,15 @@ public class NormalTransformer implements IClassTransformer { public NormalTransformer() { NormalLogger.instance.info("NormalASM is now preparing to bytecode manipulate your game."); isOptifineInstalled = NormalReflector.doesClassExist("optifine.OptiFineForgeTweaker"); - if (squashBakedQuads && isOptifineInstalled) { - squashBakedQuads = false; - NormalLogger.instance.info("Optifine is installed. BakedQuads won't be squashed as it is incompatible with OptiFine."); + isSodiumPortInstalled = NormalReflector.doesClassExist("me.jellysquid.mods.sodium.client.SodiumMixinTweaker"); + if (squashBakedQuads) { + if (isOptifineInstalled) { + squashBakedQuads = false; + NormalLogger.instance.info("Optifine is installed. BakedQuads won't be squashed as it is incompatible with OptiFine."); + } else if (isSodiumPortInstalled) { + squashBakedQuads = false; + NormalLogger.instance.info("A sodium port is installed. BakedQuads won't be squashed as it is incompatible with Sodium."); + } } transformations = MultimapBuilder.hashKeys(30).arrayListValues(1).build(); if (NormalLoadingPlugin.isClient) { From 593abe81d6334a376e865aac3aaf722cb4b84f14 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 13 Apr 2024 01:25:59 +0100 Subject: [PATCH 43/51] Update cacerts (cherry picked from commit 8f76f3016b1945e6dbd5747e0c74bf01652139e5) --- src/main/resources/cacerts | Bin 158913 -> 164295 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/cacerts b/src/main/resources/cacerts index f6bd60c6fb47e22c4d009b074f8fb2d0801d0087..54e56c272615dc9fff655b86b4654263b6dad7e4 100644 GIT binary patch delta 33484 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 EFSfho9{>OV delta 29517 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 Date: Sat, 13 Apr 2024 02:26:49 +0200 Subject: [PATCH 44/51] Allow normalasm to load earlier (stupid forge gimmick) (cherry picked from commit 9176ad8b8529224121d588f487b718f95a911ddc) --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index fdfb9431..04c82f90 100644 --- a/build.gradle +++ b/build.gradle @@ -166,7 +166,6 @@ jar { 'FMLCorePluginContainsFMLMod': true, 'FMLCorePlugin': 'mirror.normalasm.core.NormalLoadingPlugin', 'ForceLoadAsMod': project.gradle.startParameter.taskNames[0] == 'build', - 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker', 'FMLAT': 'normalasm_at.cfg' ]) } From da76575ac01191a9bd0243180e72a64b2694d6ec Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 13 Apr 2024 02:27:24 +0200 Subject: [PATCH 45/51] Bump to 5.20, I swear v6 is coming soon! (cherry picked from commit 4ac51b1505dad41fb44971d32f85c2f9a556f556) --- gradle.properties | 2 +- .../java/mirror/normalasm/core/NormalLoadingPlugin.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 76842b86..02c751c8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.19 +mod_version = 5.20 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index dc1dc219..ef6857c7 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.19"; + public static final String VERSION = "5.20"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); @@ -81,8 +81,8 @@ public NormalLoadingPlugin() { FileUtils.copyInputStreamToFile(is, cacertsCopy); System.setProperty("javax.net.ssl.trustStore", cacertsCopy.getAbsolutePath()); NormalLogger.instance.warn("Replacing CA Certs with an updated one..."); - } catch (IOException e) { - NormalLogger.instance.warn("Unable to replace CA Certs", e); + } catch (Exception e) { + NormalLogger.instance.warn("Unable to replace CA Certs.", e); } } if (NormalConfig.instance.removeForgeSecurityManager) { From 46c05f8022cf4df8120b035693c8b27584d6aa7e Mon Sep 17 00:00:00 2001 From: CaliforniaDemise <155631180+californiademise@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:53:38 +0100 Subject: [PATCH 46/51] fix optimizeNBTTagCompoundBackingMap option (#269) * Compound NBT will replace array map with hash map if maps size reaches threshold. * Set default threshold to 5. * Implement TagMap structure for better readability. * Respect threshold on clear and putAll method calls. (cherry picked from commit 99a6753f76f110e4408b64c11aea4e4afb2cd248) --- .../normalasm/api/datastructures/TagMap.java | 98 +++++++++++++++++++ .../canonical/AutoCanonizingHashMap.java | 28 ++++++ .../mirror/normalasm/config/NormalConfig.java | 12 +++ .../mirror/normalasm/core/NormalHooks.java | 1 - .../normalasm/core/NormalTransformer.java | 13 ++- 5 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 src/main/java/mirror/normalasm/api/datastructures/TagMap.java create mode 100644 src/main/java/mirror/normalasm/api/datastructures/canonical/AutoCanonizingHashMap.java diff --git a/src/main/java/mirror/normalasm/api/datastructures/TagMap.java b/src/main/java/mirror/normalasm/api/datastructures/TagMap.java new file mode 100644 index 00000000..f4fd3ac3 --- /dev/null +++ b/src/main/java/mirror/normalasm/api/datastructures/TagMap.java @@ -0,0 +1,98 @@ +package mirror.normalasm.api.datastructures; + +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mirror.normalasm.api.datastructures.canonical.AutoCanonizingArrayMap; +import mirror.normalasm.api.datastructures.canonical.AutoCanonizingHashMap; +import mirror.normalasm.config.NormalConfig; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class TagMap implements Map { + + private Map map; + private final int threshold; + + public TagMap() { + this(NormalConfig.instance.optimizeNBTTagCompoundMapThreshold, NormalConfig.instance.nbtBackingMapStringCanonicalization, NormalConfig.instance.optimizeNBTTagCompoundBackingMap); + } + + public TagMap(int threshold, boolean canonicalizeString, boolean optimizeMap) { + this.threshold = optimizeMap ? threshold : -1; + if (canonicalizeString) this.map = optimizeMap ? new AutoCanonizingArrayMap<>() : new AutoCanonizingHashMap<>(); + else this.map = optimizeMap ? new Object2ObjectArrayMap<>() : new Object2ObjectOpenHashMap<>(); + } + + @Override + public int size() { + return this.map.size(); + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return this.map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.map.containsValue(value); + } + + @Override + public V get(Object key) { + return this.map.get(key); + } + + @Override + public V put(K key, V value) { + if (this.size() == this.threshold) { + this.map = this.map instanceof AutoCanonizingArrayMap ? new AutoCanonizingHashMap<>(this.map) : new Object2ObjectOpenHashMap<>(this.map); + } + return this.map.put(key, value); + } + + @Override + public V remove(Object key) { + return this.map.remove(key); + } + + @Override + public void putAll(Map m) { + m.forEach(this::put); + } + + @Override + public void clear() { + if (this.threshold != -1) { + if (this.map instanceof AutoCanonizingHashMap) this.map = new AutoCanonizingArrayMap<>(); + else if (this.map instanceof Object2ObjectOpenHashMap) this.map = new Object2ObjectArrayMap<>(); + else this.map.clear(); + } + else this.map.clear(); + } + + @Nonnull + @Override + public Set keySet() { + return this.map.keySet(); + } + + @Nonnull + @Override + public Collection values() { + return this.map.values(); + } + + @Override + public Set> entrySet() { + return this.map.entrySet(); + } +} diff --git a/src/main/java/mirror/normalasm/api/datastructures/canonical/AutoCanonizingHashMap.java b/src/main/java/mirror/normalasm/api/datastructures/canonical/AutoCanonizingHashMap.java new file mode 100644 index 00000000..675c3644 --- /dev/null +++ b/src/main/java/mirror/normalasm/api/datastructures/canonical/AutoCanonizingHashMap.java @@ -0,0 +1,28 @@ +package mirror.normalasm.api.datastructures.canonical; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mirror.normalasm.api.NormalStringPool; + +import java.util.Map; + +public class AutoCanonizingHashMap extends Object2ObjectOpenHashMap { + + public AutoCanonizingHashMap() { + super(); + } + + public AutoCanonizingHashMap(Map map) { + super(map); + } + + @Override + public V put(K k, V v) { + if (k instanceof String) { + k = (K) NormalStringPool.canonicalize((String) k); + } + if (v instanceof String) { + v = (V) NormalStringPool.canonicalize((String) v); + } + return super.put(k, v); + } +} diff --git a/src/main/java/mirror/normalasm/config/NormalConfig.java b/src/main/java/mirror/normalasm/config/NormalConfig.java index f2eb7b30..a01ffd3c 100644 --- a/src/main/java/mirror/normalasm/config/NormalConfig.java +++ b/src/main/java/mirror/normalasm/config/NormalConfig.java @@ -67,6 +67,7 @@ public boolean shouldSkipClass(Class clazz) { public boolean resourceLocationCanonicalization, modelConditionCanonicalization, nbtTagStringBackingStringCanonicalization, nbtBackingMapStringCanonicalization, packageStringCanonicalization, lockCodeCanonicalization, spriteNameCanonicalization, asmDataStringCanonicalization, vertexDataCanonicalization, filePermissionsCacheCanonicalization; public boolean optimizeFMLRemapper; public boolean optimizeRegistries, optimizeNBTTagCompoundBackingMap, optimizeFurnaceRecipeStore, stripNearUselessItemStackFields, moreModelManagerCleanup, efficientHashing, replaceSearchTreeWithJEISearching; + public int optimizeNBTTagCompoundMapThreshold; public boolean releaseSpriteFramesCache, onDemandAnimatedTextures; public boolean optimizeSomeRendering, stripUnnecessaryLocalsInRenderHelper; public boolean quickerEnableUniversalBucketCheck, stripInstancedRandomFromSoundEventAccessor, classCaching, copyScreenshotToClipboard, releaseScreenshotCache, asyncScreenshot, removeExcessiveGCCalls, smoothDimensionChange, threadPriorityFix, outdatedCaCertsFix; @@ -117,6 +118,7 @@ public void load() { // optimizeDataStructures = getBoolean("optimizeDataStructures", "datastructures", "Optimizes various data structures around Minecraft", true); optimizeRegistries = getBoolean("optimizeRegistries", "datastructures", "Optimizes registries", true); optimizeNBTTagCompoundBackingMap = getBoolean("optimizeNBTTagCompoundBackingMap", "datastructures", "Optimize NBTTagCompound's backing map structure", true); + optimizeNBTTagCompoundMapThreshold = getInteger("optimizeNBTTagCompoundMapThreshold", "datastructures", "Max size NBTTagCompounds backing map can get before it gets changed to HashMap from ArrayMap", 5); optimizeFurnaceRecipeStore = getBoolean("optimizeFurnaceRecipeStore", "datastructures", "Optimizing FurnaceRecipes. FastFurnace will see very little benefit when this option is turned on", true); stripNearUselessItemStackFields = getBoolean("stripNearUselessItemStackFields", "datastructures", "EXPERIMENTAL: Strips ItemStack of some of its fields as it stores some near-useless references", true); moreModelManagerCleanup = getBoolean("moreModelManagerCleanup", "datastructures", "Clears and trims ModelManager data structures after models are loaded and baked", true); @@ -243,6 +245,16 @@ private boolean getBoolean(String name, String category, String description, boo return prop.getBoolean(defaultValue); } + private int getInteger(String name, String category, String description, int defaultValue) { + Property prop = configuration.get(category, name, defaultValue); + prop.setDefaultValue(defaultValue); + prop.setComment(description + " - "); + prop.setRequiresMcRestart(true); + prop.setShowInGui(true); + prop.setLanguageKey("normalasm.config." + name); + return prop.getInt(defaultValue); + } + private String[] getStringArray(String name, String category, String description, String... defaultValue) { Property prop = configuration.get(category, name, defaultValue); prop.setDefaultValues(defaultValue); diff --git a/src/main/java/mirror/normalasm/core/NormalHooks.java b/src/main/java/mirror/normalasm/core/NormalHooks.java index cac6e513..245a5f03 100644 --- a/src/main/java/mirror/normalasm/core/NormalHooks.java +++ b/src/main/java/mirror/normalasm/core/NormalHooks.java @@ -84,5 +84,4 @@ public static void inform(Class clazz) { public static String nbtTagString$override$ctor(String data) { return NormalStringPool.canonicalize(data); } - } diff --git a/src/main/java/mirror/normalasm/core/NormalTransformer.java b/src/main/java/mirror/normalasm/core/NormalTransformer.java index b4bd6943..c8344982 100644 --- a/src/main/java/mirror/normalasm/core/NormalTransformer.java +++ b/src/main/java/mirror/normalasm/core/NormalTransformer.java @@ -143,7 +143,7 @@ public NormalTransformer() { if (NormalConfig.instance.fixMC31681) { addTransformation("net.minecraft.client.renderer.EntityRenderer", this::fixMC31681); } - addTransformation("net.minecraft.nbt.NBTTagCompound", bytes -> nbtTagCompound$replaceDefaultHashMap(bytes, NormalConfig.instance.optimizeNBTTagCompoundBackingMap, NormalConfig.instance.nbtBackingMapStringCanonicalization)); + addTransformation("net.minecraft.nbt.NBTTagCompound", bytes -> nbtTagCompound$replaceDefaultHashMap(bytes, NormalConfig.instance.optimizeNBTTagCompoundBackingMap, NormalConfig.instance.optimizeNBTTagCompoundMapThreshold, NormalConfig.instance.nbtBackingMapStringCanonicalization)); } public void addTransformation(String key, Function value) { @@ -468,29 +468,28 @@ private byte[] removeInstancedRandom(byte[] bytes) { return writer.toByteArray(); } - private byte[] nbtTagCompound$replaceDefaultHashMap(byte[] bytes, boolean optimizeMap, boolean canonicalizeString) { - if (!optimizeMap && !canonicalizeString) { + private byte[] nbtTagCompound$replaceDefaultHashMap(byte[] bytes, boolean optimizeMap, int mapThreshold, boolean canonicalizeString) { + if ((!optimizeMap || mapThreshold == 0) && !canonicalizeString) { return bytes; } ClassReader reader = new ClassReader(bytes); ClassNode node = new ClassNode(); reader.accept(node, 0); - for (MethodNode method : node.methods) { if (method.name.equals("")) { ListIterator iter = method.instructions.iterator(); while (iter.hasNext()) { AbstractInsnNode instruction = iter.next(); if (instruction.getOpcode() == INVOKESTATIC) { - iter.set(new TypeInsnNode(NEW, canonicalizeString ? "mirror/normalasm/api/datastructures/canonical/AutoCanonizingArrayMap" : "it/unimi/dsi/fastutil/objects/Object2ObjectArrayMap")); + iter.set(new TypeInsnNode(NEW, "mirror/normalasm/api/datastructures/TagMap")); iter.add(new InsnNode(DUP)); - iter.add(new MethodInsnNode(INVOKESPECIAL, canonicalizeString ? "mirror/normalasm/api/datastructures/canonical/AutoCanonizingArrayMap" : "it/unimi/dsi/fastutil/objects/Object2ObjectArrayMap", "", "()V", false)); + iter.add(new MethodInsnNode(INVOKESPECIAL, "mirror/normalasm/api/datastructures/TagMap", "", "()V", false)); break; } } + break; } } - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES); node.accept(writer); return writer.toByteArray(); From 6a01841e926d64cf927c5f31f5fe54e7d1e171c7 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Thu, 23 Jan 2025 03:31:53 +0100 Subject: [PATCH 47/51] Bump to 5.21, we stray further away from v6 (cherry picked from commit d82c2bc2845bfa25384a01e7ad03dd09e08dd156) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 02c751c8..0dd9ae4d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.20 +mod_version = 5.21 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index ef6857c7..c6c55539 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.20"; + public static final String VERSION = "5.21"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 790b5f21469f8ca4735c845e9602fa462a9dc438 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 26 Jan 2025 00:05:51 +0100 Subject: [PATCH 48/51] Fixes #270, fixes #271 (cherry picked from commit 2bd2552e301866372466345b055694ca0d3fc598) --- .../{TagMap.java => NormalTagMap.java} | 54 +++++++++++++++---- .../normalasm/core/NormalTransformer.java | 4 +- 2 files changed, 46 insertions(+), 12 deletions(-) rename src/main/java/mirror/normalasm/api/datastructures/{TagMap.java => NormalTagMap.java} (61%) diff --git a/src/main/java/mirror/normalasm/api/datastructures/TagMap.java b/src/main/java/mirror/normalasm/api/datastructures/NormalTagMap.java similarity index 61% rename from src/main/java/mirror/normalasm/api/datastructures/TagMap.java rename to src/main/java/mirror/normalasm/api/datastructures/NormalTagMap.java index f4fd3ac3..3982b555 100644 --- a/src/main/java/mirror/normalasm/api/datastructures/TagMap.java +++ b/src/main/java/mirror/normalasm/api/datastructures/NormalTagMap.java @@ -11,19 +11,22 @@ import java.util.Map; import java.util.Set; -public class TagMap implements Map { +public class NormalTagMap implements Map { private Map map; private final int threshold; - public TagMap() { + public NormalTagMap() { this(NormalConfig.instance.optimizeNBTTagCompoundMapThreshold, NormalConfig.instance.nbtBackingMapStringCanonicalization, NormalConfig.instance.optimizeNBTTagCompoundBackingMap); } - public TagMap(int threshold, boolean canonicalizeString, boolean optimizeMap) { + public NormalTagMap(int threshold, boolean canonicalizeString, boolean optimizeMap) { this.threshold = optimizeMap ? threshold : -1; - if (canonicalizeString) this.map = optimizeMap ? new AutoCanonizingArrayMap<>() : new AutoCanonizingHashMap<>(); - else this.map = optimizeMap ? new Object2ObjectArrayMap<>() : new Object2ObjectOpenHashMap<>(); + if (canonicalizeString) { + this.map = optimizeMap ? new AutoCanonizingArrayMap<>() : new AutoCanonizingHashMap<>(); + } else { + this.map = optimizeMap ? new Object2ObjectArrayMap<>() : new Object2ObjectOpenHashMap<>(); + } } @Override @@ -72,11 +75,16 @@ public void putAll(Map m) { @Override public void clear() { if (this.threshold != -1) { - if (this.map instanceof AutoCanonizingHashMap) this.map = new AutoCanonizingArrayMap<>(); - else if (this.map instanceof Object2ObjectOpenHashMap) this.map = new Object2ObjectArrayMap<>(); - else this.map.clear(); + if (this.map instanceof AutoCanonizingHashMap) { + this.map = new AutoCanonizingArrayMap<>(); + } else if (this.map instanceof Object2ObjectOpenHashMap) { + this.map = new Object2ObjectArrayMap<>(); + } else { + this.map.clear(); + } + } else { + this.map.clear(); } - else this.map.clear(); } @Nonnull @@ -95,4 +103,30 @@ public Collection values() { public Set> entrySet() { return this.map.entrySet(); } -} + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map)) { + return false; + } + Map o = (Map) obj; + if (o.size() != this.size()) { + return false; + } + return this.entrySet().containsAll(o.entrySet()); + } + + @Override + public int hashCode() { + return this.map.hashCode(); + } + + @Override + public String toString() { + return this.map.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/mirror/normalasm/core/NormalTransformer.java b/src/main/java/mirror/normalasm/core/NormalTransformer.java index c8344982..ed119fa0 100644 --- a/src/main/java/mirror/normalasm/core/NormalTransformer.java +++ b/src/main/java/mirror/normalasm/core/NormalTransformer.java @@ -481,9 +481,9 @@ private byte[] removeInstancedRandom(byte[] bytes) { while (iter.hasNext()) { AbstractInsnNode instruction = iter.next(); if (instruction.getOpcode() == INVOKESTATIC) { - iter.set(new TypeInsnNode(NEW, "mirror/normalasm/api/datastructures/TagMap")); + iter.set(new TypeInsnNode(NEW, "mirror/normalasm/api/datastructures/NormalTagMap")); iter.add(new InsnNode(DUP)); - iter.add(new MethodInsnNode(INVOKESPECIAL, "mirror/normalasm/api/datastructures/TagMap", "", "()V", false)); + iter.add(new MethodInsnNode(INVOKESPECIAL, "mirror/normalasm/api/datastructures/NormalTagMap", "", "()V", false)); break; } } From e2bb422eddd5bcbc6a066afe2dd777c64ece6a39 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 26 Jan 2025 00:19:30 +0100 Subject: [PATCH 49/51] Bump to 5.22 (cherry picked from commit 5df6f76dee96837423232b7ec3bbc9298e49c753) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0dd9ae4d..ebe90780 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.21 +mod_version = 5.22 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index c6c55539..c5097656 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.21"; + public static final String VERSION = "5.22"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment(); From 946ffbb91d58265802b13e16cd09d8b31d47fa29 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 2 Feb 2025 19:23:20 +0100 Subject: [PATCH 50/51] Disable on demand animations when Optifine is installed (cherry picked from commit 3bbcfb86b85cfc2fb609093ab9f9b3a4ee5e7225) --- .../normalasm/core/NormalLoadingPlugin.java | 2 - .../core/NormalSpriteMixinPlugin.java | 47 +++++++++++++++++++ .../resources/mixins.ondemand_sprites.json | 2 +- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/java/mirror/normalasm/core/NormalSpriteMixinPlugin.java diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index c5097656..55a42397 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -214,8 +214,6 @@ public boolean shouldMixinConfigQueue(String mixinConfig) { return NormalConfig.instance.moreModelManagerCleanup; case "mixins.screenshot.json": return NormalConfig.instance.releaseScreenshotCache || NormalConfig.instance.asyncScreenshot; - case "mixins.ondemand_sprites.json": - return NormalConfig.instance.onDemandAnimatedTextures; case "mixins.resolve_mc2071.json": return NormalConfig.instance.resolveMC2071; case "mixins.fix_mc_skindownloading.json": diff --git a/src/main/java/mirror/normalasm/core/NormalSpriteMixinPlugin.java b/src/main/java/mirror/normalasm/core/NormalSpriteMixinPlugin.java new file mode 100644 index 00000000..9ab6b1cc --- /dev/null +++ b/src/main/java/mirror/normalasm/core/NormalSpriteMixinPlugin.java @@ -0,0 +1,47 @@ +package mirror.normalasm.core; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import mirror.normalasm.NormalLogger; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class NormalSpriteMixinPlugin implements IMixinConfigPlugin { + + static boolean logged = false; + + @Override + public void onLoad(String s) { } + + @Override + public String getRefMapperConfig() { + return ""; + } + + @Override + public boolean shouldApplyMixin(String s, String s1) { + if (!logged) { + NormalLogger.instance.error("Optifine is installed. On demand sprites won't be activated as Optifine already has Smart Animations."); + logged = true; + } + return !NormalTransformer.isOptifineInstalled; + } + + @Override + public void acceptTargets(Set set, Set set1) { } + + @Override + public List getMixins() { + return Collections.emptyList(); + } + + @Override + public void preApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) { } + + @Override + public void postApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) { } + +} diff --git a/src/main/resources/mixins.ondemand_sprites.json b/src/main/resources/mixins.ondemand_sprites.json index 8b9e477e..e49479a1 100644 --- a/src/main/resources/mixins.ondemand_sprites.json +++ b/src/main/resources/mixins.ondemand_sprites.json @@ -1,6 +1,6 @@ { "package": "mirror.normalasm.client.sprite.ondemand.mixins", - "plugin": "mirror.normalasm.core.NormalMixinPlugin", + "plugin": "mirror.normalasm.core.NormalSpriteMixinPlugin", "refmap": "mixins.normalasm.refmap.json", "target": "@env(DEFAULT)", "minVersion": "0.8", From 642179a6810209ce2327178ac12b05d0de56ce6c Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 2 Feb 2025 19:23:59 +0100 Subject: [PATCH 51/51] Bump to 5.23 (cherry picked from commit 09c0b1e3f3ead5e5db8a40448aee49ca41a26cea) --- gradle.properties | 2 +- src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index ebe90780..7af215a3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs = -Xmx3G # Mod Information -mod_version = 5.22 +mod_version = 5.23 maven_group = mirror.normalasm archives_base_name = normalasm diff --git a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java index 55a42397..76faf1ae 100644 --- a/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java +++ b/src/main/java/mirror/normalasm/core/NormalLoadingPlugin.java @@ -31,7 +31,7 @@ @IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion) public class NormalLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader { - public static final String VERSION = "5.22"; + public static final String VERSION = "5.23"; public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment();