[1.18.x] Allow blocks to hide faces on a neighboring block (#8300)

* Allow blocks to hide faces on a neighboring block
* Allow blocks to opt-out of external face hiding
This commit is contained in:
Dennis C authored and GitHub committed 2022-03-13 22:59:59 +01:00
1 parent 9bf227afeb
commit 57d6f31adb
11 files changed
+271

No files matched your search

@@ -1074,4 +1074,9 @@ public class ForgeHooksClient
MinecraftForge.EVENT_BUS.post(event);
return event.getResult();
}
public static boolean isBlockInSolidLayer(BlockState state)
{
return ItemBlockRenderTypes.canRenderInLayer(state, RenderType.solid());
}
}
@@ -32,6 +32,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.server.level.ServerLevel;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.IPlantable;
@@ -44,6 +45,7 @@ import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.ToolAction;
import net.minecraftforge.common.ToolActions;
import net.minecraftforge.fml.loading.FMLEnvironment;
@SuppressWarnings("deprecation")
public interface IForgeBlock
@@ -749,4 +751,43 @@ public interface IForgeBlock
return state.isSignalSource() && direction != null;
}
}
/**
* Whether this block hides the neighbors face pointed towards by the given direction.
* <p>
* This method should only be used for blocks you don't control, for your own blocks override
* {@link Block#skipRendering(BlockState, BlockState, Direction)} on the respective block instead
* <p>
* WARNING: This method is likely to be called from a worker thread! If you want to retrieve a
* {@link net.minecraft.world.level.block.entity.BlockEntity} from the given level, make sure to use
* {@link net.minecraftforge.common.extensions.IForgeBlockGetter#getExistingBlockEntity(BlockPos)} to not
* accidentally create a new or delete an old {@link net.minecraft.world.level.block.entity.BlockEntity}
* off of the main thread as this would cause a write operation to the given {@link BlockGetter} and cause
* a CME in the process. Any other direct or indirect write operation to the {@link BlockGetter} will have
* the same outcome.
*
* @param level The world
* @param pos The blocks position in the world
* @param state The blocks {@link BlockState}
* @param neighborState The neighboring blocks {@link BlockState}
* @param dir The direction towards the neighboring block
*/
default boolean hidesNeighborFace(BlockGetter level, BlockPos pos, BlockState state, BlockState neighborState, Direction dir)
{
return false;
}
/**
* Whether this block allows a neighboring block to hide the face of this block it touches.
* If this returns true, {@link IForgeBlockState#hidesNeighborFace(BlockGetter, BlockPos, BlockState, Direction)}
* will be called on the neighboring block.
*/
default boolean supportsExternalFaceHiding(BlockState state)
{
if (FMLEnvironment.dist.isClient())
{
return !ForgeHooksClient.isBlockInSolidLayer(state);
}
return true;
}
}
@@ -0,0 +1,49 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.common.extensions;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.*;
import org.jetbrains.annotations.Nullable;
public interface IForgeBlockGetter
{
private BlockGetter self() { return (BlockGetter) this; }
/**
* Get the {@link BlockEntity} at the given position if it exists.
* <p>
* {@link Level#getBlockEntity(BlockPos)} would create a new {@link BlockEntity} if the
* {@link net.minecraft.world.level.block.Block} has one, but it has not been placed in the world yet
* (This can happen on world load).
* @return The BlockEntity at the given position or null if it doesn't exist
*/
@Nullable
default BlockEntity getExistingBlockEntity(BlockPos pos)
{
if (this instanceof Level level)
{
if (!level.hasChunk(pos.getX(), pos.getZ()))
{
return null;
}
return level.getChunk(pos).getExistingBlockEntity(pos);
}
else if (this instanceof LevelChunk chunk)
{
return chunk.getBlockEntities().get(pos);
}
else if (this instanceof ImposterProtoChunk chunk)
{
return chunk.getWrapped().getExistingBlockEntity(pos);
}
return self().getBlockEntity(pos);
}
}
@@ -646,4 +646,31 @@ public interface IForgeBlockState
{
return self().getBlock().canConnectRedstone(self(), level, pos, direction);
}
/**
* Whether this block hides the neighbors face pointed towards by the given direction.
* <p>
* This method should only be used for blocks you don't control, for your own blocks override
* {@link net.minecraft.world.level.block.Block#skipRendering(BlockState, BlockState, Direction)}
* on the respective block instead
*
* @param level The world
* @param pos The blocks position in the world
* @param neighborState The neighboring blocks {@link BlockState}
* @param dir The direction towards the neighboring block
*/
default boolean hidesNeighborFace(BlockGetter level, BlockPos pos, BlockState neighborState, Direction dir)
{
return self().getBlock().hidesNeighborFace(level, pos, self(), neighborState, dir);
}
/**
* Whether this block allows a neighboring block to hide the face of this block it touches.
* If this returns true, {@link IForgeBlockState#hidesNeighborFace(BlockGetter, BlockPos, BlockState, Direction)}
* will be called on the neighboring block.
*/
default boolean supportsExternalFaceHiding()
{
return self().getBlock().supportsExternalFaceHiding(self());
}
}