1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-28 09:54:47 +00:00

Use a queue rather than a set in TickScheduler

We now track whether a tile is enqueued or not via an atomic boolean on
the block entity itself, rather than using a hash set. This is
significantly faster (>10x).

This is mostly intended for monitors, as they're the only peripheral
likely to call TickScheduler.schedule lots of times (rather than modems,
which just invoke it when opening/closing a channel[^1])[^2]. This
change is enough to allow me to update 120 monitors each tick without
any major tearing.

[^1]: GPS does do this on each gps.locate call, so it will help there,
but modems are typically limited by other computers sending messages,
not peripheral calls themselves.

[^2]: Note that montitors implement their own change tracking, so still
only call this once per tick. But it's enough to introduce some latency!
This commit is contained in:
Jonathan Coates 2022-05-30 14:25:51 +01:00
parent 1fd57a874f
commit 4411756b06
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
2 changed files with 16 additions and 15 deletions

View File

@ -19,9 +19,17 @@ import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.Constants;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class TileGeneric extends TileEntity 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 ) public TileGeneric( TileEntityType<? extends TileGeneric> type )
{ {
super( type ); super( type );

View File

@ -5,10 +5,8 @@
*/ */
package dan200.computercraft.shared.util; package dan200.computercraft.shared.util;
import com.google.common.collect.MapMaker;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.common.TileGeneric;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ITickList; import net.minecraft.world.ITickList;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -16,9 +14,8 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import java.util.Collections; import java.util.Queue;
import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.Set;
/** /**
* A thread-safe version of {@link ITickList#scheduleTick(BlockPos, Object, int)}. * A thread-safe version of {@link ITickList#scheduleTick(BlockPos, Object, int)}.
@ -32,16 +29,12 @@ public final class TickScheduler
{ {
} }
private static final Set<TileEntity> toTick = Collections.newSetFromMap( private static final Queue<TileGeneric> toTick = new ConcurrentLinkedDeque<>();
new MapMaker()
.weakKeys()
.makeMap()
);
public static void schedule( TileGeneric tile ) public static void schedule( TileGeneric tile )
{ {
World world = tile.getLevel(); World world = tile.getLevel();
if( world != null && !world.isClientSide ) toTick.add( tile ); if( world != null && !world.isClientSide && !tile.scheduled.getAndSet( true ) ) toTick.add( tile );
} }
@SubscribeEvent @SubscribeEvent
@ -49,11 +42,11 @@ public final class TickScheduler
{ {
if( event.phase != TickEvent.Phase.START ) return; if( event.phase != TickEvent.Phase.START ) return;
Iterator<TileEntity> iterator = toTick.iterator(); TileGeneric tile;
while( iterator.hasNext() ) while( (tile = toTick.poll()) != null )
{ {
TileEntity tile = iterator.next(); tile.scheduled.set( false );
iterator.remove(); if( tile.isRemoved() ) continue;
World world = tile.getLevel(); World world = tile.getLevel();
BlockPos pos = tile.getBlockPos(); BlockPos pos = tile.getBlockPos();