if (!safeToGenerate()) {
return defaultEmptyChunk;
}
long key = key(x, z);
Chunk chunk;
final AtomicInteger lock = getLock(key);
boolean wasGenerated = false;
try {
boolean inLoadingMap = false;
// Lock on the lock for this chunk - prevent multiple instances of the same chunk
synchronized (lock) {
chunk = (Chunk) chunks.getValueByKey(key);
if (chunk != null) {
return chunk;
}
chunk = (Chunk) loadingChunks.getValueByKey(key);
if (regenerate) {
if (!allowGenerate) {
throw new IllegalArgumentException();
}
loadingChunks.put(key, defaultEmptyChunk);
} else if (chunk == null) {
finalizeUnload(key);
chunk = safeLoadChunk(x, z);
if (chunk != null && (chunk.xPosition != x || chunk.zPosition != z)) {
Log.severe("Chunk at " + chunk.xPosition + ',' + chunk.zPosition + " was stored at " + x + ',' + z + "\nResetting this chunk.");
chunk = null;
}
if (chunk == null) {
loadingChunks.put(key, defaultEmptyChunk);
if (!allowGenerate) {
return null;
} else if (generator == null) {
return defaultEmptyChunk;
}
} else {
loadingChunks.put(key, chunk);
inLoadingMap = true;
if (!world.loadEventFired) {
Log.warning("Loaded chunk before world load event fired, this can cause many issues, including loss of multiblock data.", new Throwable());
}
}
} else if (chunk != defaultEmptyChunk) {
inLoadingMap = true;
}
}
// Unlock this chunk - avoids a deadlock
// Thread A - requests chunk A - needs genned
// Thread B - requests chunk B - needs genned
// In thread A, redpower tries to load chunk B
// because its marble gen is buggy.
// Thread B is now waiting for the generate lock,
// Thread A is waiting for the lock on chunk B
// Lock the generation lock - ChunkProviderGenerate isn't threadsafe at all
boolean locked = true;
generateLock.lock();
try {
synchronized (lock) {
chunk = (Chunk) chunks.getValueByKey(key);
if (chunk != null) {
return chunk;
}
worldGenInProgress.set(Boolean.TRUE);
try {
chunk = (Chunk) loadingChunks.getValueByKey(key);
if (chunk == null) {
Log.severe("Failed to load chunk " + chunk + " at " + x + ',' + z + " as it is missing from the loading chunks map.");
return defaultEmptyChunk;
}
if (chunk == defaultEmptyChunk) {
try {
chunk = generator.provideChunk(x, z);
if (chunk == null) {
Log.severe("Null chunk was generated for " + x + ',' + z + " by " + generator);
return defaultEmptyChunk;
}
chunk.isTerrainPopulated = false;
wasGenerated = true;
} catch (Throwable t) {
Log.severe("Failed to generate a chunk in " + Log.name(world) + " at chunk coords " + x + ',' + z);
throw UnsafeUtil.throwIgnoreChecked(t);
}
} else {
if (generator != null) {
generator.recreateStructures(x, z);
}
}
if (!inLoadingMap) {
loadingChunks.put(key, chunk);
}
locked = false;
generateLock.unlock();
chunk.threadUnsafeChunkLoad();
chunks.put(key, chunk);
} finally {
worldGenInProgress.set(Boolean.FALSE);
}
}
} finally {
if (locked) {
generateLock.unlock();
}
}
} finally {
if (lock.decrementAndGet() == 0) {
loadingChunks.remove(key);
}
}
loadedChunks.add(chunk);
chunk.onChunkLoad();
fireBukkitLoadEvent(chunk, wasGenerated);
chunkLoadLocks.remove(key);
tryPopulateChunks(chunk);
return chunk;