Posted October 4, 201312 yr 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.
October 4, 201312 yr 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...
October 4, 201312 yr Author 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.
October 4, 201312 yr 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...
October 4, 201312 yr Author 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.
October 4, 201312 yr Localisation handled by the server ? 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.
October 4, 201312 yr Author 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.
October 5, 201312 yr Author 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.
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.