Skip to content

Conversation

@RecursivePineapple
Copy link
Contributor

This ports the block property system from MM and polishes up its rough edges. There's a proper BlockState class now, and a lot of the edge cases from MM's system have been cleaned up.

I haven't looked at the modern code for BlockStates because I wanted to keep this somewhat of a cleanroom implementation, so the API is probably completely different from how it looks in modern.

I know that modern has a huge problem with allocations, so there's a built-in pooling mechanism for BlockStates and they avoid as many allocations as possible. The map operations in BlockPropertyRegistry should still be the bottleneck, I can't profile this because my linux kernel still panics whenever I run spark 🤣.

@RecursivePineapple RecursivePineapple requested review from a team and ah-OOG-ah October 22, 2025 00:55
@RecursivePineapple RecursivePineapple added the enhancement New feature or request label Oct 22, 2025
@Dream-Master Dream-Master added the 🚧 Testing on Zeta Do not merge yet, testing this PR on Zeta label Oct 28, 2025
@Nikolay-Sitnikov
Copy link

Nikolay-Sitnikov commented Oct 31, 2025

This is most certainly a block state system. I think Minecraft's is much simpler - it's just an array of immutable singletons mapping each possible meta value to a state. (Can't remember how they implement mutating the BlockState, though. Either setting each property at a time, or possibly some kind of copy-to-mutable implementation?)

Sort of like:

states = BlockState[] {
    0 => {facing: UP, powered: false}
    1 => {facing: UP, powered: true}
    2 => {facing: DOWN, powered: false}
    3 => {facing: DOWN, powered: true}
    4 => {facing: NORTH, powered: false}
    ...
    11 => {facing: WEST, powered: true}
}

fromState => facing.ordinal() * 2 + (int) powered;

(I think. I haven't touched that system in a couple years & this is the most reasonable way I can reconcile my memories into a logical implementation.)

@RecursivePineapple
Copy link
Contributor Author

@Nikolay-Sitnikov The problem with that is that a lot of properties are stored in tile data. Take GT or architecturecraft, for example. Both store their rotation in their tiles, not meta. And for GT multis, their rotation isn't as simple as one direction, it's three different directions.

Querying that data won't be useful for models, but there's no reason to not expose it. MM will need it for sure, and it would also be useful for a universal wrench. I'm sure other people could find a use for it.

Another project I'm working on is a piece-based dungeon/structure generator. It will need to be able to rotate a block arbitrarily, and the more blocks it can support, the better. https://discord.com/channels/181078474394566657/1376263277125963937/1428947792453173280

@Nikolay-Sitnikov
Copy link

Nikolay-Sitnikov commented Oct 31, 2025

@Nikolay-Sitnikov The problem with that is that a lot of properties are stored in tile data. Take GT or architecturecraft, for example. Both store their rotation in their tiles, not meta. And for GT multis, their rotation isn't as simple as one direction, it's three different directions.

Querying that data won't be useful for models, but there's no reason to not expose it. MM will need it for sure, and it would also be useful for a universal wrench. I'm sure other people could find a use for it.

Another project I'm working on is a piece-based dungeon/structure generator. It will need to be able to rotate a block arbitrarily, and the more blocks it can support, the better. https://discord.com/channels/181078474394566657/1376263277125963937/1428947792453173280

We have EndlessIDs now, right? Why not put the important properties in the meta of the block? Is it because all the multiblocks share the same block ID?

(Also, fair enough. This is a different tool for a different purpose - I'd prefer that it would have a name that is distinct from the vanilla tool, but I can't think of a better name.)

@RecursivePineapple
Copy link
Contributor Author

You could, but you'd be denormalizing a massive number of states for no real reason. You'd need 32k machine ids * 6 * 6 * 6 directions, plus whatever else would be needed. That already adds up to almost 7 million meta values, but a lot of machines have their own properties.

ME output hatches and busses store their capacities as a long (though in practice it's only ~10 different values). These, along with ender tanks/chests, are why the ItemStack portion of this system exists. In MM I added a second property system onto the first, but for this one I just merged the two.

Besides, it would be very difficult to port machines to store their rotations in meta because it would be a massive overhaul. It's easier to just write an adapter that examines tile entities and take the performance hit. MM uses a nearly identical system and the performance is decent, though there's plenty of room for improvement.

@Nikolay-Sitnikov
Copy link

To be clear, with this explanation I have no objections to this idea. I was just confused by the comparison to modern BlockStates, since those function as a replacement for meta values with all the benefits of directly indexing combinations & with all the expected consequences when you have too many combinations of properties.

This is just an interface for sharing & editing common properties of blocks across mods, which a completely good idea.

@RecursivePineapple
Copy link
Contributor Author

No worries, I started getting into debate mode 😛.

The idea behind this is the same as modern. While it isn't a direct meta mapping, blocks should have all of their major properties exposed. Materials, rotations, activity, etc, but no inventories. I suspect most modern blocks either don't bother to expose these properties, or they've figured out a way to compact everything into 16 values (I heard they increased the meta limit but idk by how much).

@Nikolay-Sitnikov
Copy link

No worries, I started getting into debate mode 😛.

The idea behind this is the same as modern. While it isn't a direct meta mapping, blocks should have all of their major properties exposed. Materials, rotations, activity, etc, but no inventories. I suspect most modern blocks either don't bother to expose these properties, or they've figured out a way to compact everything into 16 values (I heard they increased the meta limit but idk by how much).

Modern Minecraft automatically allocates IDs / meta values depending on how many combinations of block states you have. So a normal block (like dirt) has only one possible ID. A log would get three consecutive IDs, one for each possible block state; and redstone dust gets 1296 consecutive IDs. So there's no distinction between ID & meta in modern Minecraft, which makes it possible to fit a lot more blocks into the same data type.

@RecursivePineapple
Copy link
Contributor Author

@Nikolay-Sitnikov Interesting, that's a much more elegant system than we have in 1.7.10. That certainly sounds like it eats a lot of RAM though.

@Nikolay-Sitnikov
Copy link

Nikolay-Sitnikov commented Nov 1, 2025

@RecursivePineapple As far as I know, that's about right. I do think that the modern system could be made more efficient/practical by applying a bit of dynamic code generation to the technique instead of using hashmaps for all the properties.

For instance, every class could declare its own immutable block-state class (inheriting from some parent BlockState class) to store the block-state information. Commonly used properties could be implemented with interfaces or possibly some kind of annotation processing, and the block states could be enumerated by reflectively reading annotations on the fields.

Something like:

public class ObserverBlockState extends BlockState<BlockObserver>  implements IHasFacing<ObserverBlockState> {
    public final Direction facing;
    public final boolean active;

    // Automatically generated constructor - reflectively used to instantiate the variants.
    public ObserverBlockState(Block block, Direction facing, boolean active) {
        super(block);
        this.facing = facing;
        this.active = active;
    }

    // Automatically generated getter
    @Override
    public Direction getFacing() {
        return this.facing;
    }

    // Automatically generated transformer
    @Override
    public ObserverBlockState withFacing(Direction facing) {
        return BlockState.globalStates[/* implementation defined logic to find the index of the new state */];
    }
}

Getting a brand-new state would be a bit of a pain, though, since I can't figure out how to cleanly declare a method at code-writing-time with the signature dependent on the fields and the implementation being autogenerated.

(Dang Java not giving Java writers the ability to declare their own compile-time invokedynamic methods... Rust's #derive would be even better.)

@RecursivePineapple
Copy link
Contributor Author

@Nikolay-Sitnikov You'd have to make an annotation processor similar to lombok that generates those methods.

Annotation processors are given what is supposed to be a read-only AST, but you can actually modify it. I don't know why java hasn't made them properly mutable, but it's the only reason lombok works so the java devs aren't likely to change it.

I've tried to make my own (because I miss rust's AST macros) but it's pretty difficult to do so I haven't gotten very far.

@Nikolay-Sitnikov
Copy link

@RecursivePineapple The problem is that I would also need to integrate it somehow with IDEs. (Which is why I was considering that dummy method idea.) Either way, it's not particularly likely that we can properly implement my improved version of the BlockState system into 1.7.10 (because it would require compatibility rewrites of every mod) and modern Minecraft already has the less efficient hashmap BlockStates.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

#190

github-actions bot and others added 2 commits November 7, 2025 16:32
Co-authored-by: GitHub GTNH Actions <>
@github-actions
Copy link
Contributor

#213

Co-authored-by: GitHub GTNH Actions <>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚧 Testing on Zeta Do not merge yet, testing this PR on Zeta enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants