Treat chunks in the unload queue as if they are already unloaded to prevent processing on them removing them from the unload queue and leaking.
285 lines
9.4 KiB
Java
285 lines
9.4 KiB
Java
package net.minecraft.server;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
// CraftBukkit start
|
|
import java.util.Random;
|
|
|
|
import org.bukkit.Server;
|
|
import org.bukkit.craftbukkit.util.LongHashset;
|
|
import org.bukkit.craftbukkit.util.LongHashtable;
|
|
import org.bukkit.event.world.ChunkUnloadEvent;
|
|
// CraftBukkit end
|
|
|
|
public class ChunkProviderServer implements IChunkProvider {
|
|
|
|
// CraftBukkit start
|
|
public LongHashset unloadQueue = new LongHashset();
|
|
public Chunk emptyChunk;
|
|
public IChunkProvider chunkProvider; // CraftBukkit
|
|
private IChunkLoader e;
|
|
public boolean forceChunkLoad = true;
|
|
public LongHashtable<Chunk> chunks = new LongHashtable<Chunk>();
|
|
public List chunkList = new ArrayList();
|
|
public WorldServer world;
|
|
// CraftBukkit end
|
|
|
|
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, IChunkProvider ichunkprovider) {
|
|
this.emptyChunk = new EmptyChunk(worldserver, 0, 0);
|
|
this.world = worldserver;
|
|
this.e = ichunkloader;
|
|
this.chunkProvider = ichunkprovider;
|
|
}
|
|
|
|
public boolean isChunkLoaded(int i, int j) {
|
|
return !this.unloadQueue.containsKey(i, j) && this.chunks.containsKey(i, j); // CraftBukkit
|
|
}
|
|
|
|
public void queueUnload(int i, int j) {
|
|
if (this.world.worldProvider.e()) {
|
|
ChunkCoordinates chunkcoordinates = this.world.getSpawn();
|
|
int k = i * 16 + 8 - chunkcoordinates.x;
|
|
int l = j * 16 + 8 - chunkcoordinates.z;
|
|
short short1 = 128;
|
|
|
|
if (k < -short1 || k > short1 || l < -short1 || l > short1 || !(this.world.keepSpawnInMemory)) { // CraftBukkit - added 'this.world.keepSpawnInMemory'
|
|
this.unloadQueue.add(i, j); // CraftBukkit
|
|
}
|
|
} else {
|
|
this.unloadQueue.add(i, j); // CraftBukkit
|
|
}
|
|
}
|
|
|
|
public void a() {
|
|
Iterator iterator = this.chunkList.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
Chunk chunk = (Chunk) iterator.next();
|
|
|
|
this.queueUnload(chunk.x, chunk.z);
|
|
}
|
|
}
|
|
|
|
public Chunk getChunkAt(int i, int j) {
|
|
// CraftBukkit start
|
|
this.unloadQueue.remove(i, j);
|
|
Chunk chunk = (Chunk) this.chunks.get(i, j);
|
|
boolean newChunk = false;
|
|
// CraftBukkit end
|
|
|
|
if (chunk == null) {
|
|
chunk = this.loadChunk(i, j);
|
|
if (chunk == null) {
|
|
if (this.chunkProvider == null) {
|
|
chunk = this.emptyChunk;
|
|
} else {
|
|
chunk = this.chunkProvider.getOrCreateChunk(i, j);
|
|
}
|
|
newChunk = true; // CraftBukkit
|
|
}
|
|
|
|
this.chunks.put(i, j, chunk); // CraftBukkit
|
|
this.chunkList.add(chunk);
|
|
if (chunk != null) {
|
|
chunk.addEntities();
|
|
}
|
|
|
|
// CraftBukkit start
|
|
Server server = this.world.getServer();
|
|
if (server != null) {
|
|
/*
|
|
* If it's a new world, the first few chunks are generated inside
|
|
* the World constructor. We can't reliably alter that, so we have
|
|
* no way of creating a CraftWorld/CraftServer at that point.
|
|
*/
|
|
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, newChunk));
|
|
}
|
|
// CraftBukkit end
|
|
|
|
chunk.a(this, this, i, j);
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
public Chunk getOrCreateChunk(int i, int j) {
|
|
// CraftBukkit start
|
|
Chunk chunk = (Chunk) this.chunks.get(i, j);
|
|
|
|
chunk = chunk == null ? (!this.world.isLoading && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk;
|
|
if (chunk == this.emptyChunk) return chunk;
|
|
if (i != chunk.x || j != chunk.z) {
|
|
MinecraftServer.log.severe("Chunk (" + chunk.x + ", " + chunk.z + ") stored at (" + i + ", " + j + ") in world '" + world.getWorld().getName() + "'");
|
|
MinecraftServer.log.severe(chunk.getClass().getName());
|
|
Throwable ex = new Throwable();
|
|
ex.fillInStackTrace();
|
|
ex.printStackTrace();
|
|
}
|
|
return chunk;
|
|
// CraftBukkit end
|
|
}
|
|
|
|
public Chunk loadChunk(int i, int j) { // CraftBukkit - private -> public
|
|
if (this.e == null) {
|
|
return null;
|
|
} else {
|
|
try {
|
|
Chunk chunk = this.e.a(this.world, i, j);
|
|
|
|
if (chunk != null) {
|
|
chunk.n = this.world.getTime();
|
|
}
|
|
|
|
return chunk;
|
|
} catch (Exception exception) {
|
|
exception.printStackTrace();
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void saveChunkNOP(Chunk chunk) { // CraftBukkit - private -> public
|
|
if (this.e != null) {
|
|
try {
|
|
this.e.b(this.world, chunk);
|
|
} catch (Exception exception) {
|
|
exception.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void saveChunk(Chunk chunk) { // CraftBukkit - private -> public
|
|
if (this.e != null) {
|
|
try {
|
|
chunk.n = this.world.getTime();
|
|
this.e.a(this.world, chunk);
|
|
} catch (Exception ioexception) { // CraftBukkit - IOException -> Exception
|
|
ioexception.printStackTrace();
|
|
// CraftBukkit start - remove extra exception
|
|
}
|
|
// } catch (ExceptionWorldConflict exceptionworldconflict) {
|
|
// exceptionworldconflict.printStackTrace();
|
|
// }
|
|
// CraftBukkit end
|
|
}
|
|
}
|
|
|
|
public void getChunkAt(IChunkProvider ichunkprovider, int i, int j) {
|
|
Chunk chunk = this.getOrCreateChunk(i, j);
|
|
|
|
if (!chunk.done) {
|
|
chunk.done = true;
|
|
if (this.chunkProvider != null) {
|
|
this.chunkProvider.getChunkAt(ichunkprovider, i, j);
|
|
|
|
// CraftBukkit start
|
|
BlockSand.instaFall = true;
|
|
Random random = new Random();
|
|
random.setSeed(world.getSeed());
|
|
long xRand = random.nextLong() / 2L * 2L + 1L;
|
|
long zRand = random.nextLong() / 2L * 2L + 1L;
|
|
random.setSeed((long) i * xRand + (long) j * zRand ^ world.getSeed());
|
|
|
|
org.bukkit.World world = this.world.getWorld();
|
|
if (world != null) {
|
|
for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) {
|
|
populator.populate(world, random, chunk.bukkitChunk);
|
|
}
|
|
}
|
|
BlockSand.instaFall = false;
|
|
this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(chunk.bukkitChunk));
|
|
// CraftBukkit end
|
|
|
|
chunk.e();
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) {
|
|
int i = 0;
|
|
Iterator iterator = this.chunkList.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
Chunk chunk = (Chunk) iterator.next();
|
|
|
|
if (flag) {
|
|
this.saveChunkNOP(chunk);
|
|
}
|
|
|
|
if (chunk.a(flag)) {
|
|
this.saveChunk(chunk);
|
|
chunk.l = false;
|
|
++i;
|
|
if (i == 24 && !flag) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag) {
|
|
if (this.e == null) {
|
|
return true;
|
|
}
|
|
|
|
this.e.b();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean unloadChunks() {
|
|
if (!this.world.savingDisabled) {
|
|
// CraftBukkit start
|
|
Server server = this.world.getServer();
|
|
for (int i = 0; i < 50 && !this.unloadQueue.isEmpty(); i++) {
|
|
long chunkcoordinates = this.unloadQueue.popFirst();
|
|
Chunk chunk = this.chunks.get(chunkcoordinates);
|
|
if (chunk == null) continue;
|
|
|
|
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk);
|
|
server.getPluginManager().callEvent(event);
|
|
if (!event.isCancelled()) {
|
|
chunk.removeEntities();
|
|
this.saveChunk(chunk);
|
|
this.saveChunkNOP(chunk);
|
|
// this.unloadQueue.remove(integer);
|
|
this.chunks.remove(chunkcoordinates); // CraftBukkit
|
|
this.chunkList.remove(chunk);
|
|
}
|
|
}
|
|
// CraftBukkit end
|
|
|
|
if (this.e != null) {
|
|
this.e.a();
|
|
}
|
|
}
|
|
|
|
return this.chunkProvider.unloadChunks();
|
|
}
|
|
|
|
public boolean canSave() {
|
|
return !this.world.savingDisabled;
|
|
}
|
|
|
|
public String getName() {
|
|
return "ServerChunkCache: " + this.chunks.values().size() + " Drop: " + this.unloadQueue.size(); // CraftBukkit
|
|
}
|
|
|
|
public List getMobsFor(EnumCreatureType enumcreaturetype, int i, int j, int k) {
|
|
return this.chunkProvider.getMobsFor(enumcreaturetype, i, j, k);
|
|
}
|
|
|
|
public ChunkPosition findNearestMapFeature(World world, String s, int i, int j, int k) {
|
|
return this.chunkProvider.findNearestMapFeature(world, s, i, j, k);
|
|
}
|
|
|
|
public int getLoadedChunks() {
|
|
return this.chunks.values().size(); // CraftBukkit
|
|
}
|
|
}
|