[1.8] Mod Development Help Thread/Updating to 1.8 Thread

Discussion in 'Mod Development' started by Chaka, Nov 27, 2014.

  1. Strikingwolf

    Strikingwolf New Member

    And the new command block things...
     
  2. McJty

    McJty Over-Achiever Mod Developer

    Ok, that's your personal choice (about the villagers) but other players may actually like to work with villagers (like me).
     
    SatanicSanta likes this.
  3. VikeStep

    VikeStep New Member

    The reason why individually you should update to 1.8 is because:

    1. Despite your lack of interest in updating and the difficulties it will bring with modding you must realise you aren't going to be able to stop everyone. It's just not going to happen
    2. If you want your mods to be played they need to be played on what everyone else is playing. I guarantee you. People will move to 1.8 eventually and you will be left behind.

    I should mention that you obviously don't need to update now, you can wait until everyone else has started moving if you wish since I don't see any good 1.8 packs coming out in the next month or two.
     
    Elec332, Strikingwolf and Moridin like this.
  4. Strikingwolf

    Strikingwolf New Member

    Logical Vikestep *grumbles*
     
  5. ljfa

    ljfa New Member

    I'm kinda worried about all the BlockPos, Integer, IBlockState etc. instances that are created on the heap and burden the gabage collector.

    It might look more elegant in code to pass a BlockPos to a method instead of x, y, and z but god knows how much RAM and GC time it costs.

    Looks like Block States haven't completely replaced metadata: There are getStateFromMeta and getMetaFromState which have to be overridden. Before 1.8 metadata only goes up to 16 and if that's still the case then you can't store more than 4 bits in a block state. Now compare that to how much RAM all the pointers and allocated objects around a block state take.

    I doubt that 4 GB of RAM will be enough for any medium sized modpack in the future.

    I updated a small mod of mine to 1.8. Seems like the blocks work functionally now but they all look like black and purple squares yet.
     
  6. CoolSquid

    CoolSquid New Member

    Probably. Vanilla alone is using an insane amount of RAM per second.
     
  7. ratchet freak

    ratchet freak Well-Known Member

    depends if they are final or not, either way java has long since been optimized to handle many short lived objects
     
  8. immibis

    immibis New Member

    Short lived objects might be cheap to clean up, but they make garbage collections happen more frequently, which is apparently not cheap. (Regardless of what advanced optimizations Oracle say they have, the fact that lots of people suddenly have performance problems proves otherwise)
     
  9. McJty

    McJty Over-Achiever Mod Developer

    Note that a local object that is used and immediatelly has no more reference is not actually removed with the garbage collector. Java has a special optimization for that particular case since it is very common. The object space is immediatelly reclaimed.
     
  10. ljfa

    ljfa New Member

    I sure hope so. Looks like it works differently than "new" and "delete" in C++. We shouldn't worry too much about allocating short-lived objects. Java is object-oreinted after all.

    But still: If block states can only store 4 bits then the overhead is massive compared to that.
     
  11. keybounce

    keybounce New Member

    A local object that dies immediately doesn't even go through the garbage collector?

    Where did you read that? It was provably not true in java 6, and I never saw anything about that in java 7. Is that new in Java 8?
     
  12. McJty

    McJty Over-Achiever Mod Developer

  13. ratchet freak

    ratchet freak Well-Known Member

    actually short lived objects that don't leave the function scope can get allocated on the stack after optimization. Eliminating the memory allocation and the GC because it will get cleaned up after the function returns.
     
  14. keybounce

    keybounce New Member

    The first article is discussing Eden collection, which is still garbage collection. It's faster than the normal one because you are looking over a smaller set of data. There is no stack-based special "doesn't get collected" discussed there.

    In fact, the second article is discussing the exact same thing.

    Is the short-term, eden, collection faster than the tenured collection? Sure.
    Is it free, stack-based, automatically deallocated when the routine leaves? Nope.

    As I said, I don't know if J8 has some new optimization to make things stack-based. J7 did not. I've never seen anything mention this before.
     
  15. FyberOptic

    FyberOptic New Member

    I may release a small code dump or Github project or something with useful things, but I spent all freaking day today trying to hammer Minecraft and Forge into working with dynamic models, so that I could port something as simple as Hopper Ducts.

    The ducts themselves can be easily pointed in any direction by the standard model system, but it needs the little connected bits which extend to a neighboring duct so that you know they're connected. Doing this by models and states would result in hundreds of possible combinations, and that's simply ridiculous to try to do by model file. In my own work at writing my own API, none of this was a problem because I hooked the block rendering, allowed my block to return a special renderer type, and could draw shapes as dynamically as I wanted with minimal effort. But this is Forge, so nothing is easy.

    What I ended up doing, for starters, was ripping a bunch of code out of Minecraft and changing it for my own use, because neither vanilla or Forge gives you access to things in some of the model loading and baking classes that you actually need to do anything useful. In the end, I put together methods for loading models manually, as well as for baking them to whatever rotations I wanted. Combined with ISmartBlockModel, I made a system where I could load multiple model files, and using block state information, output combined lists of quads from multiple models to represent the final model. In this case, the duct is one model, and the connector bit is another model. I rotate them appropriately, and output as many connector models as necessary on the sides, all based on states.

    The downside still to ISmartBlockModel is that you don't have access to the tile entity. There are values you simply don't need to store as a block state, such as whether those neighboring ducts are present, especially when it requires 6 bits of information to do so. But there's no way around it. Lots of block states are going to be your only option for dynamic rendering, which is extremely limiting, and very annoying to set up. And none of this is going to be particularly fast or easy on RAM.

    That's another thing: Forge's system for specifying hidden properties for model baking is just dumb. Instead of a normal IProperty, you use IUnlistedProperty with Forge's system, which isn't compatible with IProperty. Vanilla Minecraft can't handle through regular means, and you have to run them through an adapter thing in Forge. This is going to be confusing for people using existing vanilla blocks to learn how to do this stuff. It's especially dumb when you consider Minecraft already provides an accessible way for hiding block properties, without doing any of that. During init, I simply do:

    Code:
            BlockModelShapes bms = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes();
            bms.registerBlockWithStateMapper(blockHopperDuct, (new StateMap.Builder()).addPropertiesToIgnore(new IProperty[] { blockHopperDuct.ENABLED, blockHopperDuct.FACING, blockHopperDuct.CONNECTORS }).build());
    
    Done. You can also create your own custom property handler, or use some of the other built-in ones. This is how I do it in a non-Forge environment, and it still works in a Forge environment, so I have no idea why Forge introduced its own which isn't as flexible in the long run.

    Another annoying thing you might run across is Forge will destroy your tile entity any time you update your block state. You need to override shouldRefresh. In my case, I just check to see whether the block of the old state is the same as the block of the new state. If they're not, I let it destroy the tile entity. Otherwise, no, because this was eating items in the ducts every time a block update happened nearby. Took me forever to track this down, because it's not vanilla behavior.

    Anyway, like I said, I'll probably put up some code so that other people don't have to fight with this mess as much as I've done today. But all in all, I think I'll be able to finish updating Hopper Ducts now without too much fuss. But good grief, what a mess of code, just to render some cubes.
     
  16. ratchet freak

    ratchet freak Well-Known Member

    Sounds like they want to be able to instance the models so they just need to fill a single buffer (per 16*16*16 chunk) with relevant data and then pass through a custom shader that applies the transformation

    though a overridable method that will allow you to push the needed quads to the buffer would have been much more flexible and allow for the same speedup over custom renderer per block
     
  17. FyberOptic

    FyberOptic New Member

    So I'll share more of my experience, in case it helps anyone else.

    Block states were not what I was led to believe. I don't remember who explained it in the way which gave this magical impression of what they were capable of. But I was under the impression that the number of block IDs had been significantly extended, and that a block would allocate a range of IDs to account for all its possible states. That this was going to give blocks the ability to store more than just 4 bits, and offer an alternative to using tile entities when you just need a bit more data. Except that's completely wrong, I don't remember why people were saying it, and I feel kinda dumb for thinking that block states were more than they actually are and causing me so many code issues. It wasn't until I actually dug into the code that I realized how it really worked.

    There are still only 4096 blocks. And there is still only 4 bits of metadata. Your block states are converted into metadata by getMetaFromState in your Block class, and converted from metadata back into a block state with getStateFromMeta. Do not try to convert your states into more than 4 bits of data. If you do, you'll have random strange things happen, because Minecraft isn't smart enough to filter the data. This was biting me for a while. You can have as many block states as you want, and they can be used for many things client-side, but just remember that anything passed through to the server or saved to the disk is only taking what your getMetaFromState can cram into those 4 bits.

    And what I said before about not having access to the tile entity with an ISmartBlockModel is not completely accurate. While the model itself doesn't, it will trigger a call to getExtendedState in your block (which by default calls getActualState) just prior to calling ISmartBlockModel's handleBlockState method. getExtendedState/getActualState is where you can take advantage of all of a block's states for rendering, regardless of the metadata limitation. It only provides an IBlockAccess for interacting with the world, but you can easily use that to retrieve your block's tile entity, and from there an actual World object. Now you can set up more complex block state information based on values saved in your tile entity or what have you, which are then passed into the ISmartBlockModel. Yes, it's a rather roundabout way to go about doing this compared to what we used to do, but hey, we work with what we've got.

    That being said, the amount of RAM being wasted keeping track of every possible combination of block states for every block is really pretty stupid, so you might keep that in mind before having too many block states, even though I realize this is your only option for shuttling data to the renderer. Having this heavy abstraction on top of metadata, and being trapped in this model rendering system, is all going to eat RAM and thrash the GC worse than this game has ever seen. What a mess.
     
    squeek502 likes this.
  18. keybounce

    keybounce New Member

    And I had thought / read Grum's (?) comments as "moving from 4096 blocks + 16 states to 65536 blocks and states combined" -- essentially, turning the whole 16 bits into block ID's, and eliminating all duplicated stuff -- for example, instead of umpteen different stairs on a single block ID, each stair is now a different block ID, and there's a state for facing east, facing west, facing north, facing south, and another 4 states for upside down and facing that way.

    That it turned into what appears to be special rendering nightmare?
    I'm glad I'm not doing mods now.
     
  19. immibis

    immibis New Member

    I'm glad I'm not doing 1.8 now.
     
    SatanicSanta likes this.
  20. FyberOptic

    FyberOptic New Member

    It might be worth using eventually, but not so much as-is.

    After writing the previous post, I ended up completely dropping support for ISmartBlockModels after I discovered that the presumed way that they should be used wasn't thread-safe. One thread's call to handleBlockState could happen just as another thread is trying to retrieve the quads, resulting in a model not meant for that location. Lex had no idea the chunk rendering was multi-threaded prior to me telling him, so the only safe alternative, as well as Lex's suggestion, is to create a new instance of the ISmartBlockModel every time a block is rendered. This just feeds the GC, and makes the instance of the ISmartBlockModel that you associated with the block itself useless for much of anything other than being the receiver for handleBlockState calls. But sure, "it works", if that's all you're going for.

    I ended up adding a coremod to Hopper Ducts ("NowWithRendering") to hook the block and block damage rendering methods and handled block rendering the right way, giving me access to the tessellator like we're supposed to have. For Hopper Ducts I'm only passing the tessellator along to model rendering methods since I'm still taking advantage of models (no reason not to when you have control over them). For Redstone Paste though I'll use it more directly, because models just don't make sense for most of that mod which is almost entirely dynamic quads other than the sticky repeaters, sticky comparators, and slab covers. I made the render hook as minimally-invasive as possible, rather than a more ideal way, because I'm sure others will inevitably hook the same methods as well.

    I'm also in the process of porting them to my own platform/API (Hopper Ducts is almost entirely ported), and at some point I'll make a layer that rides on FML for compatibility's sake, so then these kind of artificial restrictions won't be a concern from the get-go.
     

Share This Page