From dff565717b83afff3b188b980d8089a4473abfcc Mon Sep 17 00:00:00 2001 From: Thomas Kolar Date: Thu, 23 Jun 2022 16:38:13 +0200 Subject: [PATCH 1/6] Add proto-chunk rendering config definitions. The defined options are: - render_protochunks - prettify_protochunk_lighting Both are booleans. --- overviewer_core/settingsDefinition.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index 78def0ed2..2eb27bc6d 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -109,6 +109,9 @@ def get_default_config(): conf['processes'] = Setting(required=True, validator=int, default=-1) + conf['render_protochunks'] = Setting(required=False, validator=validateBool, default=False) + conf['prettify_protochunk_lighting'] = Setting(required=False, validator=validateBool, default=False) + # TODO clean up this ugly in sys.argv hack if platform.system() == 'Windows' or not sys.stdout.isatty() or "--simple" in sys.argv: obs = LoggingObserver() From d4d29ceaaa069a8df5ac0dee98e6377f1b433925 Mon Sep 17 00:00:00 2001 From: Thomas Kolar Date: Thu, 23 Jun 2022 16:51:31 +0200 Subject: [PATCH 2/6] Remember proto-chunk rendering config in World Worlds (and RegionSets) now remember how to transform proto-chunks for rendering before returning them. --- overviewer.py | 9 ++++++++- overviewer_core/world.py | 31 ++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/overviewer.py b/overviewer.py index e29c66071..1ba4cf588 100755 --- a/overviewer.py +++ b/overviewer.py @@ -477,6 +477,9 @@ def set_renderchecks(checkname, num): # TODO: optionally more caching layers here renders = config['renders'] + render_protochunks = config["render_protochunks"] + prettify_protochunk_lighting = config["prettify_protochunk_lighting"] + for render_name, render in renders.items(): logging.debug("Found the following render thing: %r", render) @@ -485,7 +488,11 @@ def set_renderchecks(checkname, num): w = worldcache[render['world']] except KeyError: try: - w = world.World(render['world']) + w = world.World( + render['world'], + render_protochunks, + prettify_protochunk_lighting, + ) except CorruptNBTError as e: logging.error("Failed to open world %r.", render['world']) raise e diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 9701c70f8..62ba024d5 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -88,7 +88,12 @@ class World(object): """ - def __init__(self, worlddir): + def __init__( + self, + worlddir, + render_protochunks=False, + prettify_protochunk_lighting=False, + ): self.worlddir = worlddir # This list, populated below, will hold RegionSet files that are in @@ -137,7 +142,12 @@ def __init__(self, worlddir): # construct a regionset object for this rel = os.path.relpath(root, self.worlddir) if os.path.basename(rel) != "poi": - rset = RegionSet(root, rel) + rset = RegionSet( + root, + rel, + render_protochunks=render_protochunks, + prettify_protochunk_lighting=prettify_protochunk_lighting, + ) if root == os.path.join(self.worlddir, "region"): self.regionsets.insert(0, rset) else: @@ -250,7 +260,7 @@ class RegionSet(object): """ - def __init__(self, regiondir, rel): + def __init__(self, regiondir, rel, render_protochunks=False, prettify_protochunk_lighting=False): """Initialize a new RegionSet to access the region files in the given directory. @@ -265,6 +275,9 @@ def __init__(self, regiondir, rel): """ self.regiondir = os.path.normpath(regiondir) self.rel = os.path.normpath(rel) + self.render_protochunks = render_protochunks + self.prettify_protochunk_lighting = prettify_protochunk_lighting + logging.debug("regiondir is %r" % self.regiondir) logging.debug("rel is %r" % self.rel) @@ -1029,9 +1042,17 @@ def __init__(self, regiondir, rel): # Re-initialize upon unpickling def __getstate__(self): - return (self.regiondir, self.rel) + return ( + (self.regiondir, self.rel), + { + "render_protochunks": self.render_protochunks, + "prettify_protochunk_lighting": self.prettify_protochunk_lighting, + }, + ) + def __setstate__(self, state): - return self.__init__(*state) + args, kwargs = state + return self.__init__(*args, **kwargs) def __repr__(self): return "" % self.regiondir From 9e5ba81a94ab2fcf8d60cf34a6283456470ee2d0 Mon Sep 17 00:00:00 2001 From: Thomas Kolar Date: Thu, 23 Jun 2022 16:57:52 +0200 Subject: [PATCH 3/6] Implement render_protochunks config option The Overviewer will now render certain proto-chunks (chunks with a status of "empty" or "structure_starts") if requested Also contains a small refactor --- overviewer_core/world.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 62ba024d5..235961cf1 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -277,6 +277,20 @@ def __init__(self, regiondir, rel, render_protochunks=False, prettify_protochunk self.rel = os.path.normpath(rel) self.render_protochunks = render_protochunks self.prettify_protochunk_lighting = prettify_protochunk_lighting + renderable_statuses = [ + "full", + "postprocessed", + "fullchunk", + "mobs_spawned", + "spawn", + "", + ] + if self.render_protochunks: + renderable_statuses += [ + "structure_starts", + "empty", + ] + self.renderable_statuses = tuple(renderable_statuses) logging.debug("regiondir is %r" % self.regiondir) logging.debug("rel is %r" % self.rel) @@ -1751,8 +1765,8 @@ def get_chunk(self, x, z): # Empty is self-explanatory, and liquid_carved and carved seem to correspond # to SkyLight not being calculated, which results in mostly-black chunks, # so we'll just pretend they aren't there. - if chunk_data.get("Status", "") not in ("full", "postprocessed", "fullchunk", - "mobs_spawned", "spawn", ""): + chunk_status = chunk_data.get("Status", "") + if chunk_status not in self.renderable_statuses: raise ChunkDoesntExist("Chunk %s,%s doesn't exist" % (x,z)) # Turn the Biomes array into a 16x16 numpy array @@ -1782,7 +1796,7 @@ def get_chunk(self, x, z): # Sometimes, Minecraft loves generating chunks with no light info. # These mostly appear to have those two properties, and in this case # we default to full-bright as it's less jarring to look at than all-black. - if chunk_data.get("Status", "") == "spawn" and 'Lights' in chunk_data: + if chunk_status == "spawn" and 'Lights' in chunk_data: section['SkyLight'] = numpy.full((16,16,16), 255, dtype=numpy.uint8) else: if 'SkyLight' in section: From 777f4f3c7df079342124325c8f83affb9d1efe93 Mon Sep 17 00:00:00 2001 From: Thomas Kolar Date: Thu, 23 Jun 2022 17:02:19 +0200 Subject: [PATCH 4/6] Proto-chunks without SkyLight => fullbright Proto-chunks that don't have skylight data are now rendered fully bright instead. --- overviewer_core/world.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 235961cf1..fd4ec8144 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -1802,6 +1802,10 @@ def get_chunk(self, x, z): if 'SkyLight' in section: skylight = numpy.frombuffer(section['SkyLight'], dtype=numpy.uint8) skylight = skylight.reshape((16,16,8)) + elif chunk_status in ["structure_starts", "empty"]: + # completely black border chunk. fullbright is definitely + # preferable here. + skylight = numpy.full((16,16,8), 255, dtype=numpy.uint8) else: # Special case introduced with 1.14 skylight = numpy.zeros((16,16,8), dtype=numpy.uint8) skylight_expanded = numpy.empty((16,16,16), dtype=numpy.uint8) From 0a8cdd2688b9bf5f9e6a9d8eccec299251c8a376 Mon Sep 17 00:00:00 2001 From: Thomas Kolar Date: Thu, 23 Jun 2022 17:04:08 +0200 Subject: [PATCH 5/6] Implement prettify_protochunk_lighting A heuristic is now applied to proto-chunks to avoid dark chunks near the edges of the map, if requested --- overviewer_core/world.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index fd4ec8144..b544438c4 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -1787,6 +1787,8 @@ def get_chunk(self, x, z): chunk_data['Biomes'] = biomes chunk_data['NewBiomes'] = (len(biomes.shape) == 3) + chunk_was_inhabited = chunk_data.get("InhabitedTime", 0) > 0 + unrecognized_block_types = {} for section in chunk_data['Sections']: @@ -1812,6 +1814,34 @@ def get_chunk(self, x, z): skylight_expanded[:,:,::2] = skylight & 0x0F skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4 del skylight + + if self.prettify_protochunk_lighting: + # this chunk might be a border chunk that was populated with + # sky light from neighboring chunks, but hasn't had its own sky + # light calculated yet. + # first, check if the chunk was inhabited. This is pretty much + # impossible for border chunks. + # then, check if the chunk is even a proto-chunk. + if not chunk_was_inhabited and chunk_status in ["structure_starts", "empty"]: + # and finally, check if there is a block with full skylight + # in the section. + # if there is one, that means that skylight was definitely + # calculated here before. + # if not, we assume that we're in a border chunk. + # this is based on the following observations: + # - sections that are underground and far from border + # chunks will not be seen on the map most of the time + # anyway + # - sections that are above ground will almost always have + # a block somewhere with full light level in naturally + # generated terrain + # - sections that are above ground and have been modified + # by a player have almost certainly been inhabited + # so, false positives shouldn't be too common, and those + # that occur shouldn't be in player-visited areas. + if not numpy.any(skylight_expanded == 15): + skylight_expanded = numpy.full((16,16,16), 0x0F, dtype=numpy.uint8) + section['SkyLight'] = skylight_expanded # Turn the BlockLight array into a 16x16x16 matrix, same as SkyLight From 62c7b0a5ba1f5731f48b8ab84e2130f961a2cee6 Mon Sep 17 00:00:00 2001 From: Thomas Kolar Date: Thu, 23 Jun 2022 17:06:32 +0200 Subject: [PATCH 6/6] Document proto-chunk rendering options Add documentation for the following options: - render_protochunks - prettify_protochunk_lighting --- docs/config.rst | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index 41a7853ff..29d785ab4 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -270,6 +270,58 @@ General processes = 2 +.. _render_protochunks: + +``render_protochunks`` + This is a boolean. When enabled, the Overviewer will render some proto-chunks, ie, + chunks that have not been fully generated yet. + + This is useful for rendering worlds that have pre-1.18 chunks. When Minecraft 1.18 + or higher loads one of these chunks, it will mark it as a proto-chunk until all of + the terrain below Y=0 is generated. So, without this option set, the Overviewer + may not render parts of the world at all. + + It's worth noting that if a world has been optimized (``--forceUpgrade``), all + upgraded but unvisited chunks will be in this state. + + However, this option will make the edges of the map contain actual proto-chunks, so + it isn't recommended for worlds started in 1.18 or newer, and disabled by default. + + e.g.:: + + render_protochunks = True + + **Default:** ``False`` + +.. _prettify_protochunk_lighting: + +``prettify_protochunk_lighting`` + This is a boolean. It is only useful in combination with + :ref:`render_protochunks `. + + If it is enabled, a heuristic is applied to get rid of certain lighting issues in + proto-chunks on map borders. + + Some proto-chunks are partially dark because their skylight has not been + calculated yet, but skylight from neighboring chunks has already been used to + generate lighting data. + + So, with this option enabled, if a chunk section + - is a proto-chunk + - has not been visited by a player before + - and does not contain a single block with full sky light + + the Overviewer will render it as fully bright instead. + + It is disabled by default as there is no guarantee that no false positives are + generated. + + e.g.:: + + prettify_protochunk_lighting = True + + **Default:** ``False`` + Observers ~~~~~~~~~