Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Adding additional SoundCategory(s)


Aeronica
 Share

Recommended Posts

I'm looking for some help to get this added as a proper PR to forge. I have a working class for adding the sound category that will work in a mod. I've tested this with with 1.9.4 and 1.10+. I have not yet tried to setup a Forge dev environment but the videos that cpw made make it look simple enough. I still pretty much a java rookies, but learning more as I go. Your comments are welcome.

 

The class below allows the addition of a new sound category. It uses nothing more that the existing Forge utility classes for reflections and enums. You new sound category will have all the attributes of the vanilla sound categories.

That is:

  • A volume control for the category displays in the vanilla "Music & Sound Options..." dialog
  • The volume level is saved and loaded from the client options.txt file.
  • The Vanilla SoundHandler will correctly use the custom sound category

Here's the class on GitHub. There are some notes about using it in the comments

MODSoundCategory

I've also included the code below.

 

I have not started work to see what's required to make the vanilla "Music & Sound Options..." dialog behave correctly. Ideally it would work like the "Controls..." dialog. That is the volume controls could be scrolled. So far I've not seen anyone other mod add a custom SoundCategory / volume control. But it might be useful for other mods. And as a part of Forge mod authors could use it without fear of causing issues with vanilla or other mods.

 

For the mxTune mod I'm working on I wanted a separate sound category for the instruments. I can get by without this PR, but I wanted to share this with the community.

 

MODSoundCategory.class

 

package net.aeronica.mods.mxtune.sound;

import java.util.Map;

import com.google.common.collect.Maps;

import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.util.SoundCategory;
import net.minecraftforge.common.util.EnumHelper;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.relauncher.FMLLaunchHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

/*
* MODSoundCategory
* 
* Add a new CONSTANT and reference name to net.minecraft.util.SoundCategory
* 
* This allows the display of a volume control in the "Music & Sound Options" dialog.
* Unfortunately the GuiScreenOptionsSounds dialog does not auto size
* properly and move the Done button lower on the screen.
* 
* To initialize the class create an instance during FMLPreInitializationEvent in
* the file with the @Mod annotation or your common proxy class.
* 
* Usage example: static final SoundCategory SC_MXTUNE = MODSoundCategory.add("MXTUNE");
* 
* The language file key is "soundCategory.mxtune"
* The game settings "options.txt" key is "soundCategory_mxtune"
* 
* To use the MXTUNE enum constant in code it must be referenced by name because
* SoundCategory.MXTUNE does not exist at compile time.
*   e.g. SoundCategory.getByName("mxtune");
* 
* @author Paul Boese aka Aeronica
*
*/
public class MODSoundCategory
{
    
    private static final String SRG_soundLevels = "field_186714_aM";
    private static final String SRG_SOUND_CATEGORIES = "field_187961_k";
    private static Map<SoundCategory, Float> soundLevels;
    private static MODSoundCategory instance = new MODSoundCategory();
    
    private MODSoundCategory() {}
    
    public static MODSoundCategory getInstance() {return instance;}
    
    /**
     * The "name" should be your MODID or MODID_name if your mod adds more
     * than one SoundCategory.
     * 
     * @param name
     * @return a unique SoundCategory
     * @throws fatal error if name is not unique
     */    
    public static SoundCategory add(String name)
    {
        Map<String, SoundCategory> SOUND_CATEGORIES;

        String constantName;
        String referenceName;
        SoundCategory soundCategory;
        constantName = new String(name.toUpperCase().replace(" ", ""));
        referenceName = new String(constantName.toLowerCase());
        soundCategory =  EnumHelper.addEnum(SoundCategory.class , constantName, new Class[]{String.class}, new Object[]{referenceName});
        SOUND_CATEGORIES = ObfuscationReflectionHelper.getPrivateValue(SoundCategory.class, SoundCategory.VOICE ,"SOUND_CATEGORIES", SRG_SOUND_CATEGORIES);
        if (SOUND_CATEGORIES.containsKey(referenceName))
            throw new Error("Clash in Sound Category name pools! Cannot insert " + constantName);
        SOUND_CATEGORIES.put(referenceName, soundCategory);
        if (FMLLaunchHandler.side() == Side.CLIENT) setSoundLevels();

        return soundCategory;
    }

    /** Game sound level options settings only exist on the client side */
    @SideOnly(Side.CLIENT)
    private static void setSoundLevels()
    {
        /** SoundCategory now contains 'name' sound category so build a new map */
        soundLevels = Maps.newEnumMap(SoundCategory.class);
        /** Replace the map in the GameSettings.class */
        ObfuscationReflectionHelper.setPrivateValue(GameSettings.class, Minecraft.getMinecraft().gameSettings, soundLevels, "soundLevels", SRG_soundLevels);
    }

}

 

From this point on I'll just use bare bones minimal/incomplete code...

Creating custom sound categories:

 

 

package net.aeronica.mods.modtest;

import net.aeronica.mods.mod189stuff.audio.MODSoundCategory;
import net.aeronica.mods.mod189stuff.caps.ISimple;
import net.minecraft.util.SoundCategory;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;

@Mod(modid = Main.MODID, name = Main.MODNAME, version = Main.VERSION)
public class Main {
public static final String MODID = "modtest";
public static final String MODNAME = "Mod Testing";
public static final String VERSION = "1.10.2-0.1.0-dev";

@Mod.Instance(MODID)
public static Main instance;

public static SoundCategory MODTEST_A;
public static SoundCategory MODTEST_B;
public static SoundCategory MODTEST_C;
public static SoundCategory MODTEST_D;
public static SoundCategory MODTEST_E;
public static SoundCategory MODTEST_F;
public static SoundCategory MODTEST_G;
public static SoundCategory MODTEST_H;

@EventHandler
public void preInit(FMLPreInitializationEvent event) {
        // You need to register these on the the client and server side. A good place is your common proxy.
MODTEST_A = MODSoundCategory.add("MODTEST_A");
        MODTEST_B = MODSoundCategory.add("MODTEST_B");
        MODTEST_C = MODSoundCategory.add("MODTEST_C");
        MODTEST_D = MODSoundCategory.add("MODTEST_D");
        MODTEST_E = MODSoundCategory.add("MODTEST_E");
        MODTEST_F = MODSoundCategory.add("MODTEST_F");
        MODTEST_G = MODSoundCategory.add("MODTEST_G");
        MODTEST_H = MODSoundCategory.add("MODTEST_H");
}

@EventHandler
public void init(FMLInitializationEvent event) {
}

@EventHandler
public void postInit(FMLPostInitializationEvent event) {
}

}

 

Here's one way to use the custom sound category

 

// some item
. . .
    @Override
    public ActionResult<ItemStack> onItemRightClick(ItemStack itemStackIn, World worldIn, EntityPlayer playerIn, EnumHand hand)
    {
        if (!worldIn.isRemote)
        {
            // worldIn.playSound(EntityPlayer, BlockPos, SoundEvent, SoundCategory, volume, pitch)
            worldIn.playSound(null, new BlockPos(playerIn.posX,playerIn.posY,playerIn.posZ), 
                    SoundEvents.ENTITY_FIREWORK_BLAST, SoundCategory.getByName("modtest_a"), 1F, 1F);
            // instead of SoundCategory.getByName("modtest_a") you could use Main.MODTEST_A
         }
}
. . .

 

If you add or change  or remove your custom sound categories in your mod, vanilla will gracefully ignore and overwrite the settings. No worries about a crash for a missing custom sound category.

 

If you goof up and add a conflicting sound category you will crash MC. Oops!, but of course as a developer you should know better right!

Section of the client side options.txt where game settings are saved.

 

key_key.hotbar.8:9
key_key.hotbar.9:10
soundCategory_master:1.0
soundCategory_music:0.03521127
soundCategory_record:0.20422535
soundCategory_weather:0.59859157
soundCategory_block:0.42957747
soundCategory_hostile:0.5633803
soundCategory_neutral:0.5140845
soundCategory_player:0.52112675
soundCategory_ambient:0.29577464
soundCategory_voice:0.57042253
soundCategory_mxtune:0.5422535
soundCategory_modtest_a:1.0
soundCategory_modtest_b:1.0
soundCategory_modtest_c:1.0
soundCategory_modtest_d:1.0
soundCategory_modtest_e:1.0
soundCategory_modtest_f:1.0
soundCategory_modtest_g:1.0
soundCategory_modtest_h:1.0
modelPart_cape:true
modelPart_jacket:true

 

 

