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

  • FTB will be shutting down this forum by the end of July. To participate in our community discussions, please join our Discord! https://ftb.team/discord

ratchet freak

Well-Known Member
Nov 11, 2012
1,198
243
79
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.
depends if they are final or not, either way java has long since been optimized to handle many short lived objects
 

immibis

New Member
Jul 29, 2019
884
0
0
depends if they are final or not, either way java has long since been optimized to handle many short lived objects

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)
 

McJty

Over-Achiever
Mod Developer
May 13, 2014
2,015
2,519
228
twitter.com
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)

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.
 

ljfa

New Member
Jul 29, 2019
2,761
-46
0
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.
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.
 

keybounce

New Member
Jul 29, 2019
1,925
0
0
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?
 

McJty

Over-Achiever
Mod Developer
May 13, 2014
2,015
2,519
228
twitter.com

ratchet freak

Well-Known Member
Nov 11, 2012
1,198
243
79
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.
 

keybounce

New Member
Jul 29, 2019
1,925
0
0
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.
 

FyberOptic

New Member
Jul 29, 2019
524
0
0
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.
 

ratchet freak

Well-Known Member
Nov 11, 2012
1,198
243
79
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
 

FyberOptic

New Member
Jul 29, 2019
524
0
0
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.
 
  • Like
Reactions: squeek502

keybounce

New Member
Jul 29, 2019
1,925
0
0
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.
 

FyberOptic

New Member
Jul 29, 2019
524
0
0
I'm glad I'm not doing 1.8 now.

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.
 

McJty

Over-Achiever
Mod Developer
May 13, 2014
2,015
2,519
228
twitter.com
I also like the new villager mechanics and that they are a bit more active (i.e. farming and such)