Jump to content

[Solved] Custom SoundManager problem!


Rexozz

Recommended Posts

Hello Forge-Community!

 

First of all, I'm an Austrian, so my English isn't the best!

Secondly, I'm a beginner in Java and Forge, please don't judge me too hard! ^^

 

Ok, I wanted to make an dynamic SoundManager which loads the sounds from a custom path respectively custom resource pack which implements IResourcePack!

Everything worked fine, my SoundManager, my ResourcePack and everything else, until I tried to register a sound manager individual for each map.

I tried to give each soundmanager an unique id, here are my classes: (and again, please don't judge me too much for my bad coding skills ^^)

 

My WorldEvents class:

http://pastebin.com/yPEH9QpX

 

My Registry class:

http://pastebin.com/DAzGip3t

 

My WorldRegistry class:

http://pastebin.com/dTRaqURj

 

My SoundManager & SoundFolder class:

http://pastebin.com/Rg1mRXWe

 

And my WorldData class:

http://pastebin.com/iqRyAQxr

 

I coded all myself except the WorldData class for which I needed a tutorial! ^^

Please don't reply with code examples (except there is no alternative)!

Just declare what's wrong if you can help me (like: something in this class must be static or mustn't be final) xD

 

Thank you :P

Link to comment
Share on other sites

Because he would then have to work with zip files
This is not true.

[/qoute]

 

And this way, the resources come with the map, so the user don't have to download and set them manually :D
Vanilla Minecraft already supports bundling resource packs with a map.

 

but then it must be a zipped file, and I don't want this ^^

 

EDIT:

And the sounds must be registered anyway!

That's my actual problem, but im open to everything!

