Package alexiil.mc.lib.multipart.api
LibMultiPart's API.
This package contains everything needed to interact with libmultipart - you should never need to use anything in the
alexiil.mc.lib.multipart.impl
package.
There are a few main concepts:
- Containers
- Individual Parts
- Part Shapes
- Accessing Parts
- Adding Parts
- Part Requirements
- Removing Parts
- Client <-> Server Networking
- Events
- Properties
- Rendering
- Converting normal blocks into parts
Containers
Every multipart is contained a BlockEntity
, and is exposed through the
MultipartContainer
interface. This is exposed through a
LibBlockAttributes Attribute
in
MultipartContainer.ATTRIBUTE
Individual Parts
Every part must extend from the base class AbstractPart
, and be
identified by a PartDefinition
. These definitions must be
(manually) added to the part definition map PartDefinition.PARTS
.
The MultipartHolder
interface acts as the bridge between the
container implementation and the part, and contains a lot more information to simplify reading and writing individual
parts.
Every part has a container-only unique ID (MultipartHolder.getUniqueId()
) which can be used to store a reference to an exact part at a given position.
Part Shapes
Every part has 4 shape methods that they can override: getShape()
, getCollisionShape()
,
getCullingShape()
, and
getDynamicShape()
. The first method
(getShape) is abstract, and is used for detecting if two parts occupy the same space inside a block. The second
method (getCollisionShape) is used for nearly everything else - calculating solidity logic and collisions with
entities. If this isn't overridden then it defaults to returning the main getShape(). The third method
(getCullingShape) is used for calculating lighting and opaque sides. The final method (getDynamicShape()) is used for
ray-tracing and client-side bounding boxes. It has a partialTicks parameter, which allows the bounding box to follow
the motion of a part. (For example a buildcraft engine and the buildcraft pipe pulser move smoothly, and the boxes
follow that motion).
Accessing Parts
Individual parts can be accessed via the various container methods all named similar to "getPart" or "getAllParts".
For example MultipartContainer.getPart(long)
will get the part with the given unique ID.
Adding Parts
New parts can be added in two ways:
- From nothing:
MultipartUtil.offerNewPart(World, BlockPos, MultipartContainer.MultipartCreator)
- To an existing MultipartContainer:
MultipartContainer.offerNewpart(MultipartContainer.MultipartCreator)
PartOffer.apply()
is called on the returned
offer.
Part Requirements
If one part "requires" another then it will always be broken if the required part is broken. This works across
multiple blocks. To make one part require another you should call
MultipartHolder.addRequiredPart
.
Removing Parts
Removing a single part is simple: just call MultipartHolder.remove()
or MultipartContainer.removePart(AbstractPart)
. This will remove both the given part and every part that required it.
This will not drop any of the items.
Client <-> Server Networking
Multipart's use LibNetworkStack
for all networking operations, exposed through
AbstractPart.NET_ID
.
Events
Unlike blocks
or entities
most events (like
neighbour updates, ticks, entity collision, etc) are delivered through
MultipartEvent
objects through the
MultipartEventBus
. You can register listeners for these events
in AbstractPart.onAdded(MultipartEventBus)
.
There are a few core events:
PartOfferedEvent
, which is fired whenever a part is offered to the container.PartAddedEvent
, which is fired after the part is offered and has been added to the container.PartContainerState.ChunkUnload
, which is fired whenever the chunk containing the container is unloaded.PartContainerState.Invalidate
, which is fired whenever the BlockEntity containing the parts isinvalidated
.PartContainerState.Validate
, which is fired whenever the BlockEntity containing the parts isvalidated
.PartContainerState.Remove
, which is fired whenever the BlockEntity containing the parts isremoved
.PartTickEvent
, which is fired once when the BlockEntity containing it is ticked.PartListenerAdded
, which is fired whenever a new listener is added to the event bus. This is only exposed to allow parts to optimise-out event calls on a per-part basis, and is not useful in normal scenarios.PartListenerRemoved
, which is fired whenever a listener is removed from the event bus. This is only exposed to allow parts to optimise-out event calls on a per-part basis, and is not useful in normal scenarios.
Properties
Properties are the way to change how the multipart block as a whole works - for example you can make the block emit
light, redstone, etc. Usage is simple: each multipart has a
MultipartPropertyContainer
, which can be
used to map a specific part to a value for any given property, via
#setValue(Object, alexiil.mc.lib.multipart.api.property.MultipartProperty, Object)
setValue
. The default properties are static final fields in
MultipartProperties
. Changing the value of a property as a whole will
fire a PartPropertyChangedEvent
.
Rendering
There are two types of renderers: static and dynamic. Both renderers are by adding a listener to the relevant event class. Static renderers are akin to normal block models, except you have to:
- Return a custom
PartModelKey
from#getModelKey() AbstractPart#getModelKey()
- Add a listener to
PartStaticModelRegisterEvent.EVENT
, which registers a handler for that class (or any of it's superclasses) withStaticModelRenderer#register(Class<? super P>, PartModelBaker<P>)
- Actually emit the quads in
PartModelBaker#emitQuads()
Dynamic rendering is slightly simpler, as you do not have to go through a
model key object
in order to render your quads.
- Add a listener to
PartDynamicModelRegisterEvent.EVENT
, which registers a handler for that class (or any of it's superclasses) withDynamicModelRenderer#register(Class<? super P>, PartRenderer<P>)
- Emit the quads in
PartRenderer#render()
.
Converting normal blocks into parts
If you wish a custom Block
to be turned into a multipart block (when another
multipart is placed) you need to implement NativeMultipart
, and
then return a non-null list.
You can initiate this change at any time by calling
MultipartUtil#turnIntoMultipart()
.
-
Interface Summary Interface Description AbstractPart.ItemDropTarget A target for retrieving item drops fromAbstractPart.addDrops(ItemDropTarget, LootContext)
.MultipartContainer MultipartContainer.MultipartCreator MultipartContainer.PartOffer MultipartEventBus The event bus forMultipartContainer
's.MultipartEventBus.ListenerInfo<E extends MultipartEvent> Information on a single registered listener.MultipartHolder Wrapper interface for anAbstractPart
in aMultipartContainer
.NativeMultipart ABlock
that can be converted (in-place) to anAbstractPart
.PartDefinition.IPartNbtReader PartDefinition.IPartNetLoader SubdividedPart<Sub> Optional interface forAbstractPart
implementations which can have sub-parts targeted and broken instead of the whole thing. -
Class Summary Class Description AbstractPart The base class for every part in a multipart block.MultipartEventBus.ContextlessListener<E extends MultipartEvent & ContextlessEvent> MultipartEventBus.ExternalListener<E extends MultipartEvent> MultipartUtil Contains various utilities for creating, accessing, or interacting withMultipartContainer
's in aWorld
.PartDefinition Contains the definition for anAbstractPart
.PartLootParams Stores someLootContextParameter
s for LMP.PartLootParams.BrokenPart AnAbstractPart
that was broken.PartLootParams.BrokenSinglePart PartLootParams.BrokenSubPart<Sub>