Skip to content

Conversation

@Swedz
Copy link
Collaborator

@Swedz Swedz commented Aug 22, 2025

This is the first of a series of PRs that will overhaul how multiblocks are handled in MI. And is definitely one of the larger ones. I did some decently extensive testing using a world containing all MI multiblocks, but please do some testing of your own too.

Comments prefaced with // TODO SWEDZ MULTIBLOCKS: are comments to remind myself in future PRs to update code at those places to help things not get missed in the process. These should all be gone by the end of this series of PRs.

Here's a summary of the changes with some explanations. Some of the changes may seem odd at face value but they were made with future changes in mind. If there's anything I missed in this list and you find questionable, let me know.

  • ShapeTemplate has been completely refactored to actually be immutable now
  • SimpleMember has been removed and replaced with MultiblockMember
    • This is designed with codecs in mind for a future PR, so we have a couple types at the moment (SimpleMultiblockMember and HatchMultiblockMember) since that is the minimal amount needed for MI at the moment (more will come with a future PR)
    • This also includes MultiblockMemberTest which is separate so that user input can be parsed into multiple tests for a single member delimited by a semicolon
  • MultiblockMemberState has some comments to explain why it is the way it is - but to further elaborate:
    • This will eventually include a @Nullable CompoundTag nbt field so NBT of BlockEntities can be compared too. That is why this is a separate class instead of just referencing Lazy<BlockState> or just BlockState everywhere. At the moment there's no real reason to use Lazy<BlockState> - it is so that in the future when we are loading snbt files for the multiblock structures, we can load those before all mods blocks are registered and thus have ShapeTemplate instances to pass to our machines on startup. Otherwise a lot more code would have to get updated to allow for ShapeTemplates being applied after startup which would get rather messy.
    • Once we would start using codecs to load shapes, we run into a downside of BlockStates being lazy loaded where that hasn't been fully parsed during play. This could cause inconsistent results that I'm not really too interested in finding out what breaks because it's not immediately obvious. So I forcefully load all of the lazy BlockStates on startup in a FMLCommonSetupEvent (see the event in ShapeTemplate - if you're not a fan of the use of an annotation event here I can move it to MI's constructor). If you think there is a better solution to this let me know, this is the best I came up with.
  • The builders for ShapeTemplate are kept pretty much the same so that existing scripts in KJS shouldn't need any changing. However addons will need to adjust to account for the package and type changes.

I request that #1159 be looked at before this PR so I can rebase here rather than there. Also, that PR is much smaller than this one :)

@Swedz Swedz changed the title Multiblock overhaul/shapetemplate Rework multiblock shape internals in preparation for future changes Aug 22, 2025
import java.util.Set;
import aztech.modern_industrialization.machines.multiblocks.shape.member.MultiblockMember;
import aztech.modern_industrialization.machines.multiblocks.shape.member.SimpleMultiblockMember;
import java.util.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no * imports :P

*/
public class ShapeTemplate {
public final Map<BlockPos, SimpleMember> simpleMembers = new HashMap<>();
public final Map<BlockPos, HatchFlags> hatchFlags = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't make the hatch flags inherit from SimpleMultiblockMember, that's messy. We could group both a member and hatch flags into a record.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for grouping the hatch flags into HatchMultiblockMember is so the hatches can be serialized as part of the structure via. user input in a GUI much easier. I know this introduces a few instanceof checks here and there which isn't ideal. Other than that I'm not sure I agree that it's messy... what's your reasoning?

private static void onSetup(FMLCommonSetupEvent event) {
for (ShapeTemplate template : templates) {
for (var member : template.members().values()) {
member.forceLoad();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also quite messy, not a fan.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that it is messy. Would you prefer members be loaded as-needed at runtime?

protected final Map<BlockPos, MultiblockMember> members;

private boolean needsRematch = true;
private boolean matchSuccessful = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The matchedHatches field should really be removed IMO.

import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.common.util.Lazy;

// TODO SWEDZ MULTIBLOCKS: remove methods that become irrelevant once structures have been implemented fully and we are
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of leaving these TODOs around. I can live with it though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't know of a better way to track this... 😅 I guess I could keep a list independently but it does help being able to see it in the code for me. I'll see what I can do to help myself there and remove these comments.


// TODO SWEDZ MULTIBLOCKS: remove methods that become irrelevant once structures have been implemented fully and we are
// done making multiblocks using code
public abstract class MultiblockMember {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should likely be an interface


import net.minecraft.world.level.block.state.BlockState;

public abstract class MultiblockMemberTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these just be new types of multiblock members?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason tests and members are separate is so we can have a hatch + simple member type (as well as some other types) that can have many different types of tests. I do think MultiblockMember should implement MultiblockMemberTest now that you point that out, though. Tests don't need to have a preview attached to them, whereas members do - because the member is the actual entry on the structure that will show in EMI/holograms/etc. If you see a better way to structure this, I am open to it.

* allows us to load multiblock structure files on startup without any problems.
* </p>
*/
public record MultiblockMemberState(Lazy<BlockState> lazyState) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Supplier (which is a supertype of Lazy).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants