Fixed block breaking detection (i.e. keep force field when chunk unload)

This commit is contained in:
Unknown 2019-06-04 22:19:51 +02:00 committed by unknown
parent 39062ee4d3
commit 34b3d83b85
9 changed files with 263 additions and 12 deletions

View file

@ -0,0 +1,226 @@
@startuml
actor "Player" as player
'participant PlayerControllerMP #cyan
'participant EntityPlayer #cyan
'participant ItemStack #cyan
'participant ItemBlock #cyan
participant World #cyan
participant Chunk #cyan
participant IBlockState #cyan
participant Block #cyan
participant TileEntity #cyan
'participant WarpDrive
player -> World: ...
activate World
...
World -[#blue]> World: setBlockState()
activate World
alt world.isOutsideBuildHeight()
break #red return false
end
end
alt !world.isRemote \n&& world.getWorldInfo().getTerrainType() == WorldType.DEBUG_ALL_BLOCK_STATES
break #red return false
end
end
World -[#blue]> World: getChunk(blockPosPassed)
activate World
World <-[#blue]- World
deactivate World
World -[#blue]> World: blockPos = blockPosPassed.toImmutable()
note right: Forge - prevent mutable BlockPos leaks
alt world.captureBlockSnapshots && !world.isRemote
World -[#blue]> World: capturedBlockSnapshots.add(new BlockSnapshot(...))
end
World -[#blue]> World: blockStateOld = world.getBlockState(blockPos)
World -[#blue]> IBlockState: lightOld = blockStateOld.getLightValue(world, blockPos)
activate IBlockState
World <-[#blue]- IBlockState
deactivate IBlockState
World -[#blue]> IBlockState: opacityOld = blockStateOld.getLightOpacity(world, blockPos)
activate IBlockState
World <-[#blue]- IBlockState
deactivate IBlockState
World -[#blue]> Chunk: blockStateEffective = chunk.setBlockState(blockPos, blockStateNew)
activate Chunk
Chunk -[#blue]> Chunk: i = pos.getX() & 15\n\
j = pos.getY()\n\
k = pos.getZ() & 15\n\
l = k << 4 | i
alt j >= this.precipitationHeightMap[l] - 1
Chunk -[#blue]> Chunk: precipitationHeightMap[l] = -999
end
Chunk -[#blue]> Chunk: i1 = this.heightMap[l]
Chunk -[#blue]> Chunk: iblockstate = getBlockState(pos)
alt iblockstate == state
break #red return null
end
end
Chunk -[#blue]> Chunk: block = state.getBlock()
Chunk -[#blue]> IBlockState: block1 = iblockstate.getBlock()
activate IBlockState
Chunk <[#blue]- IBlockState
deactivate IBlockState
Chunk -[#blue]> IBlockState: k1 = iblockstate.getLightOpacity(this.world, pos)
activate IBlockState
Chunk <[#blue]- IBlockState
deactivate IBlockState
Chunk -[#blue]> Chunk: extendedblockstorage = storageArrays[j >> 4]\n\
boolean flag = false
alt extendedblockstorage == NULL_BLOCK_STORAGE
alt block == Blocks.AIR
break #red return null
end
end
Chunk -[#blue]> World: world.provider.hasSkyLight())
activate World
Chunk <[#blue]- World
deactivate World
Chunk -[#blue]> Chunk: extendedblockstorage = new ExtendedBlockStorage(j >> 4 << 4, world.provider.hasSkyLight())\n\
storageArrays[j >> 4] = extendedblockstorage\n\
flag = j >= i1
end
Chunk -[#blue]> Chunk: extendedblockstorage.set(i, j & 15, k, state)
' //if (block1 != block)
alt !this.world.isRemote
alt block1 != block
Chunk -[#blue]> Block: block1.breakBlock(this.world, pos, iblockstate)
activate Block
Chunk <[#blue]- Block
deactivate Block
end
note right: Only fire block breaks when the block changes.
Chunk -[#blue]> Chunk: te = getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK);
Chunk -[#blue]> TileEntity: te.shouldRefresh(this.world, pos, iblockstate, state)
activate TileEntity
Chunk <[#blue]- TileEntity
deactivate TileEntity
alt te != null && shouldRefresh
Chunk -[#blue]> World: world.removeTileEntity(pos)
activate World
Chunk <[#blue]- World
deactivate World
end
else block1.hasTileEntity(iblockstate)
Chunk -[#blue]> Chunk: te = getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK)
alt te != null && te.shouldRefresh(this.world, pos, iblockstate, state)
Chunk -[#blue]> World: world.removeTileEntity(pos)
activate World
Chunk <[#blue]- World
deactivate World
end
end
alt extendedblockstorage.get(i, j & 15, k).getBlock() != block
break #red return null
end
end
alt flag
Chunk -[#blue]> Chunk: generateSkylightMap()
else
Chunk -[#blue]> IBlockState: int j1 = state.getLightOpacity(this.world, pos)
activate IBlockState
Chunk <[#blue]- IBlockState
deactivate IBlockState
alt j1 > 0
alt j >= i1
Chunk -[#blue]> Chunk: relightBlock(i, j + 1, k);
end
else j == i1 - 1
Chunk -[#blue]> Chunk: relightBlock(i, j, k);
end
alt j1 != k1\n\
&& ( j1 < k1\n\
|| this.getLightFor(EnumSkyBlock.SKY, pos) > 0\n\
|| this.getLightFor(EnumSkyBlock.BLOCK, pos) > 0 )
Chunk -[#blue]> Chunk: propagateSkylightOcclusion(i, k)
end
end
...
note right: If capturing blocks, only run block physics for TE's.\n\
Non-TE's are handled in ForgeHooks.onPlaceItemIntoWorld
alt !this.world.isRemote\n\
&& block1 != block\n\
&& ( !this.world.captureBlockSnapshots\n\
|| block.hasTileEntity(state) )
Chunk -[#blue]> Block: block.onBlockAdded(this.world, pos, state)
end
alt block.hasTileEntity(state)
Chunk -[#blue]> Chunk: tileentity1 = getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK);
alt tileentity1 == null
Chunk -[#blue]> Block: tileentity1 = block.createTileEntity(this.world, state)
activate Block
Chunk <[#blue]- Block
deactivate Block
Chunk -[#blue]> World: world.setTileEntity(pos, tileentity1)
activate World
Chunk <[#blue]- World
deactivate World
end
alt tileentity1 != null)
Chunk -[#blue]> TileEntity: tileentity1.updateContainingBlockInfo()
activate TileEntity
Chunk <[#blue]- TileEntity
deactivate TileEntity
end
end
Chunk -[#blue]> Chunk: dirty = true
break #lime iblockstate
end
World <-[#blue]- Chunk
deactivate Chunk
alt blockStateEffective == null
alt blockSnapshot != null
World -[#blue]> World: world.capturedBlockSnapshots.remove(blockSnapshot)
end
break #red return false
end
else
alt blockStateNew.getLightOpacity(world, blockPos) != opacityOld\n\
|| blockStateNew.getLightValue(world, blockPos) != lightOld
World -[#blue]> World: profiler.startSection("checkLight")
World -[#blue]> World: checkLight(blockPos)
World -[#blue]> World: profiler.endSection()
end
alt blockSnapshot == null
World -[#blue]> World: markAndNotifyBlock(blockPos, chunk, blockStateEffective, blockStateNew, flags)
end
note right: Don't notify clients or update physics while capturing blockstates
break #lime return false
end
end
World <-[#blue]- World
deactivate World
@enduml

View file

@ -183,6 +183,17 @@ public abstract class BlockAbstractContainer extends BlockContainer implements I
}
}
@Override
public void breakBlock(final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
assert world != null;
// cascade to tile entity before it's removed
final TileEntity tileEntity = world.getTileEntity(blockPos);
if (tileEntity instanceof TileEntityAbstractBase) {
((TileEntityAbstractBase) tileEntity).onBlockBroken(world, blockPos, blockState);
}
super.breakBlock(world, blockPos, blockState);
}
@Nonnull
@Override
public ItemStack getPickBlock(@Nonnull final IBlockState blockState, final RayTraceResult target, @Nonnull final World world, @Nonnull final BlockPos blockPos, final EntityPlayer entityPlayer) {

View file

@ -111,6 +111,10 @@ public abstract class TileEntityAbstractBase extends TileEntity implements IBloc
}
}
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
}
protected <T extends Comparable<T>, V extends T> void updateBlockState(final IBlockState blockState_in, final IProperty<T> property, final V value) {
IBlockState blockState = blockState_in;
if (blockState == null) {

View file

@ -8,8 +8,11 @@ import cr0s.warpdrive.event.ChunkLoadingHandler;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import net.minecraft.block.state.IBlockState;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeChunkManager.Ticket;
@ -145,12 +148,12 @@ public abstract class TileEntityAbstractChunkLoading extends TileEntityAbstractE
}
@Override
public void invalidate() {
super.invalidate();
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
if (ticket != null) {
ChunkLoadingHandler.forgeTicket_release(ticket);
ticket = null;
}
super.onBlockBroken(world, blockPos, blockState);
}
public ArrayList<ChunkPos> getChunksToLoad() {

View file

@ -12,9 +12,12 @@ import li.cil.oc.api.machine.Arguments;
import li.cil.oc.api.machine.Callback;
import li.cil.oc.api.machine.Context;
import net.minecraft.block.state.IBlockState;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.Optional;
@ -62,9 +65,9 @@ public abstract class TileEntityAbstractForceField extends TileEntityAbstractEne
}
@Override
public void invalidate() {
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
ForceFieldRegistry.removeFromRegistry(this);
super.invalidate();
super.onBlockBroken(world, blockPos, blockState);
}
@Override

View file

@ -50,6 +50,7 @@ import net.minecraft.util.EnumFacing;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fluids.Fluid;
@ -285,14 +286,14 @@ public class TileEntityForceFieldProjector extends TileEntityAbstractForceField
}
@Override
public void invalidate() {
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
destroyForceField();
try {
doScheduledForceFieldRemoval();
} catch (final Exception exception) {
exception.printStackTrace();
}
super.invalidate();
super.onBlockBroken(world, blockPos, blockState);
}
public boolean isValid() {

View file

@ -20,6 +20,8 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.relauncher.Side;
@ -199,11 +201,11 @@ public class TileEntityJumpGateCore extends TileEntityAbstractEnergyCoreOrContro
}
@Override
public void invalidate() {
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
if (!world.isRemote) {
WarpDrive.starMap.removeFromRegistry(this);
}
super.invalidate();
super.onBlockBroken(world, blockPos, blockState);
}
// IStarMapRegistryTileEntity overrides

View file

@ -54,6 +54,7 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler;
@ -1200,11 +1201,11 @@ public class TileEntityShipCore extends TileEntityAbstractShipController impleme
}
@Override
public void invalidate() {
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
if (!world.isRemote) {
WarpDrive.starMap.removeFromRegistry(this);
}
super.invalidate();
super.onBlockBroken(world, blockPos, blockState);
}
@Override

View file

@ -337,12 +337,12 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergyCoreOrCon
}
@Override
public void invalidate() {
public void onBlockBroken(@Nonnull final World world, @Nonnull final BlockPos blockPos, @Nonnull final IBlockState blockState) {
if (!world.isRemote) {
rebootTransporter();
WarpDrive.starMap.removeFromRegistry(this);
}
super.invalidate();
super.onBlockBroken(world, blockPos, blockState);
}
private void rebootTransporter() {