Here's a demo of it in use:

[embed=425,349]<iframe width="560" height="315" src="https://www.youtube.com/embed/A5lhR-pP4Mg" frameborder="0" allowfullscreen></iframe>[/embed]

 

Here's how the current 1.10.2 vanilla "Music & Sounds Options.." looks after adding several more unique sound categories. Yup, the dialog needs some loving.

 

rH79s.png

 

 

rH7au.png

 

 

rH79s.png

 

Link to comment
Share on other sites

  • 1 month later...

This is a very good feature. I would definitely use it if I update my Mod to 1.11 and it's there. (I would use it in 1.7.10 too, but I don't think the forge team is willing to add stuff to 1.7 :'().

 

One thing that I think needs to get added is a submenu. So you have a button "Mod Sound Sliders" which opens a submenu with a list of all modded sound sliders. (Similar to keybindings). Every mod is a major category, and maybe allow mods to add a minor category within the mod.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • Thanks everybody! I managed to fix it. I just had some missing attributes in my OilFluid class
    • W10 Pro. Android 11 on my S20+(or is it 12 already?). My two other computers are running on W10 home, all behind G-Data security suite, and a Fritz. I'm supposed to use Linux, no? I hate making things compatible, and Linux has limited access to some games I love, so...Mac is even worth I guess? I'm buying servers btw. It's not I couldn't set one up for Linux, but I lack the skills in online security, and I'm paranoid. So not a good combo I guess?
    • Yeah, the whole registry process can be quite confusing and daunting for how MC is working nowadays, but once you get the grasp of it, it actually saves a whole lot of effort, and quantity of code. I'd spend 3 days+ to actually figure I had to apply the getter(.get())[<-- is this even named getter?], to any of the .class references, because stupid Eclipse refused to de-obfuscate the source of problems. It was just making stupid a** auto fix suggestions, that didn't help at all. Google found the answer quite often though. And don't even get me started when I was trying to implement the Oil to world generation. I was literally tearing my hair out for days. But don't give up. We're not the only ones dealing with the reinvention of MC code wheel. I mean I get it. There's always room for improvements. But a wheel is just a wheel, right?
    • Thank you everybody, I have figured out the cause. I was accidentally passing in the constructor for the Fluids instead of the RegistryObject. I changed: super(new ForgeFlowingFluid.Properties( OilFluid.Source::new, OilFluid.Flowing::new, to: super(new ForgeFlowingFluid.Properties( ModFluids.OIL, ModFluids.FLOWING_OIL, And now the fluid actually works! But it doesn't spread though, I think I'd be able to figured it out from now. Thanks a lot Luis_ST and Cratthorax!
    • Because I was used to doing it like that in the past. Also, a block is very sparse in code quantity. Also, also, I would use the blocks to check against blocks of same type at lower levels, to mark as an indicator to what can be found digging deeper into the ground. Doing that with anything but the same object type is just unnecessarily bloating code. However, I had to use creative ways to get something working. What I did was rendering my block and its voxel shape invisible, make it nonsolid and notBlockMovement, then apply the randomTick() method, and using this: worldIn.destroyBlock(pos, true);...which basically drops the item after the blocks gets randomly destroyed. The good thing about that is, with the new DataPack function, I can do all that stuff with a single block object, and then just use instances of other block registers and their properties. I can also customize the loot tables for any of them, but don't need an actual block.class for any of them. Given how I have more then 60 ore blocks in my old mod, that is a really great thing about newer MC versions. The final solution was actually found in vanilla Block.AIR, after messing around with various methods, none of them doing what was easily done in the past MC versions with onBlockAdded(), and dropBlockAsItem(). I'll just leave the block code, and register code here for reference, which should give you a picture of the many things I was trying without success:
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.