From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Sat, 1 Feb 2025 00:33:03 +0300 Subject: [PATCH] Chunk System optimization diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..b588449cfe766c14a0cf4ea9640b04a51bbcf433 100644 --- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java @@ -59,12 +59,15 @@ public final class NearbyPlayers { public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4); private final ServerLevel world; - private final Reference2ReferenceOpenHashMap players = new Reference2ReferenceOpenHashMap<>(); - private final Long2ReferenceOpenHashMap byChunk = new Long2ReferenceOpenHashMap<>(); - private final Long2ReferenceOpenHashMap>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES]; + // DivineMC start - Chunk System optimization + private final Object callbackLock = new Object(); + private final it.unimi.dsi.fastutil.objects.Reference2ReferenceMap players = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); + private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>()); + private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap>[] directByChunk = new it.unimi.dsi.fastutil.longs.Long2ReferenceMap[TOTAL_MAP_TYPES]; + // DivineMC end - Chunk System optimization { for (int i = 0; i < this.directByChunk.length; ++i) { - this.directByChunk[i] = new Long2ReferenceOpenHashMap<>(); + this.directByChunk[i] = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>()); // DivineMC - Chunk System optimization } } @@ -188,7 +191,10 @@ public final class NearbyPlayers { final ReferenceList list = this.players[idx]; if (list == null) { ++this.nonEmptyLists; - final ReferenceList players = (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)); + // DivineMC start - Chunk System optimization + this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY); + final ReferenceList players = this.players[idx]; + // DivineMC end - Chunk System optimization this.nearbyPlayers.directByChunk[idx].put(this.chunkKey, players); players.add(player); return; diff --git a/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java b/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java index 866f38eb0f379ffbe2888023a7d1c290f521a231..08666b4aa1c7663861dc361f60e6f1cc46694521 100644 --- a/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java +++ b/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java @@ -21,13 +21,15 @@ import net.minecraft.world.level.block.state.properties.Property; public final class ZeroCollidingReferenceStateTable { - private final Int2ObjectOpenHashMap propertyToIndexer; + private final it.unimi.dsi.fastutil.ints.Int2ObjectMap propertyToIndexer; // DivineMC - Chunk System optimization private S[] lookup; private final Collection> properties; public ZeroCollidingReferenceStateTable(final Collection> properties) { - this.propertyToIndexer = new Int2ObjectOpenHashMap<>(properties.size()); - this.properties = new ReferenceArrayList<>(properties); + // DivineMC start - Chunk System optimization + this.propertyToIndexer = it.unimi.dsi.fastutil.ints.Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>(properties.size())); + this.properties = it.unimi.dsi.fastutil.objects.ReferenceLists.synchronize(new ReferenceArrayList<>(properties)); + // DivineMC end - Chunk System optimization final List> sortedProperties = new ArrayList<>(properties); @@ -77,11 +79,11 @@ public final class ZeroCollidingReferenceStateTable { return ret; } - public boolean isLoaded() { + public synchronized boolean isLoaded() { // DivineMC - Chunk System optimization return this.lookup != null; } - public void loadInTable(final Map, Comparable>, S> universe) { + public synchronized void loadInTable(final Map, Comparable>, S> universe) { // DivineMC - Chunk System optimization if (this.lookup != null) { throw new IllegalStateException(); } @@ -117,7 +119,7 @@ public final class ZeroCollidingReferenceStateTable { return ((PropertyAccess)property).moonrise$getById((int)modded); } - public > S set(final long index, final Property property, final T with) { + public synchronized > S set(final long index, final Property property, final T with) { // DivineMC - Chunk System optimization final int newValueId = ((PropertyAccess)property).moonrise$getIdFor(with); if (newValueId < 0) { return null; @@ -139,7 +141,7 @@ public final class ZeroCollidingReferenceStateTable { return this.lookup[(int)newIndex]; } - public > S trySet(final long index, final Property property, final T with, final S dfl) { + public synchronized > S trySet(final long index, final Property property, final T with, final S dfl) { // DivineMC - Chunk System optimization final Indexer indexer = this.propertyToIndexer.get(((PropertyAccess)property).moonrise$getId()); if (indexer == null) { return dfl; diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java index 8ef5a1aaac9c27873ce746eb281f77bb318a3c69..76b8d42ae530b59cdaba0583365a557da6b90ede 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java @@ -301,7 +301,7 @@ public final class RegionizedPlayerChunkLoader { return false; } - public void tick() { + public synchronized void tick() { // DivineMC - Chunk System optimization - synchronized TickThread.ensureTickThread("Cannot tick player chunk loader async"); long currTime = System.nanoTime(); for (final ServerPlayer player : new java.util.ArrayList<>(this.world.players())) { @@ -312,6 +312,7 @@ public final class RegionizedPlayerChunkLoader { } loader.update(); // can't invoke plugin logic loader.updateQueues(currTime); + player.connection.resumeFlushing(); // DivineMC - Chunk System optimization } } @@ -362,7 +363,7 @@ public final class RegionizedPlayerChunkLoader { GENERATED_TICKET_LEVEL, TICK_TICKET_LEVEL }; - private final Long2ByteOpenHashMap chunkTicketStage = new Long2ByteOpenHashMap(); + private final it.unimi.dsi.fastutil.longs.Long2ByteMap chunkTicketStage = it.unimi.dsi.fastutil.longs.Long2ByteMaps.synchronize(new Long2ByteOpenHashMap()); // DivineMC - Chunk System optimization { this.chunkTicketStage.defaultReturnValue(CHUNK_TICKET_STAGE_NONE); } @@ -384,10 +385,19 @@ public final class RegionizedPlayerChunkLoader { final int centerX = PlayerChunkLoaderData.this.lastChunkX; final int centerZ = PlayerChunkLoaderData.this.lastChunkZ; - return Integer.compare( - Math.abs(c1x - centerX) + Math.abs(c1z - centerZ), - Math.abs(c2x - centerX) + Math.abs(c2z - centerZ) - ); + // DivineMC start - Chunk Loading Priority Optimization + if (org.bxteam.divinemc.DivineConfig.chunkTaskPriority == org.bxteam.divinemc.server.chunk.ChunkTaskPriority.EUCLIDEAN_CIRCLE_PATTERN) { + return Integer.compare( + (c1x - centerX) * (c1x - centerX) + (c1z - centerZ) * (c1z - centerZ), + (c2x - centerX) * (c2x - centerX) + (c2z - centerZ) * (c2z - centerZ) + ); + } else { + return Integer.compare( + Math.abs(c1x - centerX) + Math.abs(c1z - centerZ), + Math.abs(c2x - centerX) + Math.abs(c2z - centerZ) + ); + } + // DivineMC end - Chunk Loading Priority Optimization }; private final LongHeapPriorityQueue sendQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); private final LongHeapPriorityQueue tickingQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); @@ -490,7 +500,7 @@ public final class RegionizedPlayerChunkLoader { } @Override - protected void removeCallback(final PlayerChunkLoaderData parameter, final int chunkX, final int chunkZ) { + protected synchronized void removeCallback(final PlayerChunkLoaderData parameter, final int chunkX, final int chunkZ) { // DivineMC - Chunk System optimization - synchronized final long chunk = CoordinateUtils.getChunkKey(chunkX, chunkZ); // note: by the time this is called, the tick cleanup should have ran - so, if the chunk is at // the tick stage it was deemed in range for loading. Thus, we need to move it to generated @@ -624,7 +634,7 @@ public final class RegionizedPlayerChunkLoader { return Math.max(Math.abs(dx), Math.abs(dz)) <= this.lastTickDistance; } - private boolean areNeighboursGenerated(final int chunkX, final int chunkZ, final int radius) { + private synchronized boolean areNeighboursGenerated(final int chunkX, final int chunkZ, final int radius) { // DivineMC - Chunk System optimization - synchronized for (int dz = -radius; dz <= radius; ++dz) { for (int dx = -radius; dx <= radius; ++dx) { if ((dx | dz) == 0) { @@ -643,19 +653,11 @@ public final class RegionizedPlayerChunkLoader { return true; } - void updateQueues(final long time) { + synchronized void updateQueues(final long time) { // DivineMC - Chunk System optimization - synchronized TickThread.ensureTickThread(this.player, "Cannot tick player chunk loader async"); if (this.removed) { throw new IllegalStateException("Ticking removed player chunk loader"); } - // update rate limits - final double loadRate = this.getMaxChunkLoadRate(); - final double genRate = this.getMaxChunkGenRate(); - final double sendRate = this.getMaxChunkSendRate(); - - this.chunkLoadTicketLimiter.tickAllocation(time, loadRate, loadRate); - this.chunkGenerateTicketLimiter.tickAllocation(time, genRate, genRate); - this.chunkSendLimiter.tickAllocation(time, sendRate, sendRate); // try to progress chunk loads while (!this.loadingQueue.isEmpty()) { @@ -682,8 +684,7 @@ public final class RegionizedPlayerChunkLoader { } // try to push more chunk loads - final long maxLoads = Math.max(0L, Math.min(MAX_RATE, Math.min(this.loadQueue.size(), this.getMaxChunkLoads()))); - final int maxLoadsThisTick = (int)this.chunkLoadTicketLimiter.takeAllocation(time, loadRate, maxLoads); + final int maxLoadsThisTick = this.loadQueue.size(); // DivineMC - Chunk System optimization if (maxLoadsThisTick > 0) { final LongArrayList chunks = new LongArrayList(maxLoadsThisTick); for (int i = 0; i < maxLoadsThisTick; ++i) { @@ -758,9 +759,7 @@ public final class RegionizedPlayerChunkLoader { } // try to push more chunk generations - final long maxGens = Math.max(0L, Math.min(MAX_RATE, Math.min(this.genQueue.size(), this.getMaxChunkGenerates()))); - // preview the allocations, as we may not actually utilise all of them - final long maxGensThisTick = this.chunkGenerateTicketLimiter.previewAllocation(time, genRate, maxGens); + final long maxGensThisTick = this.genQueue.size(); // DivineMC - Chunk System optimization long ratedGensThisTick = 0L; while (!this.genQueue.isEmpty()) { final long chunkKey = this.genQueue.firstLong(); @@ -790,8 +789,6 @@ public final class RegionizedPlayerChunkLoader { ); this.generatingQueue.enqueue(chunkKey); } - // take the allocations we actually used - this.chunkGenerateTicketLimiter.takeAllocation(time, genRate, ratedGensThisTick); // try to pull ticking chunks while (!this.tickingQueue.isEmpty()) { @@ -821,10 +818,10 @@ public final class RegionizedPlayerChunkLoader { } // try to pull sending chunks - final long maxSends = Math.max(0L, Math.min(MAX_RATE, Integer.MAX_VALUE)); // note: no logic to track concurrent sends - final int maxSendsThisTick = Math.min((int)this.chunkSendLimiter.takeAllocation(time, sendRate, maxSends), this.sendQueue.size()); + final int maxSendsThisTick = this.sendQueue.size(); // DivineMC - Chunk System optimization // we do not return sends that we took from the allocation back because we want to limit the max send rate, not target it for (int i = 0; i < maxSendsThisTick; ++i) { + if (this.sendQueue.isEmpty()) break; // DivineMC - Chunk System optimization final long pendingSend = this.sendQueue.firstLong(); final int pendingSendX = CoordinateUtils.getChunkX(pendingSend); final int pendingSendZ = CoordinateUtils.getChunkZ(pendingSend); @@ -889,9 +886,6 @@ public final class RegionizedPlayerChunkLoader { // reset limiters, they will start at a zero allocation final long time = System.nanoTime(); - this.chunkLoadTicketLimiter.reset(time); - this.chunkGenerateTicketLimiter.reset(time); - this.chunkSendLimiter.reset(time); // now we can update this.update(); @@ -910,10 +904,10 @@ public final class RegionizedPlayerChunkLoader { ); } - void update() { + synchronized void update() { // DivineMC - Chunk System optimization - synchronized TickThread.ensureTickThread(this.player, "Cannot update player asynchronously"); if (this.removed) { - throw new IllegalStateException("Updating removed player chunk loader"); + return; // DivineMC - Chunk System optimization } final ViewDistances playerDistances = ((ChunkSystemServerPlayer)this.player).moonrise$getViewDistanceHolder().getViewDistances(); final ViewDistances worldDistances = ((ChunkSystemServerLevel)this.world).moonrise$getViewDistanceHolder().getViewDistances(); @@ -1062,7 +1056,7 @@ public final class RegionizedPlayerChunkLoader { this.flushDelayedTicketOps(); } - void remove() { + synchronized void remove() { // DivineMC - Chunk System optimization - synchronized TickThread.ensureTickThread(this.player, "Cannot add player asynchronously"); if (this.removed) { throw new IllegalStateException("Removing removed player chunk loader"); @@ -1090,7 +1084,7 @@ public final class RegionizedPlayerChunkLoader { } public LongOpenHashSet getSentChunksRaw() { - return this.sentChunks; + return new LongOpenHashSet(this.sentChunks); // DivineMC - Chunk System optimization } } } diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java index 0c99bffa769d53562a10d23c4a9b37dc59c7f478..f4fcc3b2676b17ebc276dcb177285f18a0cdfe99 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java @@ -71,36 +71,49 @@ public final class ChunkHolderManager { private static final long PROBE_MARKER = Long.MIN_VALUE + 1; public final ReentrantAreaLock ticketLockArea; - private final ConcurrentLong2ReferenceChainedHashTable>> tickets = new ConcurrentLong2ReferenceChainedHashTable<>(); - private final ConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>(); + // DivineMC start - Chunk System optimization + private final ConcurrentLong2ReferenceChainedHashTable>> tickets = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(20, 0.9F); + private final ConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(20, 0.9F); + // DivineMC end - Chunk System optimization final ChunkUnloadQueue unloadQueue; - private final ConcurrentLong2ReferenceChainedHashTable chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); + private final ConcurrentLong2ReferenceChainedHashTable chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(20, 0.9F); // DivineMC - Chunk System optimization private final ServerLevel world; private final ChunkTaskScheduler taskScheduler; private long currentTick; - private final ArrayDeque pendingFullLoadUpdate = new ArrayDeque<>(); - private final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { - if (c1 == c2) { - return 0; - } + // DivineMC start - Chunk System optimization + public static class LevelHolderData { + private final java.util.concurrent.ConcurrentLinkedDeque pendingFullLoadUpdate = new java.util.concurrent.ConcurrentLinkedDeque<>(); + private final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { + if (c1 == c2) { + return 0; + } - final int saveTickCompare = Long.compare(c1.lastAutoSave, c2.lastAutoSave); + final int saveTickCompare = Long.compare(c1.lastAutoSave, c2.lastAutoSave); - if (saveTickCompare != 0) { - return saveTickCompare; - } + if (saveTickCompare != 0) { + return saveTickCompare; + } - final long coord1 = CoordinateUtils.getChunkKey(c1.chunkX, c1.chunkZ); - final long coord2 = CoordinateUtils.getChunkKey(c2.chunkX, c2.chunkZ); + final long coord1 = CoordinateUtils.getChunkKey(c1.chunkX, c1.chunkZ); + final long coord2 = CoordinateUtils.getChunkKey(c2.chunkX, c2.chunkZ); - if (coord1 == coord2) { - throw new IllegalStateException("Duplicate chunkholder in auto save queue"); - } + if (coord1 == coord2) { + throw new IllegalStateException("Duplicate chunkholder in auto save queue"); + } - return Long.compare(coord1, coord2); - }); + return Long.compare(coord1, coord2); + }); + } + + public LevelHolderData getData() { + if (this.world == null) { + throw new RuntimeException("World was null!"); + } + return world.chunkHolderData; + } + // DivineMC end - Chunk System optimization public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) { this.world = world; @@ -222,26 +235,29 @@ public final class ChunkHolderManager { this.taskScheduler.setShutdown(true); } - void ensureInAutosave(final NewChunkHolder holder) { - if (!this.autoSaveQueue.contains(holder)) { + // DivineMC start - Chunk System optimization + synchronized void ensureInAutosave(final NewChunkHolder holder) { + final LevelHolderData data = getData(); + if (!data.autoSaveQueue.contains(holder)) { holder.lastAutoSave = this.currentTick; - this.autoSaveQueue.add(holder); + data.autoSaveQueue.add(holder); } } - public void autoSave() { + public synchronized void autoSave() { + final LevelHolderData data = getData(); final List reschedule = new ArrayList<>(); final long currentTick = this.currentTick; final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval(this.world)); final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick(this.world); - for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) { - final NewChunkHolder holder = this.autoSaveQueue.first(); + for (int autoSaved = 0; autoSaved < maxToSave && !data.autoSaveQueue.isEmpty();) { + final NewChunkHolder holder = data.autoSaveQueue.first(); if (holder.lastAutoSave > maxSaveTime) { break; } - this.autoSaveQueue.remove(holder); + data.autoSaveQueue.remove(holder); holder.lastAutoSave = currentTick; if (holder.save(false) != null) { @@ -255,10 +271,11 @@ public final class ChunkHolderManager { for (final NewChunkHolder holder : reschedule) { if (holder.getChunkStatus().isOrAfter(FullChunkStatus.FULL)) { - this.autoSaveQueue.add(holder); + data.autoSaveQueue.add(holder); } } } + // DivineMC end - Chunk System optimization public void saveAllChunks(final boolean flush, final boolean shutdown, final boolean logProgress) { final List holders = this.getChunkHolders(); @@ -317,13 +334,9 @@ public final class ChunkHolderManager { } if (logProgress) { final long currTime = System.nanoTime(); - if ((currTime - lastLog) > TimeUnit.SECONDS.toNanos(10L)) { + if ((currTime - lastLog) > TimeUnit.SECONDS.toNanos(5L)) { // DivineMC - Log a bit more frequently lastLog = currTime; - LOGGER.info( - "Saved " + savedChunk + " block chunks, " + savedEntity + " entity chunks, " + savedPoi - + " poi chunks in world '" + WorldUtil.getWorldName(this.world) + "', progress: " - + format.format((double)(i+1)/(double)len * 100.0) - ); + LOGGER.info("Saved {} block chunks, {} entity chunks, {} poi chunks in world '{}', progress: {}", savedChunk, savedEntity, savedPoi, ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.world), format.format((double) (i + 1) / (double) len * 100.0)); // DivineMC - Beautify log } } } @@ -425,8 +438,8 @@ public final class ChunkHolderManager { final Long2ObjectOpenHashMap>> ret = new Long2ObjectOpenHashMap<>(); final Long2ObjectOpenHashMap sections = new Long2ObjectOpenHashMap<>(); final int sectionShift = this.taskScheduler.getChunkSystemLockShift(); - for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) { - final long coord = iterator.nextLong(); + for (final Iterator iterator = this.tickets.keyIterator(); iterator.hasNext();) { // DivineMC - Chunk System optimization + final long coord = iterator.next(); // DivineMC - Chunk System optimization sections.computeIfAbsent( CoordinateUtils.getChunkKey( CoordinateUtils.getChunkX(coord) >> sectionShift, @@ -523,7 +536,7 @@ public final class ChunkHolderManager { chunkZ >> sectionShift ); - this.sectionToChunkToExpireCount.computeIfAbsent(sectionKey, (final long keyInMap) -> { + this.sectionToChunkToExpireCount.computeIfAbsent(sectionKey, (keyInMap) -> { // DivineMC - Chunk System optimization return new Long2IntOpenHashMap(); }).addTo(chunkKey, 1); } @@ -567,7 +580,7 @@ public final class ChunkHolderManager { final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null; try { - final SortedArraySet> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> { + final SortedArraySet> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (keyInMap) -> { // DivineMC - Chunk System optimization return SortedArraySet.create(4); }); @@ -697,8 +710,8 @@ public final class ChunkHolderManager { final Long2ObjectOpenHashMap sections = new Long2ObjectOpenHashMap<>(); final int sectionShift = this.taskScheduler.getChunkSystemLockShift(); - for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) { - final long coord = iterator.nextLong(); + for (final Iterator iterator = this.tickets.keyIterator(); iterator.hasNext();) { // DivineMC - Chunk System optimization + final long coord = iterator.next(); // DivineMC - Chunk System optimization sections.computeIfAbsent( CoordinateUtils.getChunkKey( CoordinateUtils.getChunkX(coord) >> sectionShift, @@ -746,8 +759,8 @@ public final class ChunkHolderManager { return removeDelay <= 0L; }; - for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) { - final long sectionKey = iterator.nextLong(); + for (final Iterator iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) { // DivineMC - Chunk System optimization + final long sectionKey = iterator.next(); // DivineMC - Chunk System optimization if (!this.sectionToChunkToExpireCount.containsKey(sectionKey)) { // removed concurrently @@ -1033,7 +1046,7 @@ public final class ChunkHolderManager { } if (!TickThread.isTickThreadFor(world)) { // DivineMC - parallel world ticking this.taskScheduler.scheduleChunkTask(() -> { - final ArrayDeque pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate; + final java.util.Deque pendingFullLoadUpdate = ChunkHolderManager.this.getData().pendingFullLoadUpdate; // DivineMC - Chunk System optimization for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { pendingFullLoadUpdate.add(changedFullStatus.get(i)); } @@ -1041,16 +1054,16 @@ public final class ChunkHolderManager { ChunkHolderManager.this.processPendingFullUpdate(); }, Priority.HIGHEST); } else { - final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; + final java.util.Deque pendingFullLoadUpdate = this.getData().pendingFullLoadUpdate; // DivineMC - Chunk System optimization for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { pendingFullLoadUpdate.add(changedFullStatus.get(i)); } } } - private void removeChunkHolder(final NewChunkHolder holder) { + private synchronized void removeChunkHolder(final NewChunkHolder holder) { // DivineMC - Chunk System optimization holder.onUnload(); - this.autoSaveQueue.remove(holder); + this.getData().autoSaveQueue.remove(holder); // DivineMC - Chunk System optimization PlatformHooks.get().onChunkHolderDelete(this.world, holder.vanillaChunkHolder); this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ)); } @@ -1208,6 +1221,27 @@ public final class ChunkHolderManager { } } + // DivineMC start - Chunk System optimization + public final java.util.Set blockTickingChunkHolders = java.util.Collections.synchronizedSet(new org.agrona.collections.ObjectHashSet<>(10, 0.88f, true)); + public final java.util.Set entityTickingChunkHolders = java.util.Collections.synchronizedSet(new org.agrona.collections.ObjectHashSet<>(10, 0.88f, true)); + + public void markBlockTicking(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { + this.blockTickingChunkHolders.add(newChunkHolder.getCachedLongPos()); + } + + public void markNonBlockTickingIfPossible(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { + this.blockTickingChunkHolders.remove(newChunkHolder.getCachedLongPos()); + } + + public void markEntityTicking(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { + this.entityTickingChunkHolders.add(newChunkHolder.getCachedLongPos()); + } + + public void markNonEntityTickingIfPossible(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { + this.entityTickingChunkHolders.remove(newChunkHolder.getCachedLongPos()); + } + // DivineMC end - Chunk System optimization + public enum TicketOperationType { ADD, REMOVE, ADD_IF_REMOVED, ADD_AND_REMOVE } @@ -1381,7 +1415,7 @@ public final class ChunkHolderManager { // only call on tick thread private boolean processPendingFullUpdate() { - final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; + final java.util.Deque pendingFullLoadUpdate = this.getData().pendingFullLoadUpdate; // DivineMC - Chunk System optimization boolean ret = false; @@ -1417,8 +1451,7 @@ public final class ChunkHolderManager { final JsonArray allTicketsJson = new JsonArray(); ret.add("tickets", allTicketsJson); - for (final Iterator>>> iterator = this.tickets.entryIterator(); - iterator.hasNext();) { + for (final Iterator>>> iterator = this.tickets.entryIterator(); iterator.hasNext();) { // DivineMC - Chunk System optimization final ConcurrentLong2ReferenceChainedHashTable.TableEntry>> coordinateTickets = iterator.next(); final long coordinate = coordinateTickets.getKey(); final SortedArraySet> tickets = coordinateTickets.getValue(); diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java index e4a5fa25ed368fc4662c30934da2963ef446d782..6da0ea5cd83a00578223e0a19f952c917bcbcdae 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java @@ -644,11 +644,19 @@ public final class NewChunkHolder { } public final ChunkHolder vanillaChunkHolder; + // DivineMC start - Chunk System optimization + private final long cachedLongPos; + + public long getCachedLongPos() { + return cachedLongPos; + } + // DivineMC end - Chunk System optimization public NewChunkHolder(final ServerLevel world, final int chunkX, final int chunkZ, final ChunkTaskScheduler scheduler) { this.world = world; this.chunkX = chunkX; this.chunkZ = chunkZ; + this.cachedLongPos = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ); // DivineMC - Chunk System optimization this.scheduler = scheduler; this.vanillaChunkHolder = new ChunkHolder( new ChunkPos(chunkX, chunkZ), ChunkHolderManager.MAX_TICKET_LEVEL, world, @@ -790,9 +798,11 @@ public final class NewChunkHolder { // note: these are completed with null to indicate that no write occurred // they are also completed with null to indicate a null write occurred - private UnloadTask chunkDataUnload; - private UnloadTask entityDataUnload; - private UnloadTask poiDataUnload; + // DivineMC start - Chunk System optimization + private volatile UnloadTask chunkDataUnload; + private volatile UnloadTask entityDataUnload; + private volatile UnloadTask poiDataUnload; + // DivineMC end - Chunk System optimization public static final record UnloadTask(CallbackCompletable completable, PrioritisedExecutor.PrioritisedTask task, LazyRunnable toRun) {} @@ -1190,6 +1200,7 @@ public final class NewChunkHolder { for (int dz = -NEIGHBOUR_RADIUS; dz <= NEIGHBOUR_RADIUS; ++dz) { for (int dx = -NEIGHBOUR_RADIUS; dx <= NEIGHBOUR_RADIUS; ++dx) { final NewChunkHolder holder = (dx | dz) == 0 ? this : this.scheduler.chunkHolderManager.getChunkHolder(dx + this.chunkX, dz + this.chunkZ); + if (holder == null) continue; // DivineMC - Chunk System optimization if (loaded) { if (holder.setNeighbourFullLoaded(-dx, -dz)) { changedFullStatus.add(holder); @@ -1214,6 +1225,19 @@ public final class NewChunkHolder { private void updateCurrentState(final FullChunkStatus to) { this.currentFullChunkStatus = to; + // DivineMC start - Chunk System optimization + if (to.isOrAfter(FullChunkStatus.BLOCK_TICKING)) { + this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markBlockTicking(this); + } else { + this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markNonBlockTickingIfPossible(this); + } + + if (to.isOrAfter(FullChunkStatus.ENTITY_TICKING)) { + this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markEntityTicking(this); + } else { + this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markNonEntityTickingIfPossible(this); + } + // DivineMC end - Chunk System optimization } // only to be called on the main thread, no locks need to be held @@ -1348,11 +1372,11 @@ public final class NewChunkHolder { return this.requestedGenStatus; } - private final Reference2ObjectOpenHashMap>> statusWaiters = new Reference2ObjectOpenHashMap<>(); + private final Map>> statusWaiters = new java.util.concurrent.ConcurrentHashMap<>(); // DivineMC - Chunk System optimization void addStatusConsumer(final ChunkStatus status, final Consumer consumer) { this.statusWaiters.computeIfAbsent(status, (final ChunkStatus keyInMap) -> { - return new ArrayList<>(4); + return new java.util.concurrent.CopyOnWriteArrayList<>(); // DivineMC - Chunk System optimization }).add(consumer); } @@ -1394,11 +1418,11 @@ public final class NewChunkHolder { }, Priority.HIGHEST); } - private final Reference2ObjectOpenHashMap>> fullStatusWaiters = new Reference2ObjectOpenHashMap<>(); + private final Map>> fullStatusWaiters = new java.util.concurrent.ConcurrentHashMap<>(); void addFullStatusConsumer(final FullChunkStatus status, final Consumer consumer) { this.fullStatusWaiters.computeIfAbsent(status, (final FullChunkStatus keyInMap) -> { - return new ArrayList<>(4); + return new java.util.concurrent.CopyOnWriteArrayList<>(); // DivineMC - Chunk System optimization }).add(consumer); } diff --git a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java index e97e7d276faf055c89207385d3820debffb06463..4aeb75a2cdcfb4206bab3eee5ad674dd9890e720 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java +++ b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java @@ -2,6 +2,6 @@ package ca.spottedleaf.moonrise.patches.chunk_tick_iteration; public final class ChunkTickConstants { - public static final int PLAYER_SPAWN_TRACK_RANGE = 8; + public static final int PLAYER_SPAWN_TRACK_RANGE = (int) Math.round(org.bxteam.divinemc.DivineConfig.playerNearChunkDetectionRange / 16.0); // DivineMC - Chunk System optimization } diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..f94f443f862611f039454d1dc8ff2a4ba5f081d3 100644 --- a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java +++ b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java @@ -325,7 +325,7 @@ public final class SWMRNibbleArray { } // operation type: updating - public boolean updateVisible() { + public synchronized boolean updateVisible() { // DivineMC - Chunk System optimization if (!this.isDirty()) { return false; } diff --git a/net/minecraft/server/level/DistanceManager.java b/net/minecraft/server/level/DistanceManager.java index 5eab6179ce3913cb4e4d424f910ba423faf21c85..4b1efd53e423bdfe90d5efd472823869fc87e73b 100644 --- a/net/minecraft/server/level/DistanceManager.java +++ b/net/minecraft/server/level/DistanceManager.java @@ -178,15 +178,13 @@ public abstract class DistanceManager implements ca.spottedleaf.moonrise.patches public boolean inEntityTickingRange(long chunkPos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); - return chunkHolder != null && chunkHolder.isEntityTickingReady(); + return this.moonrise$getChunkHolderManager().entityTickingChunkHolders.contains(chunkPos); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } public boolean inBlockTickingRange(long chunkPos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); - return chunkHolder != null && chunkHolder.isTickingReady(); + return this.moonrise$getChunkHolderManager().blockTickingChunkHolders.contains(chunkPos); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java index ab30af9cd58ff7310e05be87b08f42bacf69e11e..ae0e36d198ad8243920c8e8a55c0be4945542763 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -439,8 +439,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon public boolean isPositionTicking(long chunkPos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); - return newChunkHolder != null && newChunkHolder.isTickingReady(); + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.blockTickingChunkHolders.contains(chunkPos); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 973198ae0a73d6747e73548bdcbc1de46b6fb107..1581bf8ff425c6e0df28d107300d7266e9f87ed5 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -181,6 +181,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public final ServerChunkCache chunkSource; private final MinecraftServer server; public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type + public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.LevelHolderData chunkHolderData; // DivineMC - Chunk System optimization private int lastSpawnChunkRadius; final EntityTickList entityTickList = new EntityTickList(this); // DivineMC - parallel world ticking // Paper - rewrite chunk system @@ -689,6 +690,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper start - rewrite chunk system this.moonrise$setEntityLookup(new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup((ServerLevel)(Object)this, ((ServerLevel)(Object)this).new EntityCallbacks())); this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this); + this.chunkHolderData = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.LevelHolderData(); // DivineMC - Chunk System optimization this.entityDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController( new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController.EntityRegionFileStorage( new RegionStorageInfo(levelStorageAccess.getLevelId(), dimension, "entities"), @@ -832,8 +834,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @Override public boolean shouldTickBlocksAt(long chunkPos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); - return holder != null && holder.isTickingReady(); + return this.moonrise$getChunkTaskScheduler().chunkHolderManager.blockTickingChunkHolders.contains(chunkPos); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } @@ -2525,30 +2526,25 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private boolean isPositionTickingWithEntitiesLoaded(long chunkPos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); - // isTicking implies the chunk is loaded, and the chunk is loaded now implies the entities are loaded - return chunkHolder != null && chunkHolder.isTickingReady(); + return this.moonrise$getChunkTaskScheduler().chunkHolderManager.blockTickingChunkHolders.contains(chunkPos); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } public boolean isPositionEntityTicking(BlockPos pos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); - return chunkHolder != null && chunkHolder.isEntityTickingReady(); + return this.moonrise$getChunkTaskScheduler().chunkHolderManager.entityTickingChunkHolders.contains(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } public boolean isNaturalSpawningAllowed(BlockPos pos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); - return chunkHolder != null && chunkHolder.isEntityTickingReady(); + return this.moonrise$getChunkTaskScheduler().chunkHolderManager.entityTickingChunkHolders.contains(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } public boolean isNaturalSpawningAllowed(ChunkPos chunkPos) { // Paper start - rewrite chunk system - final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkPos)); - return chunkHolder != null && chunkHolder.isEntityTickingReady(); + return this.moonrise$getChunkTaskScheduler().chunkHolderManager.entityTickingChunkHolders.contains(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkPos)); // DivineMC - Chunk System optimization // Paper end - rewrite chunk system } diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java index 26c8c1e5598daf3550aef05b12218c47bda6618b..94c824ab1457939c425e1f99929d3222ee2c18a0 100644 --- a/net/minecraft/world/level/LevelReader.java +++ b/net/minecraft/world/level/LevelReader.java @@ -70,10 +70,27 @@ public interface LevelReader extends ca.spottedleaf.moonrise.patches.chunk_syste @Override default Holder getNoiseBiome(int x, int y, int z) { - ChunkAccess chunk = this.getChunk(QuartPos.toSection(x), QuartPos.toSection(z), ChunkStatus.BIOMES, false); + ChunkAccess chunk = this.fasterChunkAccess(this, QuartPos.toSection(x), QuartPos.toSection(z), ChunkStatus.BIOMES, false); // DivineMC - Chunk System optimization return chunk != null ? chunk.getNoiseBiome(x, y, z) : this.getUncachedNoiseBiome(x, y, z); } + // DivineMC start - Chunk System optimization + private @Nullable ChunkAccess fasterChunkAccess(LevelReader instance, int x, int z, ChunkStatus chunkStatus, boolean create) { + if (!create && instance instanceof net.minecraft.server.level.ServerLevel world) { + final net.minecraft.server.level.ChunkHolder holder = (world.getChunkSource().chunkMap).getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); + if (holder != null) { + final java.util.concurrent.CompletableFuture> future = holder.getFullChunkFuture(); + final net.minecraft.server.level.ChunkResult either = future.getNow(null); + if (either != null) { + final net.minecraft.world.level.chunk.LevelChunk chunk = either.orElse(null); + if (chunk != null) return chunk; + } + } + } + return instance.getChunk(x, z, chunkStatus, create); + } + // DivineMC end - Chunk System optimization + Holder getUncachedNoiseBiome(int x, int y, int z); boolean isClientSide(); diff --git a/net/minecraft/world/level/chunk/ProtoChunk.java b/net/minecraft/world/level/chunk/ProtoChunk.java index e66239e2da91bd3ddf358d239be796719c0da327..35e9d8cfe12252d3419626f1cefb64d30e20069e 100644 --- a/net/minecraft/world/level/chunk/ProtoChunk.java +++ b/net/minecraft/world/level/chunk/ProtoChunk.java @@ -41,7 +41,7 @@ public class ProtoChunk extends ChunkAccess { @Nullable private volatile LevelLightEngine lightEngine; private volatile ChunkStatus status = ChunkStatus.EMPTY; - private final List entities = Lists.newArrayList(); + private final List entities = Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System optimization @Nullable private CarvingMask carvingMask; @Nullable diff --git a/net/minecraft/world/level/chunk/storage/IOWorker.java b/net/minecraft/world/level/chunk/storage/IOWorker.java index 2199a9e2a0141c646d108f2687a27f1d165453c5..c28c2583b257f92207b822a1fdde8f5b7e480992 100644 --- a/net/minecraft/world/level/chunk/storage/IOWorker.java +++ b/net/minecraft/world/level/chunk/storage/IOWorker.java @@ -212,7 +212,38 @@ public class IOWorker implements ChunkScanAccess, AutoCloseable { }); } + // DivineMC start - Chunk System optimization + private void checkHardLimit() { + if (this.pendingWrites.size() >= org.bxteam.divinemc.DivineConfig.chunkDataCacheLimit) { + LOGGER.warn("Chunk data cache size exceeded hard limit ({} >= {}), forcing writes to disk (you can increase chunkDataCacheLimit in c2me.toml)", this.pendingWrites.size(), org.bxteam.divinemc.DivineConfig.chunkDataCacheLimit); + while (this.pendingWrites.size() >= org.bxteam.divinemc.DivineConfig.chunkDataCacheSoftLimit * 0.75) { + writeResult0(); + } + } + } + + private void writeResult0() { + java.util.Iterator> iterator = this.pendingWrites.entrySet().iterator(); + if (iterator.hasNext()) { + java.util.Map.Entry entry = iterator.next(); + iterator.remove(); + this.runStore(entry.getKey(), entry.getValue()); + } + } + // DivineMC end - Chunk System optimization + private void storePendingChunk() { + // DivineMC start - Chunk System optimization + if (!this.pendingWrites.isEmpty()) { + checkHardLimit(); + if (this.pendingWrites.size() >= org.bxteam.divinemc.DivineConfig.chunkDataCacheSoftLimit) { + int writeFrequency = Math.min(1, (this.pendingWrites.size() - (int) org.bxteam.divinemc.DivineConfig.chunkDataCacheSoftLimit) / 16); + for (int i = 0; i < writeFrequency; i++) { + writeResult0(); + } + } + } + // DivineMC end - Chunk System optimization Entry entry = this.pendingWrites.pollFirstEntry(); if (entry != null) { this.runStore(entry.getKey(), entry.getValue()); diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java index 6ebd1300c2561116b83cb2472ac7939ead36d576..16cd10ab8de69ca3d29c84cf93715645322fd72a 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java @@ -244,7 +244,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise protected RegionFileStorage(RegionStorageInfo info, Path folder, boolean sync) { // Paper - protected this.folder = folder; - this.sync = sync; + this.sync = Boolean.parseBoolean(System.getProperty("com.ishland.c2me.chunkio.syncDiskWrites", String.valueOf(sync))); // DivineMC - C2ME: sync disk writes this.info = info; this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers } diff --git a/net/minecraft/world/level/levelgen/Column.java b/net/minecraft/world/level/levelgen/Column.java index 4a1df0f8578c9ee5538ed8c94d3c7911f36f83b8..716c2c69843234cdef34339d859babc95ffe318c 100644 --- a/net/minecraft/world/level/levelgen/Column.java +++ b/net/minecraft/world/level/levelgen/Column.java @@ -156,7 +156,7 @@ public abstract class Column { } public int height() { - return this.ceiling - this.floor - 1; + return net.minecraft.util.Mth.abs(this.ceiling - this.floor - 1); // DivineMC - Chunk System optimization } @Override diff --git a/net/minecraft/world/level/levelgen/WorldgenRandom.java b/net/minecraft/world/level/levelgen/WorldgenRandom.java index c2d7cd788071e25b8ba2503c30ae80c7a9f353ed..0a2e13c4a3db6517267e1f9e74b6152c73e8351f 100644 --- a/net/minecraft/world/level/levelgen/WorldgenRandom.java +++ b/net/minecraft/world/level/levelgen/WorldgenRandom.java @@ -73,7 +73,7 @@ public class WorldgenRandom extends LegacyRandomSource { } public static enum Algorithm { - LEGACY(LegacyRandomSource::new), + LEGACY(ThreadSafeLegacyRandomSource::new), // DivineMC - Chunk System optimization XOROSHIRO(XoroshiroRandomSource::new); private final LongFunction constructor; diff --git a/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java b/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java index f4009671880b00ecec98fe604215e2824e453cdf..c607c801223ea71343ece67b81151e31362f1c31 100644 --- a/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java +++ b/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java @@ -55,17 +55,21 @@ public class RandomizedIntStateProvider extends BlockStateProvider { @Override public BlockState getState(RandomSource random, BlockPos pos) { - BlockState state = this.source.getState(random, pos); - if (this.property == null || !state.hasProperty(this.property)) { - IntegerProperty integerProperty = findProperty(state, this.propertyName); - if (integerProperty == null) { - return state; + // DivineMC start - Chunk System optimization + BlockState blockState = this.source.getState(random, pos); + IntegerProperty propertyLocal = this.property; + if (propertyLocal == null || !blockState.hasProperty(propertyLocal)) { + IntegerProperty intProperty = findProperty(blockState, this.propertyName); + if (intProperty == null) { + return blockState; } - this.property = integerProperty; + propertyLocal = intProperty; + this.property = intProperty; } - return state.setValue(this.property, Integer.valueOf(this.values.sample(random))); + return (BlockState)blockState.setValue(propertyLocal, this.values.sample(random)); + // DivineMC end - Chunk System optimization } @Nullable diff --git a/net/minecraft/world/level/levelgen/structure/ScatteredFeaturePiece.java b/net/minecraft/world/level/levelgen/structure/ScatteredFeaturePiece.java index d122221deefb218db962e97ba2d958c33d903b8a..56311b439ac22700593d2d31da3a4efe3a519d53 100644 --- a/net/minecraft/world/level/levelgen/structure/ScatteredFeaturePiece.java +++ b/net/minecraft/world/level/levelgen/structure/ScatteredFeaturePiece.java @@ -12,7 +12,7 @@ public abstract class ScatteredFeaturePiece extends StructurePiece { protected final int width; protected final int height; protected final int depth; - protected int heightPosition = -1; + protected volatile int heightPosition = -1; // DivineMC - Chunk System optimization - make volatile protected ScatteredFeaturePiece(StructurePieceType type, int x, int y, int z, int width, int height, int depth, Direction orientation) { super(type, 0, StructurePiece.makeBoundingBox(x, y, z, orientation, width, height, depth)); diff --git a/net/minecraft/world/level/levelgen/structure/StructureStart.java b/net/minecraft/world/level/levelgen/structure/StructureStart.java index 4dafa79dd4ec55a443ba3731a79e7cd6e8052f48..8aeab4d773473ad20b1c64295c93d6fcb4ea02a1 100644 --- a/net/minecraft/world/level/levelgen/structure/StructureStart.java +++ b/net/minecraft/world/level/levelgen/structure/StructureStart.java @@ -26,7 +26,7 @@ public final class StructureStart { private final Structure structure; private final PiecesContainer pieceContainer; private final ChunkPos chunkPos; - private int references; + private final java.util.concurrent.atomic.AtomicInteger references = new java.util.concurrent.atomic.AtomicInteger(); // DivineMC - Chunk System optimization @Nullable private volatile BoundingBox cachedBoundingBox; @@ -39,7 +39,7 @@ public final class StructureStart { public StructureStart(Structure structure, ChunkPos chunkPos, int references, PiecesContainer pieceContainer) { this.structure = structure; this.chunkPos = chunkPos; - this.references = references; + this.references.set(references); // DivineMC - Chunk System optimization this.pieceContainer = pieceContainer; } @@ -126,7 +126,7 @@ public final class StructureStart { compoundTag.putString("id", context.registryAccess().lookupOrThrow(Registries.STRUCTURE).getKey(this.structure).toString()); compoundTag.putInt("ChunkX", chunkPos.x); compoundTag.putInt("ChunkZ", chunkPos.z); - compoundTag.putInt("references", this.references); + compoundTag.putInt("references", this.references.get()); // DivineMC - Chunk System optimization compoundTag.put("Children", this.pieceContainer.save(context)); return compoundTag; } else { @@ -144,15 +144,15 @@ public final class StructureStart { } public boolean canBeReferenced() { - return this.references < this.getMaxReferences(); + return this.references.get() < this.getMaxReferences(); // DivineMC - Chunk System optimization } public void addReference() { - this.references++; + this.references.getAndIncrement(); // DivineMC - Chunk System optimization } public int getReferences() { - return this.references; + return this.references.get(); // DivineMC - Chunk System optimization } protected int getMaxReferences() {