From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Sat, 1 Feb 2025 00:09:39 +0300 Subject: [PATCH] Chunk System Optimizations 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..886825a10bd06b4b656d19a05624c74f2686feb3 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java @@ -189,13 +189,13 @@ public final class RegionizedPlayerChunkLoader { } if (((ChunkSystemServerPlayer)player).moonrise$getChunkLoader() != null) { - throw new IllegalStateException("Player is already added to player chunk loader"); + return; // DivineMC - Chunk System optimization - already loaded } final PlayerChunkLoaderData loader = new PlayerChunkLoaderData(this.world, player); - ((ChunkSystemServerPlayer)player).moonrise$setChunkLoader(loader); loader.add(); + player.moonrise$setChunkLoader(loader); // DivineMC - Chunk System optimization } public void updatePlayer(final ServerPlayer player) { @@ -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 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 for (int dz = -radius; dz <= radius; ++dz) { for (int dx = -radius; dx <= radius; ++dx) { if ((dx | dz) == 0) { @@ -643,10 +653,10 @@ public final class RegionizedPlayerChunkLoader { return true; } - void updateQueues(final long time) { + synchronized void updateQueues(final long time) { // DivineMC - Chunk System optimization TickThread.ensureTickThread(this.player, "Cannot tick player chunk loader async"); if (this.removed) { - throw new IllegalStateException("Ticking removed player chunk loader"); + return; // DivineMC - Chunk System optimization - just return } // update rate limits final double loadRate = this.getMaxChunkLoadRate(); @@ -910,10 +920,10 @@ public final class RegionizedPlayerChunkLoader { ); } - void update() { + synchronized void update() { // DivineMC - Chunk System optimization TickThread.ensureTickThread(this.player, "Cannot update player asynchronously"); if (this.removed) { - throw new IllegalStateException("Updating removed player chunk loader"); + return; // DivineMC - Chunk System optimization - just return } final ViewDistances playerDistances = ((ChunkSystemServerPlayer)this.player).moonrise$getViewDistanceHolder().getViewDistances(); final ViewDistances worldDistances = ((ChunkSystemServerLevel)this.world).moonrise$getViewDistanceHolder().getViewDistances(); @@ -1062,7 +1072,7 @@ public final class RegionizedPlayerChunkLoader { this.flushDelayedTicketOps(); } - void remove() { + synchronized void remove() { // DivineMC - Chunk System optimization TickThread.ensureTickThread(this.player, "Cannot add player asynchronously"); if (this.removed) { throw new IllegalStateException("Removing removed player chunk loader"); @@ -1090,7 +1100,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 5683395e09a65b1b39748df5152fffef630ac083..6aea99f071bd1a6a1ea9507bb70739d56318eb22 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 (org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking && !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)); } @@ -1214,6 +1227,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 } @@ -1387,7 +1421,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; @@ -1423,8 +1457,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/ChunkTaskScheduler.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java index 67532b85073b7978254a0b04caadfe822679e61f..4b97b676d4245e7ece956eb4c78bed96ff452b2d 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java @@ -65,14 +65,6 @@ public final class ChunkTaskScheduler { private static final Logger LOGGER = LogUtils.getClassLogger(); - public static void init(final boolean useParallelGen) { - for (final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor executor : MoonriseCommon.RADIUS_AWARE_GROUP.getAllExecutors()) { - executor.setMaxParallelism(useParallelGen ? -1 : 1); - } - - LOGGER.info("Chunk system is using population gen parallelism: " + useParallelGen); - } - public static final TicketType CHUNK_LOAD = TicketType.create("chunk_system:chunk_load", Long::compareTo); private static final AtomicLong CHUNK_LOAD_IDS = new AtomicLong(); @@ -115,12 +107,12 @@ public final class ChunkTaskScheduler { public final ServerLevel world; public final RadiusAwarePrioritisedExecutor radiusAwareScheduler; - public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor parallelGenExecutor; - private final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor radiusAwareGenExecutor; - public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor loadExecutor; + public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor parallelGenExecutor; + private final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor radiusAwareGenExecutor; + public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor loadExecutor; public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor ioExecutor; - public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor compressionExecutor; - public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor saveExecutor; + public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor compressionExecutor; + public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor saveExecutor; private final PrioritisedTaskQueue mainThreadExecutor = new PrioritisedTaskQueue(); @@ -291,14 +283,14 @@ public final class ChunkTaskScheduler { this.lockShift = Math.max(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift(), ThreadedTicketLevelPropagator.SECTION_SHIFT); this.schedulingLockArea = new ReentrantAreaLock(this.getChunkSystemLockShift()); - this.parallelGenExecutor = MoonriseCommon.PARALLEL_GEN_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0); - this.radiusAwareGenExecutor = MoonriseCommon.RADIUS_AWARE_GROUP.createExecutor(1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0); - this.loadExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0); - this.radiusAwareScheduler = new RadiusAwarePrioritisedExecutor(this.radiusAwareGenExecutor, 16); + this.parallelGenExecutor = MoonriseCommon.PARALLEL_GEN_GROUP.createExecutor(); + this.radiusAwareGenExecutor = MoonriseCommon.RADIUS_AWARE_GROUP.createExecutor(); + this.loadExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(); + this.radiusAwareScheduler = new RadiusAwarePrioritisedExecutor(this.radiusAwareGenExecutor, 10_000); this.ioExecutor = MoonriseCommon.SERVER_REGION_IO_GROUP.createExecutor(-1, MoonriseCommon.IO_QUEUE_HOLD_TIME, 0); // we need a separate executor here so that on shutdown we can continue to process I/O tasks - this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0); - this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0); + this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(); + this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(); this.chunkHolderManager = new ChunkHolderManager(world, this); } diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java index e4a5fa25ed368fc4662c30934da2963ef446d782..b5057ffac429ea2d1910082a83c13a5b7dc550c1 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,12 +798,14 @@ 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) {} + org.bxteam.divinemc.server.chunk.ChunkRunnable toRun) {} // DivineMC - Chunk System optimization public UnloadTask getUnloadTask(final MoonriseRegionFileIO.RegionFileType type) { switch (type) { @@ -858,7 +868,7 @@ public final class NewChunkHolder { this.priorityLocked = false; if (chunk != null) { - final LazyRunnable toRun = new LazyRunnable(); + final org.bxteam.divinemc.server.chunk.ChunkRunnable toRun = new org.bxteam.divinemc.server.chunk.ChunkRunnable(this.chunkX, this.chunkZ, this.world, null); // DivineMC - Chunk System optimization' this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.saveExecutor.createTask(toRun), toRun); } if (poiChunk != null) { @@ -877,7 +887,11 @@ public final class NewChunkHolder { MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, data, type); } - this.getUnloadTask(type).completable().complete(data); + // DivineMC start - Chunk System optimization + UnloadTask task = this.getUnloadTask(type); + if (task == null) return; + task.completable().complete(data); + // DivineMC end - Chunk System optimization final ReentrantAreaLock.Node schedulingLock = this.scheduler.schedulingLockArea.lock(this.chunkX, this.chunkZ); try { // can only write to these fields while holding the schedule lock @@ -1190,6 +1204,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 +1229,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 +1376,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 +1422,11 @@ public final class NewChunkHolder { }, Priority.HIGHEST); } - private final Reference2ObjectOpenHashMap>> fullStatusWaiters = new Reference2ObjectOpenHashMap<>(); + private final Map>> fullStatusWaiters = new java.util.concurrent.ConcurrentHashMap<>(); // DivineMC - Chunk System optimization 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_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java index 28ffa653e87a4e8ef7cf614916ef3fe61681fe16..b35b92b204fbefd139c4544f15e32d46bfa30777 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java @@ -110,60 +110,27 @@ public class RadiusAwarePrioritisedExecutor { return priorityId == this.selectedQueue ? queue.tryPushTasks() : null; } + // DivineMC start - Chunk System Optimizations public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ, final int radius, - final Runnable run, final Priority priority) { + final Runnable run, final Priority priority, final net.minecraft.server.level.ServerLevel world) { if (radius < 0) { throw new IllegalArgumentException("Radius must be > 0: " + radius); } - return new Task(this, chunkX, chunkZ, radius, run, priority); + return new Task(this, chunkX, chunkZ, radius, run, priority, world); } - public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ, final int radius, - final Runnable run) { - return this.createTask(chunkX, chunkZ, radius, run, Priority.NORMAL); - } - - public PrioritisedExecutor.PrioritisedTask queueTask(final int chunkX, final int chunkZ, final int radius, - final Runnable run, final Priority priority) { - final PrioritisedExecutor.PrioritisedTask ret = this.createTask(chunkX, chunkZ, radius, run, priority); - - ret.queue(); - - return ret; - } - - public PrioritisedExecutor.PrioritisedTask queueTask(final int chunkX, final int chunkZ, final int radius, - final Runnable run) { - final PrioritisedExecutor.PrioritisedTask ret = this.createTask(chunkX, chunkZ, radius, run); - - ret.queue(); - - return ret; - } - - public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run, final Priority priority) { - return new Task(this, 0, 0, -1, run, priority); - } - - public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run) { - return this.createInfiniteRadiusTask(run, Priority.NORMAL); - } - - public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run, final Priority priority) { - final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, priority); - - ret.queue(); - - return ret; + public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run, final Priority priority, final net.minecraft.server.level.ServerLevel world) { + return new Task(this, 0, 0, -1, run, priority, world); } - public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run) { - final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, Priority.NORMAL); + public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run, final net.minecraft.server.level.ServerLevel world) { + final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, Priority.NORMAL, world); ret.queue(); return ret; } + // DivineMC end - Chunk System Optimizations private static void scheduleTasks(final List toSchedule) { if (toSchedule != null) { @@ -440,18 +407,20 @@ public class RadiusAwarePrioritisedExecutor { private final int radius; private Runnable run; private Priority priority; + public final net.minecraft.server.level.ServerLevel world; // DivineMC - Chunk System Optimization private DependencyNode dependencyNode; private PrioritisedExecutor.PrioritisedTask queuedTask; private Task(final RadiusAwarePrioritisedExecutor scheduler, final int chunkX, final int chunkZ, final int radius, - final Runnable run, final Priority priority) { + final Runnable run, final Priority priority, final net.minecraft.server.level.ServerLevel world) { // DivineMC - Chunk System Optimization this.scheduler = scheduler; this.chunkX = chunkX; this.chunkZ = chunkZ; this.radius = radius; this.run = run; this.priority = priority; + this.world = world; // DivineMC - Chunk System Optimization } private boolean isFiniteRadius() { @@ -473,6 +442,7 @@ public class RadiusAwarePrioritisedExecutor { synchronized (this.scheduler) { final DependencyNode node = this.dependencyNode; this.dependencyNode = null; + if (node == null) return; // DivineMC - Chunk System Optimization toSchedule = node.tree.returnNode(node); } @@ -489,6 +459,7 @@ public class RadiusAwarePrioritisedExecutor { final Runnable run = this.run; this.run = null; try { + if (run == null) return; // DivineMC - Chunk System Optimization run.run(); } finally { this.returnNode(); diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java index 6ab353b0d2465c3680bb3c8d0852ba0f65c00fd2..70067c12d4d82460d55d8f90d01b0bc1e5368408 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java @@ -35,7 +35,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl super(scheduler, world, chunkX, chunkZ); this.chunkHolder = chunkHolder; this.fromChunk = fromChunk; - this.convertToFullTask = scheduler.createChunkTask(chunkX, chunkZ, this, priority); + this.convertToFullTask = scheduler.radiusAwareScheduler.createInfiniteRadiusTask(this, priority, world); // DivineMC - Chunk System Optimizations } @Override diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java index e9623c334858ebc698c92d05b36b70f6d846d0b1..43ef4d7b7db64a766bb815e1c2202b1f30ae4f99 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java @@ -69,7 +69,7 @@ public abstract class ChunkProgressionTask { } } - protected final void complete(final ChunkAccess chunk, final Throwable throwable) { + protected void complete(final ChunkAccess chunk, final Throwable throwable) { // DivineMC - not final try { this.complete0(chunk, throwable); } catch (final Throwable thr2) { @@ -81,7 +81,7 @@ public abstract class ChunkProgressionTask { private void complete0(final ChunkAccess chunk, final Throwable throwable) { if ((boolean)COMPLETED_HANDLE.getAndSet((ChunkProgressionTask)this, (boolean)true)) { - throw new IllegalStateException("Already completed"); + return; // DivineMC - do not crash the server } this.completedChunk = chunk; this.completedThrowable = throwable; diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java index 25d8da4773dcee5096053e7e3788bfc224d705a7..2112ccbe382993dcfb56a50d991c3613765d7d56 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java @@ -51,9 +51,9 @@ public final class ChunkUpgradeGenericStatusTask extends ChunkProgressionTask im } else { final int writeRadius = ((ChunkSystemChunkStatus)this.toStatus).moonrise$getWriteRadius(); if (writeRadius < 0) { - this.generateTask = this.scheduler.radiusAwareScheduler.createInfiniteRadiusTask(this, priority); + this.generateTask = this.scheduler.radiusAwareScheduler.createInfiniteRadiusTask(this, priority, world); // DivineMC - Chunk System Optimizations } else { - this.generateTask = this.scheduler.radiusAwareScheduler.createTask(chunkX, chunkZ, writeRadius, this, priority); + this.generateTask = this.scheduler.radiusAwareScheduler.createTask(chunkX, chunkZ, writeRadius, this, priority, world); // DivineMC - Chunk System Optimizations } } } diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java index 95ed5a0ff3f0588f625ba48a5fee3aafbab9d13f..f2fd6d5eb024f646875868c441eb2da284206026 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java @@ -333,6 +333,12 @@ public abstract class GenericDataLoadTask { GenericDataLoadTask.this.onComplete((TaskResult)newData); } } + + // DivineMC start - Chunk System Optimizations + public GenericDataLoadTask loadTask() { + return GenericDataLoadTask.this; + } + // DivineMC end - Chunk System Optimizations } public final class ProcessOnMainTask implements Runnable { @@ -351,6 +357,12 @@ public abstract class GenericDataLoadTask { GenericDataLoadTask.this.onComplete(result); } + + // DivineMC start - Chunk System Optimizations + public GenericDataLoadTask loadTask() { + return GenericDataLoadTask.this; + } + // DivineMC end - Chunk System Optimizations } protected static final class LoadDataFromDiskTask { 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/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java index 571db5f9bf94745a8afe2cd313e593fb15db5e37..c8e59f6abed384f45314fc995d3822724c9b4001 100644 --- a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java +++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java @@ -888,7 +888,7 @@ public final class StarLightInterface { super(chunkCoordinate, lightEngine, queue); this.task = ((ChunkSystemServerLevel)(ServerLevel)lightEngine.getWorld()).moonrise$getChunkTaskScheduler().radiusAwareScheduler.createTask( CoordinateUtils.getChunkX(chunkCoordinate), CoordinateUtils.getChunkZ(chunkCoordinate), - ((ChunkSystemChunkStatus)ChunkStatus.LIGHT).moonrise$getWriteRadius(), this, priority + ((ChunkSystemChunkStatus)ChunkStatus.LIGHT).moonrise$getWriteRadius(), this, priority, lightEngine.world.getMinecraftWorld() // DivineMC - Chunk System Optimizations ); } diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java index 460bb584db04b582f3297ae419183f430aff1ec0..72c4e1876115745fbeec12fe8a1ad6f4803e9d7f 100644 --- a/io/papermc/paper/FeatureHooks.java +++ b/io/papermc/paper/FeatureHooks.java @@ -32,11 +32,6 @@ import org.bukkit.Chunk; import org.bukkit.World; public final class FeatureHooks { - - public static void initChunkTaskScheduler(final boolean useParallelGen) { - ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.init(useParallelGen); // Paper - Chunk system - } - public static void registerPaperCommands(final Map, PaperSubcommand> commands) { commands.put(Set.of("fixlight"), new FixLightCommand()); // Paper - rewrite chunk system commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand()); // Paper - rewrite chunk system diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java index 965899a98223b15bd770378c202873cbf15b714d..dccf4d4b1067e1b09e38cabeae82185929494b62 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -127,8 +127,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public final AtomicInteger tickingGenerated = new AtomicInteger(); // Paper - public private final String storageName; private final PlayerMap playerMap = new PlayerMap(); - public final Int2ObjectMap entityMap = new Int2ObjectOpenHashMap<>(); - private final Long2ByteMap chunkTypeCache = new Long2ByteOpenHashMap(); + public final Int2ObjectMap entityMap = new org.bxteam.divinemc.util.map.Int2ObjectConcurrentHashMap<>(); // DivineMC - Chunk System Optimizations + private final Long2ByteMap chunkTypeCache = it.unimi.dsi.fastutil.longs.Long2ByteMaps.synchronize(new Long2ByteOpenHashMap()); // DivineMC - Chunk System Optimizations // Paper - rewrite chunk system public int serverViewDistance; public final WorldGenContext worldGenContext; // Paper - public @@ -249,7 +249,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final ServerPlayer[] backingSet = inRange.getRawDataUnchecked(); for (int i = 0, len = inRange.size(); i < len; i++) { - ++(backingSet[i].mobCounts[index]); + // DivineMC start - Chunk System Optimizations + ServerPlayer player = backingSet[i]; + if (player == null) continue; + ++(player.mobCounts[index]); + // DivineMC end - Chunk System Optimizations } } @@ -266,7 +270,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } final ServerPlayer[] backingSet = inRange.getRawDataUnchecked(); for (int i = 0, len = inRange.size(); i < len; i++) { - ++(backingSet[i].mobBackoffCounts[idx]); + // DivineMC start - Chunk System Optimizations + ServerPlayer player = backingSet[i]; + if (player == null) continue; + ++(player.mobBackoffCounts[idx]); + // DivineMC end - Chunk System Optimizations } } // Paper end - per player mob count backoff @@ -720,27 +728,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return false; } - final ServerPlayer[] raw = players.getRawDataUnchecked(); - final int len = players.size(); - - Objects.checkFromIndexSize(0, len, raw.length); - for (int i = 0; i < len; ++i) { - final ServerPlayer serverPlayer = raw[i]; - // Paper start - PlayerNaturallySpawnCreaturesEvent - com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; - blockRange = 16384.0D; - if (reducedRange) { - event = serverPlayer.playerNaturallySpawnedEvent; - if (event == null || event.isCancelled()) continue; - blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4)); - } - // Paper end - PlayerNaturallySpawnCreaturesEvent - if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, blockRange)) { - return true; - } - } - - return false; + return !players.isEmpty(); // DivineMC - Chunk System Optimizations // Paper end - chunk tick iteration optimisation } @@ -758,10 +746,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final ServerPlayer[] raw = players.getRawDataUnchecked(); final int len = players.size(); - Objects.checkFromIndexSize(0, len, raw.length); - for (int i = 0; i < len; ++i) { + for (int i = 0; i < raw.length; ++i) { // DivineMC - Chunk System Optimizations final ServerPlayer player = raw[i]; - if (this.playerIsCloseEnoughForSpawning(player, chunkPos, 16384.0D)) { // Spigot + if (player == null) continue; // DivineMC - Chunk System Optimizations + if (this.playerIsCloseEnoughForSpawning(player, chunkPos, (org.bxteam.divinemc.DivineConfig.playerNearChunkDetectionRange^2))) { // Spigot // DivineMC - Chunk System Optimizations if (ret == null) { ret = new ArrayList<>(len - i); ret.add(player); @@ -1146,6 +1134,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } else { for (int i = 0, len = players.size(); i < len; ++i) { final ServerPlayer player = playersRaw[i]; + if (player == null) continue; // DivineMC - Chunk System Optimizations this.updatePlayer(player); } 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 52ba052fc1ff2a35786570c282a7de4e9dff99f5..9298bdca9a653622f3625190e875e7b6c5e40023 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 @@ -291,6 +292,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } // Paper end - optimise getPlayerByUUID // Paper start - rewrite chunk system + public final org.bxteam.divinemc.server.chunk.PriorityHandler chunkSystemPriorities; private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder(); private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader chunkLoader = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader((ServerLevel)(Object)this); private final ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController entityDataController; @@ -689,6 +691,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"), @@ -703,6 +706,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.bxteam.divinemc.server.ServerLevelTickExecutorThreadFactory(getWorld().getName())); // DivineMC - Parallel world ticking this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle + this.chunkSystemPriorities = new org.bxteam.divinemc.server.chunk.PriorityHandler(this); // DivineMC - Chunk System optimizations } // Paper start @@ -832,8 +836,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 } @@ -889,7 +892,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) { final LevelChunkSection[] sections = chunk.getSections(); - final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection((ServerLevel)(Object)this); + final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this); // DivineMC - Chunk System optimization final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = this.simpleRandom; final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); @@ -897,42 +900,41 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe final int offsetX = cpos.x << 4; final int offsetZ = cpos.z << 4; + // DivineMC start - Chunk System optimization for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { - final int offsetY = (sectionIndex + minSection) << 4; final LevelChunkSection section = sections[sectionIndex]; - final net.minecraft.world.level.chunk.PalettedContainer states = section.states; if (!section.isRandomlyTickingBlocks()) { continue; } + final int offsetY = (sectionIndex + minSection) << 4; + final net.minecraft.world.level.chunk.PalettedContainer states = section.states; - final ca.spottedleaf.moonrise.common.list.ShortList tickList = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$getTickingBlockList(); + final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList(); for (int i = 0; i < tickSpeed; ++i) { - final int tickingBlocks = tickList.size(); final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1); - if (index >= tickingBlocks) { + if (index >= tickList.size()) { // most of the time we fall here continue; } - final int location = (int)tickList.getRaw(index) & 0xFFFF; + final int location = tickList.getRaw(index); final BlockState state = states.get(location); // do not use a mutable pos, as some random tick implementations store the input without calling immutable()! - final BlockPos pos = new BlockPos((location & 15) | offsetX, ((location >>> (4 + 4)) & 15) | offsetY, ((location >>> 4) & 15) | offsetZ); + final BlockPos pos = new BlockPos((location & 15) | offsetX, (location >>> (4 + 4)) | offsetY, ((location >>> 4) & 15) | offsetZ); - state.randomTick((ServerLevel)(Object)this, pos, simpleRandom); + state.randomTick(this, pos, simpleRandom); if (doubleTickFluids) { final FluidState fluidState = state.getFluidState(); if (fluidState.isRandomlyTicking()) { - fluidState.randomTick((ServerLevel)(Object)this, pos, simpleRandom); + fluidState.randomTick(this, pos, simpleRandom); } } } } - - return; + // DivineMC end - Chunk System optimization } // Paper end - optimise random ticking @@ -2557,30 +2559,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/server/level/ThreadedLevelLightEngine.java b/net/minecraft/server/level/ThreadedLevelLightEngine.java index 5c9ac44a3b4bc8e047feaf61a94eb163761498a2..66dc6d77263d6f5de7d0a96b8b6575e7a363f5bf 100644 --- a/net/minecraft/server/level/ThreadedLevelLightEngine.java +++ b/net/minecraft/server/level/ThreadedLevelLightEngine.java @@ -138,7 +138,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl } } ); - }); + }, world); // DivineMC - Chunk System Optimizations return chunks.size(); } 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/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java index 73962e79a0f3d892e3155443a1b84508b0f4042e..5622345280267792861c8d508d0366cfdfb2c17a 100644 --- a/net/minecraft/world/level/biome/BiomeManager.java +++ b/net/minecraft/world/level/biome/BiomeManager.java @@ -14,6 +14,7 @@ public class BiomeManager { private static final int ZOOM_MASK = 3; private final BiomeManager.NoiseBiomeSource noiseBiomeSource; private final long biomeZoomSeed; + private static final double maxOffset = 0.4500000001D; // DivineMC - Chunk System Optimizations public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { this.noiseBiomeSource = noiseBiomeSource; @@ -29,39 +30,65 @@ public class BiomeManager { } public Holder getBiome(BlockPos pos) { - int i = pos.getX() - 2; - int i1 = pos.getY() - 2; - int i2 = pos.getZ() - 2; - int i3 = i >> 2; - int i4 = i1 >> 2; - int i5 = i2 >> 2; - double d = (i & 3) / 4.0; - double d1 = (i1 & 3) / 4.0; - double d2 = (i2 & 3) / 4.0; - int i6 = 0; - double d3 = Double.POSITIVE_INFINITY; + // DivineMC start - Chunk System Optimizations + int xMinus2 = pos.getX() - 2; + int yMinus2 = pos.getY() - 2; + int zMinus2 = pos.getZ() - 2; + int x = xMinus2 >> 2; + int y = yMinus2 >> 2; + int z = zMinus2 >> 2; + double quartX = (double) (xMinus2 & 3) / 4.0; + double quartY = (double) (yMinus2 & 3) / 4.0; + double quartZ = (double) (zMinus2 & 3) / 4.0; + int smallestX = 0; + double smallestDist = Double.POSITIVE_INFINITY; + for (int biomeX = 0; biomeX < 8; ++biomeX) { + boolean everyOtherQuad = (biomeX & 4) == 0; + boolean everyOtherPair = (biomeX & 2) == 0; + boolean everyOther = (biomeX & 1) == 0; + double quartXX = everyOtherQuad ? quartX : quartX - 1.0; + double quartYY = everyOtherPair ? quartY : quartY - 1.0; + double quartZZ = everyOther ? quartZ : quartZ - 1.0; - for (int i7 = 0; i7 < 8; i7++) { - boolean flag = (i7 & 4) == 0; - boolean flag1 = (i7 & 2) == 0; - boolean flag2 = (i7 & 1) == 0; - int i8 = flag ? i3 : i3 + 1; - int i9 = flag1 ? i4 : i4 + 1; - int i10 = flag2 ? i5 : i5 + 1; - double d4 = flag ? d : d - 1.0; - double d5 = flag1 ? d1 : d1 - 1.0; - double d6 = flag2 ? d2 : d2 - 1.0; - double fiddledDistance = getFiddledDistance(this.biomeZoomSeed, i8, i9, i10, d4, d5, d6); - if (d3 > fiddledDistance) { - i6 = i7; - d3 = fiddledDistance; + double maxQuartYY = 0.0, maxQuartZZ = 0.0; + if (biomeX != 0) { + maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset))); + maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset))); + double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset))); + if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue; } - } + int xx = everyOtherQuad ? x : x + 1; + int yy = everyOtherPair ? y : y + 1; + int zz = everyOther ? z : z + 1; + + long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx); + seed = LinearCongruentialGenerator.next(seed, yy); + seed = LinearCongruentialGenerator.next(seed, zz); + seed = LinearCongruentialGenerator.next(seed, xx); + seed = LinearCongruentialGenerator.next(seed, yy); + seed = LinearCongruentialGenerator.next(seed, zz); + double offsetX = getFiddle(seed); + double sqrX = Mth.square(quartXX + offsetX); + if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue; + seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); + double offsetY = getFiddle(seed); + double sqrY = Mth.square(quartYY + offsetY); + if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue; + seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); + double offsetZ = getFiddle(seed); + double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ); - int i7x = (i6 & 4) == 0 ? i3 : i3 + 1; - int i11 = (i6 & 2) == 0 ? i4 : i4 + 1; - int i12 = (i6 & 1) == 0 ? i5 : i5 + 1; - return this.noiseBiomeSource.getNoiseBiome(i7x, i11, i12); + if (smallestDist > biomeDist) { + smallestX = biomeX; + smallestDist = biomeDist; + } + } + return this.noiseBiomeSource.getNoiseBiome( + (smallestX & 4) == 0 ? x : x + 1, + (smallestX & 2) == 0 ? y : y + 1, + (smallestX & 1) == 0 ? z : z + 1 + ); + // DivineMC end - Chunk System Optimizations } public Holder getNoiseBiomeAtPosition(double x, double y, double z) { diff --git a/net/minecraft/world/level/biome/TheEndBiomeSource.java b/net/minecraft/world/level/biome/TheEndBiomeSource.java index cf3172be76fa4c7987ed569138439ff42f92fa7f..ed3c470056855a520a110ac7014f7839bcc85b88 100644 --- a/net/minecraft/world/level/biome/TheEndBiomeSource.java +++ b/net/minecraft/world/level/biome/TheEndBiomeSource.java @@ -27,6 +27,33 @@ public class TheEndBiomeSource extends BiomeSource { private final Holder islands; private final Holder barrens; + // DivineMC start - Chunk System Optimizations + private Holder getBiomeForNoiseGenVanilla(int x, int y, int z, Climate.Sampler noise) { + int i = QuartPos.toBlock(x); + int j = QuartPos.toBlock(y); + int k = QuartPos.toBlock(z); + int l = SectionPos.blockToSectionCoord(i); + int m = SectionPos.blockToSectionCoord(k); + if ((long)l * (long)l + (long)m * (long)m <= 4096L) { + return this.end; + } else { + int n = (SectionPos.blockToSectionCoord(i) * 2 + 1) * 8; + int o = (SectionPos.blockToSectionCoord(k) * 2 + 1) * 8; + double d = noise.erosion().compute(new DensityFunction.SinglePointContext(n, j, o)); + if (d > 0.25D) { + return this.highlands; + } else if (d >= -0.0625D) { + return this.midlands; + } else { + return d < -0.21875D ? this.islands : this.barrens; + } + } + } + + private final ThreadLocal>> cache = ThreadLocal.withInitial(it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap::new); + private final int cacheCapacity = 1024; + // DivineMC end - Chunk System Optimizations + public static TheEndBiomeSource create(HolderGetter biomeGetter) { return new TheEndBiomeSource( biomeGetter.getOrThrow(Biomes.THE_END), @@ -55,26 +82,24 @@ public class TheEndBiomeSource extends BiomeSource { return CODEC; } + // DivineMC start - Chunk System Optimizations @Override - public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { - int blockPosX = QuartPos.toBlock(x); - int blockPosY = QuartPos.toBlock(y); - int blockPosZ = QuartPos.toBlock(z); - int sectionPosX = SectionPos.blockToSectionCoord(blockPosX); - int sectionPosZ = SectionPos.blockToSectionCoord(blockPosZ); - if ((long)sectionPosX * sectionPosX + (long)sectionPosZ * sectionPosZ <= 4096L) { - return this.end; + public Holder getNoiseBiome(int biomeX, int biomeY, int biomeZ, Climate.Sampler multiNoiseSampler) { + final long key = net.minecraft.world.level.ChunkPos.asLong(biomeX, biomeZ); + final it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap> cacheThreadLocal = cache.get(); + final Holder biome = cacheThreadLocal.get(key); + if (biome != null) { + return biome; } else { - int i = (SectionPos.blockToSectionCoord(blockPosX) * 2 + 1) * 8; - int i1 = (SectionPos.blockToSectionCoord(blockPosZ) * 2 + 1) * 8; - double d = sampler.erosion().compute(new DensityFunction.SinglePointContext(i, blockPosY, i1)); - if (d > 0.25) { - return this.highlands; - } else if (d >= -0.0625) { - return this.midlands; - } else { - return d < -0.21875 ? this.islands : this.barrens; + final Holder gennedBiome = getBiomeForNoiseGenVanilla(biomeX, biomeY, biomeZ, multiNoiseSampler); + cacheThreadLocal.put(key, gennedBiome); + if (cacheThreadLocal.size() > cacheCapacity) { + for (int i = 0; i < cacheCapacity / 16; i ++) { + cacheThreadLocal.removeFirst(); + } } + return gennedBiome; } } + // DivineMC end - Chunk System Optimizations } diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java index c83d0667b19830304f22319a46a23422a8766790..aa10c5087a0fc9306b734f20ccbad73045a1b6d0 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -23,6 +23,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ public short tickingFluidCount; public final PalettedContainer states; private PalettedContainer> biomes; // CraftBukkit - read/write + private static final int sliceSize = 4; // DivineMC - Chunk System Optimizations // Paper start - block counting private static final it.unimi.dsi.fastutil.shorts.ShortArrayList FULL_LIST = new it.unimi.dsi.fastutil.shorts.ShortArrayList(16*16*16); @@ -296,7 +297,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ } public boolean maybeHas(Predicate predicate) { - return this.states.maybeHas(predicate); + return this.states.maybeHasOrCatch(predicate, Blocks.AIR.defaultBlockState()); // DivineMC - Chunk System Optimizations } public Holder getNoiseBiome(int x, int y, int z) { @@ -312,13 +313,15 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ PalettedContainer> palettedContainer = this.biomes.recreate(); int i = 4; - for (int i1 = 0; i1 < 4; i1++) { - for (int i2 = 0; i2 < 4; i2++) { - for (int i3 = 0; i3 < 4; i3++) { - palettedContainer.getAndSetUnchecked(i1, i2, i3, biomeResolver.getNoiseBiome(x + i1, y + i2, z + i3, climateSampler)); + // DivineMC start - Chunk System Optimizations + for (int posY = 0; posY < sliceSize; ++posY) { + for (int posZ = 0; posZ < sliceSize; ++posZ) { + for (int posX = 0; posX < sliceSize; ++posX) { + palettedContainer.getAndSetUnchecked(posX, posY, posZ, biomeResolver.getNoiseBiome(x + posX, y + posY, z + posZ, climateSampler)); } } } + // DivineMC end - Chunk System Optimizations this.biomes = palettedContainer; } diff --git a/net/minecraft/world/level/chunk/LinearPalette.java b/net/minecraft/world/level/chunk/LinearPalette.java index 2073f6ff41aa570102621d183ee890b076267d54..25d46d1dff23a9bffd135d6954b551991e175cd4 100644 --- a/net/minecraft/world/level/chunk/LinearPalette.java +++ b/net/minecraft/world/level/chunk/LinearPalette.java @@ -12,7 +12,7 @@ public class LinearPalette implements Palette, ca.spottedleaf.moonrise.pat private final T[] values; private final PaletteResize resizeHandler; private final int bits; - private int size; + private volatile int size; // DivineMC - Chunk System Optimizations // Paper start - optimise palette reads @Override @@ -49,11 +49,14 @@ public class LinearPalette implements Palette, ca.spottedleaf.moonrise.pat @Override public int idFor(T state) { - for (int i = 0; i < this.size; i++) { - if (this.values[i] == state) { + // DivineMC start - Chunk System Optimizations + final T[] values = this.values; + for (int i = 0; i < values.length; i++) { + if (values[i] == state) { return i; } } + // DivineMC end - Chunk System Optimizations int ix = this.size; if (ix < this.values.length) { @@ -67,17 +70,23 @@ public class LinearPalette implements Palette, ca.spottedleaf.moonrise.pat @Override public boolean maybeHas(Predicate filter) { - for (int i = 0; i < this.size; i++) { - if (filter.test(this.values[i])) { + // DivineMC start - Chunk System Optimizations + final T[] values = this.values; + final int currentSize = this.size; + + for (int i = 0; i < currentSize; i++) { + T value = values[i]; + if (value != null && filter.test(value)) { return true; } } + // DivineMC end - Chunk System Optimizations return false; } @Override - public T valueFor(int id) { + public synchronized T valueFor(int id) { // DivineMC - Chunk System Optimizations if (id >= 0 && id < this.size) { return this.values[id]; } else { diff --git a/net/minecraft/world/level/chunk/Palette.java b/net/minecraft/world/level/chunk/Palette.java index a80b2e9dceea423180a9c390d1970317dff4f1b0..6d9dfc1837dccef2073da180aaaf68b07b04a8e3 100644 --- a/net/minecraft/world/level/chunk/Palette.java +++ b/net/minecraft/world/level/chunk/Palette.java @@ -10,6 +10,12 @@ public interface Palette extends ca.spottedleaf.moonrise.patches.fast_palette boolean maybeHas(Predicate filter); + // DivineMC start - Chunk System Optimizations + public default boolean maybeHasOrCatch(Predicate filter, T defaultValue) { + return this.maybeHas(filter); + } + // DivineMC end - Chunk System Optimizations + T valueFor(int id); void read(FriendlyByteBuf buffer); diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java index 230cb433c38f9b6ffb1adeaa8b6040490f13e826..712d3d1669aecd38934957c81835e1f38289539a 100644 --- a/net/minecraft/world/level/chunk/PalettedContainer.java +++ b/net/minecraft/world/level/chunk/PalettedContainer.java @@ -393,6 +393,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer return this.data.palette.maybeHas(predicate); } + // DivineMC start - Chunk System Optimizations + public boolean maybeHasOrCatch(Predicate predicate, @org.jetbrains.annotations.NotNull T defaultValue) { + return this.data.palette.maybeHasOrCatch(predicate, defaultValue); + } + // DivineMC end - Chunk System Optimizations + @Override public PalettedContainer copy() { return new PalettedContainer<>(this, this.presetValues); // Paper - Anti-Xray - Add preset values 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/SingleValuePalette.java b/net/minecraft/world/level/chunk/SingleValuePalette.java index 2ffae24b0cb1a20c7d5a8520f1b5197c2cedea11..c3ec5e5645f680a915c95d833b589b680c82c35d 100644 --- a/net/minecraft/world/level/chunk/SingleValuePalette.java +++ b/net/minecraft/world/level/chunk/SingleValuePalette.java @@ -11,7 +11,7 @@ import org.apache.commons.lang3.Validate; public class SingleValuePalette implements Palette, ca.spottedleaf.moonrise.patches.fast_palette.FastPalette { // Paper - optimise palette reads private final IdMap registry; @Nullable - private T value; + private volatile T value; // DivineMC - Chunk System Optimizations private final PaletteResize resizeHandler; // Paper start - optimise palette reads @@ -44,6 +44,7 @@ public class SingleValuePalette implements Palette, ca.spottedleaf.moonris if (this.value != null && this.value != state) { return this.resizeHandler.onResize(1, state); } else { + if (state == null) throw new IllegalArgumentException("Null state not allowed"); // DivineMC - Chunk System Optimizations this.value = state; // Paper start - optimise palette reads if (this.rawPalette != null) { @@ -63,6 +64,19 @@ public class SingleValuePalette implements Palette, ca.spottedleaf.moonris } } + // DivineMC start - Chunk System Optimizations + @Override + public boolean maybeHasOrCatch(final Predicate filter, final T defaultValue) { + if (this.value == null) { + if (defaultValue == null) throw new IllegalArgumentException("Default value for 'maybeHasOrCatch' cannot be null!"); + this.value = defaultValue; + return maybeHas(filter); + } else { + return filter.test(this.value); + } + } + // DivineMC end - Chunk System Optimizations + @Override public T valueFor(int id) { if (this.value != null && id == 0) { 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/Aquifer.java b/net/minecraft/world/level/levelgen/Aquifer.java index c62a15ea4a1bb22e7bcc2fc544acf8a601892029..bc67039f8374ae4e471ca14e0c623e6bf334020f 100644 --- a/net/minecraft/world/level/levelgen/Aquifer.java +++ b/net/minecraft/world/level/levelgen/Aquifer.java @@ -85,6 +85,15 @@ public interface Aquifer { private final int minGridZ; private final int gridSizeX; private final int gridSizeZ; + // DivineMC start - Chunk System Optimizations + private int c2me$dist1; + private int c2me$dist2; + private int c2me$dist3; + private long c2me$pos1; + private long c2me$pos2; + private long c2me$pos3; + private double c2me$mutableDoubleThingy; + // DivineMC end - Chunk System Optimizations private static final int[][] SURFACE_SAMPLING_OFFSETS_IN_CHUNKS = new int[][]{ {0, 0}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1} }; @@ -120,6 +129,36 @@ public interface Aquifer { this.aquiferCache = new Aquifer.FluidStatus[i4]; this.aquiferLocationCache = new long[i4]; Arrays.fill(this.aquiferLocationCache, Long.MAX_VALUE); + // DivineMC start - Chunk System Optimizations + if (this.aquiferLocationCache.length % (this.gridSizeX * this.gridSizeZ) != 0) { + throw new AssertionError("Array length"); + } + + int sizeY = this.aquiferLocationCache.length / (this.gridSizeX * this.gridSizeZ); + + final RandomSource random = org.bxteam.divinemc.util.RandomUtil.getRandom(this.positionalRandomFactory); + // index: y, z, x + for (int y = 0; y < sizeY; y++) { + for (int z = 0; z < this.gridSizeZ; z++) { + for (int x = 0; x < this.gridSizeX; x++) { + final int x1 = x + this.minGridX; + final int y1 = y + this.minGridY; + final int z1 = z + this.minGridZ; + org.bxteam.divinemc.util.RandomUtil.derive(this.positionalRandomFactory, random, x1, y1, z1); + int x2 = x1 * 16 + random.nextInt(10); + int y2 = y1 * 12 + random.nextInt(9); + int z2 = z1 * 16 + random.nextInt(10); + int index = this.getIndex(x1, y1, z1); + this.aquiferLocationCache[index] = BlockPos.asLong(x2, y2, z2); + } + } + } + for (long blockPosition : this.aquiferLocationCache) { + if (blockPosition == Long.MAX_VALUE) { + throw new AssertionError("Array initialization"); + } + } + // DivineMC end - Chunk System Optimizations } private int getIndex(int gridX, int gridY, int gridZ) { @@ -132,140 +171,24 @@ public interface Aquifer { @Nullable @Override public BlockState computeSubstance(DensityFunction.FunctionContext context, double substance) { + // DivineMC start - Chunk System Optimizations int i = context.blockX(); - int i1 = context.blockY(); - int i2 = context.blockZ(); + int j = context.blockY(); + int k = context.blockZ(); if (substance > 0.0) { this.shouldScheduleFluidUpdate = false; return null; } else { - Aquifer.FluidStatus fluidStatus = this.globalFluidPicker.computeFluid(i, i1, i2); - if (fluidStatus.at(i1).is(Blocks.LAVA)) { + Aquifer.FluidStatus fluidLevel = this.globalFluidPicker.computeFluid(i, j, k); + if (fluidLevel.at(j).is(Blocks.LAVA)) { this.shouldScheduleFluidUpdate = false; return Blocks.LAVA.defaultBlockState(); } else { - int i3 = Math.floorDiv(i - 5, 16); - int i4 = Math.floorDiv(i1 + 1, 12); - int i5 = Math.floorDiv(i2 - 5, 16); - int i6 = Integer.MAX_VALUE; - int i7 = Integer.MAX_VALUE; - int i8 = Integer.MAX_VALUE; - int i9 = Integer.MAX_VALUE; - long l = 0L; - long l1 = 0L; - long l2 = 0L; - long l3 = 0L; - - for (int i10 = 0; i10 <= 1; i10++) { - for (int i11 = -1; i11 <= 1; i11++) { - for (int i12 = 0; i12 <= 1; i12++) { - int i13 = i3 + i10; - int i14 = i4 + i11; - int i15 = i5 + i12; - int index = this.getIndex(i13, i14, i15); - long l4 = this.aquiferLocationCache[index]; - long l5; - if (l4 != Long.MAX_VALUE) { - l5 = l4; - } else { - RandomSource randomSource = this.positionalRandomFactory.at(i13, i14, i15); - l5 = BlockPos.asLong( - i13 * 16 + randomSource.nextInt(10), i14 * 12 + randomSource.nextInt(9), i15 * 16 + randomSource.nextInt(10) - ); - this.aquiferLocationCache[index] = l5; - } - - int i16 = BlockPos.getX(l5) - i; - int i17 = BlockPos.getY(l5) - i1; - int i18 = BlockPos.getZ(l5) - i2; - int i19 = i16 * i16 + i17 * i17 + i18 * i18; - if (i6 >= i19) { - l3 = l2; - l2 = l1; - l1 = l; - l = l5; - i9 = i8; - i8 = i7; - i7 = i6; - i6 = i19; - } else if (i7 >= i19) { - l3 = l2; - l2 = l1; - l1 = l5; - i9 = i8; - i8 = i7; - i7 = i19; - } else if (i8 >= i19) { - l3 = l2; - l2 = l5; - i9 = i8; - i8 = i19; - } else if (i9 >= i19) { - l3 = l5; - i9 = i19; - } - } - } - } - - Aquifer.FluidStatus aquiferStatus = this.getAquiferStatus(l); - double d = similarity(i6, i7); - BlockState blockState = aquiferStatus.at(i1); - if (d <= 0.0) { - if (d >= FLOWING_UPDATE_SIMULARITY) { - Aquifer.FluidStatus aquiferStatus1 = this.getAquiferStatus(l1); - this.shouldScheduleFluidUpdate = !aquiferStatus.equals(aquiferStatus1); - } else { - this.shouldScheduleFluidUpdate = false; - } - - return blockState; - } else if (blockState.is(Blocks.WATER) && this.globalFluidPicker.computeFluid(i, i1 - 1, i2).at(i1 - 1).is(Blocks.LAVA)) { - this.shouldScheduleFluidUpdate = true; - return blockState; - } else { - MutableDouble mutableDouble = new MutableDouble(Double.NaN); - Aquifer.FluidStatus aquiferStatus2 = this.getAquiferStatus(l1); - double d1 = d * this.calculatePressure(context, mutableDouble, aquiferStatus, aquiferStatus2); - if (substance + d1 > 0.0) { - this.shouldScheduleFluidUpdate = false; - return null; - } else { - Aquifer.FluidStatus aquiferStatus3 = this.getAquiferStatus(l2); - double d2 = similarity(i6, i8); - if (d2 > 0.0) { - double d3 = d * d2 * this.calculatePressure(context, mutableDouble, aquiferStatus, aquiferStatus3); - if (substance + d3 > 0.0) { - this.shouldScheduleFluidUpdate = false; - return null; - } - } - - double d3 = similarity(i7, i8); - if (d3 > 0.0) { - double d4 = d * d3 * this.calculatePressure(context, mutableDouble, aquiferStatus2, aquiferStatus3); - if (substance + d4 > 0.0) { - this.shouldScheduleFluidUpdate = false; - return null; - } - } - - boolean flag = !aquiferStatus.equals(aquiferStatus2); - boolean flag1 = d3 >= FLOWING_UPDATE_SIMULARITY && !aquiferStatus2.equals(aquiferStatus3); - boolean flag2 = d2 >= FLOWING_UPDATE_SIMULARITY && !aquiferStatus.equals(aquiferStatus3); - if (!flag && !flag1 && !flag2) { - this.shouldScheduleFluidUpdate = d2 >= FLOWING_UPDATE_SIMULARITY - && similarity(i6, i9) >= FLOWING_UPDATE_SIMULARITY - && !aquiferStatus.equals(this.getAquiferStatus(l3)); - } else { - this.shouldScheduleFluidUpdate = true; - } - - return blockState; - } - } + aquiferExtracted$refreshDistPosIdx(i, j, k); + return aquiferExtracted$applyPost(context, substance, j, i, k); } } + // DivineMC end - Chunk System Optimizations } @Override @@ -278,65 +201,28 @@ public interface Aquifer { return 1.0 - Math.abs(secondDistance - firstDistance) / 25.0; } + // DivineMC start - Chunk System Optimizations private double calculatePressure( - DensityFunction.FunctionContext context, MutableDouble substance, Aquifer.FluidStatus firstFluid, Aquifer.FluidStatus secondFluid + DensityFunction.FunctionContext context, MutableDouble substance, Aquifer.FluidStatus fluidLevel, Aquifer.FluidStatus fluidLevel2 // DivineMC - rename args ) { int i = context.blockY(); - BlockState blockState = firstFluid.at(i); - BlockState blockState1 = secondFluid.at(i); - if ((!blockState.is(Blocks.LAVA) || !blockState1.is(Blocks.WATER)) && (!blockState.is(Blocks.WATER) || !blockState1.is(Blocks.LAVA))) { - int abs = Math.abs(firstFluid.fluidLevel - secondFluid.fluidLevel); + BlockState blockState = fluidLevel.at(i); + BlockState blockState2 = fluidLevel2.at(i); + if ((!blockState.is(Blocks.LAVA) || !blockState2.is(Blocks.WATER)) && (!blockState.is(Blocks.WATER) || !blockState2.is(Blocks.LAVA))) { + int abs = Math.abs(fluidLevel.fluidLevel - fluidLevel2.fluidLevel); if (abs == 0) { return 0.0; } else { - double d = 0.5 * (firstFluid.fluidLevel + secondFluid.fluidLevel); - double d1 = i + 0.5 - d; - double d2 = abs / 2.0; - double d3 = 0.0; - double d4 = 2.5; - double d5 = 1.5; - double d6 = 3.0; - double d7 = 10.0; - double d8 = 3.0; - double d9 = d2 - Math.abs(d1); - double d11; - if (d1 > 0.0) { - double d10 = 0.0 + d9; - if (d10 > 0.0) { - d11 = d10 / 1.5; - } else { - d11 = d10 / 2.5; - } - } else { - double d10 = 3.0 + d9; - if (d10 > 0.0) { - d11 = d10 / 3.0; - } else { - d11 = d10 / 10.0; - } - } - - double d10x = 2.0; - double d12; - if (!(d11 < -2.0) && !(d11 > 2.0)) { - double value = substance.getValue(); - if (Double.isNaN(value)) { - double d13 = this.barrierNoise.compute(context); - substance.setValue(d13); - d12 = d13; - } else { - d12 = value; - } - } else { - d12 = 0.0; - } + double d = 0.5 * (double)(fluidLevel.fluidLevel + fluidLevel2.fluidLevel); + final double q = aquiferExtracted$getQ(i, d, abs); - return 2.0 * (d12 + d11); + return aquiferExtracted$postCalculateDensity(context, substance, q); } } else { return 2.0; } } + // DivineMC end - Chunk System Optimizations private int gridX(int x) { return Math.floorDiv(x, 16); @@ -350,23 +236,25 @@ public interface Aquifer { return Math.floorDiv(z, 16); } - private Aquifer.FluidStatus getAquiferStatus(long packedPos) { - int x = BlockPos.getX(packedPos); - int y = BlockPos.getY(packedPos); - int z = BlockPos.getZ(packedPos); - int i = this.gridX(x); - int i1 = this.gridY(y); - int i2 = this.gridZ(z); - int index = this.getIndex(i, i1, i2); - Aquifer.FluidStatus fluidStatus = this.aquiferCache[index]; - if (fluidStatus != null) { - return fluidStatus; + // DivineMC start - Chunk System Optimizations + private Aquifer.FluidStatus getAquiferStatus(long pos) { + int i = BlockPos.getX(pos); + int j = BlockPos.getY(pos); + int k = BlockPos.getZ(pos); + int l = i >> 4; // C2ME - inline: floorDiv(i, 16) + int m = Math.floorDiv(j, 12); // C2ME - inline + int n = k >> 4; // C2ME - inline: floorDiv(k, 16) + int o = this.getIndex(l, m, n); + Aquifer.FluidStatus fluidLevel = this.aquiferCache[o]; + if (fluidLevel != null) { + return fluidLevel; } else { - Aquifer.FluidStatus fluidStatus1 = this.computeFluid(x, y, z); - this.aquiferCache[index] = fluidStatus1; - return fluidStatus1; + Aquifer.FluidStatus fluidLevel2 = this.computeFluid(i, j, k); + this.aquiferCache[o] = fluidLevel2; + return fluidLevel2; } } + // DivineMC end - Chunk System Optimizations private Aquifer.FluidStatus computeFluid(int x, int y, int z) { Aquifer.FluidStatus fluidStatus = this.globalFluidPicker.computeFluid(x, y, z); @@ -407,22 +295,21 @@ public interface Aquifer { } private int computeSurfaceLevel(int x, int y, int z, Aquifer.FluidStatus fluidStatus, int maxSurfaceLevel, boolean fluidPresent) { - DensityFunction.SinglePointContext singlePointContext = new DensityFunction.SinglePointContext(x, y, z); + // DivineMC start - Chunk System Optimizations + DensityFunction.SinglePointContext unblendedNoisePos = new DensityFunction.SinglePointContext(x, y, z); double d; double d1; - if (OverworldBiomeBuilder.isDeepDarkRegion(this.erosion, this.depth, singlePointContext)) { + if (OverworldBiomeBuilder.isDeepDarkRegion(this.erosion, this.depth, unblendedNoisePos)) { d = -1.0; d1 = -1.0; } else { int i = maxSurfaceLevel + 8 - y; - int i1 = 64; - double d2 = fluidPresent ? Mth.clampedMap((double)i, 0.0, 64.0, 1.0, 0.0) : 0.0; - double d3 = Mth.clamp(this.fluidLevelFloodednessNoise.compute(singlePointContext), -1.0, 1.0); - double d4 = Mth.map(d2, 1.0, 0.0, -0.3, 0.8); - double d5 = Mth.map(d2, 1.0, 0.0, -0.8, 0.4); - d = d3 - d5; - d1 = d3 - d4; + double f = fluidPresent ? Mth.clampedLerp(1.0, 0.0, ((double) i) / 64.0) : 0.0; // inline + double g = Mth.clamp(this.fluidLevelFloodednessNoise.compute(unblendedNoisePos), -1.0, 1.0); + d = g + 0.8 + (f - 1.0) * 1.2; // inline + d1 = g + 0.3 + (f - 1.0) * 1.1; // inline } + // DivineMC end - Chunk System Optimizations int i; if (d1 > 0.0) { @@ -453,12 +340,12 @@ public interface Aquifer { private BlockState computeFluidType(int x, int y, int z, Aquifer.FluidStatus fluidStatus, int surfaceLevel) { BlockState blockState = fluidStatus.fluidType; if (surfaceLevel <= -10 && surfaceLevel != DimensionType.WAY_BELOW_MIN_Y && fluidStatus.fluidType != Blocks.LAVA.defaultBlockState()) { - int i = 64; - int i1 = 40; - int i2 = Math.floorDiv(x, 64); - int i3 = Math.floorDiv(y, 40); - int i4 = Math.floorDiv(z, 64); - double d = this.lavaNoise.compute(new DensityFunction.SinglePointContext(i2, i3, i4)); + // DivineMC start - Chunk System Optimizations + int k = x >> 6; + int l = Math.floorDiv(y, 40); + int m = z >> 6; + double d = this.lavaNoise.compute(new DensityFunction.SinglePointContext(k, l, m)); + // DivineMC end - Chunk System Optimizations if (Math.abs(d) > 0.3) { blockState = Blocks.LAVA.defaultBlockState(); } @@ -466,5 +353,183 @@ public interface Aquifer { return blockState; } + + // DivineMC start - Chunk System Optimizations + private @org.jetbrains.annotations.Nullable BlockState aquiferExtracted$applyPost(DensityFunction.FunctionContext pos, double density, int j, int i, int k) { + Aquifer.FluidStatus fluidLevel2 = this.getAquiferStatus(this.c2me$pos1); + double d = similarity(this.c2me$dist1, this.c2me$dist2); + BlockState blockState = fluidLevel2.at(j); + if (d <= 0.0) { + this.shouldScheduleFluidUpdate = d >= FLOWING_UPDATE_SIMULARITY; + return blockState; + } else if (blockState.is(Blocks.WATER) && this.globalFluidPicker.computeFluid(i, j - 1, k).at(j - 1).is(Blocks.LAVA)) { + this.shouldScheduleFluidUpdate = true; + return blockState; + } else { + this.c2me$mutableDoubleThingy = Double.NaN; + Aquifer.FluidStatus fluidLevel3 = this.getAquiferStatus(this.c2me$pos2); + double e = d * this.c2me$calculateDensityModified(pos, fluidLevel2, fluidLevel3); + if (density + e > 0.0) { + this.shouldScheduleFluidUpdate = false; + return null; + } else { + return aquiferExtracted$getFinalBlockState(pos, density, d, fluidLevel2, fluidLevel3, blockState); + } + } + } + + private BlockState aquiferExtracted$getFinalBlockState(DensityFunction.FunctionContext pos, double density, double d, Aquifer.FluidStatus fluidLevel2, Aquifer.FluidStatus fluidLevel3, BlockState blockState) { + Aquifer.FluidStatus fluidLevel4 = this.getAquiferStatus(this.c2me$pos3); + double f = similarity(this.c2me$dist1, this.c2me$dist3); + if (aquiferExtracted$extractedCheckFG(pos, density, d, fluidLevel2, f, fluidLevel4)) return null; + + double g = similarity(this.c2me$dist2, this.c2me$dist3); + if (aquiferExtracted$extractedCheckFG(pos, density, d, fluidLevel3, g, fluidLevel4)) return null; + + this.shouldScheduleFluidUpdate = true; + return blockState; + } + + private boolean aquiferExtracted$extractedCheckFG(DensityFunction.FunctionContext pos, double density, double d, Aquifer.FluidStatus fluidLevel2, double f, Aquifer.FluidStatus fluidLevel4) { + if (f > 0.0) { + double g = d * f * this.c2me$calculateDensityModified(pos, fluidLevel2, fluidLevel4); + if (density + g > 0.0) { + this.shouldScheduleFluidUpdate = false; + return true; + } + } + return false; + } + + private void aquiferExtracted$refreshDistPosIdx(int x, int y, int z) { + int gx = (x - 5) >> 4; + int gy = Math.floorDiv(y + 1, 12); + int gz = (z - 5) >> 4; + int dist1 = Integer.MAX_VALUE; + int dist2 = Integer.MAX_VALUE; + int dist3 = Integer.MAX_VALUE; + long pos1 = 0; + long pos2 = 0; + long pos3 = 0; + + for (int offY = -1; offY <= 1; ++offY) { + for (int offZ = 0; offZ <= 1; ++offZ) { + for (int offX = 0; offX <= 1; ++offX) { + int posIdx = this.getIndex(gx + offX, gy + offY, gz + offZ); + + long position = this.aquiferLocationCache[posIdx]; + + int dx = BlockPos.getX(position) - x; + int dy = BlockPos.getY(position) - y; + int dz = BlockPos.getZ(position) - z; + int dist = dx * dx + dy * dy + dz * dz; + + if (dist3 >= dist) { + pos3 = position; + dist3 = dist; + } + if (dist2 >= dist) { + pos3 = pos2; + dist3 = dist2; + pos2 = position; + dist2 = dist; + } + if (dist1 >= dist) { + pos2 = pos1; + dist2 = dist1; + pos1 = position; + dist1 = dist; + } + } + } + } + + this.c2me$dist1 = dist1; + this.c2me$dist2 = dist2; + this.c2me$dist3 = dist3; + this.c2me$pos1 = pos1; + this.c2me$pos2 = pos2; + this.c2me$pos3 = pos3; + } + + private double c2me$calculateDensityModified( + DensityFunction.FunctionContext pos, Aquifer.FluidStatus fluidLevel, Aquifer.FluidStatus fluidLevel2 + ) { + int i = pos.blockY(); + BlockState blockState = fluidLevel.at(i); + BlockState blockState2 = fluidLevel2.at(i); + if ((!blockState.is(Blocks.LAVA) || !blockState2.is(Blocks.WATER)) && (!blockState.is(Blocks.WATER) || !blockState2.is(Blocks.LAVA))) { + int j = Math.abs(fluidLevel.fluidLevel - fluidLevel2.fluidLevel); + if (j == 0) { + return 0.0; + } else { + double d = 0.5 * (double)(fluidLevel.fluidLevel + fluidLevel2.fluidLevel); + final double q = aquiferExtracted$getQ(i, d, j); + + return aquiferExtracted$postCalculateDensityModified(pos, q); + } + } else { + return 2.0; + } + } + + private double aquiferExtracted$postCalculateDensity(DensityFunction.FunctionContext pos, MutableDouble mutableDouble, double q) { + double r; + if (!(q < -2.0) && !(q > 2.0)) { + double s = mutableDouble.getValue(); + if (Double.isNaN(s)) { + double t = this.barrierNoise.compute(pos); + mutableDouble.setValue(t); + r = t; + } else { + r = s; + } + } else { + r = 0.0; + } + + return 2.0 * (r + q); + } + + private double aquiferExtracted$postCalculateDensityModified(DensityFunction.FunctionContext pos, double q) { + double r; + if (!(q < -2.0) && !(q > 2.0)) { + double s = this.c2me$mutableDoubleThingy; + if (Double.isNaN(s)) { + double t = this.barrierNoise.compute(pos); + this.c2me$mutableDoubleThingy = t; + r = t; + } else { + r = s; + } + } else { + r = 0.0; + } + + return 2.0 * (r + q); + } + + private static double aquiferExtracted$getQ(double i, double d, double j) { + double e = i + 0.5 - d; + double f = j / 2.0; + double o = f - Math.abs(e); + double q; + if (e > 0.0) { + if (o > 0.0) { + q = o / 1.5; + } else { + q = o / 2.5; + } + } else { + double p = 3.0 + o; + if (p > 0.0) { + q = p / 3.0; + } else { + q = p / 10.0; + } + } + return q; + } + // DivineMC end - Chunk System Optimizations } } diff --git a/net/minecraft/world/level/levelgen/Beardifier.java b/net/minecraft/world/level/levelgen/Beardifier.java index 131923282c9ecbcb1d7f45a826da907c02bd2716..1af75406ba69c5eec4a41fe7a8dce0d07c259099 100644 --- a/net/minecraft/world/level/levelgen/Beardifier.java +++ b/net/minecraft/world/level/levelgen/Beardifier.java @@ -29,6 +29,17 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { }); private final ObjectListIterator pieceIterator; private final ObjectListIterator junctionIterator; + // DivineMC start - Chunk System Optimizations + private Beardifier.Rigid[] c2me$pieceArray; + private JigsawJunction[] c2me$junctionArray; + + private void c2me$initArrays() { + this.c2me$pieceArray = com.google.common.collect.Iterators.toArray(this.pieceIterator, Beardifier.Rigid.class); + this.pieceIterator.back(Integer.MAX_VALUE); + this.c2me$junctionArray = com.google.common.collect.Iterators.toArray(this.junctionIterator, JigsawJunction.class); + this.junctionIterator.back(Integer.MAX_VALUE); + } + // DivineMC end - Chunk System Optimizations public static Beardifier forStructuresInChunk(StructureManager structureManager, ChunkPos chunkPos) { int minBlockX = chunkPos.getMinBlockX(); @@ -76,50 +87,44 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { this.junctionIterator = junctionIterator; } + // DivineMC start - Chunk System Optimizations @Override public double compute(DensityFunction.FunctionContext context) { + if (this.c2me$pieceArray == null || this.c2me$junctionArray == null) { + this.c2me$initArrays(); + } int i = context.blockX(); - int i1 = context.blockY(); - int i2 = context.blockZ(); + int j = context.blockY(); + int k = context.blockZ(); double d = 0.0; - while (this.pieceIterator.hasNext()) { - Beardifier.Rigid rigid = this.pieceIterator.next(); - BoundingBox boundingBox = rigid.box(); - int groundLevelDelta = rigid.groundLevelDelta(); - int max = Math.max(0, Math.max(boundingBox.minX() - i, i - boundingBox.maxX())); - int max1 = Math.max(0, Math.max(boundingBox.minZ() - i2, i2 - boundingBox.maxZ())); - int i3 = boundingBox.minY() + groundLevelDelta; - int i4 = i1 - i3; - - int i5 = switch (rigid.terrainAdjustment()) { - case NONE -> 0; - case BURY, BEARD_THIN -> i4; - case BEARD_BOX -> Math.max(0, Math.max(i3 - i1, i1 - boundingBox.maxY())); - case ENCAPSULATE -> Math.max(0, Math.max(boundingBox.minY() - i1, i1 - boundingBox.maxY())); - }; + for (Beardifier.Rigid piece : this.c2me$pieceArray) { + BoundingBox blockBox = piece.box(); + int l = piece.groundLevelDelta(); + int m = Math.max(0, Math.max(blockBox.minX() - i, i - blockBox.maxX())); + int n = Math.max(0, Math.max(blockBox.minZ() - k, k - blockBox.maxZ())); + int o = blockBox.minY() + l; + int p = j - o; - d += switch (rigid.terrainAdjustment()) { + d += switch (piece.terrainAdjustment()) { // 2 switch statement merged case NONE -> 0.0; - case BURY -> getBuryContribution(max, i5 / 2.0, max1); - case BEARD_THIN, BEARD_BOX -> getBeardContribution(max, i5, max1, i4) * 0.8; - case ENCAPSULATE -> getBuryContribution(max / 2.0, i5 / 2.0, max1 / 2.0) * 0.8; + case BURY -> getBuryContribution(m, (double)p / 2.0, n); + case BEARD_THIN -> getBeardContribution(m, p, n, p) * 0.8; + case BEARD_BOX -> getBeardContribution(m, Math.max(0, Math.max(o - j, j - blockBox.maxY())), n, p) * 0.8; + case ENCAPSULATE -> getBuryContribution((double)m / 2.0, (double)Math.max(0, Math.max(blockBox.minY() - j, j - blockBox.maxY())) / 2.0, (double)n / 2.0) * 0.8; }; } - this.pieceIterator.back(Integer.MAX_VALUE); - - while (this.junctionIterator.hasNext()) { - JigsawJunction jigsawJunction = this.junctionIterator.next(); - int i6 = i - jigsawJunction.getSourceX(); - int groundLevelDelta = i1 - jigsawJunction.getSourceGroundY(); - int max = i2 - jigsawJunction.getSourceZ(); - d += getBeardContribution(i6, groundLevelDelta, max, groundLevelDelta) * 0.4; + for (JigsawJunction jigsawJunction : this.c2me$junctionArray) { + int r = i - jigsawJunction.getSourceX(); + int l = j - jigsawJunction.getSourceGroundY(); + int m = k - jigsawJunction.getSourceZ(); + d += getBeardContribution(r, l, m, l) * 0.4; } - this.junctionIterator.back(Integer.MAX_VALUE); return d; } + // DivineMC end - Chunk System Optimizations @Override public double minValue() { @@ -132,8 +137,14 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { } private static double getBuryContribution(double x, double y, double z) { - double len = Mth.length(x, y, z); - return Mth.clampedMap(len, 0.0, 6.0, 1.0, 0.0); + // DivineMC start - Chunk System Optimizations + double d = Math.sqrt(x * x + y * y + z * z); + if (d > 6.0) { + return 0.0; + } else { + return 1.0 - d / 6.0; + } + // DivineMC end - Chunk System Optimizations } private static double getBeardContribution(int x, int y, int z, int height) { diff --git a/net/minecraft/world/level/levelgen/BelowZeroRetrogen.java b/net/minecraft/world/level/levelgen/BelowZeroRetrogen.java index 4993ace2b3d615570de3d4b6621aeba3a3e7fe99..1be79332446559c95ae3048a71a6634fd01cf2e2 100644 --- a/net/minecraft/world/level/levelgen/BelowZeroRetrogen.java +++ b/net/minecraft/world/level/levelgen/BelowZeroRetrogen.java @@ -82,6 +82,7 @@ public final class BelowZeroRetrogen { } public void applyBedrockMask(ProtoChunk chunk) { + if (org.bxteam.divinemc.DivineConfig.smoothBedrockLayer) return; // DivineMC - Smooth bedrock layer LevelHeightAccessor heightAccessorForGeneration = chunk.getHeightAccessorForGeneration(); int minY = heightAccessorForGeneration.getMinY(); int maxY = heightAccessorForGeneration.getMaxY(); 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/LegacyRandomSource.java b/net/minecraft/world/level/levelgen/LegacyRandomSource.java index c67168517774a0ad9ca43422a79ef14a8ea0c2e8..026dfbbb6c3fd5cd274dcbf721e5cf3af889e3d9 100644 --- a/net/minecraft/world/level/levelgen/LegacyRandomSource.java +++ b/net/minecraft/world/level/levelgen/LegacyRandomSource.java @@ -53,13 +53,7 @@ public class LegacyRandomSource implements BitRandomSource { return this.gaussianSource.nextGaussian(); } - public static class LegacyPositionalRandomFactory implements PositionalRandomFactory { - private final long seed; - - public LegacyPositionalRandomFactory(long seed) { - this.seed = seed; - } - + public record LegacyPositionalRandomFactory(long seed) implements PositionalRandomFactory { // DivineMC - make record @Override public RandomSource at(int x, int y, int z) { long seed = Mth.getSeed(x, y, z); diff --git a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java index 65728ef17e63d71833677fdcbd5bb90794b4822b..eb61a3c995afe5af1cd385826e882acf441e2785 100644 --- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java @@ -65,11 +65,13 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { } private static Aquifer.FluidPicker createFluidPicker(NoiseGeneratorSettings settings) { - Aquifer.FluidStatus fluidStatus = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState()); - int seaLevel = settings.seaLevel(); - Aquifer.FluidStatus fluidStatus1 = new Aquifer.FluidStatus(seaLevel, settings.defaultFluid()); - Aquifer.FluidStatus fluidStatus2 = new Aquifer.FluidStatus(DimensionType.MIN_Y * 2, Blocks.AIR.defaultBlockState()); - return (x, y, z) -> y < Math.min(-54, seaLevel) ? fluidStatus : fluidStatus1; + // DivineMC start - Chunk System Optimizations + Aquifer.FluidStatus fluidLevel = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState()); + int i = settings.seaLevel(); + Aquifer.FluidStatus fluidLevel2 = new Aquifer.FluidStatus(i, settings.defaultFluid()); + final int min = Math.min(-54, i); + return (j, k, lx) -> k < min ? fluidLevel : fluidLevel2; + // DivineMC end - Chunk System Optimizations } @Override @@ -294,30 +296,32 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunk) { NoiseSettings noiseSettings = this.settings.value().noiseSettings().clampToHeightAccessor(chunk.getHeightAccessorForGeneration()); int minY = noiseSettings.minY(); - int i = Mth.floorDiv(minY, noiseSettings.getCellHeight()); - int i1 = Mth.floorDiv(noiseSettings.height(), noiseSettings.getCellHeight()); - return i1 <= 0 ? CompletableFuture.completedFuture(chunk) : CompletableFuture.supplyAsync(() -> { - int sectionIndex = chunk.getSectionIndex(i1 * noiseSettings.getCellHeight() - 1 + minY); - int sectionIndex1 = chunk.getSectionIndex(minY); - Set set = Sets.newHashSet(); - - for (int i2 = sectionIndex; i2 >= sectionIndex1; i2--) { - LevelChunkSection section = chunk.getSection(i2); - section.acquire(); - set.add(section); - } + // DivineMC start - Optimize noise fill + int minYDiv = Mth.floorDiv(minY, noiseSettings.getCellHeight()); + int cellHeightDiv = Mth.floorDiv(noiseSettings.height(), noiseSettings.getCellHeight()); - ChunkAccess var20; - try { - var20 = this.doFill(blender, structureManager, randomState, chunk, i, i1); - } finally { - for (LevelChunkSection levelChunkSection1 : set) { - levelChunkSection1.release(); - } + if (cellHeightDiv <= 0) { + return CompletableFuture.completedFuture(chunk); + } + + try { + int startIndex = chunk.getSectionIndex(cellHeightDiv * noiseSettings.getCellHeight() - 1 + minY); + int minYIndex = chunk.getSectionIndex(minY); + LevelChunkSection[] sections = chunk.getSections(); + + for (int i = startIndex; i >= minYIndex; --i) { + sections[i].acquire(); } - return var20; - }, Runnable::run); // Paper - rewrite chunk system + ChunkAccess access = this.doFill(blender, structureManager, randomState, chunk, minYDiv, cellHeightDiv); + for (int i = startIndex; i >= minYIndex; --i) { + sections[i].release(); + } + return CompletableFuture.completedFuture(access); + } catch (Throwable throwable) { + throw new RuntimeException("Unexpected error when running noise fill", throwable); + } + // DivineMC end - Optimize noise fill } private ChunkAccess doFill(Blender blender, StructureManager structureManager, RandomState random, ChunkAccess chunk, int minCellY, int cellCountY) { @@ -375,7 +379,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { interpolatedState = this.debugPreliminarySurfaceLevel(noiseChunk, i10, i7, i13, interpolatedState); if (interpolatedState != AIR && !SharedConstants.debugVoidTerrain(chunk.getPos())) { - section.setBlockState(i11, i8, i14, interpolatedState, false); + optimizedBlockSetOp(section, i11, i8, i14, interpolatedState, false); // DivineMC - Optimize noise fill heightmapUnprimed.update(i11, i7, i14, interpolatedState); heightmapUnprimed1.update(i11, i7, i14, interpolatedState); if (aquifer.shouldScheduleFluidUpdate() && !interpolatedState.getFluidState().isEmpty()) { @@ -396,6 +400,26 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { return chunk; } + // DivineMC start - Optimize noise fill + private void optimizedBlockSetOp(@org.jetbrains.annotations.NotNull LevelChunkSection chunkSection, int chunkSectionBlockPosX, int chunkSectionBlockPosY, int chunkSectionBlockPosZ, @org.jetbrains.annotations.NotNull BlockState blockState, boolean lock) { + chunkSection.nonEmptyBlockCount += 1; + + if (!blockState.getFluidState().isEmpty()) { + chunkSection.tickingFluidCount += 1; + } + + if (blockState.isRandomlyTicking()) { + chunkSection.tickingBlockCount += 1; + } + + var blockStateId = chunkSection.states.data.palette.idFor(blockState); + chunkSection.states.data.storage().set( + chunkSection.states.strategy.getIndex(chunkSectionBlockPosX, chunkSectionBlockPosY, + chunkSectionBlockPosZ + ), blockStateId); + } + // DivineMC end - Optimize noise fill + private BlockState debugPreliminarySurfaceLevel(NoiseChunk chunk, int x, int y, int z, BlockState state) { return state; } diff --git a/net/minecraft/world/level/levelgen/NoiseSettings.java b/net/minecraft/world/level/levelgen/NoiseSettings.java index 4cf3a364595ba5f81f741295695cb9a449bdf672..44df2ac0bd972c4d97fc89cd0c2d2d83480ca3e1 100644 --- a/net/minecraft/world/level/levelgen/NoiseSettings.java +++ b/net/minecraft/world/level/levelgen/NoiseSettings.java @@ -8,7 +8,7 @@ import net.minecraft.core.QuartPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.dimension.DimensionType; -public record NoiseSettings(int minY, int height, int noiseSizeHorizontal, int noiseSizeVertical) { +public record NoiseSettings(int minY, int height, int noiseSizeHorizontal, int noiseSizeVertical, int horizontalCellBlockCount, int verticalCellBlockCount) { // DivineMC - NoiseSettings optimizations public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( Codec.intRange(DimensionType.MIN_Y, DimensionType.MAX_Y).fieldOf("min_y").forGetter(NoiseSettings::minY), @@ -16,7 +16,10 @@ public record NoiseSettings(int minY, int height, int noiseSizeHorizontal, int n Codec.intRange(1, 4).fieldOf("size_horizontal").forGetter(NoiseSettings::noiseSizeHorizontal), Codec.intRange(1, 4).fieldOf("size_vertical").forGetter(NoiseSettings::noiseSizeVertical) ) - .apply(instance, NoiseSettings::new) + // DivineMC start - NoiseSettings optimizations + .apply(instance, (Integer minY1, Integer height1, Integer noiseSizeHorizontal1, Integer noiseSizeVertical1) -> new NoiseSettings(minY1, height1, noiseSizeHorizontal1, noiseSizeVertical1, + QuartPos.toBlock(noiseSizeHorizontal1), QuartPos.toBlock(noiseSizeVertical1))) + // DivineMC end - NoiseSettings optimizations ) .comapFlatMap(NoiseSettings::guardY, Function.identity()); protected static final NoiseSettings OVERWORLD_NOISE_SETTINGS = create(-64, 384, 1, 2); @@ -36,7 +39,7 @@ public record NoiseSettings(int minY, int height, int noiseSizeHorizontal, int n } public static NoiseSettings create(int minY, int height, int noiseSizeHorizontal, int noiseSizeVertical) { - NoiseSettings noiseSettings = new NoiseSettings(minY, height, noiseSizeHorizontal, noiseSizeVertical); + NoiseSettings noiseSettings = new NoiseSettings(minY, height, noiseSizeHorizontal, noiseSizeVertical, QuartPos.toBlock(noiseSizeHorizontal), QuartPos.toBlock(noiseSizeVertical)); // DivineMC - NoiseSettings optimizations guardY(noiseSettings).error().ifPresent(error -> { throw new IllegalStateException(error.message()); }); @@ -44,16 +47,16 @@ public record NoiseSettings(int minY, int height, int noiseSizeHorizontal, int n } public int getCellHeight() { - return QuartPos.toBlock(this.noiseSizeVertical()); + return verticalCellBlockCount(); // DivineMC - NoiseSettings optimizations } public int getCellWidth() { - return QuartPos.toBlock(this.noiseSizeHorizontal()); + return horizontalCellBlockCount(); // DivineMC - NoiseSettings optimizations } public NoiseSettings clampToHeightAccessor(LevelHeightAccessor heightAccessor) { int max = Math.max(this.minY, heightAccessor.getMinY()); int i = Math.min(this.minY + this.height, heightAccessor.getMaxY() + 1) - max; - return new NoiseSettings(max, i, this.noiseSizeHorizontal, this.noiseSizeVertical); + return new NoiseSettings(max, i, this.noiseSizeHorizontal, this.noiseSizeVertical, QuartPos.toBlock(this.noiseSizeHorizontal), QuartPos.toBlock(this.noiseSizeVertical)); // DivineMC - NoiseSettings optimizations } } diff --git a/net/minecraft/world/level/levelgen/SurfaceRules.java b/net/minecraft/world/level/levelgen/SurfaceRules.java index bbf2995d352c22b3f6fb0de40f2932af1771c504..af852376b61d127a5e251a7a6fcb35c25d1d4e1f 100644 --- a/net/minecraft/world/level/levelgen/SurfaceRules.java +++ b/net/minecraft/world/level/levelgen/SurfaceRules.java @@ -185,7 +185,7 @@ public class SurfaceRules { @Override protected boolean compute() { - return this.context.biome.get().is(BiomeConditionSource.this.biomeNameTest); + return this.context.biome.is(BiomeConditionSource.this.biomeNameTest); // DivineMC - Chunk System Optimizations } } @@ -281,7 +281,7 @@ public class SurfaceRules { private int minSurfaceLevel; long lastUpdateY = -9223372036854775807L; final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); - Supplier> biome; + Holder biome; // DivineMC - Chunk System Optimizations public int blockY; int waterHeight; int stoneDepthBelow; @@ -314,7 +314,10 @@ public class SurfaceRules { protected void updateY(int stoneDepthAbove, int stoneDepthBelow, int waterHeight, int blockX, int blockY, int blockZ) { this.lastUpdateY++; - this.biome = Suppliers.memoize(() -> this.biomeGetter.apply(this.pos.set(blockX, blockY, blockZ))); + // DivineMC start - Chunk System Optimizations + this.pos.set(blockX, blockY, blockZ); + this.biome = this.biomeGetter.apply(this.pos); + // DivineMC end - Chunk System Optimizations this.blockY = blockY; this.waterHeight = waterHeight; this.stoneDepthBelow = stoneDepthBelow; @@ -441,7 +444,6 @@ public class SurfaceRules { protected boolean compute() { return this.context .biome - .get() .value() .coldEnoughToSnow(this.context.pos.set(this.context.blockX, this.context.blockY, this.context.blockZ), this.context.getSeaLevel()); } 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/XoroshiroRandomSource.java b/net/minecraft/world/level/levelgen/XoroshiroRandomSource.java index 9d3a9ca1e13cd80f468f1352bbb74345f03903dd..d97b9b43686bda0a95fc02f6ca31b2d07d603a32 100644 --- a/net/minecraft/world/level/levelgen/XoroshiroRandomSource.java +++ b/net/minecraft/world/level/levelgen/XoroshiroRandomSource.java @@ -106,15 +106,7 @@ public class XoroshiroRandomSource implements RandomSource { return this.randomNumberGenerator.nextLong() >>> 64 - bits; } - public static class XoroshiroPositionalRandomFactory implements PositionalRandomFactory { - private final long seedLo; - private final long seedHi; - - public XoroshiroPositionalRandomFactory(long seedLo, long seedHi) { - this.seedLo = seedLo; - this.seedHi = seedHi; - } - + public record XoroshiroPositionalRandomFactory(long seedLo, long seedHi) implements PositionalRandomFactory { // DivineMC - make record @Override public RandomSource at(int x, int y, int z) { long seed = Mth.getSeed(x, y, z); diff --git a/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java b/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java index f4009671880b00ecec98fe604215e2824e453cdf..7b00301da0b3fb6429d04e4d10cafa30e168aa62 100644 --- a/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java +++ b/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java @@ -54,18 +54,21 @@ public class RandomizedIntStateProvider extends BlockStateProvider { } @Override + @SuppressWarnings("DataFlowIssue") 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; + 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)); } @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/StructureCheck.java b/net/minecraft/world/level/levelgen/structure/StructureCheck.java index f7dc4957b38878ddd3bfc7546be8a4e0af65c807..4023115e8120a6ccbc2a5bf7f2d17ffb2f808eb7 100644 --- a/net/minecraft/world/level/levelgen/structure/StructureCheck.java +++ b/net/minecraft/world/level/levelgen/structure/StructureCheck.java @@ -46,6 +46,7 @@ public class StructureCheck { private final LevelHeightAccessor heightAccessor; private final BiomeSource biomeSource; private final long seed; + private Object mapMutex = new Object(); // DivineMC - Chunk System Optimizations private final DataFixer fixerUpper; // Paper start - rewrite chunk system // make sure to purge entries from the maps to prevent memory leaks @@ -235,15 +236,13 @@ public class StructureCheck { } private void storeFullResults(long chunkPos, Object2IntMap structureChunks) { - // Paper start - rewrite chunk system + // DivineMC start - Chunk System Optimizations this.loadedChunksSafe.put(chunkPos, deduplicateEmptyMap(structureChunks)); - // once we insert into loadedChunks, we don't really need to be very careful about removing everything - // from this map, as everything that checks this map uses loadedChunks first - // so, one way or another it's a race condition that doesn't matter - for (ca.spottedleaf.moonrise.common.map.SynchronisedLong2BooleanMap value : this.featureChecksSafe.values()) { - value.remove(chunkPos); + + synchronized (this.mapMutex) { + this.featureChecksSafe.values().forEach((long2BooleanMap) -> long2BooleanMap.remove(chunkPos)); } - // Paper end - rewrite chunk system + // DivineMC end - Chunk System Optimizations } public void incrementReference(ChunkPos pos, Structure structure) { 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() { diff --git a/net/minecraft/world/level/levelgen/structure/pools/StructurePoolElement.java b/net/minecraft/world/level/levelgen/structure/pools/StructurePoolElement.java index c84d865837e0f009fcde19e14a44fa43aefe660a..64d7adbd4aa398044a1d68d51e463b672ee81edf 100644 --- a/net/minecraft/world/level/levelgen/structure/pools/StructurePoolElement.java +++ b/net/minecraft/world/level/levelgen/structure/pools/StructurePoolElement.java @@ -27,9 +27,9 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; public abstract class StructurePoolElement { - public static final Codec CODEC = BuiltInRegistries.STRUCTURE_POOL_ELEMENT + public static final Codec CODEC = new org.bxteam.divinemc.util.SynchronizedCodec<>(BuiltInRegistries.STRUCTURE_POOL_ELEMENT // DivineMC - Chunk System Optimizations .byNameCodec() - .dispatch("element_type", StructurePoolElement::getType, StructurePoolElementType::codec); + .dispatch("element_type", StructurePoolElement::getType, StructurePoolElementType::codec)); // DivineMC - Chunk System Optimizations private static final Holder EMPTY = Holder.direct(new StructureProcessorList(List.of())); @Nullable private volatile StructureTemplatePool.Projection projection; diff --git a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidPiece.java b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidPiece.java index e8c125ddb335bfe81b8f1dbb91f7f7ccec11d980..4b6b18b3d255f6a72e6be3b85260732b8eab533c 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidPiece.java +++ b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidPiece.java @@ -23,29 +23,44 @@ import net.minecraft.world.level.storage.loot.BuiltInLootTables; public class DesertPyramidPiece extends ScatteredFeaturePiece { public static final int WIDTH = 21; public static final int DEPTH = 21; - private final boolean[] hasPlacedChest = new boolean[4]; - private final List potentialSuspiciousSandWorldPositions = new ArrayList<>(); + private final java.util.concurrent.atomic.AtomicReferenceArray hasPlacedChestAtomic = new java.util.concurrent.atomic.AtomicReferenceArray<>(new Boolean[4]); // DivineMC - Chunk System Optimizations + private final java.util.Set potentialSuspiciousSandWorldPositions = com.google.common.collect.Sets.newConcurrentHashSet(); // DivineMC - Chunk System Optimizations private BlockPos randomCollapsedRoofPos = BlockPos.ZERO; + // DivineMC start - Chunk System Optimizations + private void init() { + for (int i = 0; i < this.hasPlacedChestAtomic.length(); ++i) { + if (this.hasPlacedChestAtomic.get(i) == null) { + this.hasPlacedChestAtomic.set(i, false); + } + } + } + // DivineMC end - Chunk System Optimizations + public DesertPyramidPiece(RandomSource random, int x, int z) { super(StructurePieceType.DESERT_PYRAMID_PIECE, x, 64, z, 21, 15, 21, getRandomHorizontalDirection(random)); + init(); // DivineMC - Chunk System Optimizations } public DesertPyramidPiece(CompoundTag tag) { super(StructurePieceType.DESERT_PYRAMID_PIECE, tag); - this.hasPlacedChest[0] = tag.getBoolean("hasPlacedChest0"); - this.hasPlacedChest[1] = tag.getBoolean("hasPlacedChest1"); - this.hasPlacedChest[2] = tag.getBoolean("hasPlacedChest2"); - this.hasPlacedChest[3] = tag.getBoolean("hasPlacedChest3"); + // DivineMC start - Chunk System Optimizations + this.hasPlacedChestAtomic.set(0, tag.getBoolean("hasPlacedChest0")); + this.hasPlacedChestAtomic.set(1, tag.getBoolean("hasPlacedChest1")); + this.hasPlacedChestAtomic.set(2, tag.getBoolean("hasPlacedChest2")); + this.hasPlacedChestAtomic.set(3, tag.getBoolean("hasPlacedChest3")); + // DivineMC end - Chunk System Optimizations } @Override protected void addAdditionalSaveData(StructurePieceSerializationContext context, CompoundTag tag) { super.addAdditionalSaveData(context, tag); - tag.putBoolean("hasPlacedChest0", this.hasPlacedChest[0]); - tag.putBoolean("hasPlacedChest1", this.hasPlacedChest[1]); - tag.putBoolean("hasPlacedChest2", this.hasPlacedChest[2]); - tag.putBoolean("hasPlacedChest3", this.hasPlacedChest[3]); + // DivineMC start - Chunk System Optimizations + tag.putBoolean("hasPlacedChest0", this.hasPlacedChestAtomic.get(0)); + tag.putBoolean("hasPlacedChest1", this.hasPlacedChestAtomic.get(1)); + tag.putBoolean("hasPlacedChest2", this.hasPlacedChestAtomic.get(2)); + tag.putBoolean("hasPlacedChest3", this.hasPlacedChestAtomic.get(3)); + // DivineMC end - Chunk System Optimizations } @Override @@ -287,12 +302,12 @@ public class DesertPyramidPiece extends ScatteredFeaturePiece { this.placeBlock(level, Blocks.CUT_SANDSTONE.defaultBlockState(), 10, -11, 13, box); for (Direction direction : Direction.Plane.HORIZONTAL) { - if (!this.hasPlacedChest[direction.get2DDataValue()]) { + if (!this.hasPlacedChestAtomic.get(direction.get2DDataValue())) { // DivineMC - Chunk System Optimizations int i4 = direction.getStepX() * 2; int i5 = direction.getStepZ() * 2; - this.hasPlacedChest[direction.get2DDataValue()] = this.createChest( + this.hasPlacedChestAtomic.set(direction.get2DDataValue(), this.createChest( // DivineMC - Chunk System Optimizations level, box, random, 10 + i4, -11, 10 + i5, BuiltInLootTables.DESERT_PYRAMID - ); + )); // DivineMC - Chunk System Optimizations } } @@ -419,7 +434,7 @@ public class DesertPyramidPiece extends ScatteredFeaturePiece { this.randomCollapsedRoofPos = new BlockPos(this.getWorldX(i1, randomInt), this.getWorldY(y), this.getWorldZ(i1, randomInt)); } - public List getPotentialSuspiciousSandWorldPositions() { + public java.util.Set getPotentialSuspiciousSandWorldPositions() { // DivineMC - Chunk System Optimizations return this.potentialSuspiciousSandWorldPositions; } diff --git a/net/minecraft/world/level/levelgen/structure/structures/JungleTemplePiece.java b/net/minecraft/world/level/levelgen/structure/structures/JungleTemplePiece.java index 82600a247e13b82fc56273e1fd8483c8102a8d3d..cd9b24f017ebea5df55c7d15ee3d793ad612fbc6 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/JungleTemplePiece.java +++ b/net/minecraft/world/level/levelgen/structure/structures/JungleTemplePiece.java @@ -30,10 +30,12 @@ import net.minecraft.world.level.storage.loot.BuiltInLootTables; public class JungleTemplePiece extends ScatteredFeaturePiece { public static final int WIDTH = 12; public static final int DEPTH = 15; - private boolean placedMainChest; - private boolean placedHiddenChest; - private boolean placedTrap1; - private boolean placedTrap2; + // DivineMC start - Chunk System Optimizations + private final java.util.concurrent.atomic.AtomicBoolean placedMainChest = new java.util.concurrent.atomic.AtomicBoolean(false); + private final java.util.concurrent.atomic.AtomicBoolean placedHiddenChest = new java.util.concurrent.atomic.AtomicBoolean(false); + private final java.util.concurrent.atomic.AtomicBoolean placedTrap1 = new java.util.concurrent.atomic.AtomicBoolean(false); + private final java.util.concurrent.atomic.AtomicBoolean placedTrap2 = new java.util.concurrent.atomic.AtomicBoolean(false); + // DivineMC end - Chunk System Optimizations private static final JungleTemplePiece.MossStoneSelector STONE_SELECTOR = new JungleTemplePiece.MossStoneSelector(); public JungleTemplePiece(RandomSource random, int x, int z) { @@ -42,19 +44,23 @@ public class JungleTemplePiece extends ScatteredFeaturePiece { public JungleTemplePiece(CompoundTag tag) { super(StructurePieceType.JUNGLE_PYRAMID_PIECE, tag); - this.placedMainChest = tag.getBoolean("placedMainChest"); - this.placedHiddenChest = tag.getBoolean("placedHiddenChest"); - this.placedTrap1 = tag.getBoolean("placedTrap1"); - this.placedTrap2 = tag.getBoolean("placedTrap2"); + // DivineMC start - Chunk System Optimizations + this.placedMainChest.set(tag.getBoolean("placedMainChest")); + this.placedHiddenChest.set(tag.getBoolean("placedHiddenChest")); + this.placedTrap1.set(tag.getBoolean("placedTrap1")); + this.placedTrap2.set(tag.getBoolean("placedTrap2")); + // DivineMC end - Chunk System Optimizations } @Override protected void addAdditionalSaveData(StructurePieceSerializationContext context, CompoundTag tag) { super.addAdditionalSaveData(context, tag); - tag.putBoolean("placedMainChest", this.placedMainChest); - tag.putBoolean("placedHiddenChest", this.placedHiddenChest); - tag.putBoolean("placedTrap1", this.placedTrap1); - tag.putBoolean("placedTrap2", this.placedTrap2); + // DivineMC start - Chunk System Optimizations + tag.putBoolean("placedMainChest", this.placedMainChest.get()); + tag.putBoolean("placedHiddenChest", this.placedHiddenChest.get()); + tag.putBoolean("placedTrap1", this.placedTrap1.get()); + tag.putBoolean("placedTrap2", this.placedTrap2.get()); + // DivineMC end - Chunk System Optimizations } @Override @@ -242,8 +248,8 @@ public class JungleTemplePiece extends ScatteredFeaturePiece { box ); this.placeBlock(level, Blocks.MOSSY_COBBLESTONE.defaultBlockState(), 3, -3, 1, box); - if (!this.placedTrap1) { - this.placedTrap1 = this.createDispenser(level, box, random, 3, -2, 1, Direction.NORTH, BuiltInLootTables.JUNGLE_TEMPLE_DISPENSER); + if (!this.placedTrap1.get()) { // DivineMC - Chunk System Optimizations + this.placedTrap1.set(this.createDispenser(level, box, random, 3, -2, 1, Direction.NORTH, BuiltInLootTables.JUNGLE_TEMPLE_DISPENSER)); // DivineMC - Chunk System Optimizations } this.placeBlock(level, Blocks.VINE.defaultBlockState().setValue(VineBlock.SOUTH, Boolean.valueOf(true)), 3, -2, 2, box); @@ -340,14 +346,14 @@ public class JungleTemplePiece extends ScatteredFeaturePiece { ); this.placeBlock(level, Blocks.MOSSY_COBBLESTONE.defaultBlockState(), 9, -3, 4, box); this.placeBlock(level, blockState4, 9, -2, 4, box); - if (!this.placedTrap2) { - this.placedTrap2 = this.createDispenser(level, box, random, 9, -2, 3, Direction.WEST, BuiltInLootTables.JUNGLE_TEMPLE_DISPENSER); + if (!this.placedTrap2.get()) { // DivineMC - Chunk System Optimizations + this.placedTrap2.set(this.createDispenser(level, box, random, 9, -2, 3, Direction.WEST, BuiltInLootTables.JUNGLE_TEMPLE_DISPENSER)); // DivineMC - Chunk System Optimizations } this.placeBlock(level, Blocks.VINE.defaultBlockState().setValue(VineBlock.EAST, Boolean.valueOf(true)), 8, -1, 3, box); this.placeBlock(level, Blocks.VINE.defaultBlockState().setValue(VineBlock.EAST, Boolean.valueOf(true)), 8, -2, 3, box); - if (!this.placedMainChest) { - this.placedMainChest = this.createChest(level, box, random, 8, -3, 3, BuiltInLootTables.JUNGLE_TEMPLE); + if (!this.placedMainChest.get()) { // DivineMC - Chunk System Optimizations + this.placedMainChest.set(this.createChest(level, box, random, 8, -3, 3, BuiltInLootTables.JUNGLE_TEMPLE)); // DivineMC - Chunk System Optimizations } this.placeBlock(level, Blocks.MOSSY_COBBLESTONE.defaultBlockState(), 9, -3, 2, box); @@ -390,8 +396,8 @@ public class JungleTemplePiece extends ScatteredFeaturePiece { this.placeBlock(level, Blocks.STICKY_PISTON.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.WEST), 10, -2, 8, box); this.placeBlock(level, Blocks.STICKY_PISTON.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.WEST), 10, -1, 8, box); this.placeBlock(level, Blocks.REPEATER.defaultBlockState().setValue(RepeaterBlock.FACING, Direction.NORTH), 10, -2, 10, box); - if (!this.placedHiddenChest) { - this.placedHiddenChest = this.createChest(level, box, random, 9, -3, 10, BuiltInLootTables.JUNGLE_TEMPLE); + if (!this.placedHiddenChest.get()) { // DivineMC - Chunk System Optimizations + this.placedHiddenChest.set(this.createChest(level, box, random, 9, -3, 10, BuiltInLootTables.JUNGLE_TEMPLE)); // DivineMC - Chunk System Optimizations } } } diff --git a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java index 1acd506f7c0679fa9f69b6ab221002b28d00c3e5..52237559477e1965cb13a94ee4e6b4ff2ee99b03 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java @@ -95,7 +95,7 @@ public class MineshaftPieces { public static class MineShaftCorridor extends MineshaftPieces.MineShaftPiece { private final boolean hasRails; private final boolean spiderCorridor; - private boolean hasPlacedSpider; + private volatile boolean hasPlacedSpider; // DivineMC - Chunk System Optimizations private final int numSections; public MineShaftCorridor(CompoundTag tag) { @@ -954,7 +954,7 @@ public class MineshaftPieces { } public static class MineShaftRoom extends MineshaftPieces.MineShaftPiece { - private final List childEntranceBoxes = Lists.newLinkedList(); + private final List childEntranceBoxes = java.util.Collections.synchronizedList(Lists.newLinkedList()); // DivineMC - Chunk System Optimizations public MineShaftRoom(int genDepth, RandomSource random, int x, int z, MineshaftStructure.Type type) { super( diff --git a/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java b/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java index 20ddc6403823f72ef15d10e3d53b5f4d13e82724..081fbe004cac5bca67210a1abcca7fa44642f417 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java @@ -1301,7 +1301,7 @@ public class NetherFortressPieces { int i = 0; for (NetherFortressPieces.PieceWeight pieceWeight : weights) { - if (pieceWeight.maxPlaceCount > 0 && pieceWeight.placeCount < pieceWeight.maxPlaceCount) { + if (pieceWeight.maxPlaceCount > 0 && pieceWeight.placeCount.get() < pieceWeight.maxPlaceCount) { // DivineMC - Chunk System Optimizations flag = true; } @@ -1341,7 +1341,7 @@ public class NetherFortressPieces { pieceWeight, pieces, random, x, y, z, orientation, genDepth ); if (netherBridgePiece != null) { - pieceWeight.placeCount++; + pieceWeight.placeCount.set(pieceWeight.placeCount.get() + 1); // DivineMC - Chunk System Optimizations startPiece.previousPiece = pieceWeight; if (!pieceWeight.isValid()) { weights.remove(pieceWeight); @@ -1576,7 +1576,7 @@ public class NetherFortressPieces { static class PieceWeight { public final Class pieceClass; public final int weight; - public int placeCount; + public final ThreadLocal placeCount = ThreadLocal.withInitial(() -> 0); // DivineMC - Chunk System Optimizations public final int maxPlaceCount; public final boolean allowInRow; @@ -1592,11 +1592,11 @@ public class NetherFortressPieces { } public boolean doPlace(int genDepth) { - return this.maxPlaceCount == 0 || this.placeCount < this.maxPlaceCount; + return this.maxPlaceCount == 0 || this.placeCount.get() < this.maxPlaceCount; // DivineMC - Chunk System Optimizations } public boolean isValid() { - return this.maxPlaceCount == 0 || this.placeCount < this.maxPlaceCount; + return this.maxPlaceCount == 0 || this.placeCount.get() < this.maxPlaceCount; // DivineMC - Chunk System Optimizations } } @@ -1746,24 +1746,24 @@ public class NetherFortressPieces { } public static class StartPiece extends NetherFortressPieces.BridgeCrossing { - public NetherFortressPieces.PieceWeight previousPiece; + public volatile NetherFortressPieces.PieceWeight previousPiece; // DivineMC - Chunk System Optimizations public List availableBridgePieces; public List availableCastlePieces; public final List pendingChildren = Lists.newArrayList(); public StartPiece(RandomSource random, int x, int z) { super(x, z, getRandomHorizontalDirection(random)); - this.availableBridgePieces = Lists.newArrayList(); + this.availableBridgePieces = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System Optimizations for (NetherFortressPieces.PieceWeight pieceWeight : NetherFortressPieces.BRIDGE_PIECE_WEIGHTS) { - pieceWeight.placeCount = 0; + pieceWeight.placeCount.remove(); // DivineMC - Chunk System Optimizations this.availableBridgePieces.add(pieceWeight); } - this.availableCastlePieces = Lists.newArrayList(); + this.availableCastlePieces = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System Optimizations for (NetherFortressPieces.PieceWeight pieceWeight : NetherFortressPieces.CASTLE_PIECE_WEIGHTS) { - pieceWeight.placeCount = 0; + pieceWeight.placeCount.remove(); // DivineMC - Chunk System Optimizations this.availableCastlePieces.add(pieceWeight); } } diff --git a/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java b/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java index b28829043c558af04511108691a4e9042a5afc62..b9d9fe3affd1f991ea3a742bc48a518f19819dfd 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java @@ -63,32 +63,36 @@ public class StrongholdPieces { } } }; - private static List currentPieces; - static Class imposedPiece; - private static int totalWeight; + // DivineMC start - Chunk System Optimizations + private static final ThreadLocal> currentPieces = new ThreadLocal>(); + static final ThreadLocal> imposedPiece = new ThreadLocal>(); + private static final ThreadLocal totalWeight = ThreadLocal.withInitial(() -> 0); + // DivineMC end - Chunk System Optimizations static final StrongholdPieces.SmoothStoneSelector SMOOTH_STONE_SELECTOR = new StrongholdPieces.SmoothStoneSelector(); public static void resetPieces() { - currentPieces = Lists.newArrayList(); + // DivineMC start - Chunk System Optimizations + currentPieces.set(Lists.newArrayList()); for (StrongholdPieces.PieceWeight pieceWeight : STRONGHOLD_PIECE_WEIGHTS) { - pieceWeight.placeCount = 0; - currentPieces.add(pieceWeight); + pieceWeight.placeCount.set(0); + currentPieces.get().add(pieceWeight); } - imposedPiece = null; + imposedPiece.set(null); + // DivineMC end - Chunk System Optimizations } private static boolean updatePieceWeight() { boolean flag = false; - totalWeight = 0; + totalWeight.set(0); // DivineMC - Chunk System Optimizations - for (StrongholdPieces.PieceWeight pieceWeight : currentPieces) { - if (pieceWeight.maxPlaceCount > 0 && pieceWeight.placeCount < pieceWeight.maxPlaceCount) { + for (StrongholdPieces.PieceWeight pieceWeight : currentPieces.get()) { // DivineMC - Chunk System Optimizations + if (pieceWeight.maxPlaceCount > 0 && pieceWeight.placeCount.get() < pieceWeight.maxPlaceCount) { // DivineMC - Chunk System Optimizations flag = true; } - totalWeight = totalWeight + pieceWeight.weight; + totalWeight.set(totalWeight.get() + pieceWeight.weight); // DivineMC - Chunk System Optimizations } return flag; @@ -138,9 +142,11 @@ public class StrongholdPieces { if (!updatePieceWeight()) { return null; } else { - if (imposedPiece != null) { - StrongholdPieces.StrongholdPiece strongholdPiece = findAndCreatePieceFactory(imposedPiece, pieces, random, x, y, z, direction, genDepth); - imposedPiece = null; + // DivineMC start - Chunk System Optimizations + if (imposedPiece.get() != null) { + StrongholdPieces.StrongholdPiece strongholdPiece = findAndCreatePieceFactory(imposedPiece.get(), pieces, random, x, y, z, direction, genDepth); + imposedPiece.set(null); + // DivineMC end - Chunk System Optimizations if (strongholdPiece != null) { return strongholdPiece; } @@ -150,9 +156,9 @@ public class StrongholdPieces { while (i < 5) { i++; - int randomInt = random.nextInt(totalWeight); + int randomInt = random.nextInt(totalWeight.get()); // DivineMC - Chunk System Optimizations - for (StrongholdPieces.PieceWeight pieceWeight : currentPieces) { + for (StrongholdPieces.PieceWeight pieceWeight : currentPieces.get()) { // DivineMC - Chunk System Optimizations randomInt -= pieceWeight.weight; if (randomInt < 0) { if (!pieceWeight.doPlace(genDepth) || pieceWeight == piece.previousPiece) { @@ -163,10 +169,10 @@ public class StrongholdPieces { pieceWeight.pieceClass, pieces, random, x, y, z, direction, genDepth ); if (strongholdPiece1 != null) { - pieceWeight.placeCount++; + pieceWeight.placeCount.set(pieceWeight.placeCount.get() + 1); // DivineMC - Chunk System Optimizations piece.previousPiece = pieceWeight; if (!pieceWeight.isValid()) { - currentPieces.remove(pieceWeight); + currentPieces.get().remove(pieceWeight); // DivineMC - Chunk System Optimizations } return strongholdPiece1; @@ -202,7 +208,7 @@ public class StrongholdPieces { private static final int WIDTH = 5; private static final int HEIGHT = 5; private static final int DEPTH = 7; - private boolean hasPlacedChest; + private volatile boolean hasPlacedChest; // DivineMC - Chunk System Optimizations public ChestCorridor(int genDepth, RandomSource random, BoundingBox box, Direction orientation) { super(StructurePieceType.STRONGHOLD_CHEST_CORRIDOR, genDepth, box); @@ -723,7 +729,7 @@ public class StrongholdPieces { static class PieceWeight { public final Class pieceClass; public final int weight; - public int placeCount; + public final ThreadLocal placeCount = ThreadLocal.withInitial(() -> 0); // DivineMC - Chunk System Optimizations public final int maxPlaceCount; public PieceWeight(Class pieceClass, int weight, int maxPlaceCount) { @@ -733,11 +739,11 @@ public class StrongholdPieces { } public boolean doPlace(int genDepth) { - return this.maxPlaceCount == 0 || this.placeCount < this.maxPlaceCount; + return this.maxPlaceCount == 0 || this.placeCount.get() < this.maxPlaceCount; // DivineMC - Chunk System Optimizations } public boolean isValid() { - return this.maxPlaceCount == 0 || this.placeCount < this.maxPlaceCount; + return this.maxPlaceCount == 0 || this.placeCount.get() < this.maxPlaceCount; // DivineMC - Chunk System Optimizations } } @@ -745,7 +751,7 @@ public class StrongholdPieces { protected static final int WIDTH = 11; protected static final int HEIGHT = 8; protected static final int DEPTH = 16; - private boolean hasPlacedSpawner; + private volatile boolean hasPlacedSpawner; // DivineMC - Chunk System Optimizations public PortalRoom(int genDepth, BoundingBox box, Direction orientation) { super(StructurePieceType.STRONGHOLD_PORTAL_ROOM, genDepth, box); @@ -1273,7 +1279,7 @@ public class StrongholdPieces { @Override public void addChildren(StructurePiece piece, StructurePieceAccessor pieces, RandomSource random) { if (this.isSource) { - StrongholdPieces.imposedPiece = StrongholdPieces.FiveCrossing.class; + StrongholdPieces.imposedPiece.set(StrongholdPieces.FiveCrossing.class); } this.generateSmallDoorChildForward((StrongholdPieces.StartPiece)piece, pieces, random, 1, 1); @@ -1322,10 +1328,10 @@ public class StrongholdPieces { } public static class StartPiece extends StrongholdPieces.StairsDown { - public StrongholdPieces.PieceWeight previousPiece; + public volatile StrongholdPieces.PieceWeight previousPiece; // DivineMC - Chunk System Optimizations @Nullable - public StrongholdPieces.PortalRoom portalRoomPiece; - public final List pendingChildren = Lists.newArrayList(); + public volatile StrongholdPieces.PortalRoom portalRoomPiece; // DivineMC - Chunk System Optimizations + public final List pendingChildren = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System Optimizations public StartPiece(RandomSource random, int x, int z) { super(StructurePieceType.STRONGHOLD_START, 0, x, z, getRandomHorizontalDirection(random)); diff --git a/net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces.java b/net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces.java index 6c5b6ba8973c88e959d57fec38abcef5f097e9b3..ad44f4915e7a9609445b62664d2a47581de8c169 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces.java @@ -126,7 +126,7 @@ public class WoodlandMansionPieces { int i = 11; this.entranceX = 7; this.entranceY = 4; - this.baseGrid = new WoodlandMansionPieces.SimpleGrid(11, 11, 5); + this.baseGrid = new org.bxteam.divinemc.util.ConcurrentFlagMatrix(11, 11, 5); // DivineMC - Chunk System Optimizations this.baseGrid.set(this.entranceX, this.entranceY, this.entranceX + 1, this.entranceY + 1, 3); this.baseGrid.set(this.entranceX - 1, this.entranceY, this.entranceX - 1, this.entranceY + 1, 2); this.baseGrid.set(this.entranceX + 2, this.entranceY - 2, this.entranceX + 3, this.entranceY + 3, 5); @@ -145,14 +145,16 @@ public class WoodlandMansionPieces { } this.floorRooms = new WoodlandMansionPieces.SimpleGrid[3]; - this.floorRooms[0] = new WoodlandMansionPieces.SimpleGrid(11, 11, 5); - this.floorRooms[1] = new WoodlandMansionPieces.SimpleGrid(11, 11, 5); - this.floorRooms[2] = new WoodlandMansionPieces.SimpleGrid(11, 11, 5); + // DivineMC start - Chunk System Optimizations + this.floorRooms[0] = new org.bxteam.divinemc.util.ConcurrentFlagMatrix(11, 11, 5); + this.floorRooms[1] = new org.bxteam.divinemc.util.ConcurrentFlagMatrix(11, 11, 5); + this.floorRooms[2] = new org.bxteam.divinemc.util.ConcurrentFlagMatrix(11, 11, 5); + // DivineMC end - Chunk System Optimizations this.identifyRooms(this.baseGrid, this.floorRooms[0]); this.identifyRooms(this.baseGrid, this.floorRooms[1]); this.floorRooms[0].set(this.entranceX + 1, this.entranceY, this.entranceX + 1, this.entranceY + 1, 8388608); this.floorRooms[1].set(this.entranceX + 1, this.entranceY, this.entranceX + 1, this.entranceY + 1, 8388608); - this.thirdFloorGrid = new WoodlandMansionPieces.SimpleGrid(this.baseGrid.width, this.baseGrid.height, 5); + this.thirdFloorGrid = new org.bxteam.divinemc.util.ConcurrentFlagMatrix(this.baseGrid.width, this.baseGrid.height, 5); // DivineMC - Chunk System Optimizations this.setupThirdFloor(); this.identifyRooms(this.thirdFloorGrid, this.floorRooms[2]); } @@ -1139,9 +1141,11 @@ public class WoodlandMansionPieces { } static class PlacementData { - public Rotation rotation; - public BlockPos position; - public String wallType; + // DivineMC start - Chunk System Optimizations + public volatile Rotation rotation; + public volatile BlockPos position; + public volatile String wallType; + // DivineMC end - Chunk System Optimizations } static class SecondFloorRoomCollection extends WoodlandMansionPieces.FloorRoomCollection { diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java b/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java index 05027cc20d174d78bef118cd2ba545ac56e1559c..32bbfe48dee44b0b491aa369dec59cbf0772c4b5 100644 --- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java +++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java @@ -22,7 +22,7 @@ public class StructurePlaceSettings { @Nullable private RandomSource random; public int palette = -1; // CraftBukkit - Set initial value so we know if the palette has been set forcefully - private final List processors = Lists.newArrayList(); + private final List processors = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System Optimizations private boolean knownShape; private boolean finalizeEntities; diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java index ab1dcbe416e2c3c94cfddf04b7ed053425a71806..a37eb2e29b4577ebc711e8ef7b47fbbc3bc66897 100644 --- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java @@ -71,8 +71,8 @@ public class StructureTemplate { public static final String ENTITY_TAG_BLOCKPOS = "blockPos"; public static final String ENTITY_TAG_NBT = "nbt"; public static final String SIZE_TAG = "size"; - public final List palettes = Lists.newArrayList(); - public final List entityInfoList = Lists.newArrayList(); + public final List palettes = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System Optimizations + public final List entityInfoList = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Chunk System Optimizations private Vec3i size = Vec3i.ZERO; private String author = "?"; // CraftBukkit start - data containers diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java index faf45ac459f7c25309d6ef6dce371d484a0dae7b..0e862e9c0d9d562e77ce02a35deb761c28fa2f90 100644 --- a/net/minecraft/world/ticks/LevelChunkTicks.java +++ b/net/minecraft/world/ticks/LevelChunkTicks.java @@ -18,10 +18,10 @@ import net.minecraft.nbt.ListTag; import net.minecraft.world.level.ChunkPos; public class LevelChunkTicks implements SerializableTickContainer, TickContainerAccess, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // Paper - rewrite chunk system - private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); + private final Queue> tickQueue = new java.util.concurrent.PriorityBlockingQueue<>(11, ScheduledTick.DRAIN_ORDER); // DivineMC - Chunk System Optimizations @Nullable private List> pendingTicks; - private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); + private final Set> ticksPerPosition = it.unimi.dsi.fastutil.objects.ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); // DivineMC - Chunk System Optimizations @Nullable private BiConsumer, ScheduledTick> onTickAdded; @@ -71,10 +71,18 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon @Nullable public ScheduledTick poll() { - ScheduledTick scheduledTick = this.tickQueue.poll(); - if (scheduledTick != null) { - this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system + // DivineMC start - Chunk System Optimizations + ScheduledTick scheduledTick = null; + try { + scheduledTick = this.tickQueue.poll(); + if (scheduledTick != null) { + this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system + } + } catch (Exception e) { + net.minecraft.server.MinecraftServer.LOGGER.error("Encountered caught exception when polling chunk ticks, blocking and returning null.", e); + return null; } + // DivineMC end - Chunk System Optimizations return scheduledTick; } @@ -87,6 +95,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon } private void scheduleUnchecked(ScheduledTick tick) { + if (tick == null) return; // DivineMC - Chunk System Optimizations this.tickQueue.add(tick); if (this.onTickAdded != null) { this.onTickAdded.accept(this, tick); @@ -127,6 +136,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon } for (ScheduledTick scheduledTick : this.tickQueue) { + if (scheduledTick == null) continue; // DivineMC - Chunk System Optimizations - fix NPE list.add(scheduledTick.toSavedTick(gametime)); } diff --git a/net/minecraft/world/ticks/LevelTicks.java b/net/minecraft/world/ticks/LevelTicks.java index 0a9805d42142678ca5213c511235daa6505ddbf3..cd73f1a3ccc80e4ab1766147fccc67d81eeb4cc3 100644 --- a/net/minecraft/world/ticks/LevelTicks.java +++ b/net/minecraft/world/ticks/LevelTicks.java @@ -30,17 +30,18 @@ public class LevelTicks implements LevelTickAccess { private static final Comparator> CONTAINER_DRAIN_ORDER = (levelChunkTicks, levelChunkTicks1) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER .compare(levelChunkTicks.peek(), levelChunkTicks1.peek()); private final LongPredicate tickCheck; - private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); - private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), map -> map.defaultReturnValue(Long.MAX_VALUE)); - private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); - private final Queue> toRunThisTick = new ArrayDeque<>(); + private final Long2ObjectMap> allContainers = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); // DivineMC - Chunk System Optimizations + private final java.util.Map nextTickForContainer = new java.util.concurrent.ConcurrentHashMap<>(); // DivineMC - Chunk System Optimizations + private final Queue> containersToTick = new java.util.concurrent.PriorityBlockingQueue<>(11, CONTAINER_DRAIN_ORDER); // DivineMC - Chunk System Optimizations + private final Queue> toRunThisTick = new java.util.concurrent.ConcurrentLinkedQueue<>(); // DivineMC - Chunk System Optimizations private final List> alreadyRunThisTick = new ArrayList<>(); - private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); + private final Set> toRunThisTickSet = com.google.common.collect.Sets.newConcurrentHashSet(); // DivineMC - Chunk System Optimizations private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (levelChunkTicks, scheduledTick) -> { if (scheduledTick.equals(levelChunkTicks.peek())) { this.updateContainerScheduling(scheduledTick); } }; + private final java.util.concurrent.atomic.AtomicInteger toRunThisTickCount = new java.util.concurrent.atomic.AtomicInteger(0); // DivineMC - Chunk System Optimizations public LevelTicks(LongPredicate tickCheck) { this.tickCheck = tickCheck; @@ -90,12 +91,14 @@ public class LevelTicks implements LevelTickAccess { } private void sortContainersToTick(long gameTime) { - ObjectIterator objectIterator = Long2LongMaps.fastIterator(this.nextTickForContainer); + java.util.Iterator> objectIterator = this.nextTickForContainer.entrySet().iterator(); // DivineMC - Chunk System Optimizations while (objectIterator.hasNext()) { - Entry entry = objectIterator.next(); - long longKey = entry.getLongKey(); - long longValue = entry.getLongValue(); + // DivineMC start - Chunk System Optimizations + java.util.Map.Entry entry = objectIterator.next(); + long longKey = entry.getKey(); + long longValue = entry.getValue(); + // DivineMC end - Chunk System Optimizations if (longValue <= gameTime) { LevelChunkTicks levelChunkTicks = this.allContainers.get(longKey); if (levelChunkTicks == null) { @@ -162,16 +165,19 @@ public class LevelTicks implements LevelTickAccess { } private void scheduleForThisTick(ScheduledTick tick) { + if (tick == null) return; // DivineMC - Chunk System Optimizations this.toRunThisTick.add(tick); + this.toRunThisTickCount.incrementAndGet(); // DivineMC - Chunk System Optimizations } private boolean canScheduleMoreTicks(int maxAllowedTicks) { - return this.toRunThisTick.size() < maxAllowedTicks; + return this.toRunThisTickCount.get() < maxAllowedTicks; // DivineMC - Chunk System Optimizations } private void runCollectedTicks(BiConsumer ticker) { while (!this.toRunThisTick.isEmpty()) { ScheduledTick scheduledTick = this.toRunThisTick.poll(); + this.toRunThisTickCount.decrementAndGet(); // DivineMC - Chunk System Optimizations if (!this.toRunThisTickSet.isEmpty()) { this.toRunThisTickSet.remove(scheduledTick); } @@ -183,6 +189,7 @@ public class LevelTicks implements LevelTickAccess { private void cleanupAfterTick() { this.toRunThisTick.clear(); + this.toRunThisTickCount.set(0); // DivineMC - Chunk System Optimizations this.containersToTick.clear(); this.alreadyRunThisTick.clear(); this.toRunThisTickSet.clear();