Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added better error handling for corrupt chunks

  • Loading branch information...
commit e9cf7475137f037a8a69dfdbb256cdc60f04af53 1 parent 9b7226e
@brownan brownan authored
View
16 overviewer_core/cache.py
@@ -59,10 +59,10 @@ def __init__(self, size=100, destructor=None):
"""
self.cache = {}
- self.listhead = LRUCache._LinkNode()
- self.listtail = LRUCache._LinkNode()
# Two sentinel nodes at the ends of the linked list simplify boundary
# conditions in the code below.
+ self.listhead = LRUCache._LinkNode()
+ self.listtail = LRUCache._LinkNode()
self.listhead.right = self.listtail
self.listtail.left = self.listhead
@@ -125,6 +125,18 @@ def __setitem__(self, key, value):
cache[key] = link
+ def __delitem__(self, key):
+ # Used to flush the cache of this key
+ link = self.cache[key]
+ del cache[key]
+ link.left.right = link.right
+ link.right.left = link.left
+
+ # Call the destructor
+ d = self.destructor
+ if d:
+ d(link.value)
+
# memcached is an option, but unless your IO costs are really high, it just
# ends up adding overhead and isn't worth it.
try:
View
11 overviewer_core/nbt.py
@@ -180,11 +180,15 @@ def read_all(self):
except (struct.error, ValueError), e:
raise CorruptNBTError("could not parse nbt: %s" % (str(e),))
-class CorruptRegionError(Exception):
+class CorruptionError(Exception):
+ pass
+class CorruptRegionError(CorruptionError):
"""An exception raised when the MCRFileReader class encounters an
error during region file parsing.
"""
pass
+class CorruptChunkError(CorruptionError):
+ pass
# For reference, the MCR format is outlined at
# <http://www.minecraftwiki.net/wiki/Beta_Level_Format>
@@ -294,4 +298,7 @@ def load_chunk(self, x, z):
raise CorruptRegionError("chunk length is invalid")
data = StringIO.StringIO(data)
- return NBTFileReader(data, is_gzip=is_gzip).read_all()
+ try:
+ return NBTFileReader(data, is_gzip=is_gzip).read_all()
+ except Exception, e:
+ raise CorruptChunkError("Misc error parsing chunk: " + str(e))
View
15 overviewer_core/tileset.py
@@ -29,6 +29,7 @@
from PIL import Image
from .util import roundrobin
+from . import nbt
from .files import FileReplacer
from .optimizeimages import optimize_image
import c_overviewer
@@ -934,9 +935,17 @@ def _render_rendertile(self, tile):
max_chunk_mtime = chunk_mtime
# draw the chunk!
- c_overviewer.render_loop(self.regionset, chunkx, chunky, chunkz,
- tileimg, xpos, ypos, self.options['rendermode'],
- self.textures)
+ try:
+ c_overviewer.render_loop(self.regionset, chunkx, chunky,
+ chunkz, tileimg, xpos, ypos,
+ self.options['rendermode'], self.textures)
+ except nbt.CorruptionError:
+ # A warning and traceback was already printed by world.py's
+ # get_chunk()
+ logging.debug("Skipping corrupt chunk at %s,%s", chunkx, chunkz)
+ except Exception, e:
+ logging.warning("Could not render chunk %s,%s for some reason. I'm going to ignore this and continue", chunkx, chunkz)
+ logging.debug("Full error was:", exc_info=1)
## Semi-handy routine for debugging the drawing routine
## Draw the outline of the top of the chunk
View
39 overviewer_core/world.py
@@ -279,6 +279,8 @@ def get_type(self):
raise Exception("Woah, what kind of dimension is this?! %r" % self.regiondir)
def _get_regionobj(self, regionfilename):
+ # Check the cache first. If it's not there, create the
+ # nbt.MCRFileReader object, cache it, and return it
try:
return self.regioncache[regionfilename]
except KeyError:
@@ -317,8 +319,36 @@ def get_chunk(self, x, z):
if regionfile is None:
raise ChunkDoesntExist("Chunk %s,%s doesn't exist (and neither does its region)" % (x,z))
- region = self._get_regionobj(regionfile)
- data = region.load_chunk(x, z)
+ # Try 3 times to load and parse this chunk before giving up and raising
+ # an error
+ tries = 5

Shouldn't it be a 3 here?

@brownan
brownan added a note

If there's one thing I need to learn, it's not to put my constants in my comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ while True:
+ try:
+ region = self._get_regionobj(regionfile)
+ data = region.load_chunk(x, z)
+ except nbt.CorruptionError, e:
+ tries -= 1
+ if tries > 0:
+ # Flush the region cache to possibly read a new region file
+ # header
+ logging.debug("Encountered a corrupt chunk at %s,%s. Flushing cache and retrying", x, z)
+ logging.debug("Error was:", exc_info=1)
+ del self.regioncache[regionfile]
+ time.sleep(0.5)
+ continue
+ else:
+ logging.warning("Tried %d times to read chunk %d,%d but I got error %s",
+ tries, x, z, e)
+ logging.debug("Full traceback:", exc_info=1)
+ # Let this exception propagate out through the C code into
+ # tileset.py, where it is caught and gracefully continues
+ # with the next chunk
+ raise
+ else:
+ # no exception raised: break out of the loop
+ break
+
+
if data is None:
raise ChunkDoesntExist("Chunk %s,%s doesn't exist" % (x,z))
@@ -599,7 +629,10 @@ def __init__(self, rsetobj, cacheobjects):
s += obj.__class__.__name__ + "."
obj = obj._r
# obj should now be the actual RegionSet object
- s += obj.regiondir
+ try:
+ s += obj.regiondir
+ except AttributeError:
+ s += repr(obj)
logging.debug("Initializing a cache with key '%s'", s)
Please sign in to comment.
Something went wrong with that request. Please try again.