diff --git a/build.gradle b/build.gradle index bd27bb7..afd6d67 100644 --- a/build.gradle +++ b/build.gradle @@ -12,9 +12,14 @@ version = "0.1.0" minecraft { } +repositories { + mavenLocal() +} + dependencies { minecraft "com.mojang:minecraft:18w50a" - mappings "net.fabricmc:yarn:18w50a.80" +// mappings "net.fabricmc:yarn:18w50a.80" + mappings "net.fabricmc:yarn:18w50a.local" // temporary until yarn #369 modCompile "net.fabricmc:fabric-loader:0.3.1.80" // Fabric API. This is technically optional, but you probably want it anyway. diff --git a/src/main/java/net/shadowfacts/simplemultipart/container/AbstractContainerBlockEntity.java b/src/main/java/net/shadowfacts/simplemultipart/container/AbstractContainerBlockEntity.java index f2f6b57..01cbaea 100644 --- a/src/main/java/net/shadowfacts/simplemultipart/container/AbstractContainerBlockEntity.java +++ b/src/main/java/net/shadowfacts/simplemultipart/container/AbstractContainerBlockEntity.java @@ -15,6 +15,7 @@ import net.minecraft.nbt.Tag; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Tickable; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.loot.context.LootContext; import net.minecraft.world.loot.context.Parameters; @@ -35,6 +36,7 @@ import java.util.stream.Collectors; public abstract class AbstractContainerBlockEntity extends BlockEntity implements MultipartContainer, ClientSerializable { protected Set parts = new HashSet<>(); + protected Map sidePartCache = new WeakHashMap<>(); public AbstractContainerBlockEntity(BlockEntityType type) { super(type); @@ -50,6 +52,36 @@ public abstract class AbstractContainerBlockEntity extends BlockEntity implement return !parts.isEmpty(); } + @Override + public MultipartView getPart(Direction side) { + Entry existing = sidePartCache.get(side); + if (existing != null) { + return existing; + } + + Optional e = parts.stream() + .min((a, b) -> { + VoxelShape aShape = a.getState().getBoundingShape(a); + VoxelShape bShape = b.getState().getBoundingShape(b); + double aCoord = side.getDirection() == Direction.AxisDirection.POSITIVE ? aShape.getMaximum(side.getAxis()) : aShape.getMinimum(side.getAxis()); + double bCoord = side.getDirection() == Direction.AxisDirection.POSITIVE ? bShape.getMaximum(side.getAxis()) : bShape.getMinimum(side.getAxis()); + return Double.compare(bCoord, aCoord); + }); + + if (!e.isPresent()) { + return null; + } + + sidePartCache.put(side, e.get()); + + return e.get(); + } + + @Override + public void invalidateSidePartCache() { + sidePartCache.clear(); + } + @Override public boolean canInsert(MultipartState partState) { VoxelShape newShape = partState.getBoundingShape(null); @@ -77,6 +109,7 @@ public abstract class AbstractContainerBlockEntity extends BlockEntity implement } parts.add(e); + invalidateSidePartCache(); updateWorld(); } @@ -91,6 +124,7 @@ public abstract class AbstractContainerBlockEntity extends BlockEntity implement if (parts.isEmpty()) { world.setBlockState(pos, Blocks.AIR.getDefaultState()); } else { + invalidateSidePartCache(); updateWorld(); } } @@ -203,18 +237,18 @@ public abstract class AbstractContainerBlockEntity extends BlockEntity implement } public static class Entry implements MultipartView { - public final MultipartContainer container; + public final AbstractContainerBlockEntity container; public MultipartState state; public MultipartEntity entity; - private Entry(MultipartContainer container, MultipartState state, MultipartEntity entity) { + private Entry(AbstractContainerBlockEntity container, MultipartState state, MultipartEntity entity) { this.container = container; this.state = state; this.entity = entity; } @Override - public MultipartContainer getContainer() { + public AbstractContainerBlockEntity getContainer() { return container; } @@ -226,6 +260,7 @@ public abstract class AbstractContainerBlockEntity extends BlockEntity implement @Override public void setState(MultipartState state) { this.state = state; + container.invalidateSidePartCache(); } @Override @@ -236,6 +271,7 @@ public abstract class AbstractContainerBlockEntity extends BlockEntity implement @Override public void setEntity(MultipartEntity entity) { this.entity = entity; + container.invalidateSidePartCache(); } @Override diff --git a/src/main/java/net/shadowfacts/simplemultipart/container/MultipartContainer.java b/src/main/java/net/shadowfacts/simplemultipart/container/MultipartContainer.java index 29a0a73..0b366e5 100644 --- a/src/main/java/net/shadowfacts/simplemultipart/container/MultipartContainer.java +++ b/src/main/java/net/shadowfacts/simplemultipart/container/MultipartContainer.java @@ -1,5 +1,6 @@ package net.shadowfacts.simplemultipart.container; +import net.minecraft.util.math.Direction; import net.shadowfacts.simplemultipart.multipart.MultipartView; import net.shadowfacts.simplemultipart.multipart.MultipartState; @@ -27,6 +28,26 @@ public interface MultipartContainer { */ boolean hasParts(); + /** + * Gets the part on the given side. + * + * Will return the part with the greatest/least min/max coordinate based on the direction's axis. + * For example, getting the part on the {@code NORTH} side will return the part with the smallest minimum Z coordinate. + * If multiple parts have the same min/max coordinate, which one will be returned is undefined. + * + * @param side The side to determine the part for. + * @return The part on the given side. + */ + MultipartView getPart(Direction side); + + /** + * Containers store a cache of which part is on which side, calculated using the bounding box. + * + * If anything changes in your part that changes its bounding shape, this method should be called. + * The container {@code insert}, {@code breakPart}, and {@code remove} methods automatically call this. + */ + void invalidateSidePartCache(); + /** * Determines whether the given multipart state can be inserted into this container. * Checks that the bounding box of the new part does not intersect with any existing ones. diff --git a/src/test/java/net/shadowfacts/simplemultipart/test/TestMultipart.java b/src/test/java/net/shadowfacts/simplemultipart/test/TestMultipart.java index 340a0f3..b4a9417 100644 --- a/src/test/java/net/shadowfacts/simplemultipart/test/TestMultipart.java +++ b/src/test/java/net/shadowfacts/simplemultipart/test/TestMultipart.java @@ -57,8 +57,7 @@ public class TestMultipart extends Multipart { @Override @Deprecated public boolean activate(MultipartView view, PlayerEntity player, Hand hand) { - Direction side = view.getState().get(Properties.FACING); - System.out.println("part activated on " + side); + System.out.println(view.getContainer().getPart(Direction.UP)); return true; }