Jump to content

[1.6.2] Creating and registering your own ResourcePack class


Akjosch

Recommended Posts

Ok, so I'm trying to do something which should be simple: Creating my own ResourcePack class. The currently available net.minecraft.client.resources.AbstractResourcePack just doesn't cut it for the following reasons (and I'd prefer to leave that alone so not to break anything else):

 

  • It's client-side only.
  • At least one of its implementations (FolderResourcePack) considers a directory to not be a valid resource (see: hasResourceName() checking for isFile()). Mind you, an archive file (which is basically the same thing: both are containers for resources) is accepted just fine, which is beyond silly.
  • There's no way to specify that your resource location is something with wildcards in it, or at least a filtered directory or archive.

 

Now, that's all fine. Implementing my own class which implements ResourcePack is easy enough. However, I have no way to tell that my mod wants to use this specific ResourcePack instead of the default FMLFolderResourcePack or FMLFileResourcePack (Or is there? I couldn't find it). At least not without changing the code of FMLModContainer's getCustomResourcePackClass() method, which means a coremod + ASM tricksery, which I loathe to do.

 

The other idea I had was to implement and inject my own ModContainer, derived from and almost identical to FMLModContainer, using (and triggered by) my own annotation instead of @cpw.mods.fml.common.Mod, but I still don't see how to achieve that. This time I get foiled, it seems, by ModContainerFactory's build() method, which has "@Mod" hardcoded as the only annotation it cares about. Again, only fixable by hacking around core Forge classes, not something I'm a fan of.

 

Do I really have to turn every single mod where I'd like to do something as trivial as use my own ResourcePack class into a coremod? Is there a better, more elegant method? My ideal solution would be a simple annotation alongside @Mod and @NetworkMod:

 

@ResourcePack(class="my.CustomResourcePack")

 

Side note: Tagged as 1.6.2 since that's what I'm using, but looking at the Forge code nothing much changed in 1.6.4 about my problems.

 

-------------------

