Brand new async lighting system w/ Harmless chunk unload optimization
This commit is contained in:
@@ -284,4 +284,9 @@ public class AkarinGlobalConfig {
|
|||||||
private static void forceHardcoreDifficulty() {
|
private static void forceHardcoreDifficulty() {
|
||||||
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean useAsyncLighting;
|
||||||
|
private static void useAsyncLighting() {
|
||||||
|
useAsyncLighting = getBoolean("core.async-lighting.enable", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.Shadow;
|
|||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
@@ -22,6 +23,7 @@ public abstract class MixinChunkProviderServer {
|
|||||||
@Shadow public Long2ObjectOpenHashMap<Chunk> chunks;
|
@Shadow public Long2ObjectOpenHashMap<Chunk> chunks;
|
||||||
|
|
||||||
public void unload(Chunk chunk) {
|
public void unload(Chunk chunk) {
|
||||||
|
if (AkarinGlobalConfig.useAsyncLighting && chunk != null && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) return; // Akarin start - Re-add asynchronous lighting updates
|
||||||
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
|
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
|
||||||
// Akarin - avoid using the queue and simply check the unloaded flag during unloads
|
// Akarin - avoid using the queue and simply check the unloaded flag during unloads
|
||||||
// this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
|
// this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
|
||||||
@@ -53,16 +55,18 @@ public abstract class MixinChunkProviderServer {
|
|||||||
if (chunk.scheduledForUnload != null) {
|
if (chunk.scheduledForUnload != null) {
|
||||||
if (now - chunk.scheduledForUnload > unloadAfter) {
|
if (now - chunk.scheduledForUnload > unloadAfter) {
|
||||||
chunk.scheduledForUnload = null;
|
chunk.scheduledForUnload = null;
|
||||||
} else continue;
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unloadChunk(chunk, true)) { // Event cancelled
|
if (!unloadChunk(chunk, true)) { // Event cancelled
|
||||||
// If a plugin cancelled it, we shouldn't trying unload it for a while
|
// If a plugin cancelled it, we shouldn't trying unload it for a while
|
||||||
chunk.setShouldUnload(false);
|
chunk.setShouldUnload(false);
|
||||||
continue;
|
} else {
|
||||||
|
it.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
it.remove();
|
|
||||||
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
|
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.destroystokyo.paper.exception.ServerInternalException;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Queues;
|
import com.google.common.collect.Queues;
|
||||||
|
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -14,6 +16,8 @@ import java.util.Map;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@@ -24,8 +28,7 @@ import org.bukkit.craftbukkit.util.CraftMagicNumbers; // Paper
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Akarin Changes Note
|
* Akarin Changes Note
|
||||||
* 1) Add volatile to fields (async lighting)
|
* 1) Async lighting (performance)
|
||||||
* 2) Expose private methods (async lighting)
|
|
||||||
*/
|
*/
|
||||||
public class Chunk {
|
public class Chunk {
|
||||||
|
|
||||||
@@ -73,11 +76,11 @@ public class Chunk {
|
|||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Akarin - public
|
final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this);
|
||||||
// Paper end
|
// Paper end
|
||||||
private volatile boolean done; // Akarin - volatile
|
private boolean done;
|
||||||
private volatile boolean lit; // Akarin - volatile
|
private boolean lit;
|
||||||
private volatile boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER // Akarin - volatile
|
private boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER
|
||||||
private boolean s;
|
private boolean s;
|
||||||
private boolean t;
|
private boolean t;
|
||||||
private long lastSaved;
|
private long lastSaved;
|
||||||
@@ -123,6 +126,10 @@ public class Chunk {
|
|||||||
this.neighbors &= ~(0x1 << (x * 5 + 12 + z));
|
this.neighbors &= ~(0x1 << (x * 5 + 12 + z));
|
||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
// Akarin start - Re-add asynchronous light updates
|
||||||
|
public AtomicInteger pendingLightUpdates = new AtomicInteger();
|
||||||
|
public long lightUpdateTime;
|
||||||
|
// Akarin end
|
||||||
|
|
||||||
public Chunk(World world, int i, int j) {
|
public Chunk(World world, int i, int j) {
|
||||||
this.sections = new ChunkSection[16];
|
this.sections = new ChunkSection[16];
|
||||||
@@ -337,7 +344,7 @@ public class Chunk {
|
|||||||
private void a(int i, int j, int k, int l) {
|
private void a(int i, int j, int k, int l) {
|
||||||
if (l > k && this.world.areChunksLoaded(new BlockPosition(i, 0, j), 16)) {
|
if (l > k && this.world.areChunksLoaded(new BlockPosition(i, 0, j), 16)) {
|
||||||
for (int i1 = k; i1 < l; ++i1) {
|
for (int i1 = k; i1 < l; ++i1) {
|
||||||
this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j));
|
this.world.updateLight(EnumSkyBlock.SKY, new BlockPosition(i, i1, j)); // Akarin - Re-add asynchronous lighting updates
|
||||||
}
|
}
|
||||||
|
|
||||||
this.s = true;
|
this.s = true;
|
||||||
@@ -1217,7 +1224,7 @@ public class Chunk {
|
|||||||
|
|
||||||
public void b(boolean flag) {
|
public void b(boolean flag) {
|
||||||
if (this.m && this.world.worldProvider.m() && !flag) {
|
if (this.m && this.world.worldProvider.m() && !flag) {
|
||||||
this.h(this.world.isClientSide);
|
this.recheckGaps(this.world.isClientSide); // Akarin - Re-add asynchronous lighting updates
|
||||||
}
|
}
|
||||||
|
|
||||||
this.r = true;
|
this.r = true;
|
||||||
@@ -1238,6 +1245,22 @@ public class Chunk {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Akarin start - Recheck gaps asynchronously.
|
||||||
|
public void recheckGapsAsync(final boolean isClientSide) {
|
||||||
|
if (!AkarinGlobalConfig.useAsyncLighting) {
|
||||||
|
this.h(isClientSide);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
world.lightingExecutor.submit(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Chunk.this.h(isClientSide);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Akarin end
|
||||||
|
|
||||||
public boolean isReady() {
|
public boolean isReady() {
|
||||||
// Spigot Start
|
// Spigot Start
|
||||||
/*
|
/*
|
||||||
@@ -1408,7 +1431,7 @@ public class Chunk {
|
|||||||
this.h(false);
|
this.h(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void a(EnumDirection enumdirection) { // Akarin - private -> public
|
private void a(EnumDirection enumdirection) {
|
||||||
if (this.done) {
|
if (this.done) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,453 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
// CraftBukkit start
|
||||||
|
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
||||||
|
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Async lighting (performance)
|
||||||
|
*/
|
||||||
|
public class ChunkProviderServer implements IChunkProvider {
|
||||||
|
|
||||||
|
private static final Logger a = LogManager.getLogger();
|
||||||
|
public final Set<Long> unloadQueue = Sets.newHashSet();
|
||||||
|
public final ChunkGenerator chunkGenerator;
|
||||||
|
private final IChunkLoader chunkLoader;
|
||||||
|
// Paper start - chunk save stats
|
||||||
|
private long lastQueuedSaves = 0L; // Paper
|
||||||
|
private long lastProcessedSaves = 0L; // Paper
|
||||||
|
private long lastSaveStatPrinted = System.currentTimeMillis();
|
||||||
|
// Paper end
|
||||||
|
// Paper start
|
||||||
|
protected Chunk lastChunkByPos = null;
|
||||||
|
public Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<Chunk>(8192) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk get(long key) {
|
||||||
|
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
|
||||||
|
return lastChunkByPos;
|
||||||
|
}
|
||||||
|
return lastChunkByPos = super.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk remove(long key) {
|
||||||
|
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
|
||||||
|
lastChunkByPos = null;
|
||||||
|
}
|
||||||
|
return super.remove(key);
|
||||||
|
}
|
||||||
|
}; // CraftBukkit
|
||||||
|
// Paper end
|
||||||
|
public final WorldServer world;
|
||||||
|
|
||||||
|
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator chunkgenerator) {
|
||||||
|
this.world = worldserver;
|
||||||
|
this.chunkLoader = ichunkloader;
|
||||||
|
this.chunkGenerator = chunkgenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Chunk> a() {
|
||||||
|
return this.chunks.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unload(Chunk chunk) {
|
||||||
|
if (AkarinGlobalConfig.useAsyncLighting && chunk != null && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) return; // Akarin start - Re-add asynchronous lighting updates
|
||||||
|
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
|
||||||
|
this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
|
||||||
|
chunk.d = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void b() {
|
||||||
|
ObjectIterator objectiterator = this.chunks.values().iterator();
|
||||||
|
|
||||||
|
while (objectiterator.hasNext()) {
|
||||||
|
Chunk chunk = (Chunk) objectiterator.next();
|
||||||
|
|
||||||
|
this.unload(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Chunk getLoadedChunkAt(int i, int j) {
|
||||||
|
long k = ChunkCoordIntPair.a(i, j);
|
||||||
|
Chunk chunk = (Chunk) this.chunks.get(k);
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
chunk.d = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Chunk getOrLoadChunkAt(int i, int j) {
|
||||||
|
Chunk chunk = this.getLoadedChunkAt(i, j);
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
// CraftBukkit start
|
||||||
|
ChunkRegionLoader loader = null;
|
||||||
|
|
||||||
|
if (this.chunkLoader instanceof ChunkRegionLoader) {
|
||||||
|
loader = (ChunkRegionLoader) this.chunkLoader;
|
||||||
|
}
|
||||||
|
if (loader != null && loader.chunkExists(i, j)) {
|
||||||
|
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Chunk originalGetOrLoadChunkAt(int i, int j) {
|
||||||
|
// CraftBukkit end
|
||||||
|
Chunk chunk = this.getLoadedChunkAt(i, j);
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = this.loadChunk(i, j);
|
||||||
|
if (chunk != null) {
|
||||||
|
this.chunks.put(ChunkCoordIntPair.a(i, j), chunk);
|
||||||
|
chunk.addEntities();
|
||||||
|
chunk.loadNearby(this, this.chunkGenerator, false); // CraftBukkit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftBukkit start
|
||||||
|
public Chunk getChunkIfLoaded(int x, int z) {
|
||||||
|
return chunks.get(ChunkCoordIntPair.a(x, z));
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
public Chunk getChunkAt(int i, int j) {
|
||||||
|
return getChunkAt(i, j, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk getChunkAt(int i, int j, Runnable runnable) {
|
||||||
|
return getChunkAt(i, j, runnable, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk getChunkAt(int i, int j, Runnable runnable, boolean generate) {
|
||||||
|
Chunk chunk = world.paperConfig.allowPermaChunkLoaders ? getLoadedChunkAt(i, j) : getChunkIfLoaded(i, j); // Paper - Configurable perma chunk loaders
|
||||||
|
ChunkRegionLoader loader = null;
|
||||||
|
|
||||||
|
if (this.chunkLoader instanceof ChunkRegionLoader) {
|
||||||
|
loader = (ChunkRegionLoader) this.chunkLoader;
|
||||||
|
|
||||||
|
}
|
||||||
|
// We can only use the queue for already generated chunks
|
||||||
|
if (chunk == null && loader != null && loader.chunkExists(i, j)) {
|
||||||
|
if (runnable != null) {
|
||||||
|
ChunkIOExecutor.queueChunkLoad(world, loader, this, i, j, runnable);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
|
||||||
|
|
||||||
|
// Paper start - If there was an issue loading the chunk from region, stage1 will fail and stage2 will load it sync
|
||||||
|
// all we need to do is fetch an instance
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = getChunkIfLoaded(i, j);
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
}
|
||||||
|
} else if (chunk == null && generate) {
|
||||||
|
chunk = originalGetChunkAt(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't load the chunk async and have a callback run it now
|
||||||
|
if (runnable != null) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk originalGetChunkAt(int i, int j) {
|
||||||
|
Chunk chunk = this.originalGetOrLoadChunkAt(i, j);
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
world.timings.syncChunkLoadTimer.startTiming(); // Spigot
|
||||||
|
long k = ChunkCoordIntPair.a(i, j);
|
||||||
|
|
||||||
|
try {
|
||||||
|
chunk = this.chunkGenerator.getOrCreateChunk(i, j);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
|
||||||
|
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
|
||||||
|
|
||||||
|
crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)}));
|
||||||
|
crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(k));
|
||||||
|
crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator);
|
||||||
|
throw new ReportedException(crashreport);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chunks.put(k, chunk);
|
||||||
|
chunk.addEntities();
|
||||||
|
chunk.loadNearby(this, this.chunkGenerator, true); // CraftBukkit
|
||||||
|
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Chunk loadChunk(int i, int j) {
|
||||||
|
try {
|
||||||
|
Chunk chunk = this.chunkLoader.a(this.world, i, j);
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
chunk.setLastSaved(this.world.getTime());
|
||||||
|
this.chunkGenerator.recreateStructures(chunk, i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
// Paper start
|
||||||
|
String msg = "Couldn\'t load chunk";
|
||||||
|
ChunkProviderServer.a.error(msg, exception);
|
||||||
|
ServerInternalException.reportInternalException(exception);
|
||||||
|
// Paper end
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveChunkNOP(Chunk chunk) {
|
||||||
|
try {
|
||||||
|
// this.chunkLoader.b(this.world, chunk); // Spigot
|
||||||
|
} catch (Exception exception) {
|
||||||
|
// Paper start
|
||||||
|
String msg = "Couldn\'t save entities";
|
||||||
|
ChunkProviderServer.a.error(msg, exception);
|
||||||
|
ServerInternalException.reportInternalException(exception);
|
||||||
|
// Paper end
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveChunk(Chunk chunk, boolean unloaded) { // Spigot
|
||||||
|
try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) {
|
||||||
|
chunk.setLastSaved(this.world.getTime());
|
||||||
|
this.chunkLoader.saveChunk(this.world, chunk, unloaded); // Spigot
|
||||||
|
} catch (IOException ioexception) {
|
||||||
|
// Paper start
|
||||||
|
String msg = "Couldn\'t save chunk";
|
||||||
|
ChunkProviderServer.a.error(msg, ioexception);
|
||||||
|
ServerInternalException.reportInternalException(ioexception);
|
||||||
|
} catch (ExceptionWorldConflict exceptionworldconflict) {
|
||||||
|
String msg = "Couldn\'t save chunk; already in use by another instance of Minecraft?";
|
||||||
|
ChunkProviderServer.a.error(msg, exceptionworldconflict);
|
||||||
|
ServerInternalException.reportInternalException(exceptionworldconflict);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(boolean flag) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// CraftBukkit start
|
||||||
|
// Paper start
|
||||||
|
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
|
||||||
|
final int queueSize = chunkLoader.getQueueSize();
|
||||||
|
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
final long timeSince = (now - lastSaveStatPrinted) / 1000;
|
||||||
|
final Integer printRateSecs = Integer.getInteger("printSaveStats");
|
||||||
|
if (printRateSecs != null && timeSince >= printRateSecs) {
|
||||||
|
final String timeStr = "/" + timeSince +"s";
|
||||||
|
final long queuedSaves = chunkLoader.getQueuedSaves();
|
||||||
|
long queuedDiff = queuedSaves - lastQueuedSaves;
|
||||||
|
lastQueuedSaves = queuedSaves;
|
||||||
|
|
||||||
|
final long processedSaves = chunkLoader.getProcessedSaves();
|
||||||
|
long processedDiff = processedSaves - lastProcessedSaves;
|
||||||
|
lastProcessedSaves = processedSaves;
|
||||||
|
|
||||||
|
lastSaveStatPrinted = now;
|
||||||
|
if (processedDiff > 0 || queueSize > 0 || queuedDiff > 0) {
|
||||||
|
System.out.println("[Chunk Save Stats] " + world.worldData.getName() +
|
||||||
|
" - Current: " + queueSize +
|
||||||
|
" - Queued: " + queuedDiff + timeStr +
|
||||||
|
" - Processed: " +processedDiff + timeStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queueSize > world.paperConfig.queueSizeAutoSaveThreshold){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final int autoSaveLimit = world.paperConfig.maxAutoSaveChunksPerTick;
|
||||||
|
// Paper end
|
||||||
|
Iterator iterator = this.chunks.values().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Chunk chunk = (Chunk) iterator.next();
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
this.saveChunkNOP(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk.a(flag)) {
|
||||||
|
this.saveChunk(chunk, false); // Spigot
|
||||||
|
chunk.f(false);
|
||||||
|
++i;
|
||||||
|
if (!flag && i >= autoSaveLimit) { // Spigot - // Paper - Incremental Auto Save - cap max per tick
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void c() {
|
||||||
|
this.chunkLoader.c();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96;
|
||||||
|
|
||||||
|
public boolean unloadChunks() {
|
||||||
|
if (!this.world.savingDisabled) {
|
||||||
|
if (!this.unloadQueue.isEmpty()) {
|
||||||
|
// Spigot start
|
||||||
|
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
|
||||||
|
activityAccountant.startActivity(0.5);
|
||||||
|
int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||||
|
// Spigot end
|
||||||
|
|
||||||
|
Iterator iterator = this.unloadQueue.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) { // Spigot
|
||||||
|
Long olong = (Long) iterator.next();
|
||||||
|
iterator.remove(); // Spigot
|
||||||
|
Chunk chunk = (Chunk) this.chunks.get(olong);
|
||||||
|
|
||||||
|
if (chunk != null && chunk.d) {
|
||||||
|
// CraftBukkit start - move unload logic to own method
|
||||||
|
chunk.setShouldUnload(false); // Paper
|
||||||
|
if (!unloadChunk(chunk, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
// Spigot start
|
||||||
|
if (this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Spigot end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activityAccountant.endActivity(); // Spigot
|
||||||
|
}
|
||||||
|
// Paper start - delayed chunk unloads
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
|
||||||
|
if (unloadAfter > 0) {
|
||||||
|
//noinspection Convert2streamapi
|
||||||
|
for (Chunk chunk : chunks.values()) {
|
||||||
|
if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) {
|
||||||
|
chunk.scheduledForUnload = null;
|
||||||
|
unload(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
this.chunkLoader.b();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftBukkit start
|
||||||
|
public boolean unloadChunk(Chunk chunk, boolean save) {
|
||||||
|
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, save);
|
||||||
|
this.world.getServer().getPluginManager().callEvent(event);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
save = event.isSaveChunk();
|
||||||
|
chunk.lightingQueue.processUnload(); // Paper
|
||||||
|
|
||||||
|
// Update neighbor counts
|
||||||
|
for (int x = -2; x < 3; x++) {
|
||||||
|
for (int z = -2; z < 3; z++) {
|
||||||
|
if (x == 0 && z == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z);
|
||||||
|
if (neighbor != null) {
|
||||||
|
neighbor.setNeighborUnloaded(-x, -z);
|
||||||
|
chunk.setNeighborUnloaded(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Moved from unloadChunks above
|
||||||
|
chunk.removeEntities();
|
||||||
|
if (save) {
|
||||||
|
this.saveChunk(chunk, true); // Spigot
|
||||||
|
this.saveChunkNOP(chunk);
|
||||||
|
}
|
||||||
|
this.chunks.remove(chunk.chunkKey);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
public boolean e() {
|
||||||
|
return !this.world.savingDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BiomeBase.BiomeMeta> a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
|
||||||
|
return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public BlockPosition a(World world, String s, BlockPosition blockposition, boolean flag) {
|
||||||
|
return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(World world, String s, BlockPosition blockposition) {
|
||||||
|
return this.chunkGenerator.a(world, s, blockposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int g() {
|
||||||
|
return this.chunks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoaded(int i, int j) {
|
||||||
|
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean e(int i, int j) {
|
||||||
|
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j)) || this.chunkLoader.chunkExists(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
3456
sources/src/main/java/net/minecraft/server/World.java
Normal file
3456
sources/src/main/java/net/minecraft/server/World.java
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user