Jump to content

[1.14.4] Load custom resources outside the jar


Eisenpaulchen

Recommended Posts

That's not a basic feature provided by Forge to my knowledge, but it is certainly possible.

Loading files outside of the jar can be achieved using basic Java. Read up on FileInputStream.

In order to use it, you can learn how the game plays sounds by exploring AudioStreamManager and SoundEngine among other related classes.

I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.

Link to comment
Share on other sites

2 hours ago, imacatlolol said:

That's not a basic feature provided by Forge to my knowledge, but it is certainly possible.

Loading files outside of the jar can be achieved using basic Java. Read up on FileInputStream.

In order to use it, you can learn how the game plays sounds by exploring AudioStreamManager and SoundEngine among other related classes.

Ok ill try looking into that. I first thought about copying the resources into the jar there are APIs in Java I think to do so, and then accessing them, but directly loading them into the game would be way better. thx for the suggestion

Link to comment
Share on other sites

This whole thing is really hard for me to understand there are so many Classes in MC regarding Sound and I have no Clue what they are meant for like there is the SoundHandler, Sound Engine, SoundHandler.Loader, SoundEvents,SoundEventAccessor and of course just Sound, but I cant find the point where the actual data goes in they just shuffel ResourceLocations from A to B. Is there an Overview of how Minecrafts soundmanagement works like here its loaded there it is registered this is to hold or process that information. Those number named Methods giving me a hard time to understand.

Link to comment
Share on other sites

Since playing arbitrary sounds isn't a normally supported feature, there's quite a lot of work to do to make things behave properly.

 

All those "func_######" methods are obfuscated, so the best way to learn what they are is to understand what is being put into them. For example, SoundSource#func_216422_a(float) doesn't have any clear purpose, but by looking at its usages in vanilla code I can find out that it sets the pitch of the sound source.

 

Here's a rough explanation of how sounds are played, based on what you're trying to do:

  1. Something creates an instance of an ISound. Most things use an instance of SimpleSound since it contains all sorts of useful properties.
  2. This ISound instance is then passed into SoundHandler#play(ISound), which is then passed SoundEngine#play(ISound). This is where the sounds get played.
  3. A whole bunch of stuff happens here. Forge runs some event hooks, a few values are validated, subtitles get activated, etc. You can ditch most of this for your system, but I recommend keeping all of it and working around it in order to keep things as close to vanilla as possible.
  4. Now, in the else part of the if(sound == SoundHandler.MISSING_SOUND) branch, this is where things get interesting, and confusing. From here, variables and methods are mostly obfuscated and arbitrary, which makes things hard to read.
  5. When reverse engineering this process, you may want to copy the code elsewhere and change the variable names as you go to make things easier to understand. Adding comments to obfuscated method calls can help remind you what those methods do when you figure them out.
  6. Now here's the biggest hurdle to get over: channelmanager$entry. As you can see, channelmanager$entry is a channel created by the ChannelManager in SoundEngine. In order to play your own arbitrary sounds, you're going to need this ChannelManager instance. Trouble is, the field is private. You have two major options here.
    1. Use reflection to make the ChannelManager field in SoundEngine accessible. This isn't too hard, but you should have a very good understanding of how reflection works in Java before doing this since reflection can be dangerous.
    2. Use an access transformer to make the field accessible through Forge. I think this is the best solution of the two. Like reflection, you should learn how these work yourself to make sure you fully understand what you're doing, since this tool can also be dangerous.
  7. After channelmanager$entry has been configured from data in the ISound and its Sound (confusing, I know) about pitch, location, etc, it's time to actually play the sound!
  8. It has to do slightly different things if the sound is being streamed or not. Because we're working with a FileInputStream, I'm going to assume the sound will be streamed.
  9. First, AudioStreamManager#func_217917_b(ResourceLocation) is run. This supplies an IAudioStream by turning a ResourceLocation into an OggAudioStream. You can create an OggAudioStream from any InputStream, so try doing that with your FileInputStream! Will it work? No idea! I haven't tested it myself.
  10. Then, it plays the IAudioStream through the configured channelmanager$entry by calling SoundSource#func_216433_a(IAudioStream) followed by SoundSource#func_216438_c().

Congratulations, if you did everything right, you will have just played a sound from a file! This is about as close as I can get without directly spoon-feeding code or teaching basic Java.

If people who are more experienced than me would like to improve any part of my explanation (or get rid of it, if it's too much hand-holding), it would be much appreciated.

Edited by imacatlolol

I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.

Link to comment
Share on other sites

Got it working I decided to use the reflection since while searching for what the access Transformer is I saw a post from diesieben07 advising to use reflection rather than the transformer

and after consulting Oracle I felt confident enought to use it especially since it seemed much less work.

Thank you for the help and the new knowledge of the existence of reflection.

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.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.

Announcements



×
×
  • Create New...

Important Information

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