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..78e3c49a233dc6bed558458d555fe740a8cc839e 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 } } diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java index ba20e87d2105ce53cdaf4049de2388d05fcd1b56..131e673c8eaa2afa801de5913982259d48908358 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java @@ -383,6 +383,14 @@ public final class ChunkEntitySlices { private E[] storage; private int size; + private it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap entityToIndex = null; // DivineMC - Chunk System optimization + + // DivineMC start - Chunk System optimization + private void setupIndexMap() { + this.entityToIndex = new it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap(2, 0.8f); + this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); + } + // DivineMC end - Chunk System optimization public BasicEntityList() { this(0); @@ -403,6 +411,7 @@ public final class ChunkEntitySlices { private void resize() { if (this.storage == EMPTY) { this.storage = (E[])new Entity[DEFAULT_CAPACITY]; + this.setupIndexMap(); // DivineMC - Chunk System optimization } else { this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); } @@ -416,6 +425,7 @@ public final class ChunkEntitySlices { } else { this.storage[idx] = entity; } + this.entityToIndex.put(entity.getId(), idx); // DivineMC - Chunk System optimization } public int indexOf(final E entity) { @@ -431,24 +441,31 @@ public final class ChunkEntitySlices { } public boolean remove(final E entity) { - final int idx = this.indexOf(entity); - if (idx == -1) { + // DivineMC start - Chunk System optimization - by Sakura, copied from EntityList + if (this.entityToIndex == null) { return false; } - final int size = --this.size; - final E[] storage = this.storage; - if (idx != size) { - System.arraycopy(storage, idx + 1, storage, idx, size - idx); + final int index = this.entityToIndex.remove(entity.getId()); + if (index == Integer.MIN_VALUE) { + return false; } - storage[size] = null; + final int endIndex = --this.size; + final E end = this.storage[endIndex]; + if (index != endIndex) { + // not empty after this call + this.entityToIndex.put(end.getId(), index); // update index + } + this.storage[index] = end; + this.storage[endIndex] = null; + // DivineMC end - Chunk System optimization - by Sakura, copied from EntityList return true; } public boolean has(final E entity) { - return this.indexOf(entity) != -1; + return this.entityToIndex != null && this.entityToIndex.containsKey(entity.getId()); // DivineMC - Chunk System optimization } } diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java index 7a52c42845bd74d0bb7649f87764aba12f442f02..03ab44b69725a18ed567826e4417f1d6701f08c9 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java @@ -187,13 +187,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) { @@ -304,7 +304,7 @@ public final class RegionizedPlayerChunkLoader { long currTime = System.nanoTime(); for (final ServerPlayer player : new java.util.ArrayList<>(this.world.players())) { final PlayerChunkLoaderData loader = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader(); - if (loader == null || loader.removed || loader.world != this.world) { + if (loader == null || loader.removed || loader.world != this.world || !loader.canTick) { // DivineMC - Chunk System optimization // not our problem anymore continue; } @@ -323,6 +323,7 @@ public final class RegionizedPlayerChunkLoader { private final ServerPlayer player; private final ServerLevel world; + public volatile boolean canTick; // DivineMC - Chunk System optimization private int lastChunkX = Integer.MIN_VALUE; private int lastChunkZ = Integer.MIN_VALUE; @@ -382,10 +383,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); @@ -853,6 +863,7 @@ public final class RegionizedPlayerChunkLoader { } void add() { + this.canTick = false; // DivineMC - Chunk System optimization TickThread.ensureTickThread(this.player, "Cannot add player asynchronously"); if (this.removed) { throw new IllegalStateException("Adding removed player chunk loader"); @@ -892,6 +903,7 @@ public final class RegionizedPlayerChunkLoader { // now we can update this.update(); + this.canTick = true; // DivineMC - Chunk System optimization } private boolean isLoadedChunkGeneratable(final int chunkX, final int chunkZ) { @@ -1060,6 +1072,7 @@ public final class RegionizedPlayerChunkLoader { } void remove() { + this.canTick = false; // DivineMC - Chunk System optimization TickThread.ensureTickThread(this.player, "Cannot add player asynchronously"); if (this.removed) { throw new IllegalStateException("Removing removed player chunk loader"); @@ -1087,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 f473999938840562b1007a789600342e5796a123..60705955e231d47c60f5a0a5e869988aed8774fc 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java @@ -72,36 +72,49 @@ public final class ChunkHolderManager { private static final long NO_TIMEOUT_MARKER = Long.MIN_VALUE; 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 private final ConcurrentLong2ReferenceChainedHashTable ticketCounters = new ConcurrentLong2ReferenceChainedHashTable<>(); @@ -224,26 +237,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) { @@ -257,10 +273,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(); @@ -319,13 +336,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 } } } @@ -459,8 +472,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, @@ -557,7 +570,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); } @@ -601,8 +614,8 @@ 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) -> { - return (SortedArraySet)SortedArraySet.create(4); + final SortedArraySet ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (keyInMap) -> { // DivineMC - Chunk System optimization + return SortedArraySet.create(4); }); final int levelBefore = getTicketLevelAt(ticketsAtChunk); @@ -782,8 +795,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, @@ -831,8 +844,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 @@ -1118,7 +1131,7 @@ public final class ChunkHolderManager { } if (!TickThread.isTickThread()) { 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)); } @@ -1126,16 +1139,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)); } @@ -1293,6 +1306,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 } @@ -1466,7 +1500,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; @@ -1502,8 +1536,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 8f8268924ac92fca5df8a11e08031fa8416c6e05..f1bc7a5e80de0293e1837b2f7401b347fc59f831 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java @@ -66,14 +66,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 = ChunkSystemTicketType.create("chunk_system:chunk_load", Long::compareTo); private static final AtomicLong CHUNK_LOAD_IDS = new AtomicLong(); @@ -116,12 +108,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(); @@ -292,14 +284,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 96ccb8f657d755b2e58a8dd0cda00ca0df4886b2..89de0fcb3f8142f76399fc00e288e03d7f3742f2 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_system/util/ParallelSearchRadiusIteration.java b/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java index 93fd23027c00cef76562098306737272fda1350a..44e443768f5163454fd11425afd3bc07b2c0c6c2 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java @@ -23,6 +23,7 @@ public final class ParallelSearchRadiusIteration { } public static long[] getSearchIteration(final int radius) { + if (radius >= SEARCH_RADIUS_ITERATION_LIST.length) return SEARCH_RADIUS_ITERATION_LIST[SEARCH_RADIUS_ITERATION_LIST.length - 1]; // DivineMC - Chunk System Optimizations return SEARCH_RADIUS_ITERATION_LIST[radius]; } diff --git a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java index 6d1fe8028739145b11fce98ad62b2f8044299548..2cd1197fa0d15e19749409cc857a0254de444ba5 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java +++ b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java @@ -2,7 +2,7 @@ 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 // the smallest distance on x/z is at 45 degrees, we need to subtract 0.5 since this is calculated from chunk center and not chunk perimeter // note: vanilla does not subtract 0.5 but the result is (luckily!) the same public static final int NARROW_SPAWN_TRACK_RANGE = (int)Math.floor(((double)PLAYER_SPAWN_TRACK_RANGE / Math.sqrt(2.0)) - 0.5); 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 51f4dd4f583dfbd16cb00f1cb4418d1044cecb1c..2f83deafbb5b50e5ce191b5351ec5ed948262008 100644 --- a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java +++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java @@ -887,7 +887,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 52b981a05ad5aabb7c85dc1e0f1d2b835163bb87..f4af8e131c2badbe0e57ccec5be8710e12d7c38d 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 b5028cc64e2a43c841801114908825102df41765..fb6e0beb35d1d6bb9a159debeb06e861051821b9 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -132,8 +132,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 @@ -256,7 +256,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 } } @@ -273,7 +277,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 @@ -774,27 +782,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)); - } - if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, blockRange)) { - // Paper end - PlayerNaturallySpawnCreaturesEvent - return true; - } - } - - return false; + return !players.isEmpty(); // DivineMC - Chunk System Optimizations // Paper end - chunk tick iteration optimisation } @@ -812,10 +800,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); @@ -1208,6 +1196,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 fd3d0f6cb53bc8b6186f0d86575f21007b2c20ed..7f3c41b59e288364d67534511fc038e6dbb268fa 100644 --- a/net/minecraft/server/level/DistanceManager.java +++ b/net/minecraft/server/level/DistanceManager.java @@ -127,15 +127,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 4d4139b5f42a4db6e22bf1d063a23dc7b9914f85..9a65321ce62f21b150d29be30dbae7dba0ff40be 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -444,8 +444,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 6268bfc05fd9d3669bef20a1ddc5477fbae0f957..2ebd77db2d13d04513689ff332404c6af6a3c11d 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -177,6 +177,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(); // Paper - rewrite chunk system @@ -287,6 +288,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; @@ -678,6 +680,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"), @@ -691,6 +694,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - rewrite chunk system this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit 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 @@ -820,8 +824,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 } @@ -877,7 +880,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(); @@ -885,42 +888,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 @@ -2519,16 +2521,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public 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 } diff --git a/net/minecraft/server/level/ThreadedLevelLightEngine.java b/net/minecraft/server/level/ThreadedLevelLightEngine.java index 216c2294f59a9d53613ac249ea63adeaa8a8efa4..b83438852a7b01fef9a736a5b0bc46f15ed1d59b 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 0842fd6488c8b27d98c4344e1244996b4c0e9912..cba72a86d99fb4b3a3f9f5aefe9993ec8e84abfd 100644 --- a/net/minecraft/world/level/LevelReader.java +++ b/net/minecraft/world/level/LevelReader.java @@ -74,10 +74,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 4db7ec2647b9f08536dc5253dac0fa8d3044e38b..e3abc97a1a66988e44b1ed78b76a198b82051da1 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 d7f4d54f73adc0d4d71fb9ba425e45a11c456961..a512609aaa823b940ed269c981fc9beec49a126e 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 13837d5508c34de3ccc49caac0d6c0ba7010d88a..3449d4dbb68353204ec67c46eed1404dd6410e96 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 27e1edbd8d8ffd80c1a3df17bc47f4a6936619f7..fae062a643692369175f58bb1687e766e873d8e3 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 8d1174f25e0e90d0533970f4ddd8448442024936..c98cb390bda4b536f97445f228e06aaebcd84609 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java @@ -269,7 +269,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 74d8202b5c9bb2a3ee832be70f95c0b5cbecb460..4c11b822fa65388c1d8d9aaa7fd70200d0eaa418 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(); @@ -75,50 +86,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() { @@ -131,8 +136,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 3379c3893227d42bb54f3a94e697a9851d279605..161aed37d14cd283a3b4341ea15d57a4a0f088b3 100644 --- a/net/minecraft/world/level/levelgen/BelowZeroRetrogen.java +++ b/net/minecraft/world/level/levelgen/BelowZeroRetrogen.java @@ -74,6 +74,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 21cbf6c1723feb1813d8cd5106e36594d140d987..29fad81878fe6902328b810e1584650cfb65aba6 100644 --- a/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java +++ b/net/minecraft/world/level/levelgen/feature/stateproviders/RandomizedIntStateProvider.java @@ -55,17 +55,21 @@ public class RandomizedIntStateProvider extends BlockStateProvider { @Override public BlockState getState(RandomSource random, BlockPos pos) { - BlockState state = this.source.getState(random, pos); - if (this.property == null || !state.hasProperty(this.property)) { - IntegerProperty integerProperty = findProperty(state, this.propertyName); - if (integerProperty == null) { - return state; + // DivineMC start - Chunk System optimization + BlockState blockState = this.source.getState(random, pos); + IntegerProperty propertyLocal = this.property; + if (propertyLocal == null || !blockState.hasProperty(propertyLocal)) { + IntegerProperty intProperty = findProperty(blockState, this.propertyName); + if (intProperty == null) { + return blockState; } - this.property = integerProperty; + propertyLocal = intProperty; + this.property = intProperty; } - return state.setValue(this.property, this.values.sample(random)); + return (BlockState) blockState.setValue(this.property, this.values.sample(random)); + // DivineMC end - Chunk System optimization } @Nullable diff --git a/net/minecraft/world/level/levelgen/structure/ScatteredFeaturePiece.java b/net/minecraft/world/level/levelgen/structure/ScatteredFeaturePiece.java index e2036a80eff3dc1a9ec625880d4aab6ef71d84fa..6c5200a311a6c5a93a49999cc0c3a8b3f3ca8240 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 48fce0845fb023d6286dac144c285b048d778564..3e8e740f08614143f09c62f07bee7b305dd7929c 100644 --- a/net/minecraft/world/level/levelgen/structure/StructureCheck.java +++ b/net/minecraft/world/level/levelgen/structure/StructureCheck.java @@ -47,6 +47,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 @@ -229,15 +230,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 f9a15c3769f29af1952ef880f6fcd2612119ecf0..13f6654e06f87e9a9fbeba2217ee0e0eaa95347d 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 0d2451a9ade43650dbbcbab69ce0f6e8f69b5aee..21b8e738c8823eac5d8fc8241dd8fb0e5ce82364 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidPiece.java +++ b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidPiece.java @@ -23,29 +23,45 @@ 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.getBooleanOr("hasPlacedChest0", false); - this.hasPlacedChest[1] = tag.getBooleanOr("hasPlacedChest1", false); - this.hasPlacedChest[2] = tag.getBooleanOr("hasPlacedChest2", false); - this.hasPlacedChest[3] = tag.getBooleanOr("hasPlacedChest3", false); + // DivineMC start - Chunk System Optimizations + this.hasPlacedChestAtomic.set(0, tag.getBooleanOr("hasPlacedChest0", false)); + this.hasPlacedChestAtomic.set(1, tag.getBooleanOr("hasPlacedChest1", false)); + this.hasPlacedChestAtomic.set(2, tag.getBooleanOr("hasPlacedChest2", false)); + this.hasPlacedChestAtomic.set(3, tag.getBooleanOr("hasPlacedChest3", false)); + init(); + // 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 +303,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 +435,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 d28f1a25e2137955402e41679d1a4220a0136579..d751eac6d7a97b8de44c83c35ff7d3e36ca3b066 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.getBooleanOr("placedMainChest", false); - this.placedHiddenChest = tag.getBooleanOr("placedHiddenChest", false); - this.placedTrap1 = tag.getBooleanOr("placedTrap1", false); - this.placedTrap2 = tag.getBooleanOr("placedTrap2", false); + // DivineMC start - Chunk System Optimizations + this.placedMainChest.set(tag.getBooleanOr("placedMainChest", false)); + this.placedHiddenChest.set(tag.getBooleanOr("placedHiddenChest", false)); + this.placedTrap1.set(tag.getBooleanOr("placedTrap1", false)); + this.placedTrap2.set(tag.getBooleanOr("placedTrap2", false)); + // 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 @@ -236,8 +242,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, true), 3, -2, 2, box); @@ -328,14 +334,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, true), 8, -1, 3, box); this.placeBlock(level, Blocks.VINE.defaultBlockState().setValue(VineBlock.EAST, 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); @@ -378,8 +384,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 53d3bf1d2a1debe46e276b1db25b420be4ad9958..5212e4a4392efa0171c9d709dc2606a4e14df07f 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java @@ -91,7 +91,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) { @@ -950,7 +950,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 ae4ffcf1859e8ff7f8fbc91246e66e20f5c33dd7..b9124999f1aede4450f25e25c55bca96077670e8 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java @@ -1112,7 +1112,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; } @@ -1152,7 +1152,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); @@ -1387,7 +1387,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; @@ -1403,11 +1403,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 } } @@ -1545,24 +1545,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 1f1ee6e2d020cd06184313d19523ea928cf242c8..ab6d51b60cdbaed7ac7395d2a27eadd6d6b4518f 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java @@ -65,32 +65,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; @@ -140,9 +144,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; } @@ -152,9 +158,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) { @@ -165,10 +171,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; @@ -204,7 +210,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); @@ -690,7 +696,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) { @@ -700,11 +706,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 } } @@ -712,7 +718,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); @@ -1174,7 +1180,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); @@ -1223,10 +1229,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 081fe9509b777bd1f853b662ca8b01c64f27b5e5..9a3d1ed95d201d24374ff15c7a8daa30bc25f777 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 bb8b5ca9044ecb33687e7f20cfb0acbf55f887c7..df3a1b9ed4ad836bd3358b6b440964e497213ea3 100644 --- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java @@ -69,8 +69,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/level/levelgen/synth/ImprovedNoise.java b/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java index 46e98a99442523614284150964ba528d0c91493f..75df6465823ebacad48f3a0b3560c89ab3b6b093 100644 --- a/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java +++ b/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java @@ -11,6 +11,27 @@ public final class ImprovedNoise { public final double yo; public final double zo; + // DivineMC start - Chunk System Optimizations + private static final double[] FLAT_SIMPLEX_GRAD = new double[]{ + 1, 1, 0, 0, + -1, 1, 0, 0, + 1, -1, 0, 0, + -1, -1, 0, 0, + 1, 0, 1, 0, + -1, 0, 1, 0, + 1, 0, -1, 0, + -1, 0, -1, 0, + 0, 1, 1, 0, + 0, -1, 1, 0, + 0, 1, -1, 0, + 0, -1, -1, 0, + 1, 1, 0, 0, + 0, -1, 1, 0, + -1, 1, 0, 0, + 0, -1, -1, 0, + }; + // DivineMC end - Chunk System Optimizations + public ImprovedNoise(RandomSource random) { this.xo = random.nextDouble() * 256.0; this.yo = random.nextDouble() * 256.0; @@ -38,9 +59,11 @@ public final class ImprovedNoise { double d = x + this.xo; double d1 = y + this.yo; double d2 = z + this.zo; - int floor = Mth.floor(d); - int floor1 = Mth.floor(d1); - int floor2 = Mth.floor(d2); + // DivineMC start - Chunk System Optimizations + double floor = Math.floor(d); + double floor1 = Math.floor(d1); + double floor2 = Math.floor(d2); + // DivineMC end - Chunk System Optimizations double d3 = d - floor; double d4 = d1 - floor1; double d5 = d2 - floor2; @@ -53,25 +76,27 @@ public final class ImprovedNoise { d6 = d4; } - d7 = Mth.floor(d6 / yScale + 1.0E-7F) * yScale; + d7 = Math.floor(d6 / yScale + 1.0E-7F) * yScale; // DivineMC - Chunk System Optimizations } else { d7 = 0.0; } - return this.sampleAndLerp(floor, floor1, floor2, d3, d4 - d7, d5, d4); + return this.sampleAndLerp((int) floor, (int) floor1, (int) floor2, d3, d4 - d7, d5, d4); // DivineMC - Chunk System Optimizations } public double noiseWithDerivative(double x, double y, double z, double[] values) { double d = x + this.xo; double d1 = y + this.yo; double d2 = z + this.zo; - int floor = Mth.floor(d); - int floor1 = Mth.floor(d1); - int floor2 = Mth.floor(d2); + // DivineMC start - Chunk System Optimizations + double floor = Math.floor(d); + double floor1 = Math.floor(d1); + double floor2 = Math.floor(d2); + // DivineMC end - Chunk System Optimizations double d3 = d - floor; double d4 = d1 - floor1; double d5 = d2 - floor2; - return this.sampleWithDerivative(floor, floor1, floor2, d3, d4, d5, values); + return this.sampleWithDerivative((int) floor, (int) floor1, (int) floor2, d3, d4, d5, values); // DivineMC - Chunk System Optimizations } private static double gradDot(int gradIndex, double xFactor, double yFactor, double zFactor) { @@ -83,24 +108,69 @@ public final class ImprovedNoise { } private double sampleAndLerp(int gridX, int gridY, int gridZ, double deltaX, double weirdDeltaY, double deltaZ, double deltaY) { - int i = this.p(gridX); - int i1 = this.p(gridX + 1); - int i2 = this.p(i + gridY); - int i3 = this.p(i + gridY + 1); - int i4 = this.p(i1 + gridY); - int i5 = this.p(i1 + gridY + 1); - double d = gradDot(this.p(i2 + gridZ), deltaX, weirdDeltaY, deltaZ); - double d1 = gradDot(this.p(i4 + gridZ), deltaX - 1.0, weirdDeltaY, deltaZ); - double d2 = gradDot(this.p(i3 + gridZ), deltaX, weirdDeltaY - 1.0, deltaZ); - double d3 = gradDot(this.p(i5 + gridZ), deltaX - 1.0, weirdDeltaY - 1.0, deltaZ); - double d4 = gradDot(this.p(i2 + gridZ + 1), deltaX, weirdDeltaY, deltaZ - 1.0); - double d5 = gradDot(this.p(i4 + gridZ + 1), deltaX - 1.0, weirdDeltaY, deltaZ - 1.0); - double d6 = gradDot(this.p(i3 + gridZ + 1), deltaX, weirdDeltaY - 1.0, deltaZ - 1.0); - double d7 = gradDot(this.p(i5 + gridZ + 1), deltaX - 1.0, weirdDeltaY - 1.0, deltaZ - 1.0); - double d8 = Mth.smoothstep(deltaX); - double d9 = Mth.smoothstep(deltaY); - double d10 = Mth.smoothstep(deltaZ); - return Mth.lerp3(d8, d9, d10, d, d1, d2, d3, d4, d5, d6, d7); + // DivineMC start - Chunk System Optimizations + final int var0 = gridX & 0xFF; + final int var1 = (gridX + 1) & 0xFF; + final int var2 = this.p[var0] & 0xFF; + final int var3 = this.p[var1] & 0xFF; + final int var4 = (var2 + gridY) & 0xFF; + final int var5 = (var3 + gridY) & 0xFF; + final int var6 = (var2 + gridY + 1) & 0xFF; + final int var7 = (var3 + gridY + 1) & 0xFF; + final int var8 = this.p[var4] & 0xFF; + final int var9 = this.p[var5] & 0xFF; + final int var10 = this.p[var6] & 0xFF; + final int var11 = this.p[var7] & 0xFF; + + final int var12 = (var8 + gridZ) & 0xFF; + final int var13 = (var9 + gridZ) & 0xFF; + final int var14 = (var10 + gridZ) & 0xFF; + final int var15 = (var11 + gridZ) & 0xFF; + final int var16 = (var8 + gridZ + 1) & 0xFF; + final int var17 = (var9 + gridZ + 1) & 0xFF; + final int var18 = (var10 + gridZ + 1) & 0xFF; + final int var19 = (var11 + gridZ + 1) & 0xFF; + final int var20 = (this.p[var12] & 15) << 2; + final int var21 = (this.p[var13] & 15) << 2; + final int var22 = (this.p[var14] & 15) << 2; + final int var23 = (this.p[var15] & 15) << 2; + final int var24 = (this.p[var16] & 15) << 2; + final int var25 = (this.p[var17] & 15) << 2; + final int var26 = (this.p[var18] & 15) << 2; + final int var27 = (this.p[var19] & 15) << 2; + final double var60 = deltaX - 1.0; + final double var61 = weirdDeltaY - 1.0; + final double var62 = deltaZ - 1.0; + final double var87 = FLAT_SIMPLEX_GRAD[(var20) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var20) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var20) | 2] * deltaZ; + final double var88 = FLAT_SIMPLEX_GRAD[(var21) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var21) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var21) | 2] * deltaZ; + final double var89 = FLAT_SIMPLEX_GRAD[(var22) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var22) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var22) | 2] * deltaZ; + final double var90 = FLAT_SIMPLEX_GRAD[(var23) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var23) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var23) | 2] * deltaZ; + final double var91 = FLAT_SIMPLEX_GRAD[(var24) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var24) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var24) | 2] * var62; + final double var92 = FLAT_SIMPLEX_GRAD[(var25) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var25) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var25) | 2] * var62; + final double var93 = FLAT_SIMPLEX_GRAD[(var26) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var26) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var26) | 2] * var62; + final double var94 = FLAT_SIMPLEX_GRAD[(var27) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var27) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var27) | 2] * var62; + + final double var95 = deltaX * 6.0 - 15.0; + final double var96 = deltaY * 6.0 - 15.0; + final double var97 = deltaZ * 6.0 - 15.0; + final double var98 = deltaX * var95 + 10.0; + final double var99 = deltaY * var96 + 10.0; + final double var100 = deltaZ * var97 + 10.0; + final double var101 = deltaX * deltaX * deltaX * var98; + final double var102 = deltaY * deltaY * deltaY * var99; + final double var103 = deltaZ * deltaZ * deltaZ * var100; + + final double var113 = var87 + var101 * (var88 - var87); + final double var114 = var93 + var101 * (var94 - var93); + final double var115 = var91 + var101 * (var92 - var91); + final double var116 = var89 + var101 * (var90 - var89); + final double var117 = var114 - var115; + final double var118 = var102 * (var116 - var113); + final double var119 = var102 * var117; + final double var120 = var113 + var118; + final double var121 = var115 + var119; + return var120 + (var103 * (var121 - var120)); + // DivineMC end - Chunk System Optimizations } private double sampleWithDerivative(int gridX, int gridY, int gridZ, double deltaX, double deltaY, double deltaZ, double[] noiseValues) { diff --git a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java index ffac5b7b1eb1364ab8442d7145a7b4ebde68ee10..d331259b0bcf2d3c27718497ff2593a29d1558d0 100644 --- a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java +++ b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java @@ -26,6 +26,10 @@ public class PerlinNoise { public final double lowestFreqValueFactor; public final double lowestFreqInputFactor; private final double maxValue; + // DivineMC start - Chunk System Optimizations + private final int octaveSamplersCount; + private final double [] amplitudesArray; + // DivineMC end - Chunk System Optimizations @Deprecated public static PerlinNoise createLegacyForBlendedNoise(RandomSource random, IntStream octaves) { @@ -127,6 +131,10 @@ public class PerlinNoise { this.lowestFreqInputFactor = Math.pow(2.0, -i); this.lowestFreqValueFactor = Math.pow(2.0, size - 1) / (Math.pow(2.0, size) - 1.0); this.maxValue = this.edgeValue(2.0); + // DivineMC start - Chunk System Optimizations + this.octaveSamplersCount = this.noiseLevels.length; + this.amplitudesArray = this.amplitudes.toDoubleArray(); + // DivineMC end - Chunk System Optimizations } protected double maxValue() { @@ -138,7 +146,26 @@ public class PerlinNoise { } public double getValue(double x, double y, double z) { - return this.getValue(x, y, z, 0.0, 0.0, false); + // DivineMC start - Chunk System Optimizations + double d = 0.0; + double e = this.lowestFreqInputFactor; + double f = this.lowestFreqValueFactor; + + for (int i = 0; i < this.octaveSamplersCount; ++i) { + ImprovedNoise perlinNoiseSampler = this.noiseLevels[i]; + if (perlinNoiseSampler != null) { + double g = perlinNoiseSampler.noise( + wrap(x * e), wrap(y * e), wrap(z * e), 0.0, 0.0 + ); + d += this.amplitudesArray[i] * g * f; + } + + e *= 2.0; + f /= 2.0; + } + + return d; + // DivineMC end - Chunk System Optimizations } @Deprecated @@ -187,7 +214,7 @@ public class PerlinNoise { } public static double wrap(double value) { - return value - Mth.lfloor(value / 3.3554432E7 + 0.5) * 3.3554432E7; + return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7; // DivineMC - Chunk System Optimizations } protected int firstOctave() { diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java index 66d0a6390febe929ef774b0a7813329015bc8cc2..d1917dee4ca6bba5f2c92475811c724caf2948cb 100644 --- a/net/minecraft/world/ticks/LevelChunkTicks.java +++ b/net/minecraft/world/ticks/LevelChunkTicks.java @@ -14,10 +14,10 @@ import javax.annotation.Nullable; import net.minecraft.core.BlockPos; 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; @@ -67,10 +67,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; } @@ -83,6 +91,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); @@ -124,6 +133,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..6e3d4e78a7d92a846e68fe60271cfe5a5cd7b569 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); } @@ -182,7 +188,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();