Use a separate object for tracking TickScheduler state

This allows us to use non-TileGeneric block entities. This is a clever
trick which will help us later!
This commit is contained in:
Jonathan Coates 2022-10-28 23:40:55 +01:00
parent 562f224c01
commit 1f910ee2ba
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
7 changed files with 47 additions and 31 deletions

View File

@ -19,17 +19,9 @@
import net.minecraftforge.common.util.Constants;
import javax.annotation.Nonnull;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class TileGeneric extends TileEntity
{
/**
* Is this block enqueued to be updated next tick? This should only be read/written by the tick scheduler.
*
* @see dan200.computercraft.shared.util.TickScheduler
*/
public final AtomicBoolean scheduled = new AtomicBoolean();
public TileGeneric( TileEntityType<? extends TileGeneric> type )
{
super( type );

View File

@ -89,8 +89,9 @@ protected void detachPeripheral( String name )
private final WiredModemElement cable = new CableElement();
private LazyOptional<IWiredElement> elementCap;
private final IWiredNode node = cable.getNode();
private final TickScheduler.Token tickToken = new TickScheduler.Token( this );
private final WiredModemPeripheral modem = new WiredModemPeripheral(
new ModemState( () -> TickScheduler.schedule( this ) ),
new ModemState( () -> TickScheduler.schedule( tickToken ) ),
cable
)
{
@ -170,7 +171,7 @@ protected void invalidateCaps()
public void onLoad()
{
super.onLoad();
TickScheduler.schedule( this );
TickScheduler.schedule( tickToken );
}
@Override
@ -246,7 +247,7 @@ private void queueRefreshPeripheral()
{
if( invalidPeripheral ) return;
invalidPeripheral = true;
TickScheduler.schedule( this );
TickScheduler.schedule( tickToken );
}
private void refreshPeripheral()

View File

@ -100,7 +100,8 @@ public Vector3d getPosition()
private boolean destroyed = false;
private boolean connectionsFormed = false;
private final ModemState modemState = new ModemState( () -> TickScheduler.schedule( this ) );
private final TickScheduler.Token tickToken = new TickScheduler.Token( this );
private final ModemState modemState = new ModemState( () -> TickScheduler.schedule( tickToken ) );
private final WiredModemElement element = new FullElement( this );
private LazyOptional<IWiredElement> elementCap;
private final IWiredNode node = element.getNode();
@ -181,7 +182,7 @@ public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
private void queueRefreshPeripheral( @Nonnull Direction facing )
{
if( invalidSides == 0 ) TickScheduler.schedule( this );
if( invalidSides == 0 ) TickScheduler.schedule( tickToken );
invalidSides |= 1 << facing.ordinal();
}
@ -263,7 +264,7 @@ private void updateBlockState()
public void onLoad()
{
super.onLoad();
TickScheduler.schedule( this );
TickScheduler.schedule( tickToken );
}
@Override

View File

@ -32,7 +32,7 @@ private static class Peripheral extends WirelessModemPeripheral
Peripheral( TileWirelessModem entity )
{
super( new ModemState( () -> TickScheduler.schedule( entity ) ), entity.advanced );
super( new ModemState( () -> TickScheduler.schedule( entity.tickToken ) ), entity.advanced );
this.entity = entity;
}
@ -71,6 +71,7 @@ public Object getTarget()
private final ModemPeripheral modem;
private boolean destroyed = false;
private LazyOptional<IPeripheral> modemCap;
private final TickScheduler.Token tickToken = new TickScheduler.Token( this );
public TileWirelessModem( TileEntityType<? extends TileWirelessModem> type, boolean advanced )
{
@ -83,7 +84,7 @@ public TileWirelessModem( TileEntityType<? extends TileWirelessModem> type, bool
public void onLoad()
{
super.onLoad();
TickScheduler.schedule( this );
TickScheduler.schedule( tickToken );
}
@Override

View File

@ -64,7 +64,7 @@ synchronized void rebuild()
private void markChanged()
{
if( !changed.getAndSet( true ) ) TickScheduler.schedule( origin );
if( !changed.getAndSet( true ) ) TickScheduler.schedule( origin.tickToken );
}
int getTextScale()

View File

@ -75,6 +75,8 @@ public class TileMonitor extends TileGeneric
private int bbX, bbY, bbWidth, bbHeight;
private AxisAlignedBB boundingBox;
TickScheduler.Token tickToken = new TickScheduler.Token( this );
public TileMonitor( TileEntityType<? extends TileMonitor> type, boolean advanced )
{
super( type );
@ -86,7 +88,7 @@ public void onLoad()
{
super.onLoad();
needsValidating = true; // Same, tbh
TickScheduler.schedule( this );
TickScheduler.schedule( tickToken );
}
@Override

View File

@ -6,7 +6,7 @@
package dan200.computercraft.shared.util;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.common.TileGeneric;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ITickList;
import net.minecraft.world.World;
@ -16,6 +16,7 @@
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A thread-safe version of {@link ITickList#scheduleTick(BlockPos, Object, int)}.
@ -29,12 +30,12 @@ private TickScheduler()
{
}
private static final Queue<TileGeneric> toTick = new ConcurrentLinkedDeque<>();
private static final Queue<Token> toTick = new ConcurrentLinkedDeque<>();
public static void schedule( TileGeneric tile )
public static void schedule( Token token )
{
World world = tile.getLevel();
if( world != null && !world.isClientSide && !tile.scheduled.getAndSet( true ) ) toTick.add( tile );
World world = token.owner.getLevel();
if( world != null && !world.isClientSide && !token.scheduled.getAndSet( true ) ) toTick.add( token );
}
@SubscribeEvent
@ -42,19 +43,37 @@ public static void tick( TickEvent.ServerTickEvent event )
{
if( event.phase != TickEvent.Phase.START ) return;
TileGeneric tile;
while( (tile = toTick.poll()) != null )
Token token;
while( (token = toTick.poll()) != null )
{
tile.scheduled.set( false );
if( tile.isRemoved() ) continue;
token.scheduled.set( false );
TileEntity blockEntity = token.owner;
if( blockEntity.isRemoved() ) continue;
World world = tile.getLevel();
BlockPos pos = tile.getBlockPos();
World world = blockEntity.getLevel();
BlockPos pos = blockEntity.getBlockPos();
if( world != null && pos != null && world.isAreaLoaded( pos, 0 ) && world.getBlockEntity( pos ) == tile )
if( world != null && world.isAreaLoaded( pos, 0 ) && world.getBlockEntity( pos ) == blockEntity )
{
world.getBlockTicks().scheduleTick( pos, tile.getBlockState().getBlock(), 0 );
world.getBlockTicks().scheduleTick( pos, blockEntity.getBlockState().getBlock(), 0 );
}
}
}
/**
* An item which can be scheduled for future ticking.
* <p>
* This tracks whether the {@link TileEntity} is queued or not, as this is more efficient than maintaining a set.
* As such, it should be unique per {@link TileEntity} instance to avoid it being queued multiple times.
*/
public static class Token
{
final TileEntity owner;
final AtomicBoolean scheduled = new AtomicBoolean();
public Token( TileEntity owner )
{
this.owner = owner;
}
}
}