Skip to content

Instantly share code, notes, and snippets.

@amadornes
Last active June 18, 2017 00:56
Show Gist options
  • Save amadornes/8757da1bcc0ea87c518b5ad73899abb3 to your computer and use it in GitHub Desktop.
Save amadornes/8757da1bcc0ea87c518b5ad73899abb3 to your computer and use it in GitHub Desktop.
Quick guide to MCMultiPart 2

Hey! So you have a 1.11 mod and want to have a go at supporting MCMultiPart 2? Great! Let's get started!

I should first of all note that the API is NOT final, though the changes to it won't be very big and most likely won't affect you. Still, I would recommend doing this in a separate branch.

Depending on MCMP2

The first thing you'll want to do is add MCMP to your dev environment. You can read about that here: https://github.com/amadornes/MCMultiPart/blob/1.11/README.md
You'll need to use experimental builds because there is no stable build yet.

Creating an addon

Next up you'll want to create a package somewhere in your code to put all MCMultiPart-related things into. Inside it, create a class which implements IMCMPAddon and is tagged with the @MCMPAddon annotation. MCMultiPart will load it at pre-init. In that class, override the "registerParts" method, which gives you an IMultipartRegistry.

Wrapping your Block

Now you'll want to create your multipart, so make a class that implements IMultipart. You'll be forced to implement some slot-related methods, but the first thing you want to do is override getBlock and return the block you're wrapping. You can do this with a static reference or by passing it into the constructor if you want to re-use your multipart class.
Let's now deal with those slot-related methods. getSlotForPlacement does exactly what you may think it does: it returns a slot based on some placement information. This behaves similarly to getStateForPlacement in Block, and in fact that state is passed in as an argument. getSlotFromWorld also does what you can already guess: it gets the slot for a block that's already in the world!
Whenever you need to deal with slots, there are a bunch of values that can be used. By default MCMP includes EnumFaceSlot, EnumEdgeSlot, EnumCornerSlot and EnumCenterSlot (yes, that one only has one value, but it made sense to make it its own class). If your block doesn't really fit into any of those slots, you have the option to make your own by implementing IPartSlot yourself and registering the slot using GameRegistry.register, though I won't explain that here. You can ask for help in #MCMultiPart if you get stuck.
Now that you have that out of the way, the question is: does your block implement getBoundingBox? If so, what does it return? This value is what is used by default in occlusion testing (aka, checking that two parts don't intersect), so you want it to be just the part of your block that cannot be shared with other multiparts. In the case of a cable or a pipe, for example, this would be the center area. And yes, it can connect on the sides, but that space could be taken up by a cover instead, whereas the center can ONLY be taken up by your multipart. If you need special occlusion handling, you can override getOcclusionBoxes or even testIntersection.

Wrapping your TileEntity

Once your Block is wrapped as a multipart, you'll want to take care of your TileEntity. If you don't have one, you can skip this part.
EDIT: If you don't need any special TileEntity behavior, you can use IMultipartTile#wrap() instead of creating your own class! First, make a new class that implements IMultipartTile. Take in your TileEntity via a constructor argument and return it in getTileEntity.
Your best option to make it useable is to add an event handler in your MCMP addon class (don't forget to register it) that listens for the AttachCapabilitiesEvent event for your tile and whenever that's fired, create a new instance of your class, passing the TE as an argument, and attach a capability provider that supplies it as a MCMPCapabilities.MULTIPART_TILE.

Handling multipart placement

Now, the only thing that's left is to link your block and multipart by calling registerPartWrapper in the registerParts method in your addon, and to register a placement wrapper for your block so that it can be placed as a multipart, which you can do by using the registerStackWrapper method in the multipart registry. The second argument in that method is there to make sure only specific variants of the item are allowed to be placed as multiparts, but in most cases you can just do () -> true, and allow all items of the type you specified.

And... you should be done! Things you may run into:

And in theory, your block should now be working as a multipart! That is... if it doesn't need any kind of networking and doesn't care about its neighbors. But if it does... it's easy enough to fix!
What you'll need to do is make a capability for your TileEntity and look for it on the null face when receiving packets if there can only be one multipart of that type in the block, or include some sort of identification system, and use capability lookups on the faces of the block to handle connections instead of looking for the block or TE directly.
This should take care of networking and neighbor lookups, but it probably won't work all that well if you're covering up a side of the block. To handle that, you'll need to look into occlusion testing. You can ask in #MCMultiPart about that :)

Here's the mod that adds vanilla multiparts and basic cover-esque blocks for you to test with (no creative tab): http://ss.amadornes.com/ama-1488149951-1739.zip
You can also get Charset, Chisels & Bits and Refined Storage, all of which support MCMP2, from GitHub.
All feedback is welcome, just make sure that you recommend breaking changes before the release! ;)

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