Add workaround for CB chunk unload issue

This commit is contained in:
Mike Primm 2012-08-13 00:17:09 -05:00
parent 6cf60b3d53
commit fde3d14abd
2 changed files with 30 additions and 71 deletions

View File

@ -179,5 +179,10 @@
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/PermissionsBukkit.jar</systemPath> <systemPath>${project.basedir}/PermissionsBukkit.jar</systemPath>
</dependency> </dependency>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.2.5-R5.1-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,8 +1,5 @@
package org.dynmap.bukkit; package org.dynmap.bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -11,7 +8,9 @@ import java.util.ListIterator;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.entity.Entity; import org.bukkit.craftbukkit.CraftChunk;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.util.LongHashset;
import org.bukkit.ChunkSnapshot; import org.bukkit.ChunkSnapshot;
import org.dynmap.DynmapChunk; import org.dynmap.DynmapChunk;
import org.dynmap.DynmapCore; import org.dynmap.DynmapCore;
@ -28,11 +27,7 @@ import org.getspout.spoutapi.block.SpoutChunk;
*/ */
public class NewMapChunkCache implements MapChunkCache { public class NewMapChunkCache implements MapChunkCache {
private static boolean init = false; private static boolean init = false;
private static Method gethandle = null;
private static Method removeentities = null;
private static Field doneflag = null;
private static boolean use_spout = false; private static boolean use_spout = false;
private static boolean use_sections = false;
private World w; private World w;
private DynmapWorld dw; private DynmapWorld dw;
@ -660,32 +655,8 @@ public class NewMapChunkCache implements MapChunkCache {
/** /**
* Construct empty cache * Construct empty cache
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" })
public NewMapChunkCache() { public NewMapChunkCache() {
if(!init) { if(!init) {
/* Get CraftChunk.getChunkSnapshot(boolean,boolean,boolean) and CraftChunk.getHandle() */
try {
Class c = Class.forName("org.bukkit.craftbukkit.CraftChunk");
gethandle = c.getDeclaredMethod("getHandle", new Class[0]);
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
}
/* Get Chunk.removeEntities() */
try {
Class c = Class.forName("net.minecraft.server.Chunk");
removeentities = c.getDeclaredMethod("removeEntities", new Class[0]);
doneflag = c.getField("done");
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
} catch (NoSuchFieldException nsfx) {
}
/* Check for ChunkSnapshot.isSectionEmpty(int) method */
try {
ChunkSnapshot.class.getDeclaredMethod("isSectionEmpty", new Class[] { int.class });
use_sections = true;
} catch (NoSuchMethodException nsmx) {
}
use_spout = DynmapPlugin.plugin.hasSpout(); use_spout = DynmapPlugin.plugin.hasSpout();
init = true; init = true;
@ -737,6 +708,8 @@ public class NewMapChunkCache implements MapChunkCache {
public int loadChunks(int max_to_load) { public int loadChunks(int max_to_load) {
long t0 = System.nanoTime(); long t0 = System.nanoTime();
CraftWorld cw = (CraftWorld)w;
LongHashset unloadqueue = cw.getHandle().chunkProviderServer.unloadQueue;
int cnt = 0; int cnt = 0;
if(iterator == null) if(iterator == null)
iterator = chunks.listIterator(); iterator = chunks.listIterator();
@ -781,8 +754,17 @@ public class NewMapChunkCache implements MapChunkCache {
chunks_attempted++; chunks_attempted++;
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z); boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
boolean didload = false; boolean didload = false;
boolean isunloadpending = unloadqueue.containsKey(chunk.x, chunk.z);
if (isunloadpending) { /* Workaround: can't be pending if not loaded */
wasLoaded = true;
}
try { try {
if (!wasLoaded) {
didload = w.loadChunk(chunk.x, chunk.z, false); didload = w.loadChunk(chunk.x, chunk.z, false);
}
else { /* If already was loaded, no need to load */
didload = true;
}
} catch (Throwable t) { /* Catch chunk error from Bukkit */ } catch (Throwable t) { /* Catch chunk error from Bukkit */
Log.warning("Bukkit error loading chunk " + chunk.x + "," + chunk.z + " on " + w.getName()); Log.warning("Bukkit error loading chunk " + chunk.x + "," + chunk.z + " on " + w.getName());
if(!wasLoaded) { /* If wasn't loaded, we loaded it if it now is */ if(!wasLoaded) { /* If wasn't loaded, we loaded it if it now is */
@ -796,16 +778,6 @@ public class NewMapChunkCache implements MapChunkCache {
/* If it did load, make cache of it */ /* If it did load, make cache of it */
if(didload) { if(didload) {
Chunk c = w.getChunkAt(chunk.x, chunk.z); /* Get the chunk */ Chunk c = w.getChunkAt(chunk.x, chunk.z); /* Get the chunk */
/* Try to get n.m.s.Chunk handle */
Object nmschunk = null;
if(gethandle != null) {
try {
nmschunk = gethandle.invoke(c);
} catch (InvocationTargetException itx) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
/* Test if chunk isn't populated */ /* Test if chunk isn't populated */
boolean populated = true; boolean populated = true;
//TODO: figure out why this doesn't appear to be reliable in Bukkit //TODO: figure out why this doesn't appear to be reliable in Bukkit
@ -847,24 +819,8 @@ public class NewMapChunkCache implements MapChunkCache {
/* It looks like bukkit "leaks" entities - they don't get removed from the world-level table /* It looks like bukkit "leaks" entities - they don't get removed from the world-level table
* when chunks are unloaded but not saved - removing them seems to do the trick */ * when chunks are unloaded but not saved - removing them seems to do the trick */
if(!(didgenerate && do_save)) { if(!(didgenerate && do_save)) {
boolean did_remove = false; CraftChunk cc = (CraftChunk)c;
if(removeentities != null) { cc.getHandle().removeEntities();
try {
if(nmschunk != null) {
removeentities.invoke(nmschunk);
did_remove = true;
}
} catch (InvocationTargetException itx) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
if(!did_remove) {
if(c != null) {
for(Entity e: c.getEntities())
e.remove();
}
}
} }
/* Since we only remember ones we loaded, and we're synchronous, no player has /* Since we only remember ones we loaded, and we're synchronous, no player has
* moved, so it must be safe (also prevent chunk leak, which appears to happen * moved, so it must be safe (also prevent chunk leak, which appears to happen
@ -874,6 +830,9 @@ public class NewMapChunkCache implements MapChunkCache {
* Also, if we did generate it, need to save it */ * Also, if we did generate it, need to save it */
w.unloadChunk(chunk.x, chunk.z, didgenerate && do_save, false); w.unloadChunk(chunk.x, chunk.z, didgenerate && do_save, false);
} }
else if (isunloadpending) { /* Else, if loaded and unload is pending */
w.unloadChunkRequest(chunk.x, chunk.z); /* Request new unload */
}
} }
cnt++; cnt++;
} }
@ -960,10 +919,6 @@ public class NewMapChunkCache implements MapChunkCache {
private void initSectionData(int idx) { private void initSectionData(int idx) {
isSectionNotEmpty[idx] = new boolean[nsect + 1]; isSectionNotEmpty[idx] = new boolean[nsect + 1];
if(snaparray[idx] != EMPTY) { if(snaparray[idx] != EMPTY) {
if(!use_sections) {
Arrays.fill(isSectionNotEmpty[idx], true);
}
else {
for(int i = 0; i < nsect; i++) { for(int i = 0; i < nsect; i++) {
if(snaparray[idx].isSectionEmpty(i) == false) { if(snaparray[idx].isSectionEmpty(i) == false) {
isSectionNotEmpty[idx][i] = true; isSectionNotEmpty[idx][i] = true;
@ -971,7 +926,6 @@ public class NewMapChunkCache implements MapChunkCache {
} }
} }
} }
}
public boolean isEmptySection(int sx, int sy, int sz) { public boolean isEmptySection(int sx, int sy, int sz) {
int idx = (sx - x_min) + (sz - z_min) * x_dim; int idx = (sx - x_min) + (sz - z_min) * x_dim;
if(isSectionNotEmpty[idx] == null) { if(isSectionNotEmpty[idx] == null) {
@ -1083,8 +1037,8 @@ public class NewMapChunkCache implements MapChunkCache {
static { static {
Biome[] b = Biome.values(); Biome[] b = Biome.values();
BiomeMap[] bm = BiomeMap.values(); BiomeMap[] bm = BiomeMap.values();
biome_to_bmap = new BiomeMap[b.length]; biome_to_bmap = new BiomeMap[256];
for(int i = 0; i < b.length; i++) { for(int i = 0; i < biome_to_bmap.length; i++) {
biome_to_bmap[i] = BiomeMap.NULL; biome_to_bmap[i] = BiomeMap.NULL;
} }
for(int i = 0; i < b.length; i++) { for(int i = 0; i < b.length; i++) {