--- /dev/null
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -1,0 +_,770 @@
+package io.papermc.paper.threadedregions;
+
+import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet;
+import ca.spottedleaf.moonrise.common.list.ReferenceList;
+import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
+import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
+import ca.spottedleaf.moonrise.common.util.TickThread;
+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
+import com.mojang.logging.LogUtils;
+import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
+import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
+import net.minecraft.CrashReport;
+import net.minecraft.ReportedException;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.Connection;
+import net.minecraft.network.PacketSendListener;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.MutableComponent;
+import net.minecraft.network.protocol.common.ClientboundDisconnectPacket;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ServerChunkCache;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.server.network.ServerGamePacketListenerImpl;
+import net.minecraft.util.VisibleForDebug;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.village.VillageSiege;
+import net.minecraft.world.entity.item.ItemEntity;
+import net.minecraft.world.level.BlockEventData;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.Explosion;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.NaturalSpawner;
+import net.minecraft.world.level.ServerExplosion;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.RedStoneWireBlock;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.entity.TickingBlockEntity;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.pathfinder.PathTypeCache;
+import net.minecraft.world.level.redstone.CollectingNeighborUpdater;
+import net.minecraft.world.level.redstone.NeighborUpdater;
+import net.minecraft.world.ticks.LevelTicks;
+import org.bukkit.craftbukkit.block.CraftBlockState;
+import org.slf4j.Logger;
+import javax.annotation.Nullable;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public final class RegionizedWorldData {
+
+    private static final Logger LOGGER = LogUtils.getLogger();
+
+    private static final Entity[] EMPTY_ENTITY_ARRAY = new Entity[0];
+
+    public static final RegionizedData.RegioniserCallback<RegionizedWorldData> REGION_CALLBACK = new RegionizedData.RegioniserCallback<>() {
+        @Override
+        public void merge(final RegionizedWorldData from, final RegionizedWorldData into, final long fromTickOffset) {
+            // connections
+            for (final Connection conn : from.connections) {
+                into.connections.add(conn);
+            }
+            // time
+            final long fromRedstoneTimeOffset = into.redstoneTime - from.redstoneTime;
+            // entities
+            for (final ServerPlayer player : from.localPlayers) {
+                into.localPlayers.add(player);
+                into.nearbyPlayers.addPlayer(player);
+            }
+            for (final Entity entity : from.allEntities) {
+                into.allEntities.add(entity);
+                entity.updateTicks(fromTickOffset, fromRedstoneTimeOffset);
+            }
+            for (final Entity entity : from.loadedEntities) {
+                into.loadedEntities.add(entity);
+            }
+            for (final Iterator<Entity> iterator = from.entityTickList.unsafeIterator(); iterator.hasNext();) {
+                into.entityTickList.add(iterator.next());
+            }
+            for (final Iterator<Mob> iterator = from.navigatingMobs.unsafeIterator(); iterator.hasNext();) {
+                into.navigatingMobs.add(iterator.next());
+            }
+            for (final Iterator<Entity> iterator = from.trackerEntities.iterator(); iterator.hasNext();) {
+                into.trackerEntities.add(iterator.next());
+            }
+            for (final Iterator<Entity> iterator = from.trackerUnloadedEntities.iterator(); iterator.hasNext();) {
+                into.trackerUnloadedEntities.add(iterator.next());
+            }
+            // block ticking
+            into.blockEvents.addAll(from.blockEvents);
+            // ticklists use game time
+            from.blockLevelTicks.merge(into.blockLevelTicks, fromRedstoneTimeOffset);
+            from.fluidLevelTicks.merge(into.fluidLevelTicks, fromRedstoneTimeOffset);
+
+            // tile entity ticking
+            for (final TickingBlockEntity tileEntityWrapped : from.pendingBlockEntityTickers) {
+                into.pendingBlockEntityTickers.add(tileEntityWrapped);
+                final BlockEntity tileEntity = tileEntityWrapped.getTileEntity();
+                if (tileEntity != null) {
+                    tileEntity.updateTicks(fromTickOffset, fromRedstoneTimeOffset);
+                }
+            }
+            for (final TickingBlockEntity tileEntityWrapped : from.blockEntityTickers) {
+                into.blockEntityTickers.add(tileEntityWrapped);
+                final BlockEntity tileEntity = tileEntityWrapped.getTileEntity();
+                if (tileEntity != null) {
+                    tileEntity.updateTicks(fromTickOffset, fromRedstoneTimeOffset);
+                }
+            }
+
+            // ticking chunks
+            for (final Iterator<ServerChunkCache.ChunkAndHolder> iterator = from.entityTickingChunks.iterator(); iterator.hasNext();) {
+                into.entityTickingChunks.add(iterator.next());
+            }
+            for (final Iterator<ServerChunkCache.ChunkAndHolder> iterator = from.tickingChunks.iterator(); iterator.hasNext();) {
+                into.tickingChunks.add(iterator.next());
+            }
+            for (final Iterator<ServerChunkCache.ChunkAndHolder> iterator = from.chunks.iterator(); iterator.hasNext();) {
+                into.chunks.add(iterator.next());
+            }
+            // redstone torches
+            if (from.redstoneUpdateInfos != null && !from.redstoneUpdateInfos.isEmpty()) {
+                if (into.redstoneUpdateInfos == null) {
+                    into.redstoneUpdateInfos = new ArrayDeque<>();
+                }
+                for (final net.minecraft.world.level.block.RedstoneTorchBlock.Toggle info : from.redstoneUpdateInfos) {
+                    info.offsetTime(fromRedstoneTimeOffset);
+                    into.redstoneUpdateInfos.add(info);
+                }
+            }
+            // mob spawning
+            into.catSpawnerNextTick = Math.max(from.catSpawnerNextTick, into.catSpawnerNextTick);
+            into.patrolSpawnerNextTick = Math.max(from.patrolSpawnerNextTick, into.patrolSpawnerNextTick);
+            into.phantomSpawnerNextTick = Math.max(from.phantomSpawnerNextTick, into.phantomSpawnerNextTick);
+            if (from.wanderingTraderTickDelay != Integer.MIN_VALUE && into.wanderingTraderTickDelay != Integer.MIN_VALUE) {
+                into.wanderingTraderTickDelay = Math.max(from.wanderingTraderTickDelay, into.wanderingTraderTickDelay);
+                into.wanderingTraderSpawnDelay = Math.max(from.wanderingTraderSpawnDelay, into.wanderingTraderSpawnDelay);
+                into.wanderingTraderSpawnChance = Math.max(from.wanderingTraderSpawnChance, into.wanderingTraderSpawnChance);
+            }
+            // chunkHoldersToBroadcast
+            for (final ChunkHolder chunkHolder : from.chunkHoldersToBroadcast) {
+                into.chunkHoldersToBroadcast.add(chunkHolder);
+            }
+        }
+
+        @Override
+        public void split(final RegionizedWorldData from, final int chunkToRegionShift,
+                          final Long2ReferenceOpenHashMap<RegionizedWorldData> regionToData,
+                          final ReferenceOpenHashSet<RegionizedWorldData> dataSet) {
+            // connections
+            for (final Connection conn : from.connections) {
+                final ServerPlayer player = conn.getPlayer();
+                final ChunkPos pos = player.chunkPosition();
+                // Note: It is impossible for an entity in the world to _not_ be in an entity chunk, which means
+                // the chunk holder must _exist_, and so the region section exists.
+                regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift))
+                    .connections.add(conn);
+            }
+            // entities
+            for (final ServerPlayer player : from.localPlayers) {
+                final ChunkPos pos = player.chunkPosition();
+                // Note: It is impossible for an entity in the world to _not_ be in an entity chunk, which means
+                // the chunk holder must _exist_, and so the region section exists.
+                final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift));
+                into.localPlayers.add(player);
+                into.nearbyPlayers.addPlayer(player);
+            }
+            for (final Entity entity : from.allEntities) {
+                final ChunkPos pos = entity.chunkPosition();
+                // Note: It is impossible for an entity in the world to _not_ be in an entity chunk, which means
+                // the chunk holder must _exist_, and so the region section exists.
+                final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift));
+                into.allEntities.add(entity);
+                // Note: entityTickList is a subset of allEntities
+                if (from.entityTickList.contains(entity)) {
+                    into.entityTickList.add(entity);
+                }
+                // Note: loadedEntities is a subset of allEntities
+                if (from.loadedEntities.contains(entity)) {
+                    into.loadedEntities.add(entity);
+                }
+                // Note: navigatingMobs is a subset of allEntities
+                if (entity instanceof Mob mob && from.navigatingMobs.contains(mob)) {
+                    into.navigatingMobs.add(mob);
+                }
+                if (from.trackerEntities.contains(entity)) {
+                    into.trackerEntities.add(entity);
+                }
+                if (from.trackerUnloadedEntities.contains(entity)) {
+                    into.trackerUnloadedEntities.add(entity);
+                }
+            }
+            // block ticking
+            for (final BlockEventData blockEventData : from.blockEvents) {
+                final BlockPos pos = blockEventData.pos();
+                final int chunkX = pos.getX() >> 4;
+                final int chunkZ = pos.getZ() >> 4;
+
+                final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(chunkX >> chunkToRegionShift, chunkZ >> chunkToRegionShift));
+                // Unlike entities, the chunk holder is not guaranteed to exist for block events, because the block events
+                // is just some list. So if it unloads, I guess it's just lost.
+                if (into != null) {
+                    into.blockEvents.add(blockEventData);
+                }
+            }
+
+            final Long2ReferenceOpenHashMap<LevelTicks<Block>> levelTicksBlockRegionData = new Long2ReferenceOpenHashMap<>(regionToData.size(), 0.75f);
+            final Long2ReferenceOpenHashMap<LevelTicks<Fluid>> levelTicksFluidRegionData = new Long2ReferenceOpenHashMap<>(regionToData.size(), 0.75f);
+
+            for (final Iterator<Long2ReferenceMap.Entry<RegionizedWorldData>> iterator = regionToData.long2ReferenceEntrySet().fastIterator();
+                 iterator.hasNext();) {
+                final Long2ReferenceMap.Entry<RegionizedWorldData> entry = iterator.next();
+                final long key = entry.getLongKey();
+                final RegionizedWorldData worldData = entry.getValue();
+
+                levelTicksBlockRegionData.put(key, worldData.blockLevelTicks);
+                levelTicksFluidRegionData.put(key, worldData.fluidLevelTicks);
+            }
+
+            from.blockLevelTicks.split(chunkToRegionShift, levelTicksBlockRegionData);
+            from.fluidLevelTicks.split(chunkToRegionShift, levelTicksFluidRegionData);
+
+            // tile entity ticking
+            for (final TickingBlockEntity tileEntity : from.pendingBlockEntityTickers) {
+                final BlockPos pos = tileEntity.getPos();
+                final int chunkX = pos.getX() >> 4;
+                final int chunkZ = pos.getZ() >> 4;
+
+                final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(chunkX >> chunkToRegionShift, chunkZ >> chunkToRegionShift));
+                if (into != null) {
+                    into.pendingBlockEntityTickers.add(tileEntity);
+                } // else: when a chunk unloads, it does not actually _remove_ the tile entity from the list, it just gets
+                  //       marked as removed. So if there is no section, it's probably removed!
+            }
+            for (final TickingBlockEntity tileEntity : from.blockEntityTickers) {
+                final BlockPos pos = tileEntity.getPos();
+                final int chunkX = pos.getX() >> 4;
+                final int chunkZ = pos.getZ() >> 4;
+
+                final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(chunkX >> chunkToRegionShift, chunkZ >> chunkToRegionShift));
+                if (into != null) {
+                    into.blockEntityTickers.add(tileEntity);
+                } // else: when a chunk unloads, it does not actually _remove_ the tile entity from the list, it just gets
+                  //       marked as removed. So if there is no section, it's probably removed!
+            }
+            // time
+            for (final RegionizedWorldData regionizedWorldData : dataSet) {
+                regionizedWorldData.redstoneTime = from.redstoneTime;
+            }
+            // ticking chunks
+            for (final Iterator<ServerChunkCache.ChunkAndHolder> iterator = from.entityTickingChunks.iterator(); iterator.hasNext();) {
+                final ServerChunkCache.ChunkAndHolder holder = iterator.next();
+                final ChunkPos pos = holder.chunk().getPos();
+
+                // Impossible for get() to return null, as the chunk is entity ticking - thus the chunk holder is loaded
+                regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift))
+                    .entityTickingChunks.add(holder);
+            }
+            for (final Iterator<ServerChunkCache.ChunkAndHolder> iterator = from.tickingChunks.iterator(); iterator.hasNext();) {
+                final ServerChunkCache.ChunkAndHolder holder = iterator.next();
+                final ChunkPos pos = holder.chunk().getPos();
+
+                // Impossible for get() to return null, as the chunk is entity ticking - thus the chunk holder is loaded
+                regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift))
+                    .tickingChunks.add(holder);
+            }
+            for (final Iterator<ServerChunkCache.ChunkAndHolder> iterator = from.chunks.iterator(); iterator.hasNext();) {
+                final ServerChunkCache.ChunkAndHolder holder = iterator.next();
+                final ChunkPos pos = holder.chunk().getPos();
+
+                // Impossible for get() to return null, as the chunk is entity ticking - thus the chunk holder is loaded
+                regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift))
+                    .chunks.add(holder);
+            }
+
+            // redstone torches
+            if (from.redstoneUpdateInfos != null && !from.redstoneUpdateInfos.isEmpty()) {
+                for (final net.minecraft.world.level.block.RedstoneTorchBlock.Toggle info : from.redstoneUpdateInfos) {
+                    final BlockPos pos = info.pos;
+
+                    final RegionizedWorldData worldData = regionToData.get(CoordinateUtils.getChunkKey((pos.getX() >> 4) >> chunkToRegionShift, (pos.getZ() >> 4) >> chunkToRegionShift));
+                    if (worldData != null) {
+                        if (worldData.redstoneUpdateInfos == null) {
+                            worldData.redstoneUpdateInfos = new ArrayDeque<>();
+                        }
+                        worldData.redstoneUpdateInfos.add(info);
+                    } // else: chunk unloaded
+                }
+            }
+            // mob spawning
+            for (final RegionizedWorldData regionizedWorldData : dataSet) {
+                regionizedWorldData.catSpawnerNextTick = from.catSpawnerNextTick;
+                regionizedWorldData.patrolSpawnerNextTick = from.patrolSpawnerNextTick;
+                regionizedWorldData.phantomSpawnerNextTick = from.phantomSpawnerNextTick;
+                regionizedWorldData.wanderingTraderTickDelay = from.wanderingTraderTickDelay;
+                regionizedWorldData.wanderingTraderSpawnChance = from.wanderingTraderSpawnChance;
+                regionizedWorldData.wanderingTraderSpawnDelay = from.wanderingTraderSpawnDelay;
+                regionizedWorldData.villageSiegeState = new VillageSiegeState(); // just re set it, as the spawn pos will be invalid
+            }
+            // chunkHoldersToBroadcast
+            for (final ChunkHolder chunkHolder : from.chunkHoldersToBroadcast) {
+                final ChunkPos pos = chunkHolder.getPos();
+
+                // Possible for get() to return null, as the chunk holder is not removed during unload
+                final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift));
+                if (into != null) {
+                    into.chunkHoldersToBroadcast.add(chunkHolder);
+                }
+            }
+        }
+    };
+
+    public final ServerLevel world;
+
+    private RegionizedServer.WorldLevelData tickData;
+
+    // connections
+    public final List<Connection> connections = new ArrayList<>();
+
+    // misc. fields
+    private boolean isHandlingTick;
+
+    public void setHandlingTick(final boolean to) {
+        this.isHandlingTick = to;
+    }
+
+    public boolean isHandlingTick() {
+        return this.isHandlingTick;
+    }
+
+    // entities
+    private final List<ServerPlayer> localPlayers = new ArrayList<>();
+    private final NearbyPlayers nearbyPlayers;
+    private final ReferenceList<Entity> allEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY);
+    private final ReferenceList<Entity> loadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY);
+    private final IteratorSafeOrderedReferenceSet<Entity> entityTickList = new IteratorSafeOrderedReferenceSet<>();
+    private final IteratorSafeOrderedReferenceSet<Mob> navigatingMobs = new IteratorSafeOrderedReferenceSet<>();
+    public final ReferenceList<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
+    public final ReferenceList<Entity> trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
+
+    // block ticking
+    private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
+    private final LevelTicks<Block> blockLevelTicks;
+    private final LevelTicks<Fluid> fluidLevelTicks;
+
+    // tile entity ticking
+    private final List<TickingBlockEntity> pendingBlockEntityTickers = new ArrayList<>();
+    private final List<TickingBlockEntity> blockEntityTickers = new ArrayList<>();
+    private boolean tickingBlockEntities;
+
+    // time
+    private long redstoneTime = 1L;
+
+    public long getRedstoneGameTime() {
+        return this.redstoneTime;
+    }
+
+    public void setRedstoneGameTime(final long to) {
+        this.redstoneTime = to;
+    }
+
+    // ticking chunks
+    private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDER_ARRAY = new ServerChunkCache.ChunkAndHolder[0];
+    private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDER_ARRAY);
+    private final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDER_ARRAY);
+    private final ReferenceList<ServerChunkCache.ChunkAndHolder> chunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDER_ARRAY);
+
+    // Paper/CB api hook misc
+    // don't bother to merge/split these, no point
+    // From ServerLevel
+    public boolean hasPhysicsEvent = true; // Paper
+    public boolean hasEntityMoveEvent = false; // Paper
+    // Paper start - Optimize Hoppers
+    public boolean skipPullModeEventFire = false;
+    public boolean skipPushModeEventFire = false;
+    public boolean skipHopperEvents = false;
+    // Paper end - Optimize Hoppers
+    public long lastMidTickExecute;
+    public long lastMidTickExecuteFailure;
+    // From Level
+    public boolean populating;
+    public final NeighborUpdater neighborUpdater;
+    public boolean preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
+    public boolean captureBlockStates = false;
+    public boolean captureTreeGeneration = false;
+    public boolean isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
+    public final Map<BlockPos, CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
+    public final Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper
+    public List<ItemEntity> captureDrops;
+    // Paper start
+    public int wakeupInactiveRemainingAnimals;
+    public int wakeupInactiveRemainingFlying;
+    public int wakeupInactiveRemainingMonsters;
+    public int wakeupInactiveRemainingVillagers;
+    // Paper end
+    public int currentPrimedTnt = 0; // Spigot
+    @Nullable
+    @VisibleForDebug
+    public NaturalSpawner.SpawnState lastSpawnState;
+    public boolean shouldSignal = true;
+    public final Map<ServerExplosion.CacheKey, Float> explosionDensityCache = new HashMap<>(64, 0.25f);
+    public final PathTypeCache pathTypesByPosCache = new PathTypeCache();
+    public final List<LevelChunk> temporaryChunkTickList = new java.util.ArrayList<>();
+    public final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceLinkedOpenHashSet<>();
+
+    // not transient
+    public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos;
+
+    // Mob spawning
+    public final ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap<ServerPlayer> spawnChunkTracker = new ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap<>();
+    public int catSpawnerNextTick = 0;
+    public int patrolSpawnerNextTick = 0;
+    public int phantomSpawnerNextTick = 0;
+    public int wanderingTraderTickDelay = Integer.MIN_VALUE;
+    public int wanderingTraderSpawnDelay;
+    public int wanderingTraderSpawnChance;
+    public VillageSiegeState villageSiegeState = new VillageSiegeState();
+
+    public static final class VillageSiegeState {
+        public boolean hasSetupSiege;
+        public VillageSiege.State siegeState = VillageSiege.State.SIEGE_DONE;
+        public int zombiesToSpawn;
+        public int nextSpawnTime;
+        public int spawnX;
+        public int spawnY;
+        public int spawnZ;
+    }
+    // Redstone
+    public final alternate.current.wire.WireHandler wireHandler;
+    public final io.papermc.paper.redstone.RedstoneWireTurbo turbo;
+
+    public RegionizedWorldData(final ServerLevel world) {
+        this.world = world;
+        this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true);
+        this.fluidLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, false);
+        this.neighborUpdater = new CollectingNeighborUpdater(world, world.neighbourUpdateMax);
+        this.nearbyPlayers = new NearbyPlayers(world);
+        this.wireHandler = new alternate.current.wire.WireHandler(world);
+        this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
+
+        // tasks may be drained before the region ticks, so we must set up the tick data early just in case
+        this.updateTickData();
+    }
+
+    public void checkWorld(final Level against) {
+        if (this.world != against) {
+            throw new IllegalStateException("World mismatch: expected " + this.world.getWorld().getName() + " but got " + (against == null ? "null" : against.getWorld().getName()));
+        }
+    }
+
+    public RegionizedServer.WorldLevelData getTickData() {
+        return this.tickData;
+    }
+
+    private long lagCompensationTick;
+
+    public long getLagCompensationTick() {
+        return this.lagCompensationTick;
+    }
+
+    public void updateTickData() {
+        this.tickData = this.world.tickData;
+        this.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
+        this.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
+        this.skipHopperEvents = this.world.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
+        // always subtract from server init so that the tick starts at zero, allowing us to cast to int without much worry
+        this.lagCompensationTick = (System.nanoTime() - MinecraftServer.SERVER_INIT) / TickRegionScheduler.TIME_BETWEEN_TICKS;
+    }
+
+    public NearbyPlayers getNearbyPlayers() {
+        return this.nearbyPlayers;
+    }
+
+    private static void cleanUpConnection(final Connection conn) {
+        // note: ALL connections HERE have a player
+        final ServerPlayer player = conn.getPlayer();
+        // now that the connection is removed, we can allow this region to die
+        player.serverLevel().chunkSource.removeTicketAtLevel(
+            ServerGamePacketListenerImpl.DISCONNECT_TICKET, player.connection.disconnectPos,
+            ChunkHolderManager.MAX_TICKET_LEVEL,
+            player.connection.disconnectTicketId
+        );
+    }
+
+    // connections
+    public void tickConnections() {
+        final List<Connection> connections = new ArrayList<>(this.connections);
+        Collections.shuffle(connections);
+        for (final Connection conn : connections) {
+            if (!conn.isConnected()) {
+                conn.handleDisconnection();
+                // global tick thread will not remove connections not owned by it, so we need to
+                RegionizedServer.getInstance().removeConnection(conn);
+                this.connections.remove(conn);
+                cleanUpConnection(conn);
+                continue;
+            }
+            if (!this.connections.contains(conn)) {
+                // removed by connection tick?
+                continue;
+            }
+
+            try {
+                conn.tick();
+            } catch (final Exception exception) {
+                if (conn.isMemoryConnection()) {
+                    throw new ReportedException(CrashReport.forThrowable(exception, "Ticking memory connection"));
+                }
+
+                LOGGER.warn("Failed to handle packet for {}", conn.getLoggableAddress(MinecraftServer.getServer().logIPs()), exception);
+                MutableComponent ichatmutablecomponent = Component.literal("Internal server error");
+
+                conn.send(new ClientboundDisconnectPacket(ichatmutablecomponent), PacketSendListener.thenRun(() -> {
+                    conn.disconnect(ichatmutablecomponent);
+                }));
+                conn.setReadOnly();
+                continue;
+            }
+        }
+    }
+
+    // entities hooks
+    public int getEntityCount() {
+        return this.allEntities.size();
+    }
+
+    public int getPlayerCount() {
+        return this.localPlayers.size();
+    }
+
+    public Iterable<Entity> getLocalEntities() {
+        return this.allEntities;
+    }
+
+    public Entity[] getLocalEntitiesCopy() {
+        return Arrays.copyOf(this.allEntities.getRawData(), this.allEntities.size(), Entity[].class);
+    }
+
+    public List<ServerPlayer> getLocalPlayers() {
+        return this.localPlayers;
+    }
+
+    public void addLoadedEntity(final Entity entity) {
+        this.loadedEntities.add(entity);
+    }
+
+    public boolean hasLoadedEntity(final Entity entity) {
+        return this.loadedEntities.contains(entity);
+    }
+
+    public void removeLoadedEntity(final Entity entity) {
+        this.loadedEntities.remove(entity);
+    }
+
+    public Iterable<Entity> getLoadedEntities() {
+        return this.loadedEntities;
+    }
+
+    public void addEntityTickingEntity(final Entity entity) {
+        if (!TickThread.isTickThreadFor(entity)) {
+            throw new IllegalArgumentException("Entity " + entity + " is not under this region's control");
+        }
+        this.entityTickList.add(entity);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public boolean hasEntityTickingEntity(final Entity entity) {
+        return this.entityTickList.contains(entity);
+    }
+
+    public void removeEntityTickingEntity(final Entity entity) {
+        if (!TickThread.isTickThreadFor(entity)) {
+            throw new IllegalArgumentException("Entity " + entity + " is not under this region's control");
+        }
+        this.entityTickList.remove(entity);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public void forEachTickingEntity(final Consumer<Entity> action) {
+        final IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entityTickList.iterator();
+        try {
+            while (iterator.hasNext()) {
+                action.accept(iterator.next());
+            }
+        } finally {
+            iterator.finishedIterating();
+        }
+    }
+
+    public void addEntity(final Entity entity) {
+        if (!TickThread.isTickThreadFor(this.world, entity.chunkPosition())) {
+            throw new IllegalArgumentException("Entity " + entity + " is not under this region's control");
+        }
+        if (this.allEntities.add(entity)) {
+            if (entity instanceof ServerPlayer player) {
+                this.localPlayers.add(player);
+            }
+            TickRegions.RegionStats.updateCurrentRegion();
+        }
+    }
+
+    public boolean hasEntity(final Entity entity) {
+        return this.allEntities.contains(entity);
+    }
+
+    public void removeEntity(final Entity entity) {
+        if (!TickThread.isTickThreadFor(entity)) {
+            throw new IllegalArgumentException("Entity " + entity + " is not under this region's control");
+        }
+        if (this.allEntities.remove(entity)) {
+            if (entity instanceof ServerPlayer player) {
+                this.localPlayers.remove(player);
+            }
+            TickRegions.RegionStats.updateCurrentRegion();
+        }
+    }
+
+    public void addNavigatingMob(final Mob mob) {
+        if (!TickThread.isTickThreadFor(mob)) {
+            throw new IllegalArgumentException("Entity " + mob + " is not under this region's control");
+        }
+        this.navigatingMobs.add(mob);
+    }
+
+    public void removeNavigatingMob(final Mob mob) {
+        if (!TickThread.isTickThreadFor(mob)) {
+            throw new IllegalArgumentException("Entity " + mob + " is not under this region's control");
+        }
+        this.navigatingMobs.remove(mob);
+    }
+
+    public Iterator<Mob> getNavigatingMobs() {
+        return this.navigatingMobs.unsafeIterator();
+    }
+
+    // block ticking hooks
+    // Since block event data does not require chunk holders to be created for the chunk they reside in,
+    // it's not actually guaranteed that when merging / splitting data that we actually own the data...
+    // Note that we can only ever not own the event data when the chunk unloads, and so I've decided to
+    // make the code easier by simply discarding it in such an event
+    public void pushBlockEvent(final BlockEventData blockEventData) {
+        TickThread.ensureTickThread(this.world, blockEventData.pos(), "Cannot queue block even data async");
+        this.blockEvents.add(blockEventData);
+    }
+
+    public void pushBlockEvents(final Collection<? extends BlockEventData> blockEvents) {
+        for (final BlockEventData blockEventData : blockEvents) {
+            this.pushBlockEvent(blockEventData);
+        }
+    }
+
+    public void removeIfBlockEvents(final Predicate<? super BlockEventData> predicate) {
+        for (final Iterator<BlockEventData> iterator = this.blockEvents.iterator(); iterator.hasNext();) {
+            final BlockEventData blockEventData = iterator.next();
+            if (predicate.test(blockEventData)) {
+                iterator.remove();
+            }
+        }
+    }
+
+    public BlockEventData removeFirstBlockEvent() {
+        BlockEventData ret;
+        while (!this.blockEvents.isEmpty()) {
+            ret = this.blockEvents.removeFirst();
+            if (TickThread.isTickThreadFor(this.world, ret.pos())) {
+                return ret;
+            } // else: chunk must have been unloaded
+        }
+
+        return null;
+    }
+
+    public LevelTicks<Block> getBlockLevelTicks() {
+        return this.blockLevelTicks;
+    }
+
+    public LevelTicks<Fluid> getFluidLevelTicks() {
+        return this.fluidLevelTicks;
+    }
+
+    // tile entity ticking
+    public void addBlockEntityTicker(final TickingBlockEntity ticker) {
+        TickThread.ensureTickThread(this.world, ticker.getPos(), "Tile entity must be owned by current region");
+
+        (this.tickingBlockEntities ? this.pendingBlockEntityTickers : this.blockEntityTickers).add(ticker);
+    }
+
+    public void seTtickingBlockEntities(final boolean to) {
+        this.tickingBlockEntities = true;
+    }
+
+    public List<TickingBlockEntity> getBlockEntityTickers() {
+        return this.blockEntityTickers;
+    }
+
+    public void pushPendingTickingBlockEntities() {
+        if (!this.pendingBlockEntityTickers.isEmpty()) {
+            this.blockEntityTickers.addAll(this.pendingBlockEntityTickers);
+            this.pendingBlockEntityTickers.clear();
+        }
+    }
+
+    // ticking chunks
+    public void addEntityTickingChunk(final ServerChunkCache.ChunkAndHolder holder) {
+        this.entityTickingChunks.add(holder);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public void removeEntityTickingChunk(final ServerChunkCache.ChunkAndHolder holder) {
+        this.entityTickingChunks.remove(holder);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public ReferenceList<ServerChunkCache.ChunkAndHolder> getEntityTickingChunks() {
+        return this.entityTickingChunks;
+    }
+
+    public void addTickingChunk(final ServerChunkCache.ChunkAndHolder holder) {
+        this.tickingChunks.add(holder);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public void removeTickingChunk(final ServerChunkCache.ChunkAndHolder holder) {
+        this.tickingChunks.remove(holder);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public ReferenceList<ServerChunkCache.ChunkAndHolder> getTickingChunks() {
+        return this.tickingChunks;
+    }
+
+    public void addChunk(final ServerChunkCache.ChunkAndHolder holder) {
+        this.chunks.add(holder);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public void removeChunk(final ServerChunkCache.ChunkAndHolder holder) {
+        this.chunks.remove(holder);
+        TickRegions.RegionStats.updateCurrentRegion();
+    }
+
+    public ReferenceList<ServerChunkCache.ChunkAndHolder> getChunks() {
+        return this.chunks;
+    }
+
+    public int getEntityTickingChunkCount() {
+        return this.entityTickingChunks.size();
+    }
+
+    public int getChunkCount() {
+        return this.chunks.size();
+    }
+}
