diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java
index 11d1140dd..a98e297b4 100644
--- a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java
+++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java
@@ -44,17 +44,32 @@
import java.util.function.Supplier;
/**
- * Fake level fake chunk :D all data related methods must redirect to fake level
- * Updating procedure is same as fake leve
+ * Fake level fake chunk :D all data related methods must redirect to fake level Updating procedure is same as fakeLevel Porting info:
+ *
+ * - uncomment last method section
+ * - fix compile errors
+ * - add override for remaining methods and sort/implement them accordingly
+ * - comment last method section
+ *
+ *
*/
public class FakeChunk extends LevelChunk
{
private final FakeLevel fakeLevel;
+ // section cache
+ int lastY;
+ LevelChunkSection lastSection = null;
+
public FakeChunk(final FakeLevel worldIn, final int x, final int z)
{
super(worldIn, new ChunkPos(x, z));
this.fakeLevel = worldIn;
+
+ // set itself to cache
+ fakeLevel.lastX = x;
+ fakeLevel.lastZ = z;
+ fakeLevel.lastChunk = this;
}
// ========================================
@@ -210,9 +225,20 @@ public boolean isYSpaceEmpty(int p_62075_, int p_62076_)
@Override
public LevelChunkSection[] getSections()
{
+ // don't cache them
return new LevelChunkSection[0];
}
+ @Override
+ public LevelChunkSection getSection(int yIdx)
+ {
+ if (lastY == yIdx && lastSection != null)
+ {
+ return lastSection;
+ }
+ return new FakeLevelChunkSection(this, yIdx);
+ }
+
// ========================================
// ============= NOOP METHODS =============
// ========================================
diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java
index 61e967c87..195d76386 100644
--- a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java
+++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java
@@ -11,10 +11,13 @@
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.SectionPos;
+import net.minecraft.resources.ResourceKey;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.AbortableIterationConsumer.Continuation;
+import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.damagesource.DamageSources;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.flag.FeatureFlagSet;
@@ -30,10 +33,12 @@
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.border.WorldBorder;
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.dimension.DimensionType;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.level.gameevent.GameEvent;
@@ -57,6 +62,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.function.Supplier;
/**
* As much as general fake level. Features:
@@ -86,13 +93,15 @@ public class FakeLevel extends Level
{
protected IFakeLevelBlockGetter levelSource;
protected final IFakeLevelLightProvider lightProvider;
+ protected Level realLevel;
protected final Scoreboard scoreboard;
protected final boolean overrideBeLevel;
protected final FakeChunkSource chunkSource;
protected final FakeLevelLightEngine lightEngine;
protected FakeLevelEntityGetterAdapter levelEntityGetter = FakeLevelEntityGetterAdapter.EMPTY;
- // TODO: this is currently manually filled by class user - ideally if not filled yet this should get constructed from levelSource manually
+ // TODO: this is currently manually filled by class user - ideally if not filled yet this should get constructed from levelSource
+ // manually
protected Map blockEntities = Collections.emptyMap();
/**
@@ -100,32 +109,42 @@ public class FakeLevel extends Level
*/
protected BlockPos worldPos = BlockPos.ZERO;
+ // chunk cache
+ int lastX, lastZ;
+ ChunkAccess lastChunk = null;
+
/**
- * @param levelSource data source, also try to set block entities/entities collections
- * @param lightProvider light source
- * @param scoreboard if null client level is used instead
+ * @param levelSource data source, also try to set block entities/entities collections
+ * @param lightProvider light source
+ * @param scoreboard if null client level is used instead
* @param overrideBeLevel if true all block entities will have set level to this instance
- *
* @see #setBlockEntities(Map) for better block entity handling, if set then levelSource BE getter is not used
* @see #setEntities(Collection) only way to add entities into fake level
+ * @see #setRealLevel(Level) if you want to reuse this instance
*/
- public FakeLevel(final IFakeLevelBlockGetter levelSource, final IFakeLevelLightProvider lightProvider, @Nullable final Scoreboard scoreboard, final boolean overrideBeLevel)
+ public FakeLevel(final IFakeLevelBlockGetter levelSource,
+ final IFakeLevelLightProvider lightProvider,
+ @Nullable final Scoreboard scoreboard,
+ final boolean overrideBeLevel)
{
super(new FakeLevelData(clientLevel().getLevelData(), lightProvider),
clientLevel().dimension(),
clientLevel().registryAccess(),
clientLevel().dimensionTypeRegistration(),
- () -> clientLevel().getProfiler(),
- true,
+ clientLevel().getProfilerSupplier(),
+ clientLevel().isClientSide(),
false,
0,
0);
this.levelSource = levelSource;
this.lightProvider = lightProvider;
- this.scoreboard = scoreboard == null ? clientLevel().getScoreboard() : scoreboard;
+ this.realLevel = clientLevel();
+ this.scoreboard = scoreboard;
this.overrideBeLevel = overrideBeLevel;
this.chunkSource = new FakeChunkSource(this);
this.lightEngine = new FakeLevelLightEngine(this);
+
+ setRealLevel(clientLevel());
}
// ========================================
@@ -138,6 +157,27 @@ protected static ClientLevel clientLevel()
return Minecraft.getInstance().level;
}
+ public void setRealLevel(final Level realLevel)
+ {
+ if (Objects.equals(this.realLevel, realLevel))
+ {
+ return;
+ }
+
+ if (realLevel != null && realLevel.isClientSide != this.isClientSide)
+ {
+ throw new IllegalArgumentException("Received wrong sided realLevel - fakeLevel.isClientSide = " + this.isClientSide);
+ }
+
+ this.realLevel = realLevel;
+ ((FakeLevelData) this.getLevelData()).vanillaLevelData = realLevel == null ? null : realLevel.getLevelData();
+ }
+
+ public Level realLevel()
+ {
+ return realLevel;
+ }
+
/**
* @param levelSource new data source
*/
@@ -172,7 +212,7 @@ public BlockPos getWorldPos()
/**
* For better block entity handling in chunk methods. If set then {@link IFakeLevelBlockGetter#getBlockEntity(BlockPos)
- * levelSource.getBlockEntity(BlockPos)} is not used
+ * levelSource.getBlockEntity(BlockPos)} is not used. Reset with empty collection
*
* @param blockEntities all block entities, should be data equivalent to levelSource
*/
@@ -182,11 +222,64 @@ public void setBlockEntities(final Map blockEntities)
}
/**
- * @param entities all entities, their level should be this fake level instance
+ * @param entities all entities, their level should be this fake level instance. Reset with empty collection
*/
public void setEntities(final Collection extends Entity> entities)
{
- levelEntityGetter = FakeLevelEntityGetterAdapter.ofEntities(entities);
+ levelEntityGetter = entities.isEmpty() ? FakeLevelEntityGetterAdapter.EMPTY : FakeLevelEntityGetterAdapter.ofEntities(entities);
+ }
+
+ // ========================================
+ // ======= CTOR REAL LEVEL REDIRECTS ======
+ // ========================================
+ // Note: must have null check because super ctor
+
+ @Override
+ public ResourceKey dimension()
+ {
+ return realLevel() != null ? realLevel().dimension() : super.dimension();
+ }
+
+ @Override
+ public RegistryAccess registryAccess()
+ {
+ return realLevel() != null ? realLevel().registryAccess() : super.registryAccess();
+ }
+
+ @Override
+ public DamageSources damageSources()
+ {
+ return realLevel() != null ? realLevel().damageSources() : super.damageSources();
+ }
+
+ @Override
+ public ProfilerFiller getProfiler()
+ {
+ return realLevel() != null ? realLevel().getProfiler() : super.getProfiler();
+ }
+
+ @Override
+ public Supplier getProfilerSupplier()
+ {
+ return realLevel() != null ? realLevel().getProfilerSupplier() : super.getProfilerSupplier();
+ }
+
+ @Override
+ public DimensionType dimensionType()
+ {
+ return realLevel() != null ? realLevel().dimensionType() : super.dimensionType();
+ }
+
+ @Override
+ public Holder dimensionTypeRegistration()
+ {
+ return realLevel() != null ? realLevel().dimensionTypeRegistration() : super.dimensionTypeRegistration();
+ }
+
+ @Override
+ public WorldBorder getWorldBorder()
+ {
+ return realLevel() != null ? realLevel().getWorldBorder() : super.getWorldBorder();
}
// ========================================
@@ -221,6 +314,10 @@ public BlockState getBlockState(final BlockPos pos)
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull)
{
+ if (lastX == x && lastZ == z && lastChunk != null)
+ {
+ return lastChunk;
+ }
return nonnull || hasChunk(x, z) ? new FakeChunk(this, x, z) : null;
}
@@ -230,27 +327,28 @@ public boolean hasChunk(int chunkX, int chunkZ)
final int posX = SectionPos.sectionToBlockCoord(chunkX);
final int posZ = SectionPos.sectionToBlockCoord(chunkZ);
return levelSource.getMinX() <= posX && posX < levelSource.getMaxX() &&
- levelSource.getMinZ() <= posZ && posZ < levelSource.getMaxZ();
+ levelSource.getMinZ() <= posZ &&
+ posZ < levelSource.getMaxZ();
}
@Override
public int getBrightness(final LightLayer lightType, final BlockPos pos)
{
return lightProvider.forceOwnLightLevel() ? lightProvider.getBrightness(lightType, pos) :
- clientLevel().getBrightness(lightType, worldPos.offset(pos));
+ realLevel().getBrightness(lightType, worldPos.offset(pos));
}
@Override
public int getRawBrightness(BlockPos pos, int amount)
{
return lightProvider.forceOwnLightLevel() ? lightProvider.getRawBrightness(pos, amount) :
- clientLevel().getRawBrightness(worldPos.offset(pos), amount);
+ realLevel().getRawBrightness(worldPos.offset(pos), amount);
}
@Override
public int getSkyDarken()
{
- return lightProvider.forceOwnLightLevel() ? lightProvider.getSkyDarken() : clientLevel().getSkyDarken();
+ return lightProvider.forceOwnLightLevel() ? lightProvider.getSkyDarken() : realLevel().getSkyDarken();
}
@Override
@@ -262,7 +360,7 @@ public boolean isDay()
@Override
public Scoreboard getScoreboard()
{
- return scoreboard;
+ return scoreboard == null ? realLevel().getScoreboard() : scoreboard;
}
@Override
@@ -362,49 +460,43 @@ public String gatherChunkSourceStats()
@Override
public float getShade(Direction p_104703_, boolean p_104704_)
{
- return clientLevel().getShade(p_104703_, p_104704_);
+ return realLevel().getShade(p_104703_, p_104704_);
}
@Override
public Holder getBiome(BlockPos pos)
{
- return clientLevel().getBiome(worldPos.offset(pos));
+ return realLevel().getBiome(worldPos.offset(pos));
}
@Override
public BiomeManager getBiomeManager()
{
- return clientLevel().getBiomeManager();
+ return realLevel().getBiomeManager();
}
@Override
public RecipeManager getRecipeManager()
{
- return clientLevel().getRecipeManager();
- }
-
- @Override
- public RegistryAccess registryAccess()
- {
- return clientLevel().registryAccess();
+ return realLevel().getRecipeManager();
}
@Override
public FeatureFlagSet enabledFeatures()
{
- return clientLevel().enabledFeatures();
+ return realLevel().enabledFeatures();
}
@Override
public Holder getUncachedNoiseBiome(int x, int y, int z)
{
- return clientLevel().getUncachedNoiseBiome(x, y, z);
+ return realLevel().getUncachedNoiseBiome(x, y, z);
}
@Override
public Holder getNoiseBiome(int x, int y, int z)
{
- return clientLevel().getNoiseBiome(x, y, z);
+ return realLevel().getNoiseBiome(x, y, z);
}
// ========================================
@@ -802,36 +894,12 @@ public void createFireworks(double p_46475_,
super.createFireworks(p_46475_, p_46476_, p_46477_, p_46478_, p_46479_, p_46480_, p_46481_);
}
- @Override
- public DamageSources damageSources()
- {
- return super.damageSources();
- }
-
- @Override
- public ResourceKey dimension()
- {
- return super.dimension();
- }
-
- @Override
- public DimensionType dimensionType()
- {
- return super.dimensionType();
- }
-
@Override
public ResourceKey dimensionTypeId()
{
return super.dimensionTypeId();
}
- @Override
- public Holder dimensionTypeRegistration()
- {
- return super.dimensionTypeRegistration();
- }
-
@Override
public void disconnect()
{
@@ -979,18 +1047,6 @@ public double getMaxEntityRadius()
return super.getMaxEntityRadius();
}
- @Override
- public ProfilerFiller getProfiler()
- {
- return super.getProfiler();
- }
-
- @Override
- public Supplier getProfilerSupplier()
- {
- return super.getProfilerSupplier();
- }
-
@Override
public RandomSource getRandom()
{
@@ -1022,12 +1078,6 @@ public float getSunAngle(float p_46491_)
return super.getSunAngle(p_46491_);
}
- @Override
- public WorldBorder getWorldBorder()
- {
- return super.getWorldBorder();
- }
-
@Override
public void globalLevelEvent(int p_46665_, BlockPos p_46666_, int p_46667_)
{
diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java
new file mode 100644
index 000000000..0397fef64
--- /dev/null
+++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java
@@ -0,0 +1,170 @@
+package com.ldtteam.structurize.client.fakelevel;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.biome.BiomeResolver;
+import net.minecraft.world.level.biome.Climate.Sampler;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import net.minecraft.world.level.chunk.PalettedContainer;
+import net.minecraft.world.level.chunk.PalettedContainerRO;
+import net.minecraft.world.level.material.FluidState;
+import java.util.function.Predicate;
+
+/**
+ * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases)
+ */
+public class FakeLevelChunkSection extends LevelChunkSection
+{
+ private final FakeChunk fakeChunk;
+ private final int yIdx;
+
+ /**
+ * @param fakeChunk parent chunk
+ * @param yIdx yLevel in chunk, multiply by section height
+ */
+ public FakeLevelChunkSection(final FakeChunk fakeChunk, final int yIdx)
+ {
+ super(null, null);
+ this.fakeChunk = fakeChunk;
+ this.yIdx = yIdx;
+
+ // set itself to cache
+ fakeChunk.lastY = yIdx;
+ fakeChunk.lastSection = this;
+ }
+
+ private BlockPos formGlobalPos(int x, int y, int z)
+ {
+ return new BlockPos(x + fakeChunk.getPos().x * SECTION_WIDTH, y + yIdx * SECTION_HEIGHT, z + fakeChunk.getPos().z * SECTION_WIDTH);
+ }
+
+ @Override
+ public BlockState setBlockState(int x, int y, int z, BlockState p_62995_, boolean p_62996_)
+ {
+ // this should return old value, but we don't allow changes
+ return getBlockState(x, y, z);
+ }
+
+ @Override
+ public BlockState getBlockState(int x, int y, int z)
+ {
+ return fakeChunk.getBlockState(formGlobalPos(x, y, z));
+ }
+
+ @Override
+ public FluidState getFluidState(int x, int y, int z)
+ {
+ return fakeChunk.getFluidState(formGlobalPos(x, y, z));
+ }
+
+ @Override
+ public Holder getNoiseBiome(int x, int y, int z)
+ {
+ return fakeChunk.getNoiseBiome(fakeChunk.getPos().x, yIdx * SECTION_HEIGHT, fakeChunk.getPos().z);
+ }
+
+ @Override
+ public PalettedContainerRO> getBiomes()
+ {
+ // TODO: need our own? use clientLevel?
+ return null;
+ }
+
+ @Override
+ public PalettedContainer getStates()
+ {
+ // TODO: need our own
+ return null;
+ }
+
+ @Override
+ public boolean hasOnlyAir()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isRandomlyTicking()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isRandomlyTickingBlocks()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isRandomlyTickingFluids()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean maybeHas(Predicate p_63003_)
+ {
+ // hard to say, so maybe yes
+ return true;
+ }
+
+ @Override
+ public int getSerializedSize()
+ {
+ // technically noop, vanilla uses it for network sync
+ return 0;
+ }
+
+ @Override
+ public void read(FriendlyByteBuf p_63005_)
+ {
+ // Noop
+ }
+
+ @Override
+ public void readBiomes(FriendlyByteBuf p_275669_)
+ {
+ // Noop
+ }
+
+ @Override
+ public void recalcBlockCounts()
+ {
+ // Noop
+ }
+
+ @Override
+ public void release()
+ {
+ // Noop
+ }
+
+ @Override
+ public void acquire()
+ {
+ // Noop
+ }
+
+ @Override
+ public void fillBiomesFromNoise(BiomeResolver p_282075_, Sampler p_283084_, int p_282310_, int p_281510_, int p_283057_)
+ {
+ // Noop
+ }
+
+ @Override
+ public void write(FriendlyByteBuf p_63012_)
+ {
+ // Noop
+ }
+
+ /*
+ @Override
+ public BlockState setBlockState(int p_62987_, int p_62988_, int p_62989_, BlockState p_62990_)
+ {
+ return super.setBlockState(p_62987_, p_62988_, p_62989_, p_62990_);
+ }
+ */
+}
diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java
index 8e5f1a53f..b99e7a8c3 100644
--- a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java
+++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java
@@ -10,7 +10,7 @@
*/
public class FakeLevelData implements WritableLevelData
{
- protected final LevelData vanillaLevelData;
+ protected LevelData vanillaLevelData;
protected final IFakeLevelLightProvider lightProvider;
protected FakeLevelData(final LevelData vanillaLevelData, final IFakeLevelLightProvider lightProvider)
diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java
new file mode 100644
index 000000000..f06f6974a
--- /dev/null
+++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java
@@ -0,0 +1,183 @@
+package com.ldtteam.structurize.client.fakelevel;
+
+import net.minecraft.CrashReportCategory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraftforge.registries.ForgeRegistries;
+import org.jetbrains.annotations.Nullable;
+import java.util.Collection;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Simple implementation of {@link IFakeLevelBlockGetter} mostly for usage in methods where {@link Level} is needed for virtual
+ * BE/entities etc.
+ */
+public class SingleBlockFakeLevel extends FakeLevel
+{
+ /**
+ * Creates simple fakeLevel instance
+ *
+ * @param realLevel actual valid vanilla instance to provide eg. registries
+ */
+ public SingleBlockFakeLevel(final Level realLevel)
+ {
+ super(new SingleBlockFakeLevelGetter(), IFakeLevelLightProvider.USE_CLIENT_LEVEL, null, true);
+ super.setRealLevel(realLevel);
+ }
+
+ @Override
+ public SingleBlockFakeLevelGetter getLevelSource()
+ {
+ return (SingleBlockFakeLevelGetter) super.getLevelSource();
+ }
+
+ /**
+ * Do not forget to unset to prevent potential memory leaks
+ *
+ * @param blockState related to blockEntity
+ * @param blockEntity related to blockState
+ * @param realLevel actual valid vanilla instance to provide eg. registries
+ * @see #unset(FakeLevel, BlockEntity)
+ * @see FakeLevel#setEntities(Collection) FakeLevel#setEntities(Collection) if you want to add entities, do not forget to reset
+ */
+ public void prepare(final BlockState blockState, @Nullable final BlockEntity blockEntity, final Level realLevel)
+ {
+ getLevelSource().blockEntity = blockEntity;
+ getLevelSource().blockState = blockState;
+ setRealLevel(realLevel);
+
+ if (blockEntity != null)
+ {
+ blockEntity.setLevel(this);
+ }
+ }
+
+ /**
+ * @param blockEntity to unlink level if needed
+ * @see #prepare(FakeLevel, BlockState, BlockEntity, Level)
+ */
+ public void unset(@Nullable final BlockEntity blockEntity)
+ {
+ getLevelSource().blockEntity = null;
+ getLevelSource().blockState = null;
+ setRealLevel(null);
+
+ if (blockEntity != null)
+ {
+ try
+ {
+ blockEntity.setLevel(null);
+ }
+ catch (final NullPointerException e)
+ {
+ // setLevel impls sometimes violates nullability of level field
+ }
+ }
+ }
+
+ /**
+ * See related methods for more information.
+ *
+ * @param blockState related to blockEntity
+ * @param blockEntity related to blockState
+ * @param realLevel actual valid vanilla instance to provide eg. registries
+ * @param action context action
+ * @see #prepare(FakeLevel, BlockState, BlockEntity, Level)
+ * @see #unset(FakeLevel, BlockEntity)
+ */
+ public void withFakeLevelContext(final BlockState blockState,
+ @Nullable final BlockEntity blockEntity,
+ final Level realLevel,
+ final Consumer action)
+ {
+ prepare(blockState, blockEntity, realLevel);
+ action.accept(this);
+ unset(blockEntity);
+ }
+
+ /**
+ * See related methods for more information.
+ *
+ * @param blockState related to blockEntity
+ * @param blockEntity related to blockState
+ * @param realLevel actual valid vanilla instance to provide eg. registries
+ * @param action context action
+ * @see #prepare(FakeLevel, BlockState, BlockEntity, Level)
+ * @see #unset(FakeLevel, BlockEntity)
+ */
+ public T useFakeLevelContext(final BlockState blockState,
+ @Nullable final BlockEntity blockEntity,
+ final Level realLevel,
+ final Function action)
+ {
+ prepare(blockState, blockEntity, realLevel);
+ final T result = action.apply(this);
+ unset(blockEntity);
+ return result;
+ }
+
+ public static class SingleBlockFakeLevelGetter implements IFakeLevelBlockGetter
+ {
+ public BlockState blockState = null;
+ public BlockEntity blockEntity = null;
+
+ @Override
+ public BlockEntity getBlockEntity(final BlockPos pos)
+ {
+ return blockEntity;
+ }
+
+ @Override
+ public BlockState getBlockState(final BlockPos pos)
+ {
+ return blockState;
+ }
+
+ @Override
+ public int getHeight()
+ {
+ return 1;
+ }
+
+ @Override
+ public short getSizeX()
+ {
+ return 1;
+ }
+
+ @Override
+ public short getSizeZ()
+ {
+ return 1;
+ }
+
+ @Override
+ public void describeSelfInCrashReport(final CrashReportCategory category)
+ {
+ category.setDetail("Single block", blockState::toString);
+ category.setDetail("Single block entity type",
+ () -> blockEntity == null ? null : ForgeRegistries.BLOCK_ENTITY_TYPES.getKey(blockEntity.getType()).toString());
+ }
+ }
+
+ public static class SidedSingleBlockFakeLevel
+ {
+ private SingleBlockFakeLevel client;
+ private SingleBlockFakeLevel server;
+
+ public SingleBlockFakeLevel get(final Level realLevel)
+ {
+ if (realLevel.isClientSide())
+ {
+ return client != null ? client : (client = new SingleBlockFakeLevel(realLevel));
+ }
+ else
+ {
+ return server != null ? server : (server = new SingleBlockFakeLevel(realLevel));
+ }
+ }
+ }
+}