Jump to content

Akjosch

Members
  • Posts

    17
  • Joined

  • Last visited

Everything posted by Akjosch

  1. This will very likely not work with a dedicated server (... but feel free to test it out and report your findings).
  2. A few notes: * I'd make an IAnimatedModelCustom interface (extends IModelCustom). No need to support all those no-op methods for model types which can't even support them, and you can easily find out if you can call the animation functions via "instanceof". * COLLADA in particular (but also other model formats) supports multiple named animations. I'd suggest getting and using those animations this way: ModelAnimation anim = animatedModel.getAnimationAll("AnimName"); /* Alternatives: ModelAnimation anim = animatedModel.getAnimationPart("AnimName", "GroupName"); ModelAnimation anim = animatedModel.getAnimationOnly("AnimName", "GroupName1", "GroupName2", ... "GroupNameX"); ModelAnimation anim = animatedModel.getAnimationAllExcept("AnimName", "ExGroupName1", ..., "ExGroupNameX"); ModelAnimation anim = animatedModel.getAnimationAll(null); // Get all the animations in one timeline */ float frame = ((float)tick - startTick + partialTick) / 20.0f * anim.framesPerSecond; anim.render(frame); /* Alternative: anim.renderTick((float)tick - startTick + partialTick); */ /* Other stuff to do with an animation: float anim.length - length in seconds float anim.lengthTicks - length in ticks int anim.frames - amount of frames (at least 1) IAnimatedModelCustom anim.model - The model this animation applies to Set<String> anim.groups - The model groups this animation applies to // Append another animation to this one, resolving animation channels by name and // applying them to the original anim's model. ModelAnimation newAnim = anim.append(ModelAnimation otherAnim); ModelAnimation newAnim = anim.append(ModelAnimation otherAnim, int blendFrames); // Blend one animation into another ModelAnimation newAnim = anim.blend(ModelAnimation otherAnim, float strength); ModelAnimation newAnim = anim.blend(ModelAnimation otherAnim, float strength, int finalAmountOfFrames); ModelAnimation newAnim = anim.blend(ModelAnimation otherAnim, AnimationBlender blendMethod); // Slow down or speed up an animation ModelAnimation newAnim = anim.forLength(float seconds); ModelAnimation newAnim = anim.forFPS(float framesPerSecond);
  3. The question is a bit ... confusing. Construction of a mob's models happens at most twice during the game run: Once into Minecraft's internal representation of it (during the constructor call of its Model) and once when the model first has to be rendered, into graphic card memory (in compileDisplayList(float) of its ModelRenderers). Neither of them involve any texture binding. Texture binding (for mobs) happens once per frame the entity is to be rendered in renderModel(EntityLivingBase, float, float, float, float, float, float) (actually more of a renderModel(<? extends EntityLivingBase>, float, float, float, float, float, float) in original code, but we don't get the generics decompiled properly right now) of the entity's renderer, and it typically binds whatever ResourceLocation gets returned from getEntityTexture(Entity) of the entity's renderer returns (though the Ender Dragon specifically also some extra code for the "dying" textures). There are no hooks or events you can set anywhere in code to manipulate that. Your best - least invasive, at least - bet would be to do a coremod and exchange RenderDragon's protected ResourceLocation getEntityTexture(Entity) method with your own. More complicated (in terms of the amount of code needed) would be to @ForgeSubscribe to the RenderLivingEvent.Pre event and do the whole dragon rendering yourself, preventing the original renderer from even running (event.setCanceled(true)).
  4. Thanks! Now I need to make "one project per mod in the same workspace" work and modify the build.gradle file to work like I want it to and I'll be a happy camper.
  5. The problem is that the Anvil file format still saves just 12 bytes off a blockID, meaning we don't gain much of anything ... yet. (See aor.class for details)
  6. DemoXin just published his "FortuneOres" mod which does exactly that and is open source, so you can check for yourself how he did that - or ask him: http://www.minecraftforum.net/topic/2079807-fortuneores/
  7. The "problem" with EntityRegistry.registerGlobalEntityID() is that it doesn't fill up a couple of FML maps, which means (from what I understood of the code) you can't use FML's custom mod spawning and tracking. As a workaround, would the following fill them up properly? Looking at the code for 1.6.4, it seems it might just work, but I'm not sure were the FML or the Forge team want to take those ... EntityRegistry.registerGlobalEntityID(EntityMyMob.class, modID + "." + mobUntranslatedName, id, backgroundEggColour, foregroundEggColour); EntityRegistry.registerModEntity(EntityMyMob.class, mobUntranslatedName, mobInternalID, this, trackingRange, updateFrequency, true); (+ the renderer code obviously)
  8. Right now, I'm registering mobs with the game as follows. In the mod's initialisation routine: // We'll need that later. Map<Class, Integer> classToIDMapping = ObfuscationReflectionHelper.getPrivateValue(EntityList.class, new EntityList(), "classToIDMapping"); // For every mob, repeat the following // Registering the mob with FML (this also fills EntityList's stringToClassMapping and classToStringMapping) EntityRegistry.registerModEntity(EntityMyMob.class, mobUntranslatedName, mobInternalID, this, trackingRange, updateFrequency, true); // Add a readable name for the default language (en_US) - this can also be done via a "lang" resource, I guess LanguageRegistry.instance().addStringLocalization("entity." + modID + "." + mobUntranslatedName + ".name", "en_US", mobName); // Find some unique and not used GLOBAL id (via checks for EntityList.getStringFromID(...) being null) Integer id = Integer.valueOf(getUniqueEntityId()); // Register the egg EntityList.entityEggs.put(id, new EntityEggInfo(id, primaryColor, secondaryColor)); // This is so that the egg can spawn the mob EntityList.IDtoClassMapping.put(id, EntityMyMob.class); // And this is so that middle click on the mob gets us the egg in creative mode (and why we needed the reflection above) classToIDMapping.put(EntityMyMob.class, id); And, of course, in the client-side proxy: RenderingRegistry.registerEntityRenderingHandler(EntityMyMob.class, new RenderMyMob()); Is this the "right way"? If no, how are we supposed to do that (the internet is full of tutorials, all of them different ...)? If yes, why so complicated?
  9. You can access getRecipeSize(), which can give you the answer for 1x1, 2x2 and 3x3 recipes (it returns 1, 4 or 9 in those cases). For everything else ... public static int[] recipeSize(ShapedOreRecipe recipe) { int width = ObfuscationReflectionHelper.getPrivateValue(ShapedOreRecipe.class, recipe, "width"); int height = ObfuscationReflectionHelper.getPrivateValue(ShapedOreRecipe.class, recipe, "height"); return new int[]{width, height}; }
  10. ok i really need an example.... let's suppose my texture is "assets/minecraft/textures/entity/MYTEXTURE.png" DON'T. Mod resources go into "assets/(modID, lower case)" and no-where else. In other words, this should be "assets/modid/textures/entity/MYTEXTURE.png" In your entity renderer (extends one of the sub-classes of net.minecraft.classes.renderer.entity.Render): private static final ResourceLocation texture = new ResourceLocation("modid", "textures/entity/MYTEXTURE.png"); @Override protected ResourceLocation getEntityTexture(Entity entity) { return texture; } That's it. It gets automatically called for entities which use that (for example by RenderLiving.renderModel() if you extend RenderLiving). If you want to implement your own doRender(Entity entity, double posX, double posY, double posZ, float yaw, float partialTickTime) method, that's how you bind it and then call the model to render: bindEntityTexture(entity); renderLivingAt(entity, posX, posY, posZ); // or use GL11.glTranslatef() yourself /* Remember to rotate the entity properly if needed, via GL11.glRotatef() or the helper function rotateCorpse() if you're rendering a living */ /* Calculate the other animation-related values for the next call */ mainModel.render(entity, limbSwingTime, limbSwingMaxDist, entity.ticksExisted + partialTickTime, headRotY, headRotX, scale);
  11. He means you can simply use GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID) with textureID being an integer with the (OpenGL-internal) texture number, usually from GL11.glGenTextures(), which you previously assigned some image data to via GL11.glTexImage2D(), GL11.GL11.glTexSubImage2D() or similar methods. If you have the textures referenced as Minecraft's ResourceLocation though (and you should), it can do all of it for you "automagically" by simply calling Minecraft.getMinecraft().renderEngine.bindTexture(textureResourceLocation).
  12. Chunks get rendered up to (64 << (3 - renderDistance)) or 400 blocks, whatever is bigger, far. The far clipping plane (which "cuts off" the rendering) is at (256 >> renderDistance) meters (blocks) in the direction your camera is pointing at.
  13. 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.
  14. 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?
  15. 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.
  16. 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. 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.
  17. 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?
×
×
  • Create New...

Important Information

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