diff --git a/BUILD.bat b/BUILD.bat deleted file mode 100644 index 224c3705..00000000 --- a/BUILD.bat +++ /dev/null @@ -1,3 +0,0 @@ -rd /s "./build/classes/main/" -del /s "./build/libs/" -gradlew build \ No newline at end of file diff --git a/README.md b/README.md index c04ef39b..527ec48e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,15 @@ # Sound-Physics A Minecraft mod that provides realistic sound attenuation, reverberation, and absorption through blocks. -This is a fork! TODO: There is still some cleanup left! Sonic Ether's original source code was very messy! \ No newline at end of file +Downloads are in the [releases tab](https://github.com/djpadbit/Sound-Physics/releases) + +This is a fork of a fork! I forked it from daipenger who forked it from sonicether, daipenger ported it to 1.12.2 and cleaned up the codebase, i just added some stuff. + +The stuff added in this fork: +* Automatic stero to mono downmixing of sounds (So the original resourcepack is not needed anymore) +* More compatibility with mods (Computronics & Immersive Railroading) +* Server-side support (right position for entity and computronics sounds and higher distance before sound cutoff) + +Todo: +* Rewrite Dynamic environement evaluation (feature removed for now) +* More mod compatibility ? I'm open to suggestions \ No newline at end of file diff --git a/build.gradle b/build.gradle index b51e22bf..1288de33 100644 --- a/build.gradle +++ b/build.gradle @@ -1,54 +1,60 @@ buildscript { - repositories { - jcenter() - maven { url = "http://files.minecraftforge.net/maven" } - } - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' - } + repositories { + jcenter() + maven { url = "http://files.minecraftforge.net/maven" } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + } } apply plugin: 'net.minecraftforge.gradle.forge' //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. -version = "1.12.2" +version = "${mc_version}-${mod_version}" +group = "com.sonicether.soundphysics" archivesBaseName = "Sound-Physics" jar { manifest { - attributes 'FMLCorePlugin': 'com.sonicether.soundphysics.CoreModLoader' - attributes 'FMLCorePluginContainsFMLMod': 'true' - } + attributes 'FMLCorePlugin': 'com.sonicether.soundphysics.CoreModLoader' + attributes 'FMLCorePluginContainsFMLMod': 'true' + attributes 'FMLAT': 'soundphysics_at.cfg' + } } sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. compileJava { - sourceCompatibility = targetCompatibility = '1.8' + sourceCompatibility = targetCompatibility = '1.8' } minecraft { - version = "1.12.2-14.23.0.2542" - runDir = "run" - mappings = "snapshot_20171003" + version = "${mc_version}-${forge_version}" + runDir = "run" + mappings = mcp_mappings + + replace '${version}', mod_version + replace '${mc_version}', mc_version + replaceIn "SoundPhysics.java" } dependencies { } 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' - - // replace version and mcversion - expand 'version':project.version, 'mcversion':project.minecraft.version - } - - // copy everything else except the mcmod.info - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } + // this will ensure that this task is redone when the versions change. + inputs.property "version", mod_version + inputs.property "mc_version", mc_version + + // replace stuff in mcmod.info, nothing else + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + + // replace version and mcversion + expand 'version':mod_version, 'mc_version':mc_version + } + + // copy everything else except the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } } diff --git a/gradle.properties b/gradle.properties index e9b9fd5a..bc130608 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,9 @@ # 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 + +forge_version=14.23.5.2847 +mcp_mappings=snapshot_20180814 + +mc_version=1.12.2 +mod_version=1.0.10-1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18cba72..9580f609 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-2.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip \ No newline at end of file diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/com/sonicether/soundphysics/Config.java b/src/main/java/com/sonicether/soundphysics/Config.java index 045043ae..97498ce5 100644 --- a/src/main/java/com/sonicether/soundphysics/Config.java +++ b/src/main/java/com/sonicether/soundphysics/Config.java @@ -1,5 +1,7 @@ package com.sonicether.soundphysics; +import java.io.File; + import java.util.ArrayList; import java.util.List; @@ -20,12 +22,18 @@ public class Config { // general public static float rolloffFactor; public static float globalReverbGain; + public static float globalVolumeMultiplier; public static float globalReverbBrightness; public static float soundDistanceAllowance; public static float globalBlockAbsorption; public static float globalBlockReflectance; public static float airAbsorption; + public static float snowAirAbsorptionFactor; public static float underwaterFilter; + public static boolean noteBlockEnable; + public static float maxDistance; + public static boolean volumeMulOnlyAffected; + public static float globalEchoMultiplier; // performance public static boolean skipRainOcclusionTracing; @@ -43,19 +51,38 @@ public class Config { public static float sandReflectivity; public static float snowReflectivity; + // compatibility + public static boolean computronicsPatching; + public static boolean irPatching; + public static boolean dsPatching; + public static boolean midnightPatching; + public static boolean midnightPatchingFix; + public static boolean ic2Patching; + public static boolean glibyVCPatching; + public static boolean glibyVCSrcPatching; + public static boolean autoSteroDownmix; + + // misc + public static boolean autoSteroDownmixLogging; + public static boolean injectorLogging; + private static final String categoryGeneral = "General"; private static final String categoryPerformance = "Performance"; private static final String categoryMaterialProperties = "Material properties"; + private static final String categoryCompatibility = "Compatibility"; + private static final String categoryMisc = "Misc"; static { instance = new Config(); } private Config() { + this.forgeConfig = new Configuration(new File(new File(CoreModLoader.mcDir, "config"), SoundPhysics.modid+".cfg")); + syncConfig(); } public void preInit(final FMLPreInitializationEvent event) { - this.forgeConfig = new Configuration(event.getSuggestedConfigurationFile()); + //this.forgeConfig = new Configuration(event.getSuggestedConfigurationFile()); syncConfig(); } @@ -76,6 +103,8 @@ public List getConfigElements() { list.add(new ConfigElement(this.forgeConfig.getCategory(Config.categoryGeneral))); list.add(new ConfigElement(this.forgeConfig.getCategory(Config.categoryPerformance))); list.add(new ConfigElement(this.forgeConfig.getCategory(Config.categoryMaterialProperties))); + list.add(new ConfigElement(this.forgeConfig.getCategory(Config.categoryCompatibility))); + list.add(new ConfigElement(this.forgeConfig.getCategory(Config.categoryMisc))); return list; } @@ -84,6 +113,8 @@ private void syncConfig() { // General rolloffFactor = this.forgeConfig.getFloat("Attenuation Factor", categoryGeneral, 1.0f, 0.2f, 1.0f, "Affects how quiet a sound gets based on distance. Lower values mean distant sounds are louder. 1.0 is the physically correct value."); + globalVolumeMultiplier = this.forgeConfig.getFloat("Global Volume Multiplier", categoryGeneral, 4.0f, 0.1f, 8.0f, + "The global volume multiplier of all sounds."); globalReverbGain = this.forgeConfig.getFloat("Global Reverb Gain", categoryGeneral, 1.0f, 0.1f, 2.0f, "The global volume of simulated reverberations."); globalReverbBrightness = this.forgeConfig.getFloat("Global Reverb Brightness", categoryGeneral, 1.0f, 0.1f, @@ -99,8 +130,18 @@ private void syncConfig() { "Minecraft won't allow sounds to play past a certain distance. This parameter is a multiplier for how far away a sound source is allowed to be in order for it to actually play. Values too high can cause polyphony issues."); airAbsorption = this.forgeConfig.getFloat("Air Absorption", categoryGeneral, 1.0f, 0.0f, 5.0f, "A value controlling the amount that air absorbs high frequencies with distance. A value of 1.0 is physically correct for air with normal humidity and temperature. Higher values mean air will absorb more high frequencies with distance. 0 disables this effect."); + snowAirAbsorptionFactor = this.forgeConfig.getFloat("Max Snow Air Absorption Factor", categoryGeneral, 5.0f, 0.0f, 10.0f, + "The maximum air absorption factor when it's snowing. The real absorption factor will depend on the snow's intensity. Set to 1 or lower to disable"); underwaterFilter = this.forgeConfig.getFloat("Underwater Filter", categoryGeneral, 0.8f, 0.0f, 1.0f, "How much sound is filtered when the player is underwater. 0.0 means no filter. 1.0 means fully filtered."); + noteBlockEnable = this.forgeConfig.getBoolean("Affect Note Blocks", categoryGeneral, true, + "If true, note blocks will be processed."); + maxDistance = this.forgeConfig.getFloat("Max ray distance", categoryGeneral, 256.0f, 1.0f, 8192.0f, + "How far the rays should be traced."); + volumeMulOnlyAffected = this.forgeConfig.getBoolean("Volume Multiplier Only On Affected", categoryGeneral, true, + "If true, the global volume multiplier will only be applied to affected sounds (so not to the ui sounds for example)."); + globalEchoMultiplier = this.forgeConfig.getFloat("Global Echo Multiplier", categoryGeneral, 1.0f, 0.0f, 2.0f, + "The global volume multiplier of the echos, put to 0 to disable echos all together"); // performance skipRainOcclusionTracing = this.forgeConfig.getBoolean("Skip Rain Occlusion Tracing", categoryPerformance, true, @@ -132,9 +173,35 @@ private void syncConfig() { snowReflectivity = this.forgeConfig.getFloat("Snow Reflectivity", categoryMaterialProperties, 0.2f, 0.0f, 1.0f, "Sound reflectivity for snow blocks."); + // compatibility + computronicsPatching = this.forgeConfig.getBoolean("Patch Computronics", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches the Computronics sound sources so it works with Sound Physics."); + irPatching = this.forgeConfig.getBoolean("Patch Immersive Railroading", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches the Immersive Railroading sound sources so it works with Sound Physics."); + dsPatching = this.forgeConfig.getBoolean("Patch Dynamic Surroundings", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches Dynamic Surroundings to fix some bugs with Sound Physics."); + midnightPatching = this.forgeConfig.getBoolean("Patch The Midnight", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches The Midnight to disable redundant functionality that causes some problems."); + midnightPatchingFix = this.forgeConfig.getBoolean("Readd The Midnight Reverb", categoryCompatibility, true, + "If true, readds The Midnight reverb that is removed with the patch."); + ic2Patching = this.forgeConfig.getBoolean("Patch IC2", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches IC2 audio to better work with Sound Physics."); + glibyVCPatching = this.forgeConfig.getBoolean("Patch Gliby's Voice Chat", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches Gliby's VC's copied soundsystem classes to restore Sound Physics."); + glibyVCSrcPatching = this.forgeConfig.getBoolean("Patch Gliby's VC sources", categoryCompatibility, true, + "REQUIRES RESTART. If true, patches Gliby's VC sources to work with Sound Physics."); + autoSteroDownmix = this.forgeConfig.getBoolean("Auto Stereo Downmix", categoryCompatibility, true, + "REQUIRES RESTART. If true, Automatically downmix stereo sounds that are loaded to mono"); + + // misc + autoSteroDownmixLogging = this.forgeConfig.getBoolean("Stereo downmix Logging", categoryMisc, false, + "If true, Prints sound name and format of the sounds that get converted"); + injectorLogging = this.forgeConfig.getBoolean("Injector Logging", categoryMisc, false, + "If true, Logs debug info about the injector"); + + SoundPhysics.applyConfigChanges(); if (this.forgeConfig.hasChanged()) { this.forgeConfig.save(); - SoundPhysics.applyConfigChanges(); } } diff --git a/src/main/java/com/sonicether/soundphysics/CoreModInjector.java b/src/main/java/com/sonicether/soundphysics/CoreModInjector.java index 76153951..c1c6a8c7 100644 --- a/src/main/java/com/sonicether/soundphysics/CoreModInjector.java +++ b/src/main/java/com/sonicether/soundphysics/CoreModInjector.java @@ -1,8 +1,12 @@ package com.sonicether.soundphysics; +import java.util.Map; import java.util.Iterator; import java.util.ListIterator; +import java.io.StringWriter; +import java.io.PrintWriter; + import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; @@ -14,77 +18,112 @@ import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.FrameNode; + +import org.objectweb.asm.util.TraceMethodVisitor; +import org.objectweb.asm.util.Printer; +import org.objectweb.asm.util.Textifier; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import net.minecraft.launchwrapper.IClassTransformer; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; + public class CoreModInjector implements IClassTransformer { + public static final Logger logger = LogManager.getLogger(SoundPhysics.modid+"injector"); + + private static boolean shouldPatchDS(boolean checkNew) { + if (Loader.isModLoaded("dsurround")) { + Map mods = Loader.instance().getIndexedModList(); + String version[] = mods.get("dsurround").getVersion().split("\\."); + if (version.length < 2) { + logError("What the hell, DS's version is not properly formatted ?"); + } else if ((!checkNew && version[1].equals("5")) || (checkNew && version[1].equals("6"))) { + return true; + } + } + return false; + } + + private static boolean isIC2Classic() { + if (Loader.isModLoaded("ic2")) { + Map mods = Loader.instance().getIndexedModList(); + String version = mods.get("ic2").getVersion(); + if (version.endsWith("ex112")) return false; + return true; + } + return false; + } + @Override public byte[] transform(final String obfuscated, final String deobfuscated, byte[] bytes) { if (obfuscated.equals("chm$a")) { // Inside SoundManager.SoundSystemStarterThread InsnList toInject = new InsnList(); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", "init", - "()V", false)); + "(Lpaulscode/sound/SoundSystem;)V", false)); // Target method: Constructor bytes = patchMethodInClass(obfuscated, bytes, "", "(Lchm;)V", Opcodes.INVOKESPECIAL, - AbstractInsnNode.METHOD_INSN, "", null, toInject, false, 0, 0, false, 0); + AbstractInsnNode.METHOD_INSN, "", null, -1, toInject, false, 0, 0, false, 0, -1); } else if (obfuscated.equals("chm")) { // Inside SoundManager InsnList toInject = new InsnList(); - + toInject.add(new VarInsnNode(Opcodes.ALOAD, 1)); toInject.add(new VarInsnNode(Opcodes.ALOAD, 7)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 4)); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "cgq", "a", "()Lnf;", false)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 3)); toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", - "setLastSoundCategory", "(Lqg;)V", false)); + "setLastSound", "(Lcgt;Lqg;Lnf;Lnf;)V", false)); // Target method: playSound bytes = patchMethodInClass(obfuscated, bytes, "c", "(Lcgt;)V", Opcodes.INVOKEVIRTUAL, - AbstractInsnNode.METHOD_INSN, "setVolume", null, toInject, false, 0, 0, false, 0); + AbstractInsnNode.METHOD_INSN, "setPitch", null, -1, toInject, false, 0, 0, false, 0, -1); toInject = new InsnList(); - toInject.add(new VarInsnNode(Opcodes.ALOAD, 1)); - toInject.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "cgt", "a", "()Lnf;", true)); - toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "nf", "toString", "()Ljava/lang/String;", false)); toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", - "setLastSoundName", "(Ljava/lang/String;)V", false)); + "applyGlobalVolumeMultiplier", "(F)F", false)); - // Target method: playSound + // Target method: playSound, target invocation setVolume bytes = patchMethodInClass(obfuscated, bytes, "c", "(Lcgt;)V", Opcodes.INVOKEVIRTUAL, - AbstractInsnNode.METHOD_INSN, "setVolume", null, toInject, false, 0, 0, false, 0); - - toInject = new InsnList(); - toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", - "globalVolumeMultiplier", "F")); - toInject.add(new InsnNode(Opcodes.FMUL)); - - // Target method: playSound, target invocation getClampedVolume - bytes = patchMethodInClass(obfuscated, bytes, "c", "(Lcgt;)V", Opcodes.INVOKESPECIAL, - AbstractInsnNode.METHOD_INSN, "e", "(Lcgt;)F", toInject, false, 0, 0, false, 0); + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, -1); } else - if (obfuscated.equals("paulscode.sound.libraries.SourceLWJGLOpenAL")) { + if (obfuscated.equals("paulscode.sound.libraries.SourceLWJGLOpenAL") || + (obfuscated.equals("ovr.paulscode.sound.libraries.SourceLWJGLOpenAL") && Config.glibyVCPatching)) { // Inside SourceLWJGLOpenAL InsnList toInject = new InsnList(); + final String classPath = obfuscated.replace(".","/"); + String channelPath = "paulscode/sound/libraries/ChannelLWJGLOpenAL"; + if (obfuscated.equals("ovr.paulscode.sound.libraries.SourceLWJGLOpenAL")) + channelPath = "ovr/"+channelPath; + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/libraries/SourceLWJGLOpenAL", "position", + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classPath, "position", "Lpaulscode/sound/Vector3D;")); toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/Vector3D", "x", "F")); toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/libraries/SourceLWJGLOpenAL", "position", + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classPath, "position", "Lpaulscode/sound/Vector3D;")); toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/Vector3D", "y", "F")); toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/libraries/SourceLWJGLOpenAL", "position", + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classPath, "position", "Lpaulscode/sound/Vector3D;")); toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/Vector3D", "z", "F")); toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/libraries/SourceLWJGLOpenAL", - "channelOpenAL", "Lpaulscode/sound/libraries/ChannelLWJGLOpenAL;")); - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "paulscode/sound/libraries/ChannelLWJGLOpenAL", "ALSource", + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classPath, + "channelOpenAL", "L"+channelPath+";")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, channelPath, "ALSource", "Ljava/nio/IntBuffer;")); toInject.add(new InsnNode(Opcodes.ICONST_0)); toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/nio/IntBuffer", "get", "(I)I", false)); @@ -93,7 +132,42 @@ public byte[] transform(final String obfuscated, final String deobfuscated, byte // Target method: play bytes = patchMethodInClass(obfuscated, bytes, "play", "(Lpaulscode/sound/Channel;)V", Opcodes.INVOKEVIRTUAL, - AbstractInsnNode.METHOD_INSN, "play", null, toInject, false, 0, 0, false, 0); + AbstractInsnNode.METHOD_INSN, "play", null, -1, toInject, false, 0, 0, false, 0, -1); + } else + + // Convert stero sounds to mono + if ((obfuscated.equals("paulscode.sound.libraries.LibraryLWJGLOpenAL") || + (obfuscated.equals("ovr.paulscode.sound.libraries.LibraryLWJGLOpenAL") && Config.glibyVCPatching)) && Config.autoSteroDownmix) { + // Inside LibraryLWJGLOpenAL + InsnList toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 4)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 1)); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "paulscode/sound/FilenameURL", "getFilename", "()Ljava/lang/String;", false)); + + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "onLoadSound", "(Lpaulscode/sound/SoundBuffer;Ljava/lang/String;)Lpaulscode/sound/SoundBuffer;", false)); + + toInject.add(new VarInsnNode(Opcodes.ASTORE, 4)); + //buffer = onLoadSound(SoundPhysics.buffer,filenameURL.getFilename()); + + // Target method: loadSound + bytes = patchMethodInClass(obfuscated, bytes, "loadSound", "(Lpaulscode/sound/FilenameURL;)Z", Opcodes.INVOKEINTERFACE, + AbstractInsnNode.METHOD_INSN, "cleanup", null, -1, toInject, false, 0, 0, false, 0, -1); + + toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 2)); + + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "onLoadSound", "(Lpaulscode/sound/SoundBuffer;Ljava/lang/String;)Lpaulscode/sound/SoundBuffer;", false)); + + toInject.add(new VarInsnNode(Opcodes.ASTORE, 1)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 1)); + + // Target method: loadSound + bytes = patchMethodInClass(obfuscated, bytes, "loadSound", "(Lpaulscode/sound/SoundBuffer;Ljava/lang/String;)Z", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", -1, toInject, true, 0, 0, false, 0, 0); } else if (obfuscated.equals("paulscode.sound.SoundSystem")) { @@ -108,7 +182,20 @@ public byte[] transform(final String obfuscated, final String deobfuscated, byte // Target method: newSource bytes = patchMethodInClass(obfuscated, bytes, "newSource", "(ZLjava/lang/String;Ljava/net/URL;Ljava/lang/String;ZFFFIF)V", Opcodes.INVOKESPECIAL, - AbstractInsnNode.METHOD_INSN, "", null, toInject, true, 2, 0, false, 0); + AbstractInsnNode.METHOD_INSN, "", null, -1, toInject, true, 2, 0, false, 0, -1); + + // Can't reuse the list for some reason + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "attenuationModel", "I")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalRolloffFactor", "F")); + + // Target method: newSource + bytes = patchMethodInClass(obfuscated, bytes, "newSource", + "(ZLjava/lang/String;Ljava/lang/String;ZFFFIF)V", Opcodes.INVOKESPECIAL, + AbstractInsnNode.METHOD_INSN, "", null, -1, toInject, true, 2, 0, false, 0, -1); } else if (obfuscated.equals("pl")) { @@ -123,7 +210,7 @@ public byte[] transform(final String obfuscated, final String deobfuscated, byte // Target method: sendToAllNearExcept bytes = patchMethodInClass(obfuscated, bytes, "a", "(Laed;DDDDILht;)V", Opcodes.DCMPG, - AbstractInsnNode.INSN, "", "", toInject, true, 0, 0, false, 0); + AbstractInsnNode.INSN, "", "", -1, toInject, true, 0, 0, false, 0, -1); } else if (obfuscated.equals("vg")) { @@ -140,17 +227,610 @@ public byte[] transform(final String obfuscated, final String deobfuscated, byte // Target method: playSound // Inside target method, target node: Entity/getSoundCategory bytes = patchMethodInClass(obfuscated, bytes, "a", "(Lqe;FF)V", Opcodes.INVOKEVIRTUAL, - AbstractInsnNode.METHOD_INSN, "bK", null, toInject, true, 0, 0, false, -3); + AbstractInsnNode.METHOD_INSN, "bK", null, -1, toInject, true, 0, 0, false, -3, -1); + } else + + // Fix for computronics's devices + if (obfuscated.equals("pl.asie.lib.audio.StreamingAudioPlayer") && Config.computronicsPatching) { + // Inside StreamingAudioPlayer + InsnList toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.FLOAD, 2)); + toInject.add(new VarInsnNode(Opcodes.FLOAD, 3)); + toInject.add(new VarInsnNode(Opcodes.FLOAD, 4)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 8)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/lib/audio/StreamingAudioPlayer$SourceEntry", "src", + "Ljava/nio/IntBuffer;")); + toInject.add(new InsnNode(Opcodes.ICONST_0)); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/nio/IntBuffer", "get", "(I)I", false)); + + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "onPlaySoundAL", "(FFFI)V", false)); + + // Target method: play + bytes = patchMethodInClass(obfuscated, bytes, "play", "(Ljava/lang/String;FFFF)V", Opcodes.INVOKESTATIC, + AbstractInsnNode.METHOD_INSN, "alSourceQueueBuffers", null, -1, toInject, true, 0, 0, false, -5, -1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.D2F)); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: setHearing + bytes = patchMethodInClass(obfuscated, bytes, "setHearing", "(FF)V", Opcodes.FLOAD, + AbstractInsnNode.VAR_INSN, "", null, 1, toInject, false, 0, 0, false, 0, -1); + } else + + if (obfuscated.equals("pl.asie.computronics.api.audio.AudioPacket") && Config.computronicsPatching) { + // Inside AudioPacket + InsnList toInject = new InsnList(); + + // This probably should only get multipled once, i don't know why i do it twice + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.DMUL)); + toInject.add(new InsnNode(Opcodes.D2I)); + toInject.add(new InsnNode(Opcodes.IMUL)); + + // Target method: canHearReceiver + bytes = patchMethodInClass(obfuscated, bytes, "canHearReceiver", "(Lnet/minecraft/entity/player/EntityPlayerMP;Lpl/asie/computronics/api/audio/IAudioReceiver;)Z", Opcodes.IMUL, + AbstractInsnNode.INSN, "", null, -1, toInject, false, 0, 0, false, 0, -1); + } else + + if (obfuscated.equals("pl.asie.computronics.tile.TileTapeDrive$1") && Config.computronicsPatching) { + // Inside TileTapeDrive.internalSpeaker + InsnList toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/computronics/tile/TileTapeDrive$1", "this$0", + "Lpl/asie/computronics/tile/TileTapeDrive;")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "pl/asie/computronics/Computronics", + "tapeReader", "Lpl/asie/computronics/block/BlockTapeReader;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/computronics/block/BlockTapeReader", "rotation", + "Lpl/asie/lib/block/BlockBase$Rotation;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/lib/block/BlockBase$Rotation", "FACING", + "Lnet/minecraft/block/properties/PropertyDirection;")); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", "computronicsOffset", + "(Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/tileentity/TileEntity;Lnet/minecraft/block/properties/PropertyDirection;)Lnet/minecraft/util/math/Vec3d;", false)); + + // Target method: getSoundPos + bytes = patchMethodInClass(obfuscated, bytes, "getSoundPos", "()Lnet/minecraft/util/math/Vec3d;", Opcodes.ARETURN, + AbstractInsnNode.INSN, "", null, -1, toInject, true, 0, 0, false, 0, -1); + } else + + if (obfuscated.equals("pl.asie.computronics.tile.TileSpeaker") && Config.computronicsPatching) { + // Inside TileSpeaker + InsnList toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "pl/asie/computronics/Computronics", + "speaker", "Lpl/asie/computronics/block/BlockSpeaker;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/computronics/block/BlockSpeaker", "rotation", + "Lpl/asie/lib/block/BlockBase$Rotation;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/lib/block/BlockBase$Rotation", "FACING", + "Lnet/minecraft/block/properties/PropertyDirection;")); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", "computronicsOffset", + "(Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/tileentity/TileEntity;Lnet/minecraft/block/properties/PropertyDirection;)Lnet/minecraft/util/math/Vec3d;", false)); + + // Target method: getSoundPos + bytes = patchMethodInClass(obfuscated, bytes, "getSoundPos", "()Lnet/minecraft/util/math/Vec3d;", Opcodes.ARETURN, + AbstractInsnNode.INSN, "", null, -1, toInject, true, 0, 0, false, 0, -1); + } else + + if (obfuscated.equals("pl.asie.computronics.tile.TileSpeechBox$1") && Config.computronicsPatching) { + // Inside TileSpeechBox.internalSpeaker + InsnList toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/computronics/tile/TileSpeechBox$1", "this$0", + "Lpl/asie/computronics/tile/TileSpeechBox;")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "pl/asie/computronics/Computronics", + "speechBox", "Lpl/asie/computronics/block/BlockSpeechBox;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/computronics/block/BlockSpeechBox", "rotation", + "Lpl/asie/lib/block/BlockBase$Rotation;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/lib/block/BlockBase$Rotation", "FACING", + "Lnet/minecraft/block/properties/PropertyDirection;")); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", "computronicsOffset", + "(Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/tileentity/TileEntity;Lnet/minecraft/block/properties/PropertyDirection;)Lnet/minecraft/util/math/Vec3d;", false)); + + // Target method: getSoundPos + bytes = patchMethodInClass(obfuscated, bytes, "getSoundPos", "()Lnet/minecraft/util/math/Vec3d;", Opcodes.ARETURN, + AbstractInsnNode.INSN, "", null, -1, toInject, true, 0, 0, false, 0, -1); + } else + + if ((obfuscated.equals("cam72cam.immersiverailroading.sound.ClientSound") || obfuscated.equals("cam72cam.mod.sound.ClientSound")) && Config.irPatching) { + // Inside ClientSound + InsnList toInject = new InsnList(); + + final boolean newIR = obfuscated.equals("cam72cam.mod.sound.ClientSound"); + final String classCS = obfuscated.replace(".","/"); + final String playDesc = newIR ? "(Lcam72cam/mod/math/Vec3d;)V" : "(Lnet/minecraft/util/math/Vec3d;)V"; + final String classRes = newIR ? "cam72cam/mod/resource/Identifier" : "nf"; + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "qg","i", "Lqg;")); // Ambient sound category + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classCS, "oggLocation", + "L"+classRes+";")); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classRes, "toString", "()Ljava/lang/String;", false)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "setLastSound", "(Lqg;Ljava/lang/String;)V", false)); + + // Target method: play + bytes = patchMethodInClass(obfuscated, bytes, "play", playDesc, Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "update", null, -1, toInject, false, 0, 0, false, 0, -1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.DMUL)); + + // Target method: play + bytes = patchMethodInClass(obfuscated, bytes, "play", playDesc, Opcodes.DCMPG, + AbstractInsnNode.INSN, "", null, -1, toInject, true, 0, 0, false, 0, -1); + + toInject = new InsnList(); + + // TODO: use applyGlobalVolumeMultiplier here + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: update + bytes = patchMethodInClass(obfuscated, bytes, "update", "()V", Opcodes.FMUL, + AbstractInsnNode.INSN, "", null, -1, toInject, true, 0, 0, false, 0, -1); + + // Commented code to change the position of the sound source depending on the scale of the train + // Could be implemented but needs more work/proper positions for like the wheels and stuff + /*toInject = new InsnList(); + + toInject.add(new LdcInsnNode(1.75d)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "cam72cam/immersiverailroading/sound/ClientSound", "gauge", + "Lcam72cam/immersiverailroading/library/Gauge;")); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "cam72cam/immersiverailroading/library/Gauge", "scale", "()D", false)); + toInject.add(new InsnNode(Opcodes.DMUL)); + toInject.add(new InsnNode(Opcodes.DADD)); + + // Target method: update + bytes = patchMethodInClass(obfuscated, bytes, "update", "()V", Opcodes.INVOKESPECIAL, + AbstractInsnNode.METHOD_INSN, "", "(ILjava/lang/String;FFF)V", -1, toInject, true, 0, 0, false, -5, -1);*/ + } else + + if (obfuscated.equals("org.orecruncher.dsurround.client.sound.SoundEffect") && Config.dsPatching && shouldPatchDS(false)) { + // Inside SoundEffect + InsnList toInject = new InsnList(); + + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/orecruncher/dsurround/client/sound/SoundInstance", + "noAttenuation", "()Lcgt$a;", false)); + + // Target method: createTrackingSound + bytes = patchMethodInClass(obfuscated, bytes, "createTrackingSound", "(Lnet/minecraft/entity/Entity;Z)Lorg/orecruncher/dsurround/client/sound/SoundInstance;", Opcodes.GETSTATIC, + AbstractInsnNode.FIELD_INSN, "", null, -1, toInject, true, 0, 0, true, 0, -1); + } else + + if (obfuscated.equals("org.orecruncher.dsurround.client.sound.ConfigSoundInstance") && Config.dsPatching && shouldPatchDS(true)) { + // Inside ConfigSoundInstance + InsnList toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "cgt$a","a", "Lcgt$a;")); // ISound.AttenuationType.NONE + + // Target method: constructor + bytes = patchMethodInClass(obfuscated, bytes, "", "(Ljava/lang/String;F)V", Opcodes.INVOKESTATIC, + AbstractInsnNode.METHOD_INSN, "noAttenuation", null, -1, toInject, false, 0, 0, true, 0, -1); + } else + + if (obfuscated.equals("com.mushroom.midnight.client.SoundReverbHandler") && Config.midnightPatching) { + // Inside SoundReverbHandler + InsnList toInject = new InsnList(); + + toInject.add(new InsnNode(Opcodes.RETURN)); + toInject.add(new FrameNode(Opcodes.F_SAME,0,new Object[] {},0,new Object[] {})); + + // Target method: onPlaySound + bytes = patchMethodInClass(obfuscated, bytes, "onPlaySound", "(I)V", Opcodes.GETSTATIC, + AbstractInsnNode.FIELD_INSN, "", null, -1, toInject, true, 0, 0, false, 0, 0); + } else + + if (obfuscated.equals("ic2.core.audio.AudioManagerClient") && Config.ic2Patching) { + // Inside AudioManagerClient + InsnList toInject = new InsnList(); + + final boolean ic2Classic = isIC2Classic(); + String posSpecClass = "ic2/core/audio/PositionSpec"; + String playOnceDesc = "(Ljava/lang/Object;Lic2/core/audio/PositionSpec;Ljava/lang/String;ZF)Ljava/lang/String;"; + int quickPlayRemoveCnt = 7; + if (ic2Classic) { + posSpecClass = "ic2/api/classic/audio/PositionSpec"; + playOnceDesc = "(Ljava/lang/Object;Lic2/api/classic/audio/PositionSpec;Lnet/minecraft/util/ResourceLocation;ZF)V"; + quickPlayRemoveCnt = 6; + } + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "attenuationModel", "I")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalRolloffFactor", "F")); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 2)); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, posSpecClass, "ordinal", "()I", false)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 3)); + if (ic2Classic)//toString + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "nf", "func_110623_a", "()Ljava/lang/String;", false)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "setLastSound", "(ILjava/lang/String;)V", false)); + + // Target method: playOnce + bytes = patchMethodInClass(obfuscated, bytes, "playOnce", playOnceDesc, Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "quickPlay", null, -1, toInject, true, quickPlayRemoveCnt, 0, false, 0, -1); + + toInject = new InsnList(); + + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "applyGlobalVolumeMultiplier", "(F)F", false)); + + // Target method: playOnce + bytes = patchMethodInClass(obfuscated, bytes, "playOnce", playOnceDesc, Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, -1); + } else + + if (obfuscated.equals("ic2.core.audio.AudioSourceClient") && Config.ic2Patching) { + // Inside AudioSourceClient + InsnList toInject = new InsnList(); + + final boolean ic2Classic = isIC2Classic(); + String posSpecClass = "ic2/core/audio/PositionSpec"; + String audioPosClass = "ic2/core/audio/AudioPosition"; + String posSpecField = "positionSpec"; + int cmpgType = Opcodes.FCMPG; + if (ic2Classic) { + posSpecClass = "ic2/api/classic/audio/PositionSpec"; + audioPosClass = "ic2/api/classic/audio/IAudioPosition"; + posSpecField = "soundType"; + cmpgType = Opcodes.DCMPG; + } + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioSourceClient", posSpecField, + "L"+posSpecClass+";")); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, posSpecClass, "ordinal", "()I", false)); + if (ic2Classic) { + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + } else { + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioSourceClient", "initialSoundFile", + "Ljava/lang/String;")); + } + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "setLastSound", "(ILjava/lang/String;)V", false)); + + // Target method: play + bytes = patchMethodInClass(obfuscated, bytes, "play", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "play", null, -1, toInject, true, 0, 0, false, 0, -1); + + if (ic2Classic) { + toInject = new InsnList(); + + /*toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL));*/ + + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", Opcodes.INVOKEINTERFACE, + AbstractInsnNode.METHOD_INSN, "getPosition", null, -1, toInject, true, 14, 0, false, -17, 1); + } else { + toInject = new InsnList(); + + toInject.add(new InsnNode(Opcodes.FCONST_1)); + + // Target method: updateVolume + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "getMasterVolume", null, -1, toInject, false, 0, 0, true, -5, 0); + + toInject = new InsnList(); + + /*toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F"));*/ + toInject.add(new InsnNode(Opcodes.FCONST_1)); + + // Target method: updateVolume + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "getMasterVolume", null, -1, toInject, false, 1, 0, true, 0, 1); + } + + if (ic2Classic) { + toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 1)); + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "calculateEntitySoundOffsetVec", "(Lbhe;Lvg;Lqe;)Lbhe;", false)); + toInject.add(new VarInsnNode(Opcodes.DLOAD, 3)); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.DMUL)); + toInject.add(new VarInsnNode(Opcodes.DSTORE, 3)); + + // Target method: updateVolume + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "func_174791_d", null, -1, toInject, false, 0, 0, false, 0, -1); + } else { + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.D2F)); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: updateVolume + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", Opcodes.INVOKESTATIC, + AbstractInsnNode.METHOD_INSN, "max", null, -1, toInject, false, 0, 0, false, 1, -1); + + toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 1)); + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "calculateEntitySoundOffset", "(Lvg;Lqe;)D", false)); + toInject.add(new InsnNode(Opcodes.DADD)); + + // Target method: updateVolume + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "getWorld", null, -1, toInject, true, 0, 0, false, -15, -1); // -11 without labels + } + + toInject = new InsnList(); + + if (ic2Classic) { + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioSourceClient", "position", + "Lic2/api/classic/audio/IAudioPosition;")); + toInject.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "ic2/api/classic/audio/IAudioPosition", + "getPosition", "()Lnet/minecraft/util/math/Vec3d;")); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 7)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "ic2DistanceCheckHook", "(DDLbhe;Lbhe;)I", false)); + } else { + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioSourceClient", "position", + "Lic2/core/audio/AudioPosition;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioPosition", "x", "F")); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioSourceClient", "position", + "Lic2/core/audio/AudioPosition;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioPosition", "y", "F")); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioSourceClient", "position", + "Lic2/core/audio/AudioPosition;")); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "ic2/core/audio/AudioPosition", "z", "F")); + toInject.add(new VarInsnNode(Opcodes.FLOAD, 6)); + toInject.add(new VarInsnNode(Opcodes.FLOAD, 7)); + toInject.add(new VarInsnNode(Opcodes.FLOAD, 8)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "ic2DistanceCheckHook", "(FFFFFFFF)I", false)); + } + + // Target method: updateVolume + bytes = patchMethodInClass(obfuscated, bytes, "updateVolume", "(Lnet/minecraft/entity/player/EntityPlayer;)V", cmpgType, + AbstractInsnNode.INSN, null, null, -1, toInject, false, 0, 0, true, 0, -1); + } else + + if (obfuscated.equals("net.gliby.voicechat.client.sound.ClientStreamManager") && Config.glibyVCSrcPatching) { + + InsnList toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 4)); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/gliby/voicechat/common/PlayerProxy", "getPlayer", "()Lnet/minecraft/entity/Entity;", false)); + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "calculateEntitySoundOffset", "(Lvg;Lqe;)D", false)); + toInject.add(new InsnNode(Opcodes.D2F)); + toInject.add(new InsnNode(Opcodes.FADD)); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "createStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "rawDataStream", null, -1, toInject, true, 0, 0, false, -8, 0); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "attenuationModel", "I")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalRolloffFactor", "F")); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "createStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "rawDataStream", null, -1, toInject, true, 6, 0, false, 0, 0); + + toInject = new InsnList(); + + toInject.add(new InsnNode(Opcodes.DUP)); + toInject.add(new VarInsnNode(Opcodes.ASTORE, 6)); + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "calculateEntitySoundOffset", "(Lvg;Lqe;)D", false)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 6)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/entity/EntityPlayerSP", "field_70163_u", "D")); + toInject.add(new InsnNode(Opcodes.DADD)); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "createStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "rawDataStream", null, -1, toInject, true, 0, 0, true, -13, 1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "attenuationModel", "I")); + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalRolloffFactor", "F")); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "createStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "rawDataStream", null, -1, toInject, true, 6, 0, false, 0, 1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "createStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, 0); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "createStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, 1); + + toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 4)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "onPlaySound", "(Ljava/lang/String;)V", false)); + + // Target method: giveStream + bytes = patchMethodInClass(obfuscated, bytes, "giveStream", "(Lnet/gliby/voicechat/client/sound/Datalet;)V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "feedRawAudioData", null, -1, toInject, false, 0, 0, false, 0, -1); + + } else + + if (obfuscated.equals("net.gliby.voicechat.client.sound.thread.ThreadUpdateStream") && Config.glibyVCSrcPatching) { + + InsnList toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: run + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, 0); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: run + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, 1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "attenuationModel", "I")); + + // Target method: run + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setAttenuation", null, -1, toInject, true, 1, 0, false, 0, -1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalRolloffFactor", "F")); + + // Target method: run + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setDistOrRoll", null, -1, toInject, true, 5, 0, false, 0, -1); + + toInject = new InsnList(); + + toInject.add(new VarInsnNode(Opcodes.ALOAD, 3)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "net/gliby/voicechat/client/sound/ClientStream", "player", "Lnet/gliby/voicechat/common/PlayerProxy;")); + toInject.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/gliby/voicechat/common/PlayerProxy", "getPlayer", "()Lnet/minecraft/entity/Entity;", false)); + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "calculateEntitySoundOffset", "(Lvg;Lqe;)D", false)); + toInject.add(new InsnNode(Opcodes.D2F)); + toInject.add(new InsnNode(Opcodes.FADD)); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setPosition", null, -1, toInject, true, 0, 0, false, -2, 0); + + toInject = new InsnList(); + + toInject.add(new InsnNode(Opcodes.DUP)); + toInject.add(new VarInsnNode(Opcodes.ASTORE, 7)); + toInject.add(new InsnNode(Opcodes.ACONST_NULL)); + toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/sonicether/soundphysics/SoundPhysics", + "calculateEntitySoundOffset", "(Lvg;Lqe;)D", false)); + toInject.add(new VarInsnNode(Opcodes.ALOAD, 7)); + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/entity/EntityPlayerSP", "field_70163_u", "D")); + toInject.add(new InsnNode(Opcodes.DADD)); + + // Target method: createStream + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setPosition", null, -1, toInject, true, 0, 0, true, -7, 1); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "globalVolumeMultiplier0", "F")); + toInject.add(new InsnNode(Opcodes.FMUL)); + + // Target method: run + bytes = patchMethodInClass(obfuscated, bytes, "run", "()V", Opcodes.INVOKEVIRTUAL, + AbstractInsnNode.METHOD_INSN, "setVolume", null, -1, toInject, true, 0, 0, false, 0, 2); + + } else + + if (obfuscated.equals("net.gliby.voicechat.common.networking.ServerStreamManager") && Config.glibyVCSrcPatching) { + + InsnList toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.DMUL)); + + // Target method: feedWithinEntityWithRadius + bytes = patchMethodInClass(obfuscated, bytes, "feedWithinEntityWithRadius", + "(Lnet/gliby/voicechat/common/networking/ServerStream;Lnet/gliby/voicechat/common/networking/ServerDatalet;)V", + Opcodes.DCMPG, AbstractInsnNode.INSN, null, null, -1, toInject, true, 0, 0, false, 0, 0); + + toInject = new InsnList(); + + toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/sonicether/soundphysics/SoundPhysics", + "soundDistanceAllowance", "D")); + toInject.add(new InsnNode(Opcodes.DMUL)); + + // Target method: feedWithinEntityWithRadius + bytes = patchMethodInClass(obfuscated, bytes, "feedWithinEntityWithRadius", + "(Lnet/gliby/voicechat/common/networking/ServerStream;Lnet/gliby/voicechat/common/networking/ServerDatalet;)V", + Opcodes.DCMPG, AbstractInsnNode.INSN, null, null, -1, toInject, true, 0, 0, false, 0, 1); + } + //log("Finished processing class: '"+obfuscated+"' ('"+deobfuscated+"')"); + return bytes; } + private static Printer printer = new Textifier(); + private static TraceMethodVisitor mp = new TraceMethodVisitor(printer); + + public static String insnToString(AbstractInsnNode insn) { + insn.accept(mp); + StringWriter sw = new StringWriter(); + printer.print(new PrintWriter(sw)); + printer.getText().clear(); + return sw.toString(); + } + private byte[] patchMethodInClass(String className, final byte[] bytes, final String targetMethod, final String targetMethodSignature, final int targetNodeOpcode, final int targetNodeType, - final String targetInvocationMethodName, final String targetInvocationMethodSignature, + final String targetInvocationMethodName, final String targetInvocationMethodSignature, final int targetVarNodeIndex, final InsnList instructionsToInject, final boolean insertBefore, final int nodesToDeleteBefore, - final int nodesToDeleteAfter, final boolean deleteTargetNode, final int targetNodeOffset) { + final int nodesToDeleteAfter, final boolean deleteTargetNode, final int targetNodeOffset, final int targetNodeNumber) { + log("Patching class : "+className); final ClassNode classNode = new ClassNode(); final ClassReader classReader = new ClassReader(bytes); @@ -159,15 +839,18 @@ private byte[] patchMethodInClass(String className, final byte[] bytes, final St while (methodIterator.hasNext()) { final MethodNode m = methodIterator.next(); + //log("@" + m.name + " " + m.desc); if (m.name.equals(targetMethod) && m.desc.equals(targetMethodSignature)) { - + log("Inside target method: " + targetMethod); + AbstractInsnNode targetNode = null; + int targetNodeNb = 0; final ListIterator nodeIterator = m.instructions.iterator(); while (nodeIterator.hasNext()) { AbstractInsnNode currentNode = nodeIterator.next(); - + //log(insnToString(currentNode).replace("\n", "")); if (currentNode.getOpcode() == targetNodeOpcode) { if (targetNodeType == AbstractInsnNode.METHOD_INSN) { @@ -176,18 +859,30 @@ private byte[] patchMethodInClass(String className, final byte[] bytes, final St if (method.name.equals(targetInvocationMethodName)) { if (method.desc.equals(targetInvocationMethodSignature) || targetInvocationMethodSignature == null) { + log("Found target method invocation for injection: " + targetInvocationMethodName); targetNode = currentNode; - // Due to collisions, do not put break - // statements here! + if (targetNodeNumber >= 0 && targetNodeNb == targetNodeNumber) break; + targetNodeNb++; } } } + } else if (targetNodeType == AbstractInsnNode.VAR_INSN) { + if (currentNode.getType() == AbstractInsnNode.VAR_INSN) { + final VarInsnNode varnode = (VarInsnNode) currentNode; + if (targetVarNodeIndex < 0 || varnode.var == targetVarNodeIndex) { + log("Found target var node for injection: " + targetVarNodeIndex); + targetNode = currentNode; + if (targetNodeNumber >= 0 && targetNodeNb == targetNodeNumber) break; + targetNodeNb++; + } + } } else { if (currentNode.getType() == targetNodeType) { + log("Found target node for injection: " + targetNodeType); targetNode = currentNode; - // Due to collisions, do not put break - // statements here! + if (targetNodeNumber >= 0 && targetNodeNb == targetNodeNumber) break; + targetNodeNb++; } } @@ -195,7 +890,7 @@ private byte[] patchMethodInClass(String className, final byte[] bytes, final St } if (targetNode == null) { - SoundPhysics.logError("Target node not found!" + className); + logError("Target node not found! " + className); break; } @@ -213,11 +908,15 @@ private byte[] patchMethodInClass(String className, final byte[] bytes, final St // If we've found the target, inject the instructions! for (int i = 0; i < nodesToDeleteBefore; i++) { final AbstractInsnNode previousNode = targetNode.getPrevious(); + //log("Removing Node " + insnToString(previousNode).replace("\n", "")); + log("Removing Node " + previousNode.getOpcode()); m.instructions.remove(previousNode); } for (int i = 0; i < nodesToDeleteAfter; i++) { final AbstractInsnNode nextNode = targetNode.getNext(); + //log("Removing Node " + insnToString(nextNode).replace("\n", "")); + log("Removing Node " + nextNode.getOpcode()); m.instructions.remove(nextNode); } @@ -234,10 +933,18 @@ private byte[] patchMethodInClass(String className, final byte[] bytes, final St break; } } + log("Class finished : "+className); final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } + public static void log(final String message) { + if (Config.injectorLogging) logger.info(message); + } + + public static void logError(final String errorMessage) { + logger.error(errorMessage); + } } diff --git a/src/main/java/com/sonicether/soundphysics/CoreModLoader.java b/src/main/java/com/sonicether/soundphysics/CoreModLoader.java index 0f2f40ec..ba50cd89 100644 --- a/src/main/java/com/sonicether/soundphysics/CoreModLoader.java +++ b/src/main/java/com/sonicether/soundphysics/CoreModLoader.java @@ -1,5 +1,7 @@ package com.sonicether.soundphysics; +import java.io.File; + import java.util.Map; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; @@ -8,6 +10,8 @@ @MCVersion(value = SoundPhysics.mcVersion) public class CoreModLoader implements IFMLLoadingPlugin { + public static File mcDir; + @Override public String[] getASMTransformerClass() { return new String[] { CoreModInjector.class.getName() }; @@ -25,6 +29,7 @@ public String getSetupClass() { @Override public void injectData(final Map data) { + mcDir = (File)data.get("mcLocation"); } @Override diff --git a/src/main/java/com/sonicether/soundphysics/ReverbParams.java b/src/main/java/com/sonicether/soundphysics/ReverbParams.java index 97f269f0..43099bc2 100644 --- a/src/main/java/com/sonicether/soundphysics/ReverbParams.java +++ b/src/main/java/com/sonicether/soundphysics/ReverbParams.java @@ -1,90 +1,100 @@ -package com.sonicether.soundphysics; - -public class ReverbParams { - - public float decayTime; // min: 0.1f max: 10.0f - public float density; // min: 0.0f max: 1.0f - public float diffusion; // min: 0.0f max: 1.0f - public float gain; // min: 0.0f max: 1.0f - public float gainHF; // min: 0.0f max: 1.0f - public float decayHFRatio; // min: 0.1f max: 2.0f - public float reflectionsGain; // min: 0.1f max: 3.16f - public float reflectionsDelay; // min: 0.0f max: 0.3f - public float lateReverbGain; // min: 0.0f max: 10.0f - public float lateReverbDelay; // min: 0.0f max: 0.1f - public float airAbsorptionGainHF; // min: 0.892f max: 1.0f - public float roomRolloffFactor; // min: 0.0f max: 10.0f - - public static ReverbParams getReverb0() { - final ReverbParams r = new ReverbParams(); - r.decayTime = 0.15f; - r.density = 0.0f; - r.diffusion = 1.0f; - r.gain = 0.2f * SoundPhysics.globalReverbMultiplier * 0.85f; - r.gainHF = 0.99f; - r.decayHFRatio = 0.6f * Config.globalReverbBrightness; - r.reflectionsGain = 2.5f; - r.reflectionsDelay = 0.001f; - r.lateReverbGain = 1.26f; - r.lateReverbDelay = 0.011f; - r.airAbsorptionGainHF = 0.994f; - r.roomRolloffFactor = 0.16f * Config.rolloffFactor; - - return r; - } - - public static ReverbParams getReverb1() { - final ReverbParams r = new ReverbParams(); - r.decayTime = 0.55f; - r.density = 0.0f; - r.diffusion = 1.0f; - r.gain = 0.3f * SoundPhysics.globalReverbMultiplier * 0.85f; - r.gainHF = 0.99f; - r.decayHFRatio = 0.7f * Config.globalReverbBrightness; - r.reflectionsGain = 0.2f; - r.reflectionsDelay = 0.015f; - r.lateReverbGain = 1.26f; - r.lateReverbDelay = 0.011f; - r.airAbsorptionGainHF = 0.994f; - r.roomRolloffFactor = 0.15f * Config.rolloffFactor; - - return r; - } - - public static ReverbParams getReverb2() { - final ReverbParams r = new ReverbParams(); - r.decayTime = 1.68f; - r.density = 0.1f; - r.diffusion = 1.0f; - r.gain = 0.5f * SoundPhysics.globalReverbMultiplier * 0.85f; - r.gainHF = 0.99f; - r.decayHFRatio = 0.7f * Config.globalReverbBrightness; - r.reflectionsGain = 0.0f; - r.reflectionsDelay = 0.021f; - r.lateReverbGain = 1.26f; - r.lateReverbDelay = 0.021f; - r.airAbsorptionGainHF = 0.994f; - r.roomRolloffFactor = 0.13f * Config.rolloffFactor; - - return r; - } - - public static ReverbParams getReverb3() { - final ReverbParams r = new ReverbParams(); - r.decayTime = 4.142f; - r.density = 0.5f; - r.diffusion = 1.0f; - r.gain = 0.4f * SoundPhysics.globalReverbMultiplier * 0.85f; - r.gainHF = 0.89f; - r.decayHFRatio = 0.7f * Config.globalReverbBrightness; - r.reflectionsGain = 0.0f; - r.reflectionsDelay = 0.025f; - r.lateReverbGain = 1.26f; - r.lateReverbDelay = 0.021f; - r.airAbsorptionGainHF = 0.994f; - r.roomRolloffFactor = 0.11f * Config.rolloffFactor; - - return r; - } - -} +package com.sonicether.soundphysics; + +public class ReverbParams { + + public float decayTime; // min: 0.1f max: 10.0f + public float density; // min: 0.0f max: 1.0f + public float diffusion; // min: 0.0f max: 1.0f + public float gain; // min: 0.0f max: 1.0f + public float gainHF; // min: 0.0f max: 1.0f + public float decayHFRatio; // min: 0.1f max: 2.0f + public float reflectionsGain; // min: 0.1f max: 3.16f + public float reflectionsDelay; // min: 0.0f max: 0.3f + public float lateReverbGain; // min: 0.0f max: 10.0f + public float lateReverbDelay; // min: 0.0f max: 0.1f + public float airAbsorptionGainHF; // min: 0.892f max: 1.0f + public float roomRolloffFactor; // min: 0.0f max: 10.0f + public float echoTime; // min: 0.075f max: 0.25f + public float echoDepth; // min: 0.0f max: 1.0f + + public static ReverbParams getReverb0() { + final ReverbParams r = new ReverbParams(); + r.decayTime = 0.15f; + r.density = 0.0f; + r.diffusion = 1.0f; + r.gain = 0.2f * SoundPhysics.globalReverbMultiplier * 0.85f; + r.gainHF = 0.99f; + r.decayHFRatio = 0.6f * Config.globalReverbBrightness; + r.reflectionsGain = 2.5f; + r.reflectionsDelay = 0.001f; + r.lateReverbGain = 1.26f; + r.lateReverbDelay = 0.011f; + r.airAbsorptionGainHF = 0.994f; + r.roomRolloffFactor = 0.16f * Config.rolloffFactor; + r.echoTime = 0.090f; + r.echoDepth = 0.1f * Config.globalEchoMultiplier; + + return r; + } + + public static ReverbParams getReverb1() { + final ReverbParams r = new ReverbParams(); + r.decayTime = 0.55f; + r.density = 0.0f; + r.diffusion = 1.0f; + r.gain = 0.3f * SoundPhysics.globalReverbMultiplier * 0.85f; + r.gainHF = 0.99f; + r.decayHFRatio = 0.7f * Config.globalReverbBrightness; + r.reflectionsGain = 0.2f; + r.reflectionsDelay = 0.015f; + r.lateReverbGain = 1.26f; + r.lateReverbDelay = 0.011f; + r.airAbsorptionGainHF = 0.994f; + r.roomRolloffFactor = 0.15f * Config.rolloffFactor; + r.echoTime = 0.1f; + r.echoDepth = 0.15f * Config.globalEchoMultiplier; + + return r; + } + + public static ReverbParams getReverb2() { + final ReverbParams r = new ReverbParams(); + r.decayTime = 1.68f; + r.density = 0.1f; + r.diffusion = 1.0f; + r.gain = 0.5f * SoundPhysics.globalReverbMultiplier * 0.85f; + r.gainHF = 0.99f; + r.decayHFRatio = 0.7f * Config.globalReverbBrightness; + r.reflectionsGain = 0.0f; + r.reflectionsDelay = 0.021f; + r.lateReverbGain = 1.26f; + r.lateReverbDelay = 0.021f; + r.airAbsorptionGainHF = 0.994f; + r.roomRolloffFactor = 0.13f * Config.rolloffFactor; + r.echoTime = 0.13f; + r.echoDepth = 0.3f * Config.globalEchoMultiplier; + + return r; + } + + public static ReverbParams getReverb3() { + final ReverbParams r = new ReverbParams(); + r.decayTime = 4.142f; + r.density = 0.5f; + r.diffusion = 1.0f; + r.gain = 0.4f * SoundPhysics.globalReverbMultiplier * 0.85f; + r.gainHF = 0.89f; + r.decayHFRatio = 0.7f * Config.globalReverbBrightness; + r.reflectionsGain = 0.0f; + r.reflectionsDelay = 0.025f; + r.lateReverbGain = 1.26f; + r.lateReverbDelay = 0.021f; + r.airAbsorptionGainHF = 0.994f; + r.roomRolloffFactor = 0.11f * Config.rolloffFactor; + r.echoTime = 0.20f; + r.echoDepth = 0.5f * Config.globalEchoMultiplier; + + return r; + } + +} diff --git a/src/main/java/com/sonicether/soundphysics/SoundPhysics.java b/src/main/java/com/sonicether/soundphysics/SoundPhysics.java index 57a41b1b..ad15b7aa 100644 --- a/src/main/java/com/sonicether/soundphysics/SoundPhysics.java +++ b/src/main/java/com/sonicether/soundphysics/SoundPhysics.java @@ -1,640 +1,902 @@ -package com.sonicether.soundphysics; - -import java.util.regex.Pattern; - -import org.lwjgl.openal.AL10; -import org.lwjgl.openal.AL11; -import org.lwjgl.openal.ALC10; -import org.lwjgl.openal.ALCcontext; -import org.lwjgl.openal.ALCdevice; -import org.lwjgl.openal.EFX10; - -import net.minecraft.block.Block; -import net.minecraft.block.SoundType; -import net.minecraft.block.material.Material; -import net.minecraft.client.Minecraft; -import net.minecraft.entity.Entity; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import paulscode.sound.SoundSystemConfig; - -@Mod(modid = SoundPhysics.modid, clientSideOnly = true, acceptedMinecraftVersions = SoundPhysics.mcVersion, version = SoundPhysics.version, guiFactory = "com.sonicether.soundphysics.SPGuiFactory") -public class SoundPhysics { - - public static final String modid = "soundphysics"; - public static final String version = "1.0.4"; - public static final String mcVersion = "1.12.2"; - - private static final Pattern rainPattern = Pattern.compile(".*rain.*"); - private static final Pattern stepPattern = Pattern.compile(".*step.*"); - private static final Pattern blockPattern = Pattern.compile(".*block.*"); - - @Mod.EventHandler - public void preInit(final FMLPreInitializationEvent event) { - Config.instance.preInit(event); - } - - @Mod.EventHandler - public void init(final FMLInitializationEvent event) { - Config.instance.init(event); - } - - private static final String logPrefix = "[SOUND PHYSICS]"; - private static int auxFXSlot0; - private static int auxFXSlot1; - private static int auxFXSlot2; - private static int auxFXSlot3; - private static int reverb0; - private static int reverb1; - private static int reverb2; - private static int reverb3; - private static int directFilter0; - private static int sendFilter0; - private static int sendFilter1; - private static int sendFilter2; - private static int sendFilter3; - - private static Minecraft mc; - - private static SoundCategory lastSoundCategory; - private static String lastSoundName; - - // THESE VARIABLES ARE CONSTANTLY ACCESSED AND USED BY ASM INJECTED CODE! DO - // NOT REMOVE! - public static int attenuationModel = SoundSystemConfig.ATTENUATION_ROLLOFF; - public static float globalRolloffFactor = Config.rolloffFactor; - public static float globalVolumeMultiplier = 4.0f; - public static float globalReverbMultiplier = 0.7f * Config.globalReverbGain; - public static double soundDistanceAllowance = Config.soundDistanceAllowance; - - /** - * CALLED BY ASM INJECTED CODE! - */ - public static void init() { - setupEFX(); - mc = Minecraft.getMinecraft(); - } - - public static void applyConfigChanges() { - globalRolloffFactor = Config.rolloffFactor; - globalReverbMultiplier = 0.7f * Config.globalReverbGain; - soundDistanceAllowance = Config.soundDistanceAllowance; - - if (auxFXSlot0 != 0) { - // Set the global reverb parameters and apply them to the effect and - // effectslot - setReverbParams(ReverbParams.getReverb0(), auxFXSlot0, reverb0); - setReverbParams(ReverbParams.getReverb1(), auxFXSlot1, reverb1); - setReverbParams(ReverbParams.getReverb2(), auxFXSlot2, reverb2); - setReverbParams(ReverbParams.getReverb3(), auxFXSlot3, reverb3); - } - } - - private static void setupEFX() { - // Get current context and device - final ALCcontext currentContext = ALC10.alcGetCurrentContext(); - final ALCdevice currentDevice = ALC10.alcGetContextsDevice(currentContext); - - if (ALC10.alcIsExtensionPresent(currentDevice, "ALC_EXT_EFX")) { - log("EFX Extension recognized."); - } else { - logError("EFX Extension not found on current device. Aborting."); - return; - } - - // Create auxiliary effect slots - auxFXSlot0 = EFX10.alGenAuxiliaryEffectSlots(); - log("Aux slot " + auxFXSlot0 + " created"); - EFX10.alAuxiliaryEffectSloti(auxFXSlot0, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); - - auxFXSlot1 = EFX10.alGenAuxiliaryEffectSlots(); - log("Aux slot " + auxFXSlot1 + " created"); - EFX10.alAuxiliaryEffectSloti(auxFXSlot1, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); - - auxFXSlot2 = EFX10.alGenAuxiliaryEffectSlots(); - log("Aux slot " + auxFXSlot2 + " created"); - EFX10.alAuxiliaryEffectSloti(auxFXSlot2, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); - - auxFXSlot3 = EFX10.alGenAuxiliaryEffectSlots(); - log("Aux slot " + auxFXSlot3 + " created"); - EFX10.alAuxiliaryEffectSloti(auxFXSlot3, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); - checkErrorLog("Failed creating auxiliary effect slots!"); - - reverb0 = EFX10.alGenEffects(); - EFX10.alEffecti(reverb0, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); - checkErrorLog("Failed creating reverb effect slot 0!"); - reverb1 = EFX10.alGenEffects(); - EFX10.alEffecti(reverb1, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); - checkErrorLog("Failed creating reverb effect slot 1!"); - reverb2 = EFX10.alGenEffects(); - EFX10.alEffecti(reverb2, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); - checkErrorLog("Failed creating reverb effect slot 2!"); - reverb3 = EFX10.alGenEffects(); - EFX10.alEffecti(reverb3, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); - checkErrorLog("Failed creating reverb effect slot 3!"); - - // Create filters - directFilter0 = EFX10.alGenFilters(); - EFX10.alFilteri(directFilter0, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); - - sendFilter0 = EFX10.alGenFilters(); - EFX10.alFilteri(sendFilter0, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); - - sendFilter1 = EFX10.alGenFilters(); - EFX10.alFilteri(sendFilter1, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); - - sendFilter2 = EFX10.alGenFilters(); - EFX10.alFilteri(sendFilter2, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); - - sendFilter3 = EFX10.alGenFilters(); - EFX10.alFilteri(sendFilter3, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); - checkErrorLog("Error creating lowpass filters!"); - - applyConfigChanges(); - } - - /** - * CALLED BY ASM INJECTED CODE! - */ - public static void setLastSoundCategory(final SoundCategory sc) { - lastSoundCategory = sc; - } - - /** - * CALLED BY ASM INJECTED CODE! - */ - public static void setLastSoundName(final String name) { - lastSoundName = name; - } - - /** - * CALLED BY ASM INJECTED CODE! - */ - public static void onPlaySound(final float posX, final float posY, final float posZ, final int sourceID) { - evaluateEnvironment(sourceID, posX, posY, posZ); - } - - /** - * CALLED BY ASM INJECTED CODE! - */ - public static double calculateEntitySoundOffset(final Entity entity, final SoundEvent sound) { - if (stepPattern.matcher(sound.getSoundName().getResourcePath()).matches()) { - return 0; - } - - return entity.getEyeHeight(); - } - - @SuppressWarnings("deprecation") - private static float getBlockReflectivity(final BlockPos blockPos) { - final Block block = mc.world.getBlockState(blockPos).getBlock(); - final SoundType soundType = block.getSoundType(); - - float reflectivity = 0.5f; - - if (soundType == SoundType.STONE) { - reflectivity = Config.stoneReflectivity; - } else if (soundType == SoundType.WOOD) { - reflectivity = Config.woodReflectivity; - } else if (soundType == SoundType.GROUND) { - reflectivity = Config.groundReflectivity; - } else if (soundType == SoundType.PLANT) { - reflectivity = Config.plantReflectivity; - } else if (soundType == SoundType.METAL) { - reflectivity = Config.metalReflectivity; - } else if (soundType == SoundType.GLASS) { - reflectivity = Config.glassReflectivity; - } else if (soundType == SoundType.CLOTH) { - reflectivity = Config.clothReflectivity; - } else if (soundType == SoundType.SAND) { - reflectivity = Config.sandReflectivity; - } else if (soundType == SoundType.SNOW) { - reflectivity = Config.snowReflectivity; - } else if (soundType == SoundType.LADDER) { - reflectivity = Config.woodReflectivity; - } else if (soundType == SoundType.ANVIL) { - reflectivity = Config.metalReflectivity; - } - - reflectivity *= Config.globalBlockReflectance; - - return reflectivity; - } - - private static Vec3d getNormalFromFacing(final EnumFacing sideHit) { - return new Vec3d(sideHit.getDirectionVec()); - } - - private static Vec3d reflect(final Vec3d dir, final Vec3d normal) { - final double dot2 = dir.dotProduct(normal) * 2; - - final double x = dir.x - dot2 * normal.x; - final double y = dir.y - dot2 * normal.y; - final double z = dir.z - dot2 * normal.z; - - return new Vec3d(x, y, z); - } - - private static Vec3d offsetSoundByName(final double soundX, final double soundY, final double soundZ, - final Vec3d playerPos, final String name, final SoundCategory category) { - double offsetX = 0.0; - double offsetY = 0.0; - double offsetZ = 0.0; - double offsetTowardsPlayer = 0.0; - - double tempNormX = 0; - double tempNormY = 0; - double tempNormZ = 0; - - if (soundY % 1.0 < 0.001 || stepPattern.matcher(name).matches()) { - offsetY = 0.1; - } - - if (category == SoundCategory.BLOCKS || blockPattern.matcher(name).matches()) { - // The ray will probably hit the block that it's emitting from - // before - // escaping. Offset the ray start position towards the player by the - // diagonal half length of a cube - - tempNormX = playerPos.x - soundX; - tempNormY = playerPos.y - soundY; - tempNormZ = playerPos.z - soundZ; - final double length = Math.sqrt(tempNormX * tempNormX + tempNormY * tempNormY + tempNormZ * tempNormZ); - tempNormX /= length; - tempNormY /= length; - tempNormZ /= length; - // 0.867 > square root of 0.5^2 * 3 - offsetTowardsPlayer = 0.867; - offsetX += tempNormX * offsetTowardsPlayer; - offsetY += tempNormY * offsetTowardsPlayer; - offsetZ += tempNormZ * offsetTowardsPlayer; - } - - return new Vec3d(soundX + offsetX, soundY + offsetY, soundZ + offsetZ); - } - - @SuppressWarnings("deprecation") - private static void evaluateEnvironment(final int sourceID, final float posX, final float posY, final float posZ) { - if (mc.player == null | mc.world == null | posY <= 0 | lastSoundCategory == SoundCategory.RECORDS - | lastSoundCategory == SoundCategory.MUSIC) { - // posY <= 0 as a condition has to be there: Ingame - // menu clicks do have a player and world present - setEnvironment(sourceID, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); - return; - } - - final boolean isRain = rainPattern.matcher(lastSoundName).matches(); - - if (Config.skipRainOcclusionTracing && isRain) { - setEnvironment(sourceID, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); - return; - } - - float directCutoff = 1.0f; - final float absorptionCoeff = Config.globalBlockAbsorption * 3.0f; - - final Vec3d playerPos = new Vec3d(mc.player.posX, mc.player.posY + mc.player.getEyeHeight(), mc.player.posZ); - final Vec3d soundPos = offsetSoundByName(posX, posY, posZ, playerPos, lastSoundName, lastSoundCategory); - final Vec3d normalToPlayer = playerPos.subtract(soundPos).normalize(); - - Vec3d rayOrigin = soundPos; - - float occlusionAccumulation = 0.0f; - - for (int i = 0; i < 10; i++) { - final RayTraceResult rayHit = mc.world.rayTraceBlocks(rayOrigin, playerPos, true); - - if (rayHit == null) { - break; - } - - final Block blockHit = mc.world.getBlockState(rayHit.getBlockPos()).getBlock(); - - float blockOcclusion = 1.0f; - - if (!blockHit.isOpaqueCube(blockHit.getDefaultState())) { - // log("not a solid block!"); - blockOcclusion *= 0.15f; - } - - occlusionAccumulation += blockOcclusion; - - rayOrigin = new Vec3d(rayHit.hitVec.x + normalToPlayer.x * 0.1, rayHit.hitVec.y + normalToPlayer.y * 0.1, - rayHit.hitVec.z + normalToPlayer.z * 0.1); - } - - directCutoff = (float) Math.exp(-occlusionAccumulation * absorptionCoeff); - float directGain = (float) Math.pow(directCutoff, 0.1); - - // Calculate reverb parameters for this sound - float sendGain0 = 0.0f; - float sendGain1 = 0.0f; - float sendGain2 = 0.0f; - float sendGain3 = 0.0f; - - float sendCutoff0 = 1.0f; - float sendCutoff1 = 1.0f; - float sendCutoff2 = 1.0f; - float sendCutoff3 = 1.0f; - - if (mc.player.isInsideOfMaterial(Material.WATER)) { - directCutoff *= 1.0f - Config.underwaterFilter; - } - - if (isRain) { - setEnvironment(sourceID, sendGain0, sendGain1, sendGain2, sendGain3, sendCutoff0, sendCutoff1, sendCutoff2, - sendCutoff3, directCutoff, directGain); - return; - } - - // Shoot rays around sound - final float phi = 1.618033988f; - final float gAngle = phi * (float) Math.PI * 2.0f; - final float maxDistance = 256.0f; - - final int numRays = Config.environmentEvaluationRays; - final int rayBounces = 4; - - final float[] bounceReflectivityRatio = new float[rayBounces]; - - float sharedAirspace = 0.0f; - - final float rcpTotalRays = 1.0f / (numRays * rayBounces); - final float rcpPrimaryRays = 1.0f / numRays; - - for (int i = 0; i < numRays; i++) { - final float fi = i; - final float fiN = fi / numRays; - final float longitude = gAngle * fi; - final float latitude = (float) Math.asin(fiN * 2.0f - 1.0f); - - final Vec3d rayDir = new Vec3d(Math.cos(latitude) * Math.cos(longitude), - Math.cos(latitude) * Math.sin(longitude), Math.sin(latitude)); - - final Vec3d rayStart = new Vec3d(soundPos.x, soundPos.y, soundPos.z); - - final Vec3d rayEnd = new Vec3d(rayStart.x + rayDir.x * maxDistance, rayStart.y + rayDir.y * maxDistance, - rayStart.z + rayDir.z * maxDistance); - - final RayTraceResult rayHit = mc.world.rayTraceBlocks(rayStart, rayEnd, true); - - if (rayHit != null) { - final double rayLength = soundPos.distanceTo(rayHit.hitVec); - - // Additional bounces - BlockPos lastHitBlock = rayHit.getBlockPos(); - Vec3d lastHitPos = rayHit.hitVec; - Vec3d lastHitNormal = getNormalFromFacing(rayHit.sideHit); - Vec3d lastRayDir = rayDir; - - float totalRayDistance = (float) rayLength; - - // Secondary ray bounces - for (int j = 0; j < rayBounces; j++) { - final Vec3d newRayDir = reflect(lastRayDir, lastHitNormal); - // Vec3d newRayDir = lastHitNormal; - final Vec3d newRayStart = new Vec3d(lastHitPos.x + lastHitNormal.x * 0.01, - lastHitPos.y + lastHitNormal.y * 0.01, lastHitPos.z + lastHitNormal.z * 0.01); - final Vec3d newRayEnd = new Vec3d(newRayStart.x + newRayDir.x * maxDistance, - newRayStart.y + newRayDir.y * maxDistance, newRayStart.z + newRayDir.z * maxDistance); - - final RayTraceResult newRayHit = mc.world.rayTraceBlocks(newRayStart, newRayEnd, true); - - float energyTowardsPlayer = 0.25f; - final float blockReflectivity = getBlockReflectivity(lastHitBlock); - energyTowardsPlayer *= blockReflectivity * 0.75f + 0.25f; - - if (newRayHit == null) { - totalRayDistance += lastHitPos.distanceTo(playerPos); - } else { - final double newRayLength = lastHitPos.distanceTo(newRayHit.hitVec); - - bounceReflectivityRatio[j] += blockReflectivity; - - totalRayDistance += newRayLength; - - lastHitPos = newRayHit.hitVec; - lastHitNormal = getNormalFromFacing(newRayHit.sideHit); - lastRayDir = newRayDir; - lastHitBlock = newRayHit.getBlockPos(); - - // Cast one final ray towards the player. If it's - // unobstructed, then the sound source and the player - // share airspace. - if (Config.simplerSharedAirspaceSimulation && j == rayBounces - 1 - || !Config.simplerSharedAirspaceSimulation) { - final Vec3d finalRayStart = new Vec3d(lastHitPos.x + lastHitNormal.x * 0.01, - lastHitPos.y + lastHitNormal.y * 0.01, lastHitPos.z + lastHitNormal.z * 0.01); - - final RayTraceResult finalRayHit = mc.world.rayTraceBlocks(finalRayStart, playerPos, true); - - if (finalRayHit == null) { - // log("Secondary ray hit the player!"); - sharedAirspace += 1.0f; - } - } - } - - final float reflectionDelay = (float) Math.max(totalRayDistance, 0.0) * 0.12f * blockReflectivity; - - final float cross0 = 1.0f - MathHelper.clamp(Math.abs(reflectionDelay - 0.0f), 0.0f, 1.0f); - final float cross1 = 1.0f - MathHelper.clamp(Math.abs(reflectionDelay - 1.0f), 0.0f, 1.0f); - final float cross2 = 1.0f - MathHelper.clamp(Math.abs(reflectionDelay - 2.0f), 0.0f, 1.0f); - final float cross3 = MathHelper.clamp(reflectionDelay - 2.0f, 0.0f, 1.0f); - - sendGain0 += cross0 * energyTowardsPlayer * 6.4f * rcpTotalRays; - sendGain1 += cross1 * energyTowardsPlayer * 12.8f * rcpTotalRays; - sendGain2 += cross2 * energyTowardsPlayer * 12.8f * rcpTotalRays; - sendGain3 += cross3 * energyTowardsPlayer * 12.8f * rcpTotalRays; - - // Nowhere to bounce off of, stop bouncing! - if (newRayHit == null) { - break; - } - } - } - - } - - // log("total reflectivity ratio: " + totalReflectivityRatio); - - bounceReflectivityRatio[0] = bounceReflectivityRatio[0] / numRays; - bounceReflectivityRatio[1] = bounceReflectivityRatio[1] / numRays; - bounceReflectivityRatio[2] = bounceReflectivityRatio[2] / numRays; - bounceReflectivityRatio[3] = bounceReflectivityRatio[3] / numRays; - - sharedAirspace *= 64.0f; - - if (Config.simplerSharedAirspaceSimulation) { - sharedAirspace *= rcpPrimaryRays; - } else { - sharedAirspace *= rcpTotalRays; - } - - final float sharedAirspaceWeight0 = MathHelper.clamp(sharedAirspace / 20.0f, 0.0f, 1.0f); - final float sharedAirspaceWeight1 = MathHelper.clamp(sharedAirspace / 15.0f, 0.0f, 1.0f); - final float sharedAirspaceWeight2 = MathHelper.clamp(sharedAirspace / 10.0f, 0.0f, 1.0f); - final float sharedAirspaceWeight3 = MathHelper.clamp(sharedAirspace / 10.0f, 0.0f, 1.0f); - - sendCutoff0 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.0f) * (1.0f - sharedAirspaceWeight0) - + sharedAirspaceWeight0; - sendCutoff1 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.0f) * (1.0f - sharedAirspaceWeight1) - + sharedAirspaceWeight1; - sendCutoff2 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.5f) * (1.0f - sharedAirspaceWeight2) - + sharedAirspaceWeight2; - sendCutoff3 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.5f) * (1.0f - sharedAirspaceWeight3) - + sharedAirspaceWeight3; - - // attempt to preserve directionality when airspace is shared by - // allowing some of the dry signal through but filtered - final float averageSharedAirspace = (sharedAirspaceWeight0 + sharedAirspaceWeight1 + sharedAirspaceWeight2 - + sharedAirspaceWeight3) * 0.25f; - directCutoff = Math.max((float) Math.pow(averageSharedAirspace, 0.5) * 0.2f, directCutoff); - - directGain = (float) Math.pow(directCutoff, 0.1); - - sendGain1 *= bounceReflectivityRatio[1]; - sendGain2 *= (float) Math.pow(bounceReflectivityRatio[2], 3.0); - sendGain3 *= (float) Math.pow(bounceReflectivityRatio[3], 4.0); - - sendGain0 = MathHelper.clamp(sendGain0, 0.0f, 1.0f); - sendGain1 = MathHelper.clamp(sendGain1, 0.0f, 1.0f); - sendGain2 = MathHelper.clamp(sendGain2 * 1.05f - 0.05f, 0.0f, 1.0f); - sendGain3 = MathHelper.clamp(sendGain3 * 1.05f - 0.05f, 0.0f, 1.0f); - - sendGain0 *= (float) Math.pow(sendCutoff0, 0.1); - sendGain1 *= (float) Math.pow(sendCutoff1, 0.1); - sendGain2 *= (float) Math.pow(sendCutoff2, 0.1); - sendGain3 *= (float) Math.pow(sendCutoff3, 0.1); - - if (mc.player.isInWater()) { - sendCutoff0 *= 0.4f; - sendCutoff1 *= 0.4f; - sendCutoff2 *= 0.4f; - sendCutoff3 *= 0.4f; - } - - setEnvironment(sourceID, sendGain0, sendGain1, sendGain2, sendGain3, sendCutoff0, sendCutoff1, sendCutoff2, - sendCutoff3, directCutoff, directGain); - } - - private static void setEnvironment(final int sourceID, final float sendGain0, final float sendGain1, - final float sendGain2, final float sendGain3, final float sendCutoff0, final float sendCutoff1, - final float sendCutoff2, final float sendCutoff3, final float directCutoff, final float directGain) { - // Set reverb send filter values and set source to send to all reverb fx - // slots - EFX10.alFilterf(sendFilter0, EFX10.AL_LOWPASS_GAIN, sendGain0); - EFX10.alFilterf(sendFilter0, EFX10.AL_LOWPASS_GAINHF, sendCutoff0); - AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot0, 0, sendFilter0); - - EFX10.alFilterf(sendFilter1, EFX10.AL_LOWPASS_GAIN, sendGain1); - EFX10.alFilterf(sendFilter1, EFX10.AL_LOWPASS_GAINHF, sendCutoff1); - AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot1, 1, sendFilter1); - - EFX10.alFilterf(sendFilter2, EFX10.AL_LOWPASS_GAIN, sendGain2); - EFX10.alFilterf(sendFilter2, EFX10.AL_LOWPASS_GAINHF, sendCutoff2); - AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot2, 2, sendFilter2); - - EFX10.alFilterf(sendFilter3, EFX10.AL_LOWPASS_GAIN, sendGain3); - EFX10.alFilterf(sendFilter3, EFX10.AL_LOWPASS_GAINHF, sendCutoff3); - AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot3, 3, sendFilter3); - - EFX10.alFilterf(directFilter0, EFX10.AL_LOWPASS_GAIN, directGain); - EFX10.alFilterf(directFilter0, EFX10.AL_LOWPASS_GAINHF, directCutoff); - AL10.alSourcei(sourceID, EFX10.AL_DIRECT_FILTER, directFilter0); - - AL10.alSourcef(sourceID, EFX10.AL_AIR_ABSORPTION_FACTOR, Config.airAbsorption); - } - - /** - * Applies the parameters in the enum ReverbParams to the main reverb - * effect. - */ - protected static void setReverbParams(final ReverbParams r, final int auxFXSlot, final int reverbSlot) { - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DENSITY, r.density); - checkErrorLog("Error while assigning reverb density: " + r.density); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DIFFUSION, r.diffusion); - checkErrorLog("Error while assigning reverb diffusion: " + r.diffusion); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_GAIN, r.gain); - checkErrorLog("Error while assigning reverb gain: " + r.gain); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_GAINHF, r.gainHF); - checkErrorLog("Error while assigning reverb gainHF: " + r.gainHF); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DECAY_TIME, r.decayTime); - checkErrorLog("Error while assigning reverb decayTime: " + r.decayTime); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DECAY_HFRATIO, r.decayHFRatio); - checkErrorLog("Error while assigning reverb decayHFRatio: " + r.decayHFRatio); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_REFLECTIONS_GAIN, r.reflectionsGain); - checkErrorLog("Error while assigning reverb reflectionsGain: " + r.reflectionsGain); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_LATE_REVERB_GAIN, r.lateReverbGain); - checkErrorLog("Error while assigning reverb lateReverbGain: " + r.lateReverbGain); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_LATE_REVERB_DELAY, r.lateReverbDelay); - checkErrorLog("Error while assigning reverb lateReverbDelay: " + r.lateReverbDelay); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_AIR_ABSORPTION_GAINHF, r.airAbsorptionGainHF); - checkErrorLog("Error while assigning reverb airAbsorptionGainHF: " + r.airAbsorptionGainHF); - - EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, r.roomRolloffFactor); - checkErrorLog("Error while assigning reverb roomRolloffFactor: " + r.roomRolloffFactor); - - // Attach updated effect object - EFX10.alAuxiliaryEffectSloti(auxFXSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbSlot); - } - - public static void log(final String message) { - System.out.println(logPrefix.concat(" : ").concat(message)); - } - - public static void logError(final String errorMessage) { - System.out.println(logPrefix.concat(" [ERROR]: ").concat(errorMessage)); - } - - protected static boolean checkErrorLog(final String errorMessage) { - final int error = AL10.alGetError(); - if (error == AL10.AL_NO_ERROR) { - return false; - } - - String errorName; - - switch (error) { - case AL10.AL_INVALID_NAME: - errorName = "AL_INVALID_NAME"; - break; - case AL10.AL_INVALID_ENUM: - errorName = "AL_INVALID_ENUM"; - break; - case AL10.AL_INVALID_VALUE: - errorName = "AL_INVALID_VALUE"; - break; - case AL10.AL_INVALID_OPERATION: - errorName = "AL_INVALID_OPERATION"; - break; - case AL10.AL_OUT_OF_MEMORY: - errorName = "AL_OUT_OF_MEMORY"; - break; - default: - errorName = Integer.toString(error); - break; - } - - logError(errorMessage + " OpenAL error " + errorName); - return true; - } - -} +package com.sonicether.soundphysics; + +import java.util.regex.Pattern; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ByteOrder; + +import javax.sound.sampled.AudioFormat; + +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.AL11; +import org.lwjgl.openal.ALC10; +import org.lwjgl.openal.ALCcontext; +import org.lwjgl.openal.ALCdevice; +import org.lwjgl.openal.EFX10; + +import net.minecraft.block.Block; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.PropertyDirection; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.ISound; +import net.minecraft.client.audio.MovingSound; +import net.minecraft.entity.Entity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +import paulscode.sound.SoundSystemConfig; +import paulscode.sound.SoundSystem; +import paulscode.sound.SoundBuffer; +import paulscode.sound.Library; +import paulscode.sound.Source; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import org.apache.commons.lang3.reflect.FieldUtils; + +@Mod(modid = SoundPhysics.modid, clientSideOnly = true, acceptedMinecraftVersions = SoundPhysics.mcVersion, + version = SoundPhysics.version, guiFactory = "com.sonicether.soundphysics.SPGuiFactory") +public class SoundPhysics { + + public static final String modid = "soundphysics"; + public static final String version = "${version}"; + public static final String mcVersion = "${mc_version}"; + + public static final Logger logger = LogManager.getLogger(modid); + + public static boolean onServer = false; + + private static final Pattern rainPattern = Pattern.compile(".*rain.*"); + private static final Pattern stepPattern = Pattern.compile(".*step.*"); + private static final Pattern blockPattern = Pattern.compile(".*block.*"); + private static final Pattern uiPattern = Pattern.compile(".*\\/ui\\/.*"); + private static final Pattern clickPattern = Pattern.compile(".*random.click.*"); + private static final Pattern noteBlockPattern = Pattern.compile(".*block.note.*"); + private static final Pattern betweenlandsPattern = Pattern.compile("thebetweenlands:sounds\\/rift_.*\\.ogg"); + private static final Pattern travelPattern = Pattern.compile(".*portal\\/travel*.*"); + + @Mod.EventHandler + public void preInit(final FMLPreInitializationEvent event) { + Config.instance.preInit(event); + } + + @Mod.EventHandler + public void init(final FMLInitializationEvent event) { + Config.instance.init(event); + } + + private static int auxFXSlot0; + private static int auxFXSlot1; + private static int auxFXSlot2; + private static int auxFXSlot3; + private static int reverb0; + private static int reverb1; + private static int reverb2; + private static int reverb3; + private static int directFilter0; + private static int sendFilter0; + private static int sendFilter1; + private static int sendFilter2; + private static int sendFilter3; + + private static Minecraft mc; + private static SoundSystem sndSystem; + + private static SoundCategory lastSoundCategory; + private static String lastSoundName; + private static ISound.AttenuationType lastSoundAtt; + + // THESE VARIABLES ARE CONSTANTLY ACCESSED AND USED BY ASM INJECTED CODE! DO + // NOT REMOVE! + public static int attenuationModel = SoundSystemConfig.ATTENUATION_ROLLOFF; + public static float globalRolloffFactor = Config.rolloffFactor; + public static float globalVolumeMultiplier0 = Config.globalVolumeMultiplier; // 0 is because of DS trying to read the value of the original name + public static float globalReverbMultiplier = 0.7f * Config.globalReverbGain; + public static double soundDistanceAllowance = Config.soundDistanceAllowance; + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static void init(SoundSystem snds) { + mc = Minecraft.getMinecraft(); + sndSystem = snds; + try { + setupEFX(); + } catch (Throwable e) { + logError("Failed to init EFX"); + logError(e.toString()); + } + } + + public static void applyConfigChanges() { + globalRolloffFactor = Config.rolloffFactor; + globalReverbMultiplier = 0.7f * Config.globalReverbGain; + soundDistanceAllowance = Config.soundDistanceAllowance; + globalVolumeMultiplier0 = Config.globalVolumeMultiplier; + + if (auxFXSlot0 != 0) { + // Set the global reverb parameters and apply them to the effect and + // effectslot + setReverbParams(ReverbParams.getReverb0(), auxFXSlot0, reverb0); + setReverbParams(ReverbParams.getReverb1(), auxFXSlot1, reverb1); + setReverbParams(ReverbParams.getReverb2(), auxFXSlot2, reverb2); + setReverbParams(ReverbParams.getReverb3(), auxFXSlot3, reverb3); + } + } + + private static void setupEFX() { + // Get current context and device + final ALCcontext currentContext = ALC10.alcGetCurrentContext(); + final ALCdevice currentDevice = ALC10.alcGetContextsDevice(currentContext); + + if (ALC10.alcIsExtensionPresent(currentDevice, "ALC_EXT_EFX")) { + log("EFX Extension recognized."); + } else { + logError("EFX Extension not found on current device. Aborting."); + return; + } + + // Create auxiliary effect slots + auxFXSlot0 = EFX10.alGenAuxiliaryEffectSlots(); + log("Aux slot " + auxFXSlot0 + " created"); + EFX10.alAuxiliaryEffectSloti(auxFXSlot0, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); + + auxFXSlot1 = EFX10.alGenAuxiliaryEffectSlots(); + log("Aux slot " + auxFXSlot1 + " created"); + EFX10.alAuxiliaryEffectSloti(auxFXSlot1, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); + + auxFXSlot2 = EFX10.alGenAuxiliaryEffectSlots(); + log("Aux slot " + auxFXSlot2 + " created"); + EFX10.alAuxiliaryEffectSloti(auxFXSlot2, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); + + auxFXSlot3 = EFX10.alGenAuxiliaryEffectSlots(); + log("Aux slot " + auxFXSlot3 + " created"); + EFX10.alAuxiliaryEffectSloti(auxFXSlot3, EFX10.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL10.AL_TRUE); + checkErrorLog("Failed creating auxiliary effect slots!"); + + reverb0 = EFX10.alGenEffects(); + EFX10.alEffecti(reverb0, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); + checkErrorLog("Failed creating reverb effect slot 0!"); + reverb1 = EFX10.alGenEffects(); + EFX10.alEffecti(reverb1, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); + checkErrorLog("Failed creating reverb effect slot 1!"); + reverb2 = EFX10.alGenEffects(); + EFX10.alEffecti(reverb2, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); + checkErrorLog("Failed creating reverb effect slot 2!"); + reverb3 = EFX10.alGenEffects(); + EFX10.alEffecti(reverb3, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_EAXREVERB); + checkErrorLog("Failed creating reverb effect slot 3!"); + + // Create filters + directFilter0 = EFX10.alGenFilters(); + EFX10.alFilteri(directFilter0, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); + + sendFilter0 = EFX10.alGenFilters(); + EFX10.alFilteri(sendFilter0, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); + + sendFilter1 = EFX10.alGenFilters(); + EFX10.alFilteri(sendFilter1, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); + + sendFilter2 = EFX10.alGenFilters(); + EFX10.alFilteri(sendFilter2, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); + + sendFilter3 = EFX10.alGenFilters(); + EFX10.alFilteri(sendFilter3, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); + checkErrorLog("Error creating lowpass filters!"); + + applyConfigChanges(); + } + + private static SoundCategory getSoundCategory(final SoundCategory sc, final String name) { + if (Config.noteBlockEnable && sc == SoundCategory.RECORDS && noteBlockPattern.matcher(name).matches()) { + return SoundCategory.BLOCKS; + } else { + return sc; + } + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static void setLastSound(final ISound snd, final SoundCategory sc, final ResourceLocation soundRes, final ResourceLocation eventRes) { + lastSoundName = eventRes.toString()+"|"+soundRes.getPath(); // Quick and dirty hack to check the event and sound name + lastSoundCategory = getSoundCategory(sc,lastSoundName); + lastSoundAtt = snd.getAttenuationType(); + if (snd instanceof MovingSound) // Hacky fix until i properly do moving sounds (I'm currently thinking about how) + lastSoundCategory = SoundCategory.RECORDS;// because all (at least vanilla) moving sounds don't init their position when played + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static void setLastSound(final SoundCategory sc, final String soundName) { + lastSoundCategory = sc; + lastSoundName = soundName; + lastSoundAtt = ISound.AttenuationType.LINEAR; + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + // For IC2 + public static void setLastSound(final int position, final String soundName) { + lastSoundCategory = position == 0 ? SoundCategory.BLOCKS : SoundCategory.PLAYERS; + if (soundName != null) + lastSoundName = "ic2:"+soundName; + else // Can't get easliy get sound name for normal IC2 Classic sources + lastSoundName = "ic2"; + lastSoundAtt = ISound.AttenuationType.LINEAR; + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static int ic2DistanceCheckHook(float i, float dst, float x1, float y1, float z1, float x2, float y2, float z2) { + if (i >= dst || + (MathHelper.floor(x1) == MathHelper.floor(x2) && + MathHelper.floor(y1) == MathHelper.floor(y2) && + MathHelper.floor(z1) == MathHelper.floor(z2))) return 1; + if (dst == i) return 0; + return -1; + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static int ic2DistanceCheckHook(double i, double dst, Vec3d p1, Vec3d p2) { + if (i >= dst || + (MathHelper.floor(p1.x) == MathHelper.floor(p2.x) && + MathHelper.floor(p1.y) == MathHelper.floor(p2.y) && + MathHelper.floor(p1.z) == MathHelper.floor(p2.z))) return 1; + if (dst == i) return 0; + return -1; + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static float applyGlobalVolumeMultiplier(final float volume) { + if (!Config.volumeMulOnlyAffected || !(mc.player == null || mc.world == null || lastSoundCategory == SoundCategory.MASTER || + lastSoundAtt == ISound.AttenuationType.NONE || lastSoundCategory == SoundCategory.RECORDS || lastSoundCategory == SoundCategory.MUSIC)) { + return volume*globalVolumeMultiplier0; + } else { + return volume; + } + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + // For sounds that get played normally + public static void onPlaySound(final float posX, final float posY, final float posZ, final int sourceID) { + onPlaySound(posX, posY, posZ, sourceID, lastSoundCategory, lastSoundName, lastSoundAtt); + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + // For Gliby's VC, sound system source id + // This is kind of hacked together, it's not great + public static void onPlaySound(final String id) { + try { // Getting the sound library here is not ideal but i can't really do better because gibly changes it after startup + Library sndLibrary = (Library)FieldUtils.readField(sndSystem,"soundLibrary",true); + if (sndLibrary == null) + return; + + Source src = sndLibrary.getSource(id); + if (src == null || src.channel == null) + return; + + IntBuffer srcid = (IntBuffer)FieldUtils.readField(src.channel,"ALSource"); + if (srcid == null) + return; + + onPlaySound(src.position.x, src.position.y, src.position.z, srcid.get(0), SoundCategory.PLAYERS, id, ISound.AttenuationType.LINEAR); + } catch (Exception e) { + logError("Error trying to get source info"); + logError(e.toString()); + } + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + // For sounds that get played using OpenAL directly or just not using the minecraft sound system + public static void onPlaySoundAL(final float posX, final float posY, final float posZ, final int sourceID) { + onPlaySound(posX, posY, posZ, sourceID, SoundCategory.AMBIENT, "openal", ISound.AttenuationType.LINEAR); + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static void onPlaySound(final float posX, final float posY, final float posZ, final int sourceID, SoundCategory soundCat, String soundName, ISound.AttenuationType attType) { + //log(String.valueOf(posX)+" "+String.valueOf(posY)+" "+String.valueOf(posZ)+" - "+String.valueOf(sourceID)+" - "+soundCat.toString()+" - "+attType.toString()+" - "+soundName); + evaluateEnvironment(sourceID, posX, posY, posZ, soundCat, soundName, attType); + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static SoundBuffer onLoadSound(SoundBuffer buff, String filename) { + if (buff == null || buff.audioFormat.getChannels() == 1 || !Config.autoSteroDownmix) return buff; + if (mc == null || mc.player == null || mc.world == null || lastSoundCategory == SoundCategory.RECORDS || lastSoundCategory == SoundCategory.MUSIC || + uiPattern.matcher(filename).matches() || clickPattern.matcher(filename).matches() || betweenlandsPattern.matcher(filename).matches() || + travelPattern.matcher(filename).matches()) { + if (Config.autoSteroDownmixLogging) log("Not converting sound '"+filename+"'("+buff.audioFormat.toString()+")"); + return buff; + } + AudioFormat orignalformat = buff.audioFormat; + int bits = orignalformat.getSampleSizeInBits(); + boolean bigendian = orignalformat.isBigEndian(); + AudioFormat monoformat = new AudioFormat(orignalformat.getEncoding(), orignalformat.getSampleRate(), bits, + 1, orignalformat.getFrameSize(), orignalformat.getFrameRate(), bigendian); + if (Config.autoSteroDownmixLogging) log("Converting sound '"+filename+"'("+orignalformat.toString()+") to mono ("+monoformat.toString()+")"); + + ByteBuffer bb = ByteBuffer.wrap(buff.audioData,0,buff.audioData.length); + bb.order(bigendian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + if (bits == 8) { + for (int i = 0; i < buff.audioData.length; i+=2) { + bb.put(i/2,(byte)((bb.get(i)+bb.get(i+1))/2)); + } + } else if (bits == 16) { + for (int i = 0; i < buff.audioData.length; i+=4) { + bb.putShort((i/2),(short)((bb.getShort(i)+bb.getShort(i+2))/2)); + } + } + buff.audioFormat = monoformat; + buff.trimData(buff.audioData.length/2); + return buff; + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static double calculateEntitySoundOffset(final Entity entity, final SoundEvent sound) { + if (sound == null) return entity.getEyeHeight(); + if (stepPattern.matcher(sound.soundName.getPath()).matches()) { + return 0; + } + + return entity.getEyeHeight(); + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static Vec3d calculateEntitySoundOffsetVec(final Vec3d pos, final Entity entity, final SoundEvent sound) { + return new Vec3d(pos.x,pos.y+calculateEntitySoundOffset(entity,sound),pos.z); + } + + /** + * CALLED BY ASM INJECTED CODE! + */ + public static Vec3d computronicsOffset(Vec3d or, TileEntity te, PropertyDirection pd) { + if (!te.hasWorld()) return or; + EnumFacing ef = te.getWorld().getBlockState(te.getPos()).getValue(pd); + Vec3d efv = getNormalFromFacing(ef).scale(0.51); + return or.add(efv); + } + + // Copy of isRainingAt + private static boolean isSnowingAt(BlockPos position, boolean check_rain) { + if ((check_rain && !mc.world.isRaining()) || !mc.world.canSeeSky(position) || + mc.world.getPrecipitationHeight(position).getY() > position.getY()) { + return false; + } else { + return mc.world.canSnowAt(position, false) || mc.world.getBiome(position).getEnableSnow(); + } + } + + @SuppressWarnings("deprecation") + private static float getBlockReflectivity(final BlockPos blockPos) { + final Block block = mc.world.getBlockState(blockPos).getBlock(); + final SoundType soundType = block.getSoundType(); + + float reflectivity = 0.5f; + + if (soundType == SoundType.STONE) { + reflectivity = Config.stoneReflectivity; + } else if (soundType == SoundType.WOOD) { + reflectivity = Config.woodReflectivity; + } else if (soundType == SoundType.GROUND) { + reflectivity = Config.groundReflectivity; + } else if (soundType == SoundType.PLANT) { + reflectivity = Config.plantReflectivity; + } else if (soundType == SoundType.METAL) { + reflectivity = Config.metalReflectivity; + } else if (soundType == SoundType.GLASS) { + reflectivity = Config.glassReflectivity; + } else if (soundType == SoundType.CLOTH) { + reflectivity = Config.clothReflectivity; + } else if (soundType == SoundType.SAND) { + reflectivity = Config.sandReflectivity; + } else if (soundType == SoundType.SNOW) { + reflectivity = Config.snowReflectivity; + } else if (soundType == SoundType.LADDER) { + reflectivity = Config.woodReflectivity; + } else if (soundType == SoundType.ANVIL) { + reflectivity = Config.metalReflectivity; + } + + reflectivity *= Config.globalBlockReflectance; + + return reflectivity; + } + + private static Vec3d getNormalFromFacing(final EnumFacing sideHit) { + return new Vec3d(sideHit.getDirectionVec()); + } + + private static Vec3d reflect(final Vec3d dir, final Vec3d normal) { + final double dot2 = dir.dotProduct(normal) * 2; + + final double x = dir.x - dot2 * normal.x; + final double y = dir.y - dot2 * normal.y; + final double z = dir.z - dot2 * normal.z; + + return new Vec3d(x, y, z); + } + + private static Vec3d offsetSoundByName(final double soundX, final double soundY, final double soundZ, + final Vec3d playerPos, final String name, final SoundCategory category) { + double offsetX = 0.0; + double offsetY = 0.0; + double offsetZ = 0.0; + double offsetTowardsPlayer = 0.0; + + double tempNormX = 0; + double tempNormY = 0; + double tempNormZ = 0; + + if (soundY % 1.0 < 0.001 || stepPattern.matcher(name).matches()) { + offsetY = 0.225; + } + + if ((category == SoundCategory.BLOCKS || blockPattern.matcher(name).matches() || + (name == "openal" && !mc.world.isAirBlock(new BlockPos(soundX,soundY,soundZ)))) && + (MathHelper.floor(playerPos.x) != MathHelper.floor(soundX) || + MathHelper.floor(playerPos.y) != MathHelper.floor(soundY) || + MathHelper.floor(playerPos.z) != MathHelper.floor(soundZ))) { + // The ray will probably hit the block that it's emitting from + // before + // escaping. Offset the ray start position towards the player by the + // diagonal half length of a cube + + tempNormX = playerPos.x - soundX; + tempNormY = playerPos.y - soundY; + tempNormZ = playerPos.z - soundZ; + final double length = Math.sqrt(tempNormX * tempNormX + tempNormY * tempNormY + tempNormZ * tempNormZ); + tempNormX /= length; + tempNormY /= length; + tempNormZ /= length; + // 0.867 > square root of 0.5^2 * 3 + offsetTowardsPlayer = 0.867; + offsetX += tempNormX * offsetTowardsPlayer; + offsetY += tempNormY * offsetTowardsPlayer; + offsetZ += tempNormZ * offsetTowardsPlayer; + } + + return new Vec3d(soundX + offsetX, soundY + offsetY, soundZ + offsetZ); + } + + private static float getPlayerEyeHeight() throws IllegalStateException { + ReentrantReadWriteLock lock = (ReentrantReadWriteLock)mc.player.getDataManager().lock; + if (lock.isWriteLocked()) { + logError("Deadlock detected, avoiding it by throwing exception"); + throw new IllegalStateException("Player's Data Mananger is write locked"); + } + return mc.player.getEyeHeight(); + } + + @SuppressWarnings("deprecation") + private static void evaluateEnvironment(final int sourceID, final float posX, final float posY, final float posZ, final SoundCategory category, + final String name, ISound.AttenuationType attType) { + try { + if (mc.player == null || mc.world == null || category == SoundCategory.MASTER || attType == ISound.AttenuationType.NONE || + category == SoundCategory.RECORDS || category == SoundCategory.MUSIC) { + // posY <= 0 as a condition has to be there: Ingame + // menu clicks do have a player and world present + // The Y position check has been removed due to problems with Cubic Chunks + setEnvironment(sourceID, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + return; + } + + final boolean isRain = rainPattern.matcher(name).matches(); + + if (Config.skipRainOcclusionTracing && isRain) { + setEnvironment(sourceID, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + return; + } + + float directCutoff = 1.0f; + final float absorptionCoeff = Config.globalBlockAbsorption * 3.0f; + + final Vec3d playerPos = new Vec3d(mc.player.posX, mc.player.posY + getPlayerEyeHeight(), mc.player.posZ); + final Vec3d soundPos = offsetSoundByName(posX, posY, posZ, playerPos, name, category); + final Vec3d normalToPlayer = playerPos.subtract(soundPos).normalize(); + + float airAbsorptionFactor = 1.0f; + + if (Config.snowAirAbsorptionFactor > 1.0f && mc.world.isRaining()) { + final Vec3d middlePos = playerPos.add(soundPos).scale(0.5); + final BlockPos playerPosBlock = new BlockPos(playerPos); + final BlockPos soundPosBlock = new BlockPos(soundPos); + final BlockPos middlePosBlock = new BlockPos(middlePos); + final int snowingPlayer = isSnowingAt(playerPosBlock,false) ? 1 : 0; + final int snowingSound = isSnowingAt(soundPosBlock,false) ? 1 : 0; + final int snowingMiddle = isSnowingAt(middlePosBlock,false) ? 1 : 0; + final float snowFactor = snowingPlayer * 0.25f + snowingMiddle * 0.5f + snowingSound * 0.25f; + airAbsorptionFactor = Math.max(Config.snowAirAbsorptionFactor*mc.world.getRainStrength(1.0f)*snowFactor,airAbsorptionFactor); + } + + Vec3d rayOrigin = soundPos; + + float occlusionAccumulation = 0.0f; + + for (int i = 0; i < 10; i++) { + final RayTraceResult rayHit = mc.world.rayTraceBlocks(rayOrigin, playerPos, true); + + if (rayHit == null) { + break; + } + + final Block blockHit = mc.world.getBlockState(rayHit.getBlockPos()).getBlock(); + + float blockOcclusion = 1.0f; + + if (!blockHit.isOpaqueCube(blockHit.getDefaultState())) { + // log("not a solid block!"); + blockOcclusion *= 0.15f; + } + + occlusionAccumulation += blockOcclusion; + + rayOrigin = new Vec3d(rayHit.hitVec.x + normalToPlayer.x * 0.1, rayHit.hitVec.y + normalToPlayer.y * 0.1, + rayHit.hitVec.z + normalToPlayer.z * 0.1); + } + + directCutoff = (float) Math.exp(-occlusionAccumulation * absorptionCoeff); + float directGain = (float) Math.pow(directCutoff, 0.1); + + // Calculate reverb parameters for this sound + float sendGain0 = 0.0f; + float sendGain1 = 0.0f; + float sendGain2 = 0.0f; + float sendGain3 = 0.0f; + + float sendCutoff0 = 1.0f; + float sendCutoff1 = 1.0f; + float sendCutoff2 = 1.0f; + float sendCutoff3 = 1.0f; + + if (mc.player.isInsideOfMaterial(Material.WATER)) { + directCutoff *= 1.0f - Config.underwaterFilter; + } + + if (isRain) { + setEnvironment(sourceID, sendGain0, sendGain1, sendGain2, sendGain3, sendCutoff0, sendCutoff1, sendCutoff2, + sendCutoff3, directCutoff, directGain, airAbsorptionFactor); + return; + } + + // Shoot rays around sound + final float phi = 1.618033988f; + final float gAngle = phi * (float) Math.PI * 2.0f; + final float maxDistance = Config.maxDistance; + + final int numRays = Config.environmentEvaluationRays; + final int rayBounces = 4; + + final float[] bounceReflectivityRatio = new float[rayBounces]; + + float sharedAirspace = 0.0f; + + final float rcpTotalRays = 1.0f / (numRays * rayBounces); + final float rcpPrimaryRays = 1.0f / numRays; + + for (int i = 0; i < numRays; i++) { + final float fi = i; + final float fiN = fi / numRays; + final float longitude = gAngle * fi; + final float latitude = (float) Math.asin(fiN * 2.0f - 1.0f); + + final Vec3d rayDir = new Vec3d(Math.cos(latitude) * Math.cos(longitude), + Math.cos(latitude) * Math.sin(longitude), Math.sin(latitude)); + + final Vec3d rayStart = new Vec3d(soundPos.x, soundPos.y, soundPos.z); + + final Vec3d rayEnd = new Vec3d(rayStart.x + rayDir.x * maxDistance, rayStart.y + rayDir.y * maxDistance, + rayStart.z + rayDir.z * maxDistance); + + final RayTraceResult rayHit = mc.world.rayTraceBlocks(rayStart, rayEnd, true); + + if (rayHit != null) { + final double rayLength = soundPos.distanceTo(rayHit.hitVec); + + // Additional bounces + BlockPos lastHitBlock = rayHit.getBlockPos(); + Vec3d lastHitPos = rayHit.hitVec; + Vec3d lastHitNormal = getNormalFromFacing(rayHit.sideHit); + Vec3d lastRayDir = rayDir; + + float totalRayDistance = (float) rayLength; + + // Secondary ray bounces + for (int j = 0; j < rayBounces; j++) { + final Vec3d newRayDir = reflect(lastRayDir, lastHitNormal); + // Vec3d newRayDir = lastHitNormal; + final Vec3d newRayStart = new Vec3d(lastHitPos.x + lastHitNormal.x * 0.01, + lastHitPos.y + lastHitNormal.y * 0.01, lastHitPos.z + lastHitNormal.z * 0.01); + final Vec3d newRayEnd = new Vec3d(newRayStart.x + newRayDir.x * maxDistance, + newRayStart.y + newRayDir.y * maxDistance, newRayStart.z + newRayDir.z * maxDistance); + + final RayTraceResult newRayHit = mc.world.rayTraceBlocks(newRayStart, newRayEnd, true); + + float energyTowardsPlayer = 0.25f; + final float blockReflectivity = getBlockReflectivity(lastHitBlock); + energyTowardsPlayer *= blockReflectivity * 0.75f + 0.25f; + + if (newRayHit == null) { + totalRayDistance += lastHitPos.distanceTo(playerPos); + } else { + final double newRayLength = lastHitPos.distanceTo(newRayHit.hitVec); + + bounceReflectivityRatio[j] += blockReflectivity; + + totalRayDistance += newRayLength; + + lastHitPos = newRayHit.hitVec; + lastHitNormal = getNormalFromFacing(newRayHit.sideHit); + lastRayDir = newRayDir; + lastHitBlock = newRayHit.getBlockPos(); + + // Cast one final ray towards the player. If it's + // unobstructed, then the sound source and the player + // share airspace. + if (Config.simplerSharedAirspaceSimulation && j == rayBounces - 1 + || !Config.simplerSharedAirspaceSimulation) { + final Vec3d finalRayStart = new Vec3d(lastHitPos.x + lastHitNormal.x * 0.01, + lastHitPos.y + lastHitNormal.y * 0.01, lastHitPos.z + lastHitNormal.z * 0.01); + + final RayTraceResult finalRayHit = mc.world.rayTraceBlocks(finalRayStart, playerPos, true); + + if (finalRayHit == null) { + // log("Secondary ray hit the player!"); + sharedAirspace += 1.0f; + } + } + } + + final float reflectionDelay = (float) Math.max(totalRayDistance, 0.0) * 0.12f * blockReflectivity; + + final float cross0 = 1.0f - MathHelper.clamp(Math.abs(reflectionDelay - 0.0f), 0.0f, 1.0f); + final float cross1 = 1.0f - MathHelper.clamp(Math.abs(reflectionDelay - 1.0f), 0.0f, 1.0f); + final float cross2 = 1.0f - MathHelper.clamp(Math.abs(reflectionDelay - 2.0f), 0.0f, 1.0f); + final float cross3 = MathHelper.clamp(reflectionDelay - 2.0f, 0.0f, 1.0f); + + sendGain0 += cross0 * energyTowardsPlayer * 6.4f * rcpTotalRays; + sendGain1 += cross1 * energyTowardsPlayer * 12.8f * rcpTotalRays; + sendGain2 += cross2 * energyTowardsPlayer * 12.8f * rcpTotalRays; + sendGain3 += cross3 * energyTowardsPlayer * 12.8f * rcpTotalRays; + + // Nowhere to bounce off of, stop bouncing! + if (newRayHit == null) { + break; + } + } + } + + } + + // log("total reflectivity ratio: " + totalReflectivityRatio); + + bounceReflectivityRatio[0] = bounceReflectivityRatio[0] / numRays; + bounceReflectivityRatio[1] = bounceReflectivityRatio[1] / numRays; + bounceReflectivityRatio[2] = bounceReflectivityRatio[2] / numRays; + bounceReflectivityRatio[3] = bounceReflectivityRatio[3] / numRays; + + sharedAirspace *= 64.0f; + + if (Config.simplerSharedAirspaceSimulation) { + sharedAirspace *= rcpPrimaryRays; + } else { + sharedAirspace *= rcpTotalRays; + } + + final float sharedAirspaceWeight0 = MathHelper.clamp(sharedAirspace / 20.0f, 0.0f, 1.0f); + final float sharedAirspaceWeight1 = MathHelper.clamp(sharedAirspace / 15.0f, 0.0f, 1.0f); + final float sharedAirspaceWeight2 = MathHelper.clamp(sharedAirspace / 10.0f, 0.0f, 1.0f); + final float sharedAirspaceWeight3 = MathHelper.clamp(sharedAirspace / 10.0f, 0.0f, 1.0f); + + sendCutoff0 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.0f) * (1.0f - sharedAirspaceWeight0) + + sharedAirspaceWeight0; + sendCutoff1 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.0f) * (1.0f - sharedAirspaceWeight1) + + sharedAirspaceWeight1; + sendCutoff2 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.5f) * (1.0f - sharedAirspaceWeight2) + + sharedAirspaceWeight2; + sendCutoff3 = (float) Math.exp(-occlusionAccumulation * absorptionCoeff * 1.5f) * (1.0f - sharedAirspaceWeight3) + + sharedAirspaceWeight3; + + // attempt to preserve directionality when airspace is shared by + // allowing some of the dry signal through but filtered + final float averageSharedAirspace = (sharedAirspaceWeight0 + sharedAirspaceWeight1 + sharedAirspaceWeight2 + + sharedAirspaceWeight3) * 0.25f; + directCutoff = Math.max((float) Math.pow(averageSharedAirspace, 0.5) * 0.2f, directCutoff); + + directGain = (float) Math.pow(directCutoff, 0.1); + + sendGain1 *= bounceReflectivityRatio[1]; + sendGain2 *= (float) Math.pow(bounceReflectivityRatio[2], 3.0); + sendGain3 *= (float) Math.pow(bounceReflectivityRatio[3], 4.0); + + sendGain0 = MathHelper.clamp(sendGain0, 0.0f, 1.0f); + sendGain1 = MathHelper.clamp(sendGain1, 0.0f, 1.0f); + sendGain2 = MathHelper.clamp(sendGain2 * 1.05f - 0.05f, 0.0f, 1.0f); + sendGain3 = MathHelper.clamp(sendGain3 * 1.05f - 0.05f, 0.0f, 1.0f); + + sendGain0 *= (float) Math.pow(sendCutoff0, 0.1); + sendGain1 *= (float) Math.pow(sendCutoff1, 0.1); + sendGain2 *= (float) Math.pow(sendCutoff2, 0.1); + sendGain3 *= (float) Math.pow(sendCutoff3, 0.1); + + if (mc.player.isInWater()) { + sendCutoff0 *= 0.4f; + sendCutoff1 *= 0.4f; + sendCutoff2 *= 0.4f; + sendCutoff3 *= 0.4f; + } + + if (Config.midnightPatching && Config.midnightPatchingFix && mc.world.provider.getDimensionType().getName() == "midnight") { + // Since the patch removes the incompatble reverb, readd some reverb everywhere + // It's not a great fix but it works fine + sendGain1 = MathHelper.clamp(sendGain1, 0.3f, 1.0f); + sendGain2 = MathHelper.clamp(sendGain2, 0.5f, 1.0f); + sendGain3 = MathHelper.clamp(sendGain3, 0.7f, 1.0f); + sendCutoff1 = MathHelper.clamp(sendCutoff1, 0.3f, 1.0f); + sendCutoff2 = MathHelper.clamp(sendCutoff2, 0.5f, 1.0f); + sendCutoff3 = MathHelper.clamp(sendCutoff3, 0.7f, 1.0f); + } + + setEnvironment(sourceID, sendGain0, sendGain1, sendGain2, sendGain3, sendCutoff0, sendCutoff1, sendCutoff2, + sendCutoff3, directCutoff, directGain, airAbsorptionFactor); + } catch(Exception e) { + logger.error("Error while evaluation environment:", e); + setEnvironment(sourceID, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + } + } + + private static void setEnvironment(final int sourceID, final float sendGain0, final float sendGain1, + final float sendGain2, final float sendGain3, final float sendCutoff0, final float sendCutoff1, + final float sendCutoff2, final float sendCutoff3, final float directCutoff, final float directGain, + final float airAbsorptionFactor) { + // Set reverb send filter values and set source to send to all reverb fx + // slots + EFX10.alFilterf(sendFilter0, EFX10.AL_LOWPASS_GAIN, sendGain0); + EFX10.alFilterf(sendFilter0, EFX10.AL_LOWPASS_GAINHF, sendCutoff0); + AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot0, 0, sendFilter0); + + EFX10.alFilterf(sendFilter1, EFX10.AL_LOWPASS_GAIN, sendGain1); + EFX10.alFilterf(sendFilter1, EFX10.AL_LOWPASS_GAINHF, sendCutoff1); + AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot1, 1, sendFilter1); + + EFX10.alFilterf(sendFilter2, EFX10.AL_LOWPASS_GAIN, sendGain2); + EFX10.alFilterf(sendFilter2, EFX10.AL_LOWPASS_GAINHF, sendCutoff2); + AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot2, 2, sendFilter2); + + EFX10.alFilterf(sendFilter3, EFX10.AL_LOWPASS_GAIN, sendGain3); + EFX10.alFilterf(sendFilter3, EFX10.AL_LOWPASS_GAINHF, sendCutoff3); + AL11.alSource3i(sourceID, EFX10.AL_AUXILIARY_SEND_FILTER, auxFXSlot3, 3, sendFilter3); + + EFX10.alFilterf(directFilter0, EFX10.AL_LOWPASS_GAIN, directGain); + EFX10.alFilterf(directFilter0, EFX10.AL_LOWPASS_GAINHF, directCutoff); + AL10.alSourcei(sourceID, EFX10.AL_DIRECT_FILTER, directFilter0); + + AL10.alSourcef(sourceID, EFX10.AL_AIR_ABSORPTION_FACTOR, MathHelper.clamp(Config.airAbsorption * airAbsorptionFactor,0.0f,10.0f)); + checkErrorLog("Error while setting environment for source: " + sourceID); + } + + /** + * Applies the parameters in the enum ReverbParams to the main reverb + * effect. + */ + protected static void setReverbParams(final ReverbParams r, final int auxFXSlot, final int reverbSlot) { + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DENSITY, r.density); + checkErrorLog("Error while assigning reverb density: " + r.density); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DIFFUSION, r.diffusion); + checkErrorLog("Error while assigning reverb diffusion: " + r.diffusion); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_GAIN, r.gain); + checkErrorLog("Error while assigning reverb gain: " + r.gain); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_GAINHF, r.gainHF); + checkErrorLog("Error while assigning reverb gainHF: " + r.gainHF); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DECAY_TIME, r.decayTime); + checkErrorLog("Error while assigning reverb decayTime: " + r.decayTime); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_DECAY_HFRATIO, r.decayHFRatio); + checkErrorLog("Error while assigning reverb decayHFRatio: " + r.decayHFRatio); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_REFLECTIONS_GAIN, r.reflectionsGain); + checkErrorLog("Error while assigning reverb reflectionsGain: " + r.reflectionsGain); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_LATE_REVERB_GAIN, r.lateReverbGain); + checkErrorLog("Error while assigning reverb lateReverbGain: " + r.lateReverbGain); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_LATE_REVERB_DELAY, r.lateReverbDelay); + checkErrorLog("Error while assigning reverb lateReverbDelay: " + r.lateReverbDelay); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_AIR_ABSORPTION_GAINHF, r.airAbsorptionGainHF); + checkErrorLog("Error while assigning reverb airAbsorptionGainHF: " + r.airAbsorptionGainHF); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, r.roomRolloffFactor); + checkErrorLog("Error while assigning reverb roomRolloffFactor: " + r.roomRolloffFactor); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_ECHO_TIME, r.echoTime); + checkErrorLog("Error while assigning reverb echoTime: " + r.echoTime); + + EFX10.alEffectf(reverbSlot, EFX10.AL_EAXREVERB_ECHO_DEPTH, r.echoDepth); + checkErrorLog("Error while assigning reverb echoDepth: " + r.echoDepth); + + // Attach updated effect object + EFX10.alAuxiliaryEffectSloti(auxFXSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbSlot); + checkErrorLog("Error while assigning reverb effect slot: " + reverbSlot); + } + + public static void log(final String message) { + logger.info(message); + } + + public static void logError(final String errorMessage) { + logger.error(errorMessage); + } + + protected static boolean checkErrorLog(final String errorMessage) { + final int error = AL10.alGetError(); + if (error == AL10.AL_NO_ERROR) { + return false; + } + + String errorName; + + switch (error) { + case AL10.AL_INVALID_NAME: + errorName = "AL_INVALID_NAME"; + break; + case AL10.AL_INVALID_ENUM: + errorName = "AL_INVALID_ENUM"; + break; + case AL10.AL_INVALID_VALUE: + errorName = "AL_INVALID_VALUE"; + break; + case AL10.AL_INVALID_OPERATION: + errorName = "AL_INVALID_OPERATION"; + break; + case AL10.AL_OUT_OF_MEMORY: + errorName = "AL_OUT_OF_MEMORY"; + break; + default: + errorName = Integer.toString(error); + break; + } + + logError(errorMessage + " OpenAL error " + errorName); + return true; + } + +} diff --git a/src/main/java/com/sonicether/soundphysics/SoundPhysicsServer.java b/src/main/java/com/sonicether/soundphysics/SoundPhysicsServer.java new file mode 100644 index 00000000..13a964ec --- /dev/null +++ b/src/main/java/com/sonicether/soundphysics/SoundPhysicsServer.java @@ -0,0 +1,21 @@ +package com.sonicether.soundphysics; + +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +//Server side mod to load the config +@Mod(modid = SoundPhysics.modid, serverSideOnly = true, acceptedMinecraftVersions = SoundPhysics.mcVersion,version = SoundPhysics.version, + acceptableRemoteVersions = "*", guiFactory = "com.sonicether.soundphysics.SPGuiFactory") +public class SoundPhysicsServer { + @Mod.EventHandler + public void preInit(final FMLPreInitializationEvent event) { + SoundPhysics.onServer = true; + Config.instance.preInit(event); + } + + @Mod.EventHandler + public void init(final FMLInitializationEvent event) { + Config.instance.init(event); + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/soundphysics_at.cfg b/src/main/resources/META-INF/soundphysics_at.cfg new file mode 100644 index 00000000..c651c639 --- /dev/null +++ b/src/main/resources/META-INF/soundphysics_at.cfg @@ -0,0 +1,2 @@ +public net.minecraft.util.SoundEvent field_187506_b # soundName +public net.minecraft.network.datasync.EntityDataManager field_187235_d # lock \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 8241d747..7d49cafa 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -3,9 +3,9 @@ "modid": "soundphysics", "name": "Sound Physics", "description": "Provides realistic sound attenuation, reverberation, and absorption through blocks.", - "version": "1.0.4", - "mcversion": "1.12.2", - "url": "", - "authorList": ["Sonic Ether","daipenger"] + "version": "${version}", + "mcversion": "${mc_version}", + "url": "https://github.com/djpadbit/Sound-Physics", + "authorList": ["Sonic Ether","daipenger","djpadbit"] } ] diff --git a/z_Link.bat b/z_Link.bat deleted file mode 100644 index a1a90a1d..00000000 --- a/z_Link.bat +++ /dev/null @@ -1 +0,0 @@ -gradlew eclipse \ No newline at end of file diff --git a/z_Setup.bat b/z_Setup.bat deleted file mode 100644 index 57a83e41..00000000 --- a/z_Setup.bat +++ /dev/null @@ -1 +0,0 @@ -gradlew setupDecompWorkspace \ No newline at end of file