Thinking about it a bit more, can I just modify Minecraft.getMinecraft().defaultResourcePacks (I know it's private ...) at some point before the last call to refreshResources(), or modify it and call the method myself, or will it horribly break things?

ItemBlock is not a Block

ItemStack is not an Item

Damage value is not metadata

 

Stop confusing them.

Link to comment
Share on other sites

I have no good replies for you, but I wanted to give you a heads up that there may be sever changes to the way such packs works in the upcoming 1.7 update.

 

For your last paragraph:

You can use Reflection to change a private member to public if that would help?

Else you could use a coremod and take advantage of the ASM library to modify the class at runtime (no direct base edits!)

 

 

 

If you guys dont get it.. then well ya.. try harder...

Link to comment
Share on other sites

I have no good replies for you, but I wanted to give you a heads up that there may be sever changes to the way such packs works in the upcoming 1.7 update.

 

Yeah ... would be nice to have it running for 1.6 though. Given how long some mods are taking to upgrade from 1.5 already, I'm not in a hurry.

 

You can use Reflection to change a private member to public if that would help?

Else you could use a coremod and take advantage of the ASM library to modify the class at runtime (no direct base edits!)

 

Forge already changes "private" class members to "public" at Minecraft startup (and before the mods load), so it works by simply changing them to "public" in your development environment and making sure you don't include anything which isn't in your packages when you make the distribution.

 

... and changing the functionality via ASM hacks is what I'm trying to avoid doing. ;)

ItemBlock is not a Block

ItemStack is not an Item

Damage value is not metadata

 

Stop confusing them.

Link to comment
Share on other sites

Personally, i would use

Minecraft.getMinecraft().mcResourcePackRepository.setRepositoryEntries(array);

to add a custom ResourcePackRepositoryEntry which override updateResourcePack() to call your custom ResourcePack.

 

As a side note, there is no "last call to refreshResources()". Minecraft itself does it regularly.

So you can do that anytime, i assume.

 

It's client-side only.

And how is that a drawback for a ResourcePack ? A server doesn't need multiple versions of the same thing...

Link to comment
Share on other sites

It's client-side only.

And how is that a drawback for a ResourcePack ? A server doesn't need multiple versions of the same thing...

 

Well yes, yes they do. Examples:

 

  • Localisation of server messages (depending on client's language setting)
  • Server-generated resources (again, depending on client's capabilities, localisation or settings)
  • Dynamically extending instead of replacing resources, which is the whole point of the exercise of having directories and wildcards as valid resource locations.

ItemBlock is not a Block

ItemStack is not an Item

Damage value is not metadata

 

Stop confusing them.

Link to comment
Share on other sites

Localisation handled by the server ?  :o

Are you crazy ?

This would mean different packet length for each receiving part depending on the receiver settings...this a f* nightmare !

Let that to client, please...

Or next time you are going to try handling the rendering on server side.:P

Link to comment
Share on other sites

Some test reports:

 

Well, hooking up my resource pack in FMLClientHandler.instance().resourcePackList and FMLClientHandler.instance().resourcePackMap in pre-init, followed by Minecraft.getMinecraft().refreshResources() works to a point - the FallbackResourceManager calls it up. However, it then tries to construct a SimpleResource, which needs an InputStream, which won't work and makes no sense with a directory.

 

Time to implement my own ResourceManager and convince Minecraft to use that for my mods ... Which will be kinda hard given it only uses one, in Minecraft.getMinecraft().mcResourceManager- Yay. Fun. Anybody got an easier, more straightforward idea?

 

ItemBlock is not a Block

ItemStack is not an Item

Damage value is not metadata

 

Stop confusing them.

Link to comment
Share on other sites

Test #2, time to implement my own InputStream which can deal with directories (and handles zip files as directories as well). Wish me luck. ;)

 

package my.package;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

/**
* An InputStream supporting files and directories.
* <p>
* It works in one of several modes, depending on what the argument to the constructor was.
* <p>
* If the argument is a directory, it'll behave like a zero-length input stream. In addition,
* isDirectory() will return <i>true</i>, isFile() will return <i>false</i>. The specific files
* and directories are available through the default Iterable interface returned by
* the interface() method.
* <p>
* If the argument is an archive file (currently only ZIP files supported), it'll behave like
* a directory, but also allow for direct reading of the file same as a FileInputStream would
* and return <i>true</i> for isFile().
* <p>
* If the argument is any other file, this class behaves like a FileInputStream. In particular,
* iterator() returns <i>null</i>.
*/
public class DirectoryInputStream extends InputStream implements Iterable<InputStream>
{
private File baseFile = null;
private ZipFile zipFile = null;
private InputStream baseInputStream = null;

public DirectoryInputStream(String name) throws FileNotFoundException
{
	this(name != null ? new File(name) : null);
}

public DirectoryInputStream(File file) throws FileNotFoundException
{
	String name = (file != null ? file.getPath() : null);
        if (name == null) {
            throw new NullPointerException();
        }
        baseFile = file;
        try
        {
		zipFile = new ZipFile(file);
	}
        catch (ZipException ze)
        {
        	// This is clearly not a zip file. 
	}
        catch (IOException ioe)
        {
        	// Neither is this
	}
        if( baseFile.isFile() )
        {
        	baseInputStream = new FileInputStream(baseFile);
        }
   }

public boolean isFile()
{
	return baseFile.isFile();
}

public boolean isDirectory()
{
	return baseFile.isDirectory() || null != zipFile;
}

@Override
public int read() throws IOException
{
	return( null != baseInputStream ? baseInputStream.read() : -1 );
}

@Override
public int read(byte[] b) throws IOException
{
	return( null != baseInputStream ? baseInputStream.read(b) : -1 );
}

@Override
public int read(byte[] b, int off, int len) throws IOException
{
	return( null != baseInputStream ? baseInputStream.read(b, off, len) : -1);
}

@Override
public long skip(long n) throws IOException
{
	return( null != baseInputStream ? baseInputStream.skip(n) : 0);
}

@Override
public int available() throws IOException
{
	return( null != baseInputStream ? baseInputStream.available() : 0);
}

@Override
public void close() throws IOException
{
	if( null != baseInputStream )
	{
		baseInputStream.close();
	}
	if( null != zipFile )
	{
		zipFile.close();
	}
}

@Override
public synchronized void reset() throws IOException
{
	if( null != baseInputStream )
	{
		baseInputStream.reset();
	}
}

@Override
public Iterator<InputStream> iterator()
{
	if( baseFile.isDirectory() )
	{
		Iterator<InputStream> it = new Iterator<InputStream>()
		{
			private int currentIndex = 0;
			private File[] files = baseFile.listFiles();

			@Override
			public boolean hasNext()
			{
				return currentIndex + 1 < files.length;
			}

			@Override
			public InputStream next()
			{
				++ currentIndex;
				try {
					if( files[currentIndex].isDirectory() )
					{
						return new DirectoryInputStream(files[currentIndex]);
					}
					else
					{
						return new FileInputStream(files[currentIndex]);
					}
				}
				catch (FileNotFoundException e)
				{
					// Shouldn't happen
					return null;
				}
			}

			@Override
			public void remove()
			{
				// We don't supply remove()
			}
		};
		return it;
	}
	else if( null != zipFile )
	{
		// TODO: Make sure we don't accidentally return non-working InputStreams, like
		// for directories, and not crash and burn either when we encounter one.
		Iterator<InputStream> it = new Iterator<InputStream>()
		{
			private Enumeration<? extends ZipEntry> entries = zipFile.entries();

			@Override
			public boolean hasNext()
			{
				return entries.hasMoreElements();
			}

			@Override
			public InputStream next()
			{
				ZipEntry zipEntry = entries.nextElement();
				try
				{
					return zipFile.getInputStream(zipEntry);
				}
				catch (IOException e)
				{
					// Shouldn't happen. Did someone close the file on us?
					return null;
				}
			}

			@Override
			public void remove()
			{
				// We don't supply remove()
			}
		};
		return it;
	}
	else
	{
		return null;
	}
}

}

 

Input, as usual, very much appreciated.

ItemBlock is not a Block

ItemStack is not an Item

Damage value is not metadata

 

Stop confusing them.

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.