(sry if my first post wasn't declaring this obviously)

Link to comment
Share on other sites

Because the user don't need additional programs to edit content, not beacause he hasn't such programms, but it costs more time for the user ^^

second reason, my code will be much bigger with zip methods if I want my mod to change files in that zip!

But if you recommend this way rather than mine, then I really should do that ^^

 

Ok, now with the sound registry

Register the sounds everytime the user added a new sound

Should I do this with a FileWatcher that notifies the mod to register new sounds?

But then the sounds also registered for other maps :/

Link to comment
Share on other sites

Because the user don't need additional programs to edit content, not beacause he hasn't such programms, but it costs more time for the user ^^
While developing the map the user can use the folder resource pack method and only zip up the resource pack when they distribute the map.

 

Must the user do this by itself, or do i have to make this possible with my mod

 

second reason, my code will be much bigger with zip methods if I want my mod to change files in that zip!
Why do you want to edit the user's resource pack?  :o

 

Haha no, I meant that if the user use the method to change single textures, my mod changes them!

But now I know, that I should do this with resourcepacks too ^^

 

Register the sounds everytime the user added a new sound

Should I do this with a FileWatcher that notifies the mod to register new sounds?

Make the IResourcePack regenerate the generated sounds.json data using an
IResourceManagerReloadListener

, which will tell you when Minecraft reloads the resources. You can then use the file watch API to tell Minecraft to reload resources automatically if a new file is added.

 

Yeah, but the new sounds aren't registered in the forge registry then ^^

(wow, i really should do something for my english, horrible :o)

Link to comment
Share on other sites

You cannot register stuff to registries after preInit. If you must create new SoundEvents, you must restart the game.

Sure, it has worked when I registered them by loading the world ^^

But then they also registered for all worlds, because they registered for the mod and not for single world

And if a world uses a sound with the same name as another, there was a conflict, because soundevents can't registered twice

but I think you already know that haha :D

so i tried to give each world an UUID which the sound also gets

and thats why I let my mod write the sounds JSON, otherwise the user has to write the uuid in front of each sound in the sounds.json :P

Link to comment
Share on other sites

Doing this will cause all kinds of problems because of dynamic ID remapping (IDs change between worlds). If you just add stuff later, the registries might break.

 

Where would these newly created sound events be played from anyways?

 

Ok, didn't know that!

NPCs for example.

The user can set the sound via a GUI.

Or for background music, like in adventure games triggered by a block ^^

 

Maybe I should create a new instance of the existing soundsystem and edit some base classes to mute the old one?

But I think that's not a good idea, mod compatibility is a big problem then!

Although it doesn't matter if my mod is compatible or not, because it should be a single mod anyway.

Ah, maybe I should say that this mod should be a SinglePlayer mod!

I think that's very important information ^^

Link to comment
Share on other sites

I know that's not supported, but it's possible!

I've done it many times!

 

Unfortunately I am not currently at my IDE so I am not 100% sure how you might want to solve this. One idea would be to bypass the SoundEvent registry, but I am not sure how feasible that is.

Maybe i could create an class implementing the IWorldEventListener and create my own

 

public void playSound(@Nullable EntityPlayer player, double x, double y, double z, SoundEvent soundIn, SoundCategory category, float volume, float pitch)
    {
        net.minecraftforge.event.entity.PlaySoundAtEntityEvent event = net.minecraftforge.event.ForgeEventFactory.onPlaySoundAtEntity(player, soundIn, category, volume, pitch);
        if (event.isCanceled() || event.getSound() == null) return;
        soundIn = event.getSound();
        category = event.getCategory();
        volume = event.getVolume();
        pitch = event.getPitch();

        for (int i = 0; i < this.eventListeners.size(); ++i)
        {
            ((IWorldEventListener)this.eventListeners.get(i)).playSoundToAllNearExcept(player, soundIn, category, x, y, z, volume, pitch);
        }
    }

method.

 

I know that this isn't easy, because there are so many other classes needed to play the sound,

but if i could create my own SoundPlayEvent and register the soundevents in an additional map (temporary data, until i unloaded a map and register new soundevents after loading another map)

in my mod, would that work then?

I create a new superclass for my entities that handle the new sound management and let my existing entity inherit from that class!

Maybe this should work ^^

 

EDIT:

I could read thorugh the classes to learn how the sound system is working and then create my OWN CUSTOM sound system, especially for this mod ^^

Link to comment
Share on other sites

After many hours of struggling, i've found this useful method:

 

public void playSound(ISound p_sound)
    {
        if (this.loaded)
        {
            p_sound = net.minecraftforge.client.ForgeHooksClient.playSound(this, p_sound);
            if (p_sound == null) return;

            SoundEventAccessor soundeventaccessor = p_sound.createAccessor(this.sndHandler);
            ResourceLocation resourcelocation = p_sound.getSoundLocation();

            if (soundeventaccessor == null)
            {
                if (UNABLE_TO_PLAY.add(resourcelocation))
                {
                    LOGGER.warn(LOG_MARKER, "Unable to play unknown soundEvent: {}", new Object[] {resourcelocation});
                }
            }
            else
            {
                if (!this.listeners.isEmpty())
                {
                    for (ISoundEventListener isoundeventlistener : this.listeners)
                    {
                        isoundeventlistener.soundPlay(p_sound, soundeventaccessor);
                    }
                }

                if (this.sndSystem.getMasterVolume() <= 0.0F)
                {
                    LOGGER.debug(LOG_MARKER, "Skipped playing soundEvent: {}, master volume was zero", new Object[] {resourcelocation});
                }
                else
                {
                    Sound sound = p_sound.getSound();

                    if (sound == SoundHandler.MISSING_SOUND)
                    {
                        if (UNABLE_TO_PLAY.add(resourcelocation))
                        {
                            LOGGER.warn(LOG_MARKER, "Unable to play empty soundEvent: {}", new Object[] {resourcelocation});
                        }
                    }
                    else
                    {
                        float f3 = p_sound.getVolume();
                        float f = 16.0F;

                        if (f3 > 1.0F)
                        {
                            f *= f3;
                        }

                        SoundCategory soundcategory = p_sound.getCategory();
                        float f1 = this.getClampedVolume(p_sound);
                        float f2 = this.getClampedPitch(p_sound);

                        if (f1 == 0.0F)
                        {
                            LOGGER.debug(LOG_MARKER, "Skipped playing sound {}, volume was zero.", new Object[] {sound.getSoundLocation()});
                        }
                        else
                        {
                            boolean flag = p_sound.canRepeat() && p_sound.getRepeatDelay() == 0;
                            String s = MathHelper.getRandomUuid(ThreadLocalRandom.current()).toString();
                            ResourceLocation resourcelocation1 = sound.getSoundAsOggLocation();

                            if (sound.isStreaming())
                            {
                                this.sndSystem.newStreamingSource(false, s, getURLForSoundResource(resourcelocation1), resourcelocation1.toString(), flag, p_sound.getXPosF(), p_sound.getYPosF(), p_sound.getZPosF(), p_sound.getAttenuationType().getTypeInt(), f);
                                net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.sound.PlayStreamingSourceEvent(this, p_sound, s));
                            }
                            else
                            {
                                this.sndSystem.newSource(false, s, getURLForSoundResource(resourcelocation1), resourcelocation1.toString(), flag, p_sound.getXPosF(), p_sound.getYPosF(), p_sound.getZPosF(), p_sound.getAttenuationType().getTypeInt(), f);
                                net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.sound.PlaySoundSourceEvent(this, p_sound, s));
                            }

                            LOGGER.debug(LOG_MARKER, "Playing sound {} for event {} as channel {}", new Object[] {sound.getSoundLocation(), resourcelocation1, s});
                            this.sndSystem.setPitch(s, f2);
                            this.sndSystem.setVolume(s, f1);
                            this.sndSystem.play(s);
                            this.playingSoundsStopTime.put(s, Integer.valueOf(this.playTime + 20));
                            this.playingSounds.put(s, p_sound);

                            if (soundcategory != SoundCategory.MASTER)
                            {
                                this.categorySounds.put(soundcategory, s);
                            }

                            if (p_sound instanceof ITickableSound)
                            {
                                this.tickableSounds.add((ITickableSound)p_sound);
                            }
                        }
                    }
                }
            }
        }
    }

 

Could this work?:

1. Create custom SoundManager and inherit from the existing one

2. Get the most important fields via Java Reflection (because they're all private)

3. Override the playsound method

 

Maybe this should do it! ^^

Link to comment
Share on other sites

I see opportunity here:

 

            p_sound = net.minecraftforge.client.ForgeHooksClient.playSound(this, p_sound);
            if (p_sound == null) return;

 

That thar is a Forge hook. You can write an event handler to do almost anything with that incoming p-sound, and then return null so that the vanilla method quits without doing anything else.

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

Does this mean that I don't need to overwrite the existing method?

Just create an implemention of ISound?

 

That would be really awesome :D

 

But I think that wouldn't work, because the SoundEventAccesor has a list with sounds!

 

EDIT: Oh, okay, in this case, the SoundEventAccesor is just a null check ^^

Link to comment
Share on other sites

Yeah, I wanted to do so but I thought that would be way too insecure!

What if my sound overlaps another playing sounds and causes some problems?

I could use the SoundSystem in the SoundManager, but is this safe enough?

 

this.sndSystem.newStreamingSource(false, s, getURLForSoundResource(resourcelocation1), resourcelocation1.toString(), flag, p_sound.getXPosF(), p_sound.getYPosF(), p_sound.getZPosF(), p_sound.getAttenuationType().getTypeInt(), f);

 

For example!

 

When i was digging deeper, I found this one:

class SoundSystemStarterThread extends SoundSystem
    {
        private SoundSystemStarterThread()
        {
        }

        public boolean playing(String p_playing_1_)
        {
            synchronized (SoundSystemConfig.THREAD_SYNC)
            {
                if (this.soundLibrary == null)
                {
                    return false;
                }
                else
                {
                    Source source = (Source)this.soundLibrary.getSources().get(p_playing_1_);
                    return source == null ? false : source.playing() || source.paused() || source.preLoad;
                }
            }
        }
    }

 

But Threads are not my friends!

I know almost nothing about threads.

Doesn't matter how often I've read something about them!

Link to comment
Share on other sites

How did I know that there will be a thread exception?

 

http://pastebin.com/pb8U8i2m

 

First time playing the sound worked just fine, but then:

 

[05:44:04] [Thread-14/ERROR]: Error in class 'CodecJOrbis'
[05:44:04] [Thread-14/ERROR]: Unable to acquire inputstream in method 'initialize'.
[05:44:04] [Thread-14/WARN]: ERROR MESSAGE:
[05:44:04] [Thread-14/INFO]: C:\Users\Rexozz\Desktop\forge 1.10 modding\PixelEngine\saves\Neue Welt\mapres\sounds\sound\block.grass.step.ogg (Das System kann die angegebene Datei nicht finden)
[05:44:04] [Thread-14/WARN]: STACK TRACE:
[05:44:04] [Thread-14/INFO]: java.io.FileInputStream.open0(Native Method)
[05:44:04] [Thread-14/INFO]: java.io.FileInputStream.open(Unknown Source)
[05:44:04] [Thread-14/INFO]: java.io.FileInputStream.<init>(Unknown Source)
[05:44:04] [Thread-14/INFO]: java.io.FileInputStream.<init>(Unknown Source)
[05:44:04] [Thread-14/INFO]: sun.net.www.protocol.file.FileURLConnection.connect(Unknown Source)
[05:44:04] [Thread-14/INFO]: sun.net.www.protocol.file.FileURLConnection.getInputStream(Unknown Source)
[05:44:04] [Thread-14/INFO]: paulscode.sound.codecs.CodecJOrbis.initialize(CodecJOrbis.java:281)
[05:44:04] [Thread-14/INFO]: paulscode.sound.libraries.LibraryLWJGLOpenAL.loadSound(LibraryLWJGLOpenAL.java:392)
[05:44:04] [Thread-14/INFO]: paulscode.sound.libraries.LibraryLWJGLOpenAL.newSource(LibraryLWJGLOpenAL.java:640)
[05:44:04] [Thread-14/INFO]: paulscode.sound.SoundSystem.CommandNewSource(SoundSystem.java:1800)
[05:44:04] [Thread-14/INFO]: paulscode.sound.SoundSystem.CommandQueue(SoundSystem.java:2415)
[05:44:04] [Thread-14/INFO]: paulscode.sound.CommandThread.run(CommandThread.java:121)

 

This is where the sound plays:

http://pastebin.com/gUsSSujt

Link to comment
Share on other sites

Haha, it works now, I just wonder how long!

Are there any improvements that I could do?

 

PacketClass:

http://pastebin.com/KvCqVDM7

 

PlaySound in PEEntityCreature:

public class PEEntityCreature extends EntityCreature implements INpc{
public PEEntityCreature(World worldIn) {
	super(worldIn);
}

@Override
public void playSound(SoundEvent sound, float volume, float pitch) {
        if (!this.isSilent()){
        	if(sound.getSoundName().equals(this.getDeathSound().getSoundName())||sound.getSoundName().equals(this.getHurtSound().getSoundName()))
            	PacketHandler.sendToAllAround(new C00PacketSendSound(new ImplSound(sound,this.getSoundCategory(),volume,pitch,this.posX,this.posY,this.posZ)), this.dimension, this.posX, this.posY, this.posZ, volume > 1.0F ? (double)(16.0F * volume) : 16.0D);
        	else
        		super.playSound(sound, volume, pitch);
        }
    }
}

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.