From cb864a867aa41f982aff9444308a6a47ab26fa63 Mon Sep 17 00:00:00 2001 From: Seelder Date: Sat, 20 Jan 2024 23:40:29 -0800 Subject: [PATCH] Implemented ServerLevel, ServerPlayer, Level, ClientLevel for Phase 1 functionality --- .../cubicchunks/CanBeCubic.java | 5 + .../cubicchunks/MarkableAsCubic.java | 2 +- .../client/multiplayer/MixinClientLevel.java | 11 +- .../level/MixinChunkTaskPriorityQueue.java | 7 +- .../MixinChunkTaskPriorityQueueSorter.java | 7 +- .../server/level/MixinChunkTracker.java | 4 + .../server/level/MixinDistanceManager.java | 4 + .../common/server/level/MixinServerLevel.java | 17 +- .../server/level/MixinServerPlayer.java | 4 + .../core/common/world/level/MixinLevel.java | 221 +++++++++++-- .../common/world/level/MixinLevelReader.java | 8 + .../world/level/chunk/MixinChunkSource.java | 43 +++ .../cubicchunks/world/level/CubicLevel.java | 6 +- .../world/level/CubicLevelReader.java | 74 +++++ .../world/level/cube/CubicChunkSource.java | 20 ++ .../resources/cubicchunks.mixins.core.json | 4 +- src/main/resources/dasm/sets/sets.dasm | 1 + .../server/level/TestCubicServerLevel.java | 12 +- .../test/world/level/TestCubicLevel.java | 300 ++++++++++++++++++ 19 files changed, 700 insertions(+), 50 deletions(-) create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/CanBeCubic.java create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevelReader.java create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/chunk/MixinChunkSource.java create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevelReader.java create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/world/level/cube/CubicChunkSource.java diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/CanBeCubic.java b/src/main/java/io/github/opencubicchunks/cubicchunks/CanBeCubic.java new file mode 100644 index 00000000..939d02de --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/CanBeCubic.java @@ -0,0 +1,5 @@ +package io.github.opencubicchunks.cubicchunks; + +public interface CanBeCubic { + boolean cc_isCubic(); +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/MarkableAsCubic.java b/src/main/java/io/github/opencubicchunks/cubicchunks/MarkableAsCubic.java index bd9c065a..cd2f4ea7 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/MarkableAsCubic.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/MarkableAsCubic.java @@ -1,5 +1,5 @@ package io.github.opencubicchunks.cubicchunks; -public interface MarkableAsCubic { +public interface MarkableAsCubic extends CanBeCubic { void cc_setCubic(); } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/client/multiplayer/MixinClientLevel.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/client/multiplayer/MixinClientLevel.java index a8e0874d..ad0a02c5 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/client/multiplayer/MixinClientLevel.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/client/multiplayer/MixinClientLevel.java @@ -2,16 +2,25 @@ import io.github.opencubicchunks.cubicchunks.MarkableAsCubic; import io.github.opencubicchunks.cubicchunks.client.multiplayer.CubicClientLevel; +import io.github.opencubicchunks.cubicchunks.mixin.core.common.world.level.MixinLevel; import net.minecraft.client.multiplayer.ClientLevel; import org.spongepowered.asm.mixin.Mixin; @Mixin(ClientLevel.class) -public class MixinClientLevel implements CubicClientLevel, MarkableAsCubic { +public abstract class MixinClientLevel extends MixinLevel implements CubicClientLevel, MarkableAsCubic { protected boolean cc_isCubic; @Override public void cc_setCubic() { cc_isCubic = true;} + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + + @Override public boolean cc_hasCube(int x, int y, int z) { + return true; + } + // unload // TODO: Phase 2 - this interacts with the lighting engine and will need to change to support cubes diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueue.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueue.java index 33f40ec6..127a763d 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueue.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueue.java @@ -16,11 +16,14 @@ public abstract class MixinChunkTaskPriorityQueue implements MarkableAsCubic { protected boolean cc_isCubic; - @Override - public void cc_setCubic() { + @Override public void cc_setCubic() { cc_isCubic = true; } + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + @Inject(method = "resortChunkTasks", at = @At("HEAD")) private void cc_onResortChunkTasks(int p_140522_, ChunkPos p_140523_, int p_140524_, CallbackInfo ci) { assert !cc_isCubic; diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueueSorter.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueueSorter.java index 4be4aeb5..74082560 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueueSorter.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTaskPriorityQueueSorter.java @@ -24,11 +24,14 @@ public abstract class MixinChunkTaskPriorityQueueSorter implements CubicTaskPriorityQueueSorter, MarkableAsCubic { protected boolean cc_isCubic; - @Override - public void cc_setCubic() { + @Override public void cc_setCubic() { cc_isCubic = true; } + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + /** * This is a method that is only used for debugging, so we don't currently test it. */ diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTracker.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTracker.java index 8e688e9d..f0f43fb4 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTracker.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinChunkTracker.java @@ -50,6 +50,10 @@ public void cc_setCubic() { cc_noChunkLevel = levelCount-1; } + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + @Redirect(method="*", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/ChunkPos;INVALID_CHUNK_POS:J")) private long cc_sentinelValue() { if (cc_isCubic) diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinDistanceManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinDistanceManager.java index 13b2d6a6..614dc729 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinDistanceManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinDistanceManager.java @@ -40,6 +40,10 @@ public void cc_setCubic() { cc_isCubic = true; } + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + @Inject(method = {"addTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V", "removeTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V", "addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V", diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerLevel.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerLevel.java index 2ff92a58..1b400cc2 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerLevel.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerLevel.java @@ -28,16 +28,21 @@ public void cc_setCubic() { cc_isCubic = true; } - // isNaturalSpawningAllowed - mixin? new function? + @Override + public boolean cc_isCubic() { + return cc_isCubic; + } + + // TODO: phase 3 - isNaturalSpawningAllowed - // invalidateCapabilites - phase 3, neoforge api + // TODO: phase 3 - invalidateCapabilites, neoforge api - // tickCube - new function + // TODO: phase 2 - tickCube - new function - // setCubeForced - new function + // TODO: phase 4 - setCubeForced - new function - // saveDebugReport - mixins, debug only, low priority + // TODO: saveDebugReport - mixins, debug only, low priority, if we really really really really need it - // isPositionEntityTicking - mixin + // TODO: phase 2 - isPositionEntityTicking - mixin } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerPlayer.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerPlayer.java index f9fed5e5..6a723a6d 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerPlayer.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/level/MixinServerPlayer.java @@ -24,6 +24,10 @@ public void cc_setCubic() { cc_isCubic = true; } + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + /** * This mixin steals the x/y/z coordinates from a call to ChunkPos and replaces the ChunkPos in the addRegionTicketCall with a CloPos instead. */ diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevel.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevel.java index def22a4b..6d148d43 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevel.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevel.java @@ -1,44 +1,209 @@ package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.level; +import javax.annotation.Nullable; + +import com.llamalad7.mixinextras.injector.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import com.llamalad7.mixinextras.sugar.Share; +import io.github.opencubicchunks.cc_core.utils.Coords; import io.github.opencubicchunks.cubicchunks.MarkableAsCubic; +import io.github.opencubicchunks.cubicchunks.mixin.TransformFrom; +import io.github.opencubicchunks.cubicchunks.world.level.CubicLevelReader; +import io.github.opencubicchunks.cubicchunks.world.level.cube.CubicChunkSource; import io.github.opencubicchunks.cubicchunks.world.level.CubicLevel; +import io.github.opencubicchunks.cubicchunks.world.level.cube.CubeAccess; +import io.github.opencubicchunks.cubicchunks.world.level.cube.LevelCube; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.material.FluidState; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(Level.class) -public class MixinLevel implements CubicLevel, MarkableAsCubic { - protected boolean cc_isCubic; - - @Override - public void cc_setCubic() { cc_isCubic = true;} - - // getCubeAt - new function - - // getCube (LevelChunk) - new function - - // getCube (ChunkAccess) - new function - - // setBlock - mixin +public abstract class MixinLevel implements CubicLevel, MarkableAsCubic, LevelAccessor { + @Shadow @Nullable public abstract ChunkAccess getChunk(int p_46502_, int p_46503_, ChunkStatus p_46504_, boolean p_46505_); - // markAndNotifyBlock - maybe? + @Shadow public abstract long getDayTime(); - // getBlockState - mixin - - // getBlockEntity - mixin - - // setBlockEntity - mixin - - // removeBlockEntity - mixin - - // isLoaded - mixin + protected boolean cc_isCubic; - // loadedAndEntityCanStandOnFace - mixin + @Override + public void cc_setCubic() { + cc_isCubic = true; + } - // getChunkForCollisions - mixin + @Override public boolean cc_isCubic() { + return cc_isCubic; + } - // blockEntityChanged - mixin + public LevelCube cc_getCubeAt(BlockPos blockPos) { + return (LevelCube)((CubicLevelReader)this).cc_getCube(Coords.blockToCube(blockPos.getX()), Coords.blockToCube(blockPos.getY()), + Coords.blockToCube(blockPos.getZ())); + } - // getCurrentDifficultyAt - mixin + public LevelCube cc_getCube(int x, int y, int z) { + return (LevelCube)this.cc_getCube(x, y, z, ChunkStatus.FULL); + } - // TODO: Phase 3 low priority: Add a method to modify isOutsideSpawnableHeight to go from -30 mil to 30 mil, not -20 mil to 20 mil + @Nullable + @Override + public CubeAccess cc_getCube(int cubeX, int cubeY, int cubeZ, ChunkStatus status, boolean forceLoad) { + CubeAccess cubeaccess = ((CubicChunkSource)this.getChunkSource()).cc_getCube(cubeX, cubeY, cubeZ, status, forceLoad); + if (cubeaccess == null && forceLoad) { + throw new IllegalStateException("Should always be able to create a cube!"); + } else { + return cubeaccess; + } + } + + // setBlock + @WrapOperation(method = "setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;II)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level" + + "/Level;getChunkAt(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/chunk/LevelChunk;")) + private LevelChunk cc_replaceLevelChunkInGetChunkAt(Level level, BlockPos blockPos, Operation original, @Share("levelCube") LocalRef levelCubeLocalRef) { + if(cc_isCubic) { + levelCubeLocalRef.set(this.cc_getCubeAt(blockPos)); + return null; + } + return original.call(level, blockPos); + } + + @WrapOperation(method = "setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;II)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;setBlockState(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Z)Lnet/minecraft/world/level/block/state/BlockState;")) + private BlockState cc_replaceLevelChunkInSetBlockState(LevelChunk levelChunk, BlockPos blockPos, BlockState blockState, boolean flag1, Operation original, + @Share("levelCube") LocalRef levelCubeLocalRef) { + if(cc_isCubic) { + levelCubeLocalRef.get().setBlockState(blockPos, blockState, flag1); + return null; + } + return original.call(levelChunk, blockPos, blockState, flag1); + } + + @WrapWithCondition(method = "setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;II)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;markAndNotifyBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;II)V")) + private boolean cc_replaceLevelChunkInMarkAndNotifyBlock(Level level, BlockPos blockPos, LevelChunk levelChunk, BlockState blockStatePrev, BlockState blockStateNew, int flags, + int p_46607_, @Share("levelCube") LocalRef levelCubeLocalRef) { + if(cc_isCubic) { + this.markAndNotifyBlock(blockPos, levelCubeLocalRef.get(), blockStatePrev, blockStateNew, flags, p_46607_); + return false; + } + return true; + } + + @TransformFrom("markAndNotifyBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;II)V") + public native void markAndNotifyBlock(BlockPos blockPos, @Nullable LevelCube levelCube, BlockState blockStatePrev, BlockState blockStateNew, int flags, int p_46608_); + + // getBlockState + @Inject(method = "getBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getChunk(II)Lnet/minecraft/world/level/chunk/LevelChunk;")) + private void cc_replaceLevelChunkInGetBlockState(BlockPos blockPos, CallbackInfoReturnable cir, @Share("levelCube") LocalRef levelCubeLocalRef) { + if(cc_isCubic) { + levelCubeLocalRef.set(this.cc_getCubeAt(blockPos)); + } + } + + @WrapOperation(method = "getBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;getBlockState(Lnet/minecraft/core/BlockPos;)" + + "Lnet/minecraft/world/level/block/state/BlockState;")) + private BlockState cc_replaceLevelChunkInGetBlockState(LevelChunk levelChunk, BlockPos blockPos, Operation original, + @Share("levelCube") LocalRef levelCubeLocalRef) { + if(cc_isCubic) { + return levelCubeLocalRef.get().getBlockState(blockPos); + } + return original.call(levelChunk, blockPos); + } + + // getBlockEntity + @Inject(method = "getBlockEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getChunkAt(Lnet/minecraft/core/BlockPos;)" + + "Lnet/minecraft/world/level/chunk/LevelChunk;"), cancellable = true) + private void cc_replaceGetChunkAtInSetBlockEntity(BlockPos blockPos, CallbackInfoReturnable cir) { + if(cc_isCubic) { + cir.setReturnValue(this.cc_getCubeAt(blockPos).getBlockEntity(blockPos, LevelChunk.EntityCreationType.IMMEDIATE)); + } + } + + // getFluidState + @WrapOperation(method = "getFluidState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;getFluidState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/material/FluidState;")) + private FluidState cc_replaceGetChunkAtInGetFluidState(LevelChunk levelChunk, BlockPos blockPos, Operation original) { + if(cc_isCubic) { + return this.cc_getCubeAt(blockPos).getFluidState(blockPos); + } + return original.call(levelChunk, blockPos); + } + + // setBlockEntity + @Inject(method = "setBlockEntity", at = @At(value="INVOKE", target="Lnet/minecraft/world/level/Level;getChunkAt(Lnet/minecraft/core/BlockPos;)" + + "Lnet/minecraft/world/level/chunk/LevelChunk;"), cancellable=true) + private void cc_replaceLevelChunkInSetBlockEntity(BlockEntity blockEntity, CallbackInfo ci, @Local(ordinal = 0) BlockPos blockPos) { + if(cc_isCubic) { + this.cc_getCubeAt(blockPos).addAndRegisterBlockEntity(blockEntity); + ci.cancel(); + } + } + + // removeBlockEntity + @Inject(method = "removeBlockEntity", at = @At(value="INVOKE", target="Lnet/minecraft/world/level/Level;getChunkAt(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/chunk/LevelChunk;"), cancellable=true) + private void cc_replaceGetChunkAtInRemoveBlockEntity(BlockPos blockPos, CallbackInfo ci) { + if(cc_isCubic) { + this.cc_getCubeAt(blockPos).removeBlockEntity(blockPos); + ci.cancel(); + } + } + + // isLoaded + @WrapOperation(method = "isLoaded", at = @At(value="INVOKE", target="Lnet/minecraft/world/level/chunk/ChunkSource;hasChunk(II)Z")) + private boolean cc_replaceHasChunkInIsLoaded(ChunkSource chunkSource, int x, int z, Operation original, BlockPos blockPos) { + if(cc_isCubic) { + return ((CubicChunkSource)this.getChunkSource()).cc_hasCube(Coords.blockToCube(blockPos.getX()), Coords.blockToCube(blockPos.getY()), Coords.blockToCube(blockPos.getZ())); + } + return false; + } + + // loadedAndEntityCanStandOnFace + @Inject(method = "loadedAndEntityCanStandOnFace", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getChunk(IILnet/minecraft/world/level/chunk/ChunkStatus;Z)" + + "Lnet/minecraft/world/level/chunk/ChunkAccess;"), cancellable = true) + private void cc_replaceGetChunkAtInLoadedAndEntityCanStandOnFace(BlockPos blockPos, Entity entity, Direction direction, CallbackInfoReturnable cir) { + if(cc_isCubic) { + CubeAccess cubeAccess = this.cc_getCube(Coords.blockToCube(blockPos.getX()), Coords.blockToCube(blockPos.getY()), Coords.blockToCube(blockPos.getZ()), ChunkStatus.FULL, false); + cir.setReturnValue(cubeAccess == null ? false : cubeAccess.getBlockState(blockPos).entityCanStandOnFace(this, blockPos, entity, direction)); + } + } + + // blockEntityChanged + @Inject(method = "blockEntityChanged", at = @At(value = "HEAD"), cancellable = true) + private void cc_replaceBlockEntityChanged(BlockPos blockPos, CallbackInfo ci) { + if(cc_isCubic) { + if (this.cc_hasCubeAt(blockPos)) { + this.cc_getCubeAt(blockPos).setUnsaved(true); + } + ci.cancel(); + } + } + + // getCurrentDifficultyAt + @Inject(method = "getCurrentDifficultyAt", at = @At(value = "HEAD"), cancellable = true) + private void cc_replaceGetCurrentDifficultyAt(BlockPos blockPos, CallbackInfoReturnable cir) { + if(cc_isCubic) { + long i = 0L; + float f = 0.0F; + if (this.cc_hasCubeAt(blockPos)) { + f = this.getMoonBrightness(); + i = this.cc_getCubeAt(blockPos).getInhabitedTime(); + } + cir.setReturnValue(new DifficultyInstance(this.getDifficulty(), this.getDayTime(), i, f)); + } + } + // TODO: Phase 3 low priority: Add a method to modify isOutsideSpawnableHeight to respect the limits of the packing for CloPos } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevelReader.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevelReader.java new file mode 100644 index 00000000..babbf31c --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/MixinLevelReader.java @@ -0,0 +1,8 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.level; + +import io.github.opencubicchunks.cubicchunks.world.level.CubicLevelReader; +import net.minecraft.world.level.LevelReader; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LevelReader.class) +public interface MixinLevelReader extends CubicLevelReader {} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/chunk/MixinChunkSource.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/chunk/MixinChunkSource.java new file mode 100644 index 00000000..939dee44 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/level/chunk/MixinChunkSource.java @@ -0,0 +1,43 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.level.chunk; + +import io.github.opencubicchunks.cc_core.api.CubePos; +import io.github.opencubicchunks.cubicchunks.MarkableAsCubic; +import io.github.opencubicchunks.cubicchunks.world.level.cube.CubicChunkSource; +import io.github.opencubicchunks.cubicchunks.world.level.cube.CubeAccess; +import io.github.opencubicchunks.cubicchunks.world.level.cube.LevelCube; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ChunkSource.class) +public abstract class MixinChunkSource implements CubicChunkSource, MarkableAsCubic { + private boolean cc_isCubic; + + @Override public void cc_setCubic() { + cc_isCubic = true; + } + + @Override public boolean cc_isCubic() { + return cc_isCubic; + } + + public LevelCube cc_getCube(int x, int y, int z, boolean forceLoad) { + return (LevelCube)this.cc_getCube(x, y, z, ChunkStatus.FULL, forceLoad); + } + + public LevelCube cc_getCubeNow(int x, int y, int z) { + return this.cc_getCube(x, y, z,false); + } + + // TODO: Phase 2 - getCubeForLighting + + public boolean cc_hasCube(int x, int y, int z) { + return this.cc_getCube(x, y, z, ChunkStatus.FULL, false) != null; + } + + public abstract CubeAccess cc_getCube(int x, int y, int z, ChunkStatus status, boolean forceLoad); + + public abstract int cc_getLoadedCubeCount(); + + public void cc_updateCubeForced(CubePos cubePos, boolean forced) {} +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevel.java b/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevel.java index 6ccee1d7..49157896 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevel.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevel.java @@ -1,4 +1,8 @@ package io.github.opencubicchunks.cubicchunks.world.level; -public interface CubicLevel { +import io.github.opencubicchunks.cubicchunks.world.level.cube.LevelCube; +import net.minecraft.core.BlockPos; + +public interface CubicLevel extends CubicLevelReader { + LevelCube cc_getCubeAt(BlockPos blockPos); } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevelReader.java b/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevelReader.java new file mode 100644 index 00000000..1bbb4316 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/CubicLevelReader.java @@ -0,0 +1,74 @@ +package io.github.opencubicchunks.cubicchunks.world.level; + +import javax.annotation.Nullable; + +import io.github.opencubicchunks.cubicchunks.world.level.cube.CubeAccess; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.chunk.ChunkStatus; + +public interface CubicLevelReader { + + @Nullable + CubeAccess cc_getCube(int x, int y, int z, ChunkStatus chunkStatus, boolean forceLoad); + + @Deprecated + boolean cc_hasCube(int x, int y, int z); + + default CubeAccess cc_getCube(BlockPos blockPos) { + return this.cc_getCube(SectionPos.blockToSectionCoord(blockPos.getX()), SectionPos.blockToSectionCoord(blockPos.getY()), SectionPos.blockToSectionCoord(blockPos.getZ())); + } + + default CubeAccess cc_getCube(int x, int y, int z) { + return this.cc_getCube(x, y, z, ChunkStatus.FULL, true); + } + + default CubeAccess cc_getCube(int x, int y, int z, ChunkStatus chunkStatus) { + return this.cc_getCube(x, y, z, chunkStatus, true); + } + + // TODO: This might break if we try to calculate a falling block outside of loaded cubes, since BlockCollisions uses chunks to do some of its math +// @Nullable +// @Override +// default BlockGetter getCubeForCollisions(int x, int y, int z) { +// return this.getCube(x, y, z, ChunkStatus.EMPTY, false); +// } + + @Deprecated + default boolean cc_hasCubeAt(int x, int y, int z) { + return this.cc_hasCube(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(y), SectionPos.blockToSectionCoord(z)); + } + + @Deprecated + default boolean cc_hasCubeAt(BlockPos pos) { + return this.cc_hasCubeAt(pos.getX(), pos.getY(), pos.getZ()); + } + + @Deprecated + default boolean cc_hasCubesAt(BlockPos from, BlockPos to) { + return this.cc_hasCubesAt(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); + } + + @Deprecated + default boolean cc_hasCubesAt(int fromX, int toX, int fromY, int toY, int fromZ, int toZ) { + int sectionFromX = SectionPos.blockToSectionCoord(fromX); + int sectionToX = SectionPos.blockToSectionCoord(toX); + int sectionFromY = SectionPos.blockToSectionCoord(fromY); + int sectionToY = SectionPos.blockToSectionCoord(toY); + int sectionFromZ = SectionPos.blockToSectionCoord(fromZ); + int sectionToZ = SectionPos.blockToSectionCoord(toZ); + + for(int i1 = sectionFromX; i1 <= sectionToX; ++i1) { + for(int j1 = sectionFromY; j1 <= sectionToY; ++j1) { + for(int k1 = sectionFromZ; k1 <= sectionToZ; ++k1) { + if (!this.cc_hasCube(i1, j1, k1)) { + return false; + } + } + } + } + + return true; + } + +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/cube/CubicChunkSource.java b/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/cube/CubicChunkSource.java new file mode 100644 index 00000000..a2c13048 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/world/level/cube/CubicChunkSource.java @@ -0,0 +1,20 @@ +package io.github.opencubicchunks.cubicchunks.world.level.cube; + +import io.github.opencubicchunks.cc_core.api.CubePos; +import net.minecraft.world.level.chunk.ChunkStatus; + +public interface CubicChunkSource { + CubeAccess cc_getCube(int x, int y, int z, ChunkStatus status, boolean forceLoad); + + LevelCube cc_getCube(int x, int y, int z, boolean forceLoad); + + LevelCube cc_getCubeNow(int x, int y, int z); + + // TODO: Phase 2 - getCubeForLighting + + boolean cc_hasCube(int x, int y, int z); + + int cc_getLoadedCubeCount(); + + void cc_updateCubeForced(CubePos cubePos, boolean forced); +} diff --git a/src/main/resources/cubicchunks.mixins.core.json b/src/main/resources/cubicchunks.mixins.core.json index b9626519..c954993a 100644 --- a/src/main/resources/cubicchunks.mixins.core.json +++ b/src/main/resources/cubicchunks.mixins.core.json @@ -32,7 +32,9 @@ "common.world.level.cube.MixinLevelCube", "common.world.level.cube.MixinLevelCube$BoundTickingBlockEntity", "common.world.level.cube.MixinLevelCube$RebindableTickingBlockEntityWrapper", - "common.world.level.MixinLevel" + "common.world.level.chunk.MixinChunkSource", + "common.world.level.MixinLevel", + "common.world.level.MixinLevelReader" ], "client": [], "server": [] diff --git a/src/main/resources/dasm/sets/sets.dasm b/src/main/resources/dasm/sets/sets.dasm index bbb3bd25..24386dda 100644 --- a/src/main/resources/dasm/sets/sets.dasm +++ b/src/main/resources/dasm/sets/sets.dasm @@ -8,6 +8,7 @@ "net.minecraft.world.level.chunk.LevelChunk$PostLoadProcessor", "net.minecraft.world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapper", "net.minecraft.world.level.chunk.ChunkAccess", + "net.minecraft.server.level.FullChunkStatus", "io.github.opencubicchunks.cubicchunks.world.level.chunklike.CloAccess", "io.github.opencubicchunks.cubicchunks.world.level.chunklike.LevelClo", "io.github.opencubicchunks.cubicchunks.world.level.cube.CubeAccess", diff --git a/src/test/java/io/github/opencubicchunks/cubicchunks/test/server/level/TestCubicServerLevel.java b/src/test/java/io/github/opencubicchunks/cubicchunks/test/server/level/TestCubicServerLevel.java index 7b201985..af4f9b92 100644 --- a/src/test/java/io/github/opencubicchunks/cubicchunks/test/server/level/TestCubicServerLevel.java +++ b/src/test/java/io/github/opencubicchunks/cubicchunks/test/server/level/TestCubicServerLevel.java @@ -32,10 +32,6 @@ public static void setup() { SharedConstants.IS_RUNNING_IN_IDE = true; } - private void unregisterMocks() { - Mockito.mockStatic(RandomState.class, withSettings().defaultAnswer(Answers.RETURNS_DEEP_STUBS)); - } - // TODO: Phase 3 - This needs a more rigorous test down the line when we actually care about entities @Test public void testVanillaSpawningAllowed() throws Exception { try (CloseableReference serverLevelReference = setupServerLevel()) { @@ -44,28 +40,28 @@ private void unregisterMocks() { } // TODO: Phase 3 - This is part of the neoforge API and will need a more rigorous test when we need to support their API - @Test public void testInvalidateCapabilities() throws Exception { + @Test public void testVanillaInvalidateCapabilities() throws Exception { try (CloseableReference serverLevelReference = setupServerLevel()) { serverLevelReference.value().invalidateCapabilities(new ChunkPos(0, 0)); } } // TODO: Stub. - @Test public void testTickChunk() throws Exception { + @Test public void testVanillaTickChunk() throws Exception { try (CloseableReference serverLevelReference = setupServerLevel()) { serverLevelReference.value().tickChunk(new LevelChunk(serverLevelReference.value(), new ChunkPos(0, 0)), 10); } } // TODO: Stub. This test hangs. Maybe due to ForcedChunksSavedData? - @Test @Disabled public void testSetChunkForced() throws Exception{ + @Test @Disabled public void testVanillaSetChunkForced() throws Exception{ try (CloseableReference serverLevelReference = setupServerLevel()) { serverLevelReference.value().setChunkForced(0, 0, true); } } // TODO: Stub. - @Test public void testIsPositionEntityTicking() throws Exception { + @Test public void testVanillaIsPositionEntityTicking() throws Exception { try (CloseableReference serverLevelReference = setupServerLevel()) { assertFalse(serverLevelReference.value().isPositionEntityTicking(BlockPos.ZERO)); } diff --git a/src/test/java/io/github/opencubicchunks/cubicchunks/test/world/level/TestCubicLevel.java b/src/test/java/io/github/opencubicchunks/cubicchunks/test/world/level/TestCubicLevel.java index 13f1b38d..b8250353 100644 --- a/src/test/java/io/github/opencubicchunks/cubicchunks/test/world/level/TestCubicLevel.java +++ b/src/test/java/io/github/opencubicchunks/cubicchunks/test/world/level/TestCubicLevel.java @@ -1,10 +1,74 @@ package io.github.opencubicchunks.cubicchunks.test.world.level; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import java.nio.file.Files; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import io.github.opencubicchunks.cubicchunks.MarkableAsCubic; +import io.github.opencubicchunks.cubicchunks.testutils.CloseableReference; +import io.github.opencubicchunks.cubicchunks.world.level.CubicLevel; +import io.github.opencubicchunks.cubicchunks.world.level.cube.CubicChunkSource; +import io.github.opencubicchunks.cubicchunks.world.level.cube.LevelCube; import net.minecraft.SharedConstants; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.Bootstrap; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.TickRateManager; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.WritableLevelData; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.mockito.Answers; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +/** + * Tests for {@link CubicLevel}. + * + * Currently only tests that the methods exist and don't throw exceptions or hang. + * + * The unit tests will not be further developed. We are just going to integration test this class once we have enough working functionality elsewhere. + */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestCubicLevel { @BeforeAll @@ -13,4 +77,240 @@ public static void setup() { Bootstrap.bootStrap(); SharedConstants.IS_RUNNING_IN_IDE = true; } + + public static class TestLevel extends Level { + ChunkSource mockChunkSource = mock(ChunkSource.class, RETURNS_DEEP_STUBS); + + public TestLevel( + WritableLevelData p_270739_, + ResourceKey p_270683_, + RegistryAccess p_270200_, + Holder p_270240_, + Supplier p_270692_, + boolean p_270904_, + boolean p_270470_, + long p_270248_, + int p_270466_ + ) { + super(p_270739_, p_270683_, p_270200_, p_270240_, p_270692_, p_270904_, p_270470_, p_270248_, p_270466_); + when(((CubicChunkSource)mockChunkSource).cc_getCube(anyInt(), anyInt(), anyInt(), anyBoolean())).thenReturn(mock(LevelCube.class)); + when(((CubicChunkSource)mockChunkSource).cc_getCube(anyInt(), anyInt(), anyInt(), any(), anyBoolean())).thenReturn(mock(LevelCube.class)); + } + + @Override public void sendBlockUpdated(BlockPos p_46612_, BlockState p_46613_, BlockState p_46614_, int p_46615_) { + + } + + @Override + public void playSeededSound(@Nullable Player p_262953_, double p_263004_, double p_263398_, double p_263376_, Holder p_263359_, SoundSource p_263020_, float p_263055_, + float p_262914_, long p_262991_) { + + } + + @Override + public void playSeededSound(@Nullable Player p_220372_, Entity p_220373_, Holder p_263500_, SoundSource p_220375_, float p_220376_, float p_220377_, long p_220378_) { + + } + + @Override public String gatherChunkSourceStats() { + return null; + } + + @Nullable @Override public Entity getEntity(int p_46492_) { + return null; + } + + @Override public TickRateManager tickRateManager() { + return null; + } + + @Nullable @Override public MapItemSavedData getMapData(String p_46650_) { + return null; + } + + @Override public void setMapData(String p_151533_, MapItemSavedData p_151534_) { + + } + + @Override public int getFreeMapId() { + return 0; + } + + @Override public void destroyBlockProgress(int p_46506_, BlockPos p_46507_, int p_46508_) { + + } + + @Override public Scoreboard getScoreboard() { + return null; + } + + @Override public RecipeManager getRecipeManager() { + return null; + } + + @Override protected LevelEntityGetter getEntities() { + return null; + } + + @Override public LevelTickAccess getBlockTicks() { + return null; + } + + @Override public LevelTickAccess getFluidTicks() { + return null; + } + + @Override public ChunkSource getChunkSource() { + return mockChunkSource; + } + + @Override public void levelEvent(@Nullable Player p_46771_, int p_46772_, BlockPos p_46773_, int p_46774_) { + + } + + @Override public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, GameEvent.Context p_220406_) { + + } + + @Override public float getShade(Direction p_45522_, boolean p_45523_) { + return 0; + } + + @Override public List players() { + return null; + } + + @Override public Holder getUncachedNoiseBiome(int p_204159_, int p_204160_, int p_204161_) { + return null; + } + + @Override public FeatureFlagSet enabledFeatures() { + return null; + } + + // This overrides CubicLevel.hasCube + @SuppressWarnings("unused") public boolean cc_hasCube(int x, int y, int z) { + return true; + } + } + + private CloseableReference setupTestLevel() { + MockedStatic randomStateMockedStatic = Mockito.mockStatic(RandomState.class, withSettings().defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + ChunkGenerator noiseBasedChunkGeneratorMock = mock(ChunkGenerator.class, withSettings().defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + when(noiseBasedChunkGeneratorMock.createBiomes(any(),any(),any(),any(),any())).thenAnswer(i -> CompletableFuture.completedFuture(i.getArguments()[4])); + when(noiseBasedChunkGeneratorMock.fillFromNoise(any(),any(),any(),any(),any())).thenAnswer(i -> CompletableFuture.completedFuture(i.getArguments()[4])); + LevelStem levelStemMock = mock(RETURNS_DEEP_STUBS); + when(levelStemMock.type().value().height()).thenReturn(384); + LevelStorageSource.LevelStorageAccess levelStorageAccessMock = mock(RETURNS_DEEP_STUBS); + try { + when(levelStorageAccessMock.getDimensionPath(any())).thenReturn(Files.createTempDirectory("cc_test")); + } catch (Exception e) { + e.printStackTrace(); + } + Holder holderMock = mock(Holder.class, RETURNS_DEEP_STUBS); + when(holderMock.unwrapKey()).thenReturn(Optional.of(ResourceKey.create(mock(), mock()))); + when(holderMock.value()).thenReturn(mock(DimensionType.class)); + return new CloseableReference<>( + new TestLevel(mock(RETURNS_DEEP_STUBS), + mock(RETURNS_DEEP_STUBS), + mock(RETURNS_DEEP_STUBS), + holderMock, + mock(RETURNS_DEEP_STUBS), + false, + false, + 0, + 0), + randomStateMockedStatic); + } + + @Test public void testGetCubeAt() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic) testLevelReference.value()).cc_setCubic(); + ((CubicLevel) testLevelReference.value()).cc_getCubeAt(new BlockPos(0, 0, 0)); + } + } + + @Test public void testGetCube() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic) testLevelReference.value()).cc_setCubic(); + ((CubicLevel) testLevelReference.value()).cc_getCube(0, 0, 0); + } + } + + @Test public void testGetCubeCubeAccess() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic) testLevelReference.value()).cc_setCubic(); + ((CubicLevel) testLevelReference.value()).cc_getCube(0, 0, 0, ChunkStatus.FULL, true); + } + } + + @Test public void testSetBlock() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).setBlock(new BlockPos(0, 0, 0), mock(BlockState.class), 0); + } + } + + @Test public void testGetBlockState() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).getBlockState(new BlockPos(0, 0, 0)); + } + } + + @Test public void testGetBlockEntity() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).getBlockEntity(new BlockPos(0, 0, 0)); + } + } + + @Test public void testGetFluidState() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).getFluidState(new BlockPos(0, 0, 0)); + } + } + + @Test public void testSetBlockEntity() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).setBlockEntity(mock(BlockEntity.class, RETURNS_DEEP_STUBS)); + } + } + + @Test public void testRemoveBlockEntity() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).removeBlockEntity(new BlockPos(0, 0, 0)); + } + } + + @Test public void testIsLoaded() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic)testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).isLoaded(new BlockPos(0, 0, 0)); + } + } + + @Test public void testLoadedAndEntityCanStandOnFace() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic) testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).loadedAndEntityCanStandOnFace(new BlockPos(0, 0, 0), mock(Entity.class), Direction.UP); + } + } + + @Test public void testBlockEntityChanged() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic) testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).blockEntityChanged(new BlockPos(0, 0, 0)); + } + } + + @Test public void getCurrentDifficultyAt() throws Exception { + try (CloseableReference testLevelReference = setupTestLevel()) { + ((MarkableAsCubic) testLevelReference.value()).cc_setCubic(); + (testLevelReference.value()).getCurrentDifficultyAt(new BlockPos(0, 0, 0)); + } + } }