Skip to content

Commit

Permalink
initial port of the multiblock api
Browse files Browse the repository at this point in the history
  • Loading branch information
Thepigcat76 committed Sep 4, 2024
1 parent cdde82e commit f7e0068
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.portingdeadmods.modjam.api.blockentities;

public interface MultiblockEntity {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package com.portingdeadmods.modjam.api.multiblocks;

import com.portingdeadmods.modjam.api.blockentities.MultiblockEntity;
import com.portingdeadmods.modjam.api.utils.HorizontalDirection;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntIntPair;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.commons.lang3.IntegerRange;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

public interface Multiblock {
/**
* This method provides the controller block of your unformed multiblock.
* Your multiblock needs at least one of these in its structure.
* <br>
* <br>
* Example: {@link com.indref.industrial_reforged.registries.multiblocks.BlastFurnaceMultiblock#getUnformedController() BlastFurnaceMultiblock.getUnformedController()}
* <br>
* @return The controller block of your unformed multiblock
*/
Block getUnformedController();

/**
* This method provides the controller block of your formed multiblock.
* Your multiblock needs at least one of these in its structure.
* <br>
* <br>
* Example: {@link com.indref.industrial_reforged.registries.multiblocks.BlastFurnaceMultiblock#getUnformedController() BlastFurnaceMultiblock.getFormedController()}
* <br>
* @return The controller block of your formed multiblock
*/
Block getFormedController();

/**
* This method provides the layout of your unformed multiblock.
* <br>
* It consists of an array of multiblock layers. Each layer
* is constructed with a method call.
* <br>
* For this, you can use {@link Multiblock#layer(int...)}
* <br>
* Each of these methods ask you to provide you a list of integers.
* These integers represent the actual blocks used.
* Nonetheless, you still need to provide the actual blocks using
* the {@link Multiblock#getDefinition()} method.
* This provides the minimum and maximum height for this multiblock.
* <br>
* Example: {@code IntegerRange.of(1, 3)}
* <br>
* <br>
* Note: The first layer in this array also represents the bottom layer of the multiblock
* <br>
* <br>
* Example: {@link com.indref.industrial_reforged.registries.multiblocks.BlastFurnaceMultiblock#getLayout() BlastFurnaceMultiblock.getLayout()}.
* @return An array of multiblock layers that describes the layout of the multiblock
*/
MultiblockLayer[] getLayout();

/**
* This method provides a definition map that can be used to look up
* an integer key in {@link Multiblock#getLayout()} and will return a block.
* <br>
* <br>
* The keyset of this map needs to include
* every key that is used in {@link Multiblock#getLayout()}.
* <br>
* <br>
* The values of this map need to contain the block for each
* integer key. If you do not care about a block you can use {@code null}
* instead of a value.
* <br>
* <br>
* Example: {@link com.indref.industrial_reforged.registries.multiblocks.BlastFurnaceMultiblock#getDefinition() BlastFurnaceMultiblock.getDefintion()}
* @return The integer to block map that provides the integer keys and their block values
*/
Int2ObjectMap<Block> getDefinition();

/**
* This method provides the block entity type for the controller of your multiblock.
* @return the blockentity type of your controllers blockentity
*/
BlockEntityType<? extends MultiblockEntity> getMultiBlockEntityType();

/**
* This method provides a list of widths for every layer
* of your multiblock.
* <br>
* <br>
* This method has a default implementation meaning that
* you do not have to override it, unless one of your
* multiblock layers is not quadratic. (And it's width
* can therefore not be determined by getting the
* square root of the integer arrays length)
* <br>
* <br>
* The size of this list needs to be {@link Multiblock#getMaxSize()}
* and needs to contain the widths for every possible layer, this also
* includes dynamic layers.
*
* @return a list of integer pairs where left is the x- and right is the z-width
*/
default List<IntIntPair> getWidths() {
List<IntIntPair> widths = new ArrayList<>(getMaxSize());
for (MultiblockLayer layer : getLayout()) {
if (layer.dynamic()) {
for (int i = 0; i < layer.range().getMaximum(); i++) {
widths.add(layer.getWidths());
}
} else {
widths.add(layer.getWidths());
}
}
return widths;
}

/**
* This method is used to form a block. It is called for that block and also when unforming the multi.
* This is why this should only return the blockState, not perform any interactions on the level/player....
* For interactions with the world/player..., use {@link Multiblock#afterFormBlock(Level, BlockPos, BlockPos, int, int, MultiblockData, Player)}
* @param level Level of the multiblock, should only be used for reading things, not setting new things.
* @param blockPos BlockPos of the block that is being formed
* @param controllerPos BlockPos of this multiblocks controller
* @param layerIndex index of the current layers block (array of integer)
* @param layoutIndex index of the current multiblock layer (array of multiblock layer)
* @param multiblockData Information about the unformed multiblock, like the layers of the concrete multiblock and the direction it is formed in.
* @param player Player that is trying to form this multiblock. Note that there does not necessarily have to be a player that is responsible for forming the multiblock
* @return Formed BlockState. This will replace the unformed block in the multiblock. Return {@code null} if you do not want to change the block.
*/
@Nullable BlockState formBlock(Level level, BlockPos blockPos, BlockPos controllerPos, int layerIndex, int layoutIndex, MultiblockData multiblockData, @Nullable Player player);

/**
* This method is called after the block is formed. It can be used to interact with the level/player...
* as it is only called, when the multiblock is formed.
* @param level Level of the multiblock
* @param blockPos BlockPos of the block that is being formed
* @param controllerPos BlockPos of this multiblocks controller
* @param layerIndex index of the current layers block (array of integer)
* @param layoutIndex index of the current multiblock layer (array of multiblock layer)
* @param multiblockData Information about the unformed multiblock, like the layers of the concrete multiblock and the direction it is formed in.
* @param player Player that is trying to form this multiblock. Note that there does not necessarily have to be a player that is responsible for forming the multiblock
*/
default void afterFormBlock(Level level, BlockPos blockPos, BlockPos controllerPos, int layerIndex, int layoutIndex, MultiblockData multiblockData, @Nullable Player player) {
}

/**
* This method is called after the block is unformed. It can be used to interact with the level/player...
* as it is only called, when the multiblock is unformed.
* @param level Level of the multiblock
* @param direction Direction of the multiblock
* @param blockPos BlockPos of the block that is being unformed
* @param controllerPos BlockPos of this multiblocks controller
* @param layerIndex index of the current layers block (array of integer)
* @param layoutIndex index of the current multiblock layer (array of multiblock layer)
* @param player Player that is trying to unform this multiblock. Note that there does not necessarily have to be a player that is responsible for unforming the multiblock
*/
default void afterUnformBlock(Level level, BlockPos blockPos, BlockPos controllerPos, int layerIndex, int layoutIndex, HorizontalDirection direction, @Nullable Player player) {
}

/**
* This method determines whether the block at the specified position
* is a formed part of this multiblock.
* @param level Level of the multiblock
* @param blockPos BlockPos that needs to be checked if it is formed.
* @return Whether the block at this position is formed
*/
boolean isFormed(Level level, BlockPos blockPos);

/**
* This method can make the direction of this multiblock fixed. This only works,
* if the multiblock cannot be rotated, like the crucible or firebox.
* Providing a fixed direction can improve performance while forming the multiblock
* by a bit.
* @return a horizontal direction, if the direction can be fixed.
*/
default @Nullable HorizontalDirection getFixedDirection() {
return null;
}

/**
* This method provides the maximum possible
* size for this multiblock.
* @return the maximum possible size
*/
default int getMaxSize() {
int maxSize = 0;
for (MultiblockLayer layer : getLayout()) {
maxSize += layer.range().getMaximum();
}
return maxSize;
}

/**
* Create a new layer for your multiblock
* @param layer The block indices for your multiblock layer
* @return the newly created layer
*/
default MultiblockLayer layer(int... layer) {
return new MultiblockLayer(false, IntegerRange.of(1, 1), layer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.portingdeadmods.modjam.api.multiblocks;

import com.portingdeadmods.modjam.api.utils.HorizontalDirection;
import net.minecraft.nbt.CompoundTag;

public record MultiblockData(boolean valid, HorizontalDirection direction, MultiblockLayer[] layers) {
public static final MultiblockData EMPTY = new MultiblockData(false, HorizontalDirection.NORTH, new MultiblockLayer[0]);

public CompoundTag serializeNBT() {
CompoundTag tag = new CompoundTag();
tag.putInt("layersLength", layers.length);
CompoundTag listTag = new CompoundTag();
for (int i = 0, expandedLayersLength = layers.length; i < expandedLayersLength; i++) {
MultiblockLayer layer = layers[i];
listTag.put(String.valueOf(i), layer.save());
}
tag.put("layersList", listTag);
tag.putInt("direction", this.direction.ordinal());
tag.putBoolean("valid", this.valid);
return tag;
}

public static MultiblockData deserializeNBT(CompoundTag nbt) {
int layersLength = nbt.getInt("layersLength");
CompoundTag listTag = nbt.getCompound("layersList");
MultiblockLayer[] layers = new MultiblockLayer[layersLength];
for (int i = 0; i < layers.length; i++) {
layers[i] = MultiblockLayer.load(listTag.getCompound(String.valueOf(i)));
}
HorizontalDirection direction = HorizontalDirection.values()[nbt.getInt("direction")];
boolean valid = nbt.getBoolean("valid");
return new MultiblockData(valid, direction, layers);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.portingdeadmods.modjam.api.multiblocks;

import it.unimi.dsi.fastutil.ints.IntIntPair;
import net.minecraft.nbt.CompoundTag;
import org.apache.commons.lang3.IntegerRange;

import java.util.Arrays;

public record MultiblockLayer(boolean dynamic, IntegerRange range, int[] layer, IntIntPair widths) {
public MultiblockLayer(boolean dynamic, IntegerRange range, int[] layer) {
this(dynamic, range, layer, IntIntPair.of((int) Math.sqrt(layer.length), (int) Math.sqrt(layer.length)));
}

public MultiblockLayer setDynamic(IntegerRange size) {
return new MultiblockLayer(true, size, layer, widths);
}

public MultiblockLayer setWidths(int xWidth, int zWidth) {
return new MultiblockLayer(dynamic, range, layer, IntIntPair.of(xWidth, zWidth));
}

public static MultiblockLayer load(CompoundTag tag) {
return new MultiblockLayer(
tag.getBoolean("dynamic"),
IntegerRange.of(tag.getInt("rangeMin"), tag.getInt("rangeMax")),
tag.getIntArray("layer"),
IntIntPair.of(tag.getInt("widthsX"), tag.getInt("widthsZ"))
);
}

public CompoundTag save() {
CompoundTag tag = new CompoundTag();
tag.putBoolean("dynamic", dynamic);
tag.putInt("rangeMin", range.getMinimum());
tag.putInt("rangeMax", range.getMaximum());
tag.putIntArray("layer", layer);
tag.putInt("widthsX", widths.leftInt());
tag.putInt("widthsZ", widths.rightInt());
return tag;
}

public IntIntPair getWidths() {
return widths;
}

@Override
public String toString() {
return "MultiblockLayer{" +
"dynamic=" + dynamic +
", range=" + range +
", layer=" + Arrays.toString(layer) +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.portingdeadmods.modjam.api.utils;

import net.minecraft.core.Direction;
import org.jetbrains.annotations.Nullable;

public enum HorizontalDirection {
NORTH,
EAST,
SOUTH,
WEST;

public Direction toRegularDirection() {
return switch (this) {
case NORTH -> Direction.NORTH;
case EAST -> Direction.EAST;
case SOUTH -> Direction.SOUTH;
case WEST -> Direction.WEST;
};
}

public static @Nullable HorizontalDirection fromRegularDirection(Direction direction) {
return switch (direction) {
case NORTH -> NORTH;
case EAST -> EAST;
case SOUTH -> SOUTH;
case WEST -> WEST;
default -> null;
};
}
}

0 comments on commit f7e0068

Please sign in to comment.