From 086820ac7226880c38eb8cdbb632fee0ea023fc1 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Wed, 3 Sep 2014 11:28:47 +0200 Subject: [PATCH 01/15] Adding StructureOverlay an overlay to color the map according to structures. A usecase to demonstrate a possible application of the extended functionality: **"Rails Overlay that draws only the rails that are on Cobblestone for a subway map."** With this patch it is very easy to achive that: ```python MineralOverlay(minerals=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))]) ``` In this case the overlay will be red for rails on cobblestone and green for powerrails on cobblestone. The syntax is `(, )` * where `` is a 4 tuple with a `(r, g, b, a)` color * and `` is a tuple with an arbitrary number of conditions with the following syntax: `((relx, rely, relz, blkid), ...)` where the `rel<>` parameters specify the relative coordinates to the block that is checked if it matches bklid. In the example the fist tuple `(0,0,0,66)` checks if at the current position is a rail while `(0,-1,0,4)` checks if at one below the current position is a cobblestone. If both are true then the color `(255, 0, 0, 255)` is used. A Sample Config file exploiting the capabilities: ``` python worlds['My World'] = "~/.minecraft/saves/test/" outputdir = "/tmp/test_render" rendermode = "lighting" renders["render1"] = { 'world': 'My World', 'title': 'A regular render', } renders["render_overlay_dafault_rails"] = { 'world': 'My World', 'title': 'Default Rails', 'rendermode': [ClearBase(), StructureOverlay()], 'overlay': ['render1'], } renders["render_overlay_cust_rails"] = { 'world': 'My World', 'title': 'Custom Rails', #relative coordinates [[(relx, rely, relz, mineral)], (red, green, blue, alpha)] 'rendermode': [ClearBase(), StructureOverlay(structures=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))])], 'overlay': ['render1'], } ``` The "Default Rails" overlay uses default coloring of the structures overlay. "Custom Rails" uses some custom coloring. fixes overviewer/Minecraft-Overviewer#556 and fixes overviewer/Minecraft-Overviewer#787 --- docs/config.rst | 26 ++ overviewer_core/rendermodes.py | 13 + .../src/primitives/overlay-structure.c | 242 ++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 overviewer_core/src/primitives/overlay-structure.c diff --git a/docs/config.rst b/docs/config.rst index c617f52..812b4ee 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1099,6 +1099,32 @@ MineralOverlay MineralOverlay(minerals=[(64,(255,255,0)), (13,(127,0,127))]) +StructureOverlay + Color the map according to patterns of blocks. With this rail overlays + or overlays for other small structures can be realized. It can also be + a MineralOverlay with alpha support. + + This Overlay colors according to a patterns that are specified as + multiple tuples of the form ``(relx, rely, relz, blockid)``. So + by specifying ``(0, -1, 0, 4)`` the block below the current one has to + be a cobblestone. + + One color is then specified as + ``((relblockid1, relblockid2, ...), (r, g, b, a))`` where the + ``relblockid*`` are relative coordinates and the blockid as specified + above. The ``relblockid*`` must match all at the same time for the + color to apply. + + Example:: + + MineralOverlay(minerals=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), + (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))]) + + In this example all rails(66) on top of cobblestone are rendered in + pure red. And all powerrails(27) are rendered in green. + + If ``minerals`` is not provided, a default rail coloring is used. + BiomeOverlay Color the map according to the biome at that point. Either use on top of other modes or on top of ClearBase to create a pure overlay. diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index 8fff824..00cac4a 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -205,6 +205,19 @@ class SpawnOverlay(Overlay): class SlimeOverlay(Overlay): name = "overlay-slime" + +class StructureOverlay(Overlay): + name = "overlay-structure" + options = { + 'structures': ('a list of ((((relx, rely, relz), blockid), ...), (r, g, b, a)) tuples for coloring minerals', + [(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), + (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255)), + (((0, 0, 0, 28), (0, -1, 0, 4)), (255, 255, 0, 255)), + (((0, 0, 0, 157), (0, -1, 0, 4)), (255, 100, 0, 255)), + ]), + } + + class MineralOverlay(Overlay): name = "overlay-mineral" options = { diff --git a/overviewer_core/src/primitives/overlay-structure.c b/overviewer_core/src/primitives/overlay-structure.c new file mode 100644 index 0000000..b0292cb --- /dev/null +++ b/overviewer_core/src/primitives/overlay-structure.c @@ -0,0 +1,242 @@ +/* + * This file is part of the Minecraft Overviewer. + * + * Minecraft Overviewer is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Minecraft Overviewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the Overviewer. If not, see . + */ + +#include "overlay.h" + +typedef enum { false, true } bool; + +typedef struct { + /* inherits from overlay */ + RenderPrimitiveOverlay parent; + void *structures; + int numcolors; +} RenderPrimitiveStructure; + +struct Condition{ + int relx, rely, relz; + unsigned char block; +}; + +struct Color { + int numconds; + struct Condition *conditions; + unsigned char r, g, b, a; +}; + +static void get_color(void *data, + RenderState *state, + unsigned char *r, + unsigned char *g, + unsigned char *b, + unsigned char *a) { + /** + * Calculate the color at the current position and store the values to r,g,b,a. + **/ + RenderPrimitiveStructure *self = (RenderPrimitiveStructure *)data; + int x = state->x, z = state->z, y_max, y, col, cond; + struct Color *structures = (struct Color *)(self->structures); + struct Condition * c = NULL; + bool all = true; + y_max = state->y + 1; + + /** + * Check for every color in every y level if all its Conditions are met. + * If all conditions are met for one y level set r,b,g,a accordingly. + **/ + // iterate over all the colors + for ( col = 0; col < self->numcolors; col++) { + // iterate over all y levels + for (y = state->chunky * -16; y <= y_max; y++) { + // iterate over all the conditions + for (cond = 0; cond < structures[col].numconds; cond++) { + all = true; + c = (struct Condition *)&structures[col].conditions[cond]; + // check if the condition does apply and break from the conditions loop if not. + if(!(c->block == get_data(state, BLOCKS, x+c->relx, y+c->rely, z+c->relz))) { + all = false; + break; + } + } + if (all){ + // set the color + *r = structures[col].r; + *g = structures[col].g; + *b = structures[col].b; + *a = structures[col].a; + return; + } + } + } + return; +} + +static int overlay_structure_start(void *data, RenderState *state, PyObject *support) { + /** + * Initializing the search for structures by parsing the arguments and storing them into + * appropriate structures. If no arguments are passed create and use default values. + **/ + PyObject *opt; + RenderPrimitiveStructure* self; + + /* first, chain up */ + int ret = primitive_overlay.start(data, state, support); + if (ret != 0) + return ret; + + /* now do custom initializations */ + self = (RenderPrimitiveStructure *)data; + + // opt is a borrowed reference. do not deref + // store the structures python object into opt. + if (!render_mode_parse_option(support, "structures", "O", &(opt))) + return 1; + + /** + * Check if a sane option was passed. + **/ + if (opt && opt != Py_None) { + struct Color *structures = NULL; + struct Condition *cond = NULL; + Py_ssize_t structures_size = 0, i, cond_size = 0, n = 0; + bool cont = true; + + opt = PySequence_Fast(opt, "expected a sequence"); + if (!opt) { + PyErr_SetString(PyExc_TypeError, "'structures' must be a a sequence"); + return 1; + } + + structures_size = PySequence_Fast_GET_SIZE(opt); + // Getting space on the heap and do not forget to set self->numcolors. + structures = self->structures = calloc(structures_size, sizeof(struct Color)); + self->numcolors = structures_size; + if (structures == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate memory"); + return 1; + } + + /** + * Try to parse the definitions of conditions and colors. + **/ + if (cont) { + for (i = 0; i < structures_size; i++) { + PyObject *structure = PyList_GET_ITEM(opt, i); + // condspy holding the conditions tuple of variable length (python object) + PyObject *condspy; + // colorpy holding the 4 tuple with r g b a values of the color + PyObject *colorpy; + + // getting the condspy and colorpy out of the structures. + if (!PyArg_ParseTuple(structure, "OO", &condspy, &colorpy)) { + // Exception set automatically + free(structures); + self->structures = NULL; + return 1; + } + + // Parse colorpy into a c-struct. + if (!PyArg_ParseTuple( colorpy, "bbbb", + &structures[i].r, + &structures[i].g, + &structures[i].b, + &structures[i].a)) { + free(structures); + self->structures = NULL; + return 1; + } + + // Convert condspy to a fast sequence + condspy = PySequence_Fast(condspy, "Failed to parse conditions"); + if(condspy == NULL) { + free(structures); + self->structures = NULL; + return 1; + } + + // get the number of conditions. + structures[i].numconds = PySequence_Fast_GET_SIZE(condspy); + // reserve enough memory for the conditions. + cond = calloc(structures[i].numconds, sizeof(struct Condition)); + structures[i].conditions = cond; + + if (structures[i].conditions == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate memory"); + free(structures); + self->structures = NULL; + return 1; + } + + // iterate over all the conditions and read them. + for (n = 0; n < structures[i].numconds; n++) { + PyObject *ccond = PySequence_Fast_GET_ITEM(condspy, n); + if(!PyArg_ParseTuple( ccond, "iiib", + &cond[n].relx, + &cond[n].rely, + &cond[n].relz, + &cond[n].block)){ + int x = 0; + for(x = 0; x < structures_size; x++){ + free(structures[x].conditions); + } + free(structures); + self->structures = NULL; + return 1; + } + } + } + } + } + + /* setup custom color */ + self->parent.get_color = get_color; + + return 0; +} + +static void overlay_structure_finish(void *data, RenderState *state) { + /* first free all *our* stuff */ + RenderPrimitiveStructure* self = (RenderPrimitiveStructure *)data; + int i = 0; + + if(self->structures) { + // freeing the nested structure + struct Color * m = self->structures; + for(i = 0; i < self->numcolors; i++){ + if(m[i].conditions) + free(m[i].conditions); + } + } + + if (self->structures) { + free(self->structures); + self->structures = NULL; + } + + /* now, chain up */ + primitive_overlay.finish(data, state); +} + +RenderPrimitiveInterface primitive_overlay_structure = { + "overlay-structure", + sizeof(RenderPrimitiveStructure), + overlay_structure_start, + overlay_structure_finish, + NULL, + NULL, + overlay_draw, +}; + From c6830a35dbe234fe91bee57e3f517d3c62a71972 Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Mon, 6 Oct 2014 21:40:09 +0200 Subject: [PATCH 02/15] Added RCon Observer It does the rcons! And the observings! --- overviewer_core/observer.py | 41 ++++++++++++++++ overviewer_core/rcon.py | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 overviewer_core/rcon.py diff --git a/overviewer_core/observer.py b/overviewer_core/observer.py index e17c8f5..e068acb 100644 --- a/overviewer_core/observer.py +++ b/overviewer_core/observer.py @@ -19,6 +19,7 @@ import progressbar import sys import os import json +import rcon class Observer(object): """Base class that defines the observer interface. @@ -392,3 +393,43 @@ class ServerAnnounceObserver(Observer): self.target_handle.write('say %s\n' % output) self.target_handle.flush() + +# Fair amount of code duplication incoming +# Perhaps both ServerAnnounceObserver and RConObserver +# could share the percentage interval code. + +class RConObserver(Observer): + """Send the output to a Minecraft server via rcon""" + + def __init__(self, target, password, port=25575, pct_interval=10): + self.pct_interval = pct_interval + self.conn = rcon.RConConnection(target, port) + self.conn.login(password) + self.last_update = 0 + super(RConObserver, self).__init__() + + def start(self, max_value): + self._send_output("Starting render of %d total tiles" % max_value) + super(RConObserver, self).start(max_value) + + def finish(self): + self._send_output("Render complete!") + super(RConObserver, self).finish() + self.conn.close() + + def update(self, current_value): + super(RConObserver, self).update(current_value) + if self._need_update(): + self._send_output('Rendered %d of %d tiles, %d%% complete' % + (self.get_current_value(), self.get_max_value(), + self.get_percentage())) + self.last_update = current_value + + def _need_update(self): + return self.get_percentage() - \ + (self.last_update * 100.0 / self.get_max_value()) >= self.pct_interval + + def _send_output(self, output): + self.conn.command("say", output) + + diff --git a/overviewer_core/rcon.py b/overviewer_core/rcon.py new file mode 100644 index 0000000..52a1434 --- /dev/null +++ b/overviewer_core/rcon.py @@ -0,0 +1,95 @@ +# This file is part of the Minecraft Overviewer. +# +# Minecraft Overviewer is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# Minecraft Overviewer is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the Overviewer. If not, see . + +import socket +#from enum import Enum +import struct +import select + +class RConException(Exception): + def __init__(self, request_id, reason): + self.request_id = request_id + self.reason = reason + + def __str__(self): + return ("Failed RCon request with request ID %d, reason %s" % + (self.request_id, self.reason)) + +# In D, enums are just that, enums. They're a group of named constants, +# sometimes with a tag, sometimes anonymous. +# In Python, Enums use the same syntax as class objects that derive from +# the "Enum" base class, even though they are not normal python classes +# and work as singletons anyway, but instead of using a different syntax, +# Python instead decided to have a chapter in their docs about how Enums +# are different from regular classes while looking exactly the same. +# You can look at said document of failure right here: +# https://docs.python.org/3/library/enum.html#how-are-enums-different +# +# "D has too much shit going on for me" -- agrif, 2014 +# +# Fortunately, we're not allowed to use Enums in Python 2. + +#class RConType(Enum): +# command = 2 +# login = 3 + + +class RConConnection(): + rid = 0 + + def __init__(self, target, port): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((target, port)) + + def send(self, t, payload): + self.rid = self.rid + 1 + header = struct.pack(" Date: Mon, 6 Oct 2014 21:52:21 +0200 Subject: [PATCH 03/15] Add documentation for RConObserver --- docs/config.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index c617f52..c8892b0 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -376,6 +376,31 @@ Observers once for every 1% of progress. **Required** + + ``RConObserver(target, password[, port][, pct_interval])`` + This Observer will announce render progress with the server's ``say`` + command through RCon. + + * ``target=
`` + Address of the target Minecraft server. + + **Required** + + * ``password=`` + The server's rcon password. + + **Required** + + * ``port=`` + Port on which the Minecraft server listens for incoming RCon connections. + + **Default:** ``25575`` + + * ``pct_interval=`` + Percentage interval in which the progress should be announced, the same as + for ``ServerAnnounceObserver``. + + **Default:** ``10`` From 3a0f3349706bff5fe275396307b834d987962f43 Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Fri, 10 Oct 2014 20:54:47 +0200 Subject: [PATCH 04/15] optimizeimages: Add jpegoptim interface Only options that appear to be interesting for us have been exposed, namely -m and -S. --- overviewer_core/optimizeimages.py | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/overviewer_core/optimizeimages.py b/overviewer_core/optimizeimages.py index 0ccd4a2..af56039 100644 --- a/overviewer_core/optimizeimages.py +++ b/overviewer_core/optimizeimages.py @@ -120,6 +120,42 @@ class optipng(Optimizer, PNGOptimizer): def is_crusher(self): return True + +class jpegoptim(Optimizer, JPEGOptimizer): + binaryname = "jpegoptim" + crusher = True + quality = None + target_size = None + + def __init__(self, quality = None, target_size = None): + if quality is not None: + if quality < 0 or quality > 100: + raise Exception("Invalid target quality %d for jpegoptim" % quality) + self.quality = quality + + if target_size is not None: + self.target_size = target_size + + def optimize(self, img): + args = [self.binaryname, "-q", "-p"] + if self.quality is not None: + args.append("-m" + str(self.quality)) + + if self.target_size is not None: + args.append("-S" + str(self.target_size)) + + args.append(img) + + Optimizer.fire_and_forget(self, args) + + def is_crusher(self): + # Technically, optimisation is lossless if input image quality + # is below target quality, but this is irrelevant in this case + if (self.quality is not None) or (self.target_size is not None): + return False + else: + return True + def optimize_image(imgpath, imgformat, optimizers): From 79e60972e91ab25762a67ce5f9056010d85c3a7d Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Fri, 10 Oct 2014 21:10:07 +0200 Subject: [PATCH 05/15] optimizeimages: Add documentation for jpegoptim --- docs/config.rst | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index c617f52..eaf0968 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -683,8 +683,8 @@ Image options Using image optimizers will increase render times significantly. This option specifies which additional tools overviewer should use to - optimize the filesize of png tiles. - The tools used must be placed somewhere, where overviewer can find them, for + optimize the filesize of rendered tiles. + The tools used must be placed somewhere where overviewer can find them, for example the "PATH" environment variable or a directory like /usr/bin. The option is a list of Optimizer objects, which are then executed in @@ -766,6 +766,34 @@ Image options **Default:** ``False`` + ``jpegoptim`` + jpegoptim can do both lossy and lossless JPEG optimisation. If no options are specified, + jpegoptim will only do lossless optimisations. + Available settings: + + ``quality`` + A number between 0 and 100 that corresponds to the jpeg quality level. If the input + image has a lower quality specified than the output image, jpegoptim will only do + lossless optimisations. + + If this option is specified and the above condition does not apply, jpegoptim will + do lossy optimisation. + + **Default:** ``None`` *(= Unspecified)* + + ``target_size`` + Either a percentage of the original filesize (e.g. ``"50%"``) or a target filesize + in kilobytes (e.g. ``15``). jpegoptim will then try to reach this as its target size. + + If specified, jpegoptim will do lossy optimisation. + + .. warning:: + This appears to have a greater performance impact than just setting ``quality``. + Unless predictable filesizes are a thing you need, you should probably use ``quality`` + instead. + + **Default:** ``None`` *(= Unspecified)* + **Default:** ``[]`` Zoom From c0d4fc9bc236c06aa3d412e870309331ff399868 Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Tue, 14 Oct 2014 14:21:26 +0200 Subject: [PATCH 06/15] Add rollbacks to tile re-arrangements Previously, any kind of interruption during tile re-arrangements would leave the tileset in a half re-arranged state. This commit makes _increase_depth roll back possible changes it did during the re-arrangement prior to it being interrupted. Fixes issue #1166. --- overviewer_core/tileset.py | 50 +++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index b744d6e..5cf56f1 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -689,6 +689,25 @@ class TileSet(object): # 2 is now 2/1 # 3 is now 3/0 # then all that needs to be done is to regenerate the new top level + + def rollback_mkdir(dnum): + p = getpath("new" + str(dnum)) + if os.path.exists(p): + os.rmdir(p) + + def rollback_filerename(dnum): + newnum = (3,2,1,0)[dnum] + qimg = getpath("new%d/%d.%s" % (dnum, newnum, self.imgextension)) + qdir = getpath("new%d/%d" % (dnum, newnum)) + + if os.path.exists(qimg): + os.rename(qimg, getpath("%d.%s" % (dnum, self.imgextension))) + if os.path.exists(qdir): + os.rename(qdir, getpath(str(dnum))) + + def rollback_dirrename(dnum): + os.rename(getpath(str(dnum)), getpath("new" + str(dnum))) + for dirnum in range(4): newnum = (3,2,1,0)[dirnum] @@ -698,12 +717,31 @@ class TileSet(object): files = [str(dirnum)+"."+self.imgextension, str(dirnum)] newfiles = [str(newnum)+"."+self.imgextension, str(newnum)] - os.mkdir(newdirpath) - for f, newf in zip(files, newfiles): - p = getpath(f) - if os.path.exists(p): - os.rename(p, getpath(newdir, newf)) - os.rename(newdirpath, getpath(str(dirnum))) + try: + try: + os.mkdir(newdirpath) + try: + for f, newf in zip(files, newfiles): + p = getpath(f) + if os.path.exists(p): + os.rename(p, getpath(newdir, newf)) + except: + rollback_filerename(dirnum) + raise + except: + rollback_mkdir(dirnum) + raise + os.rename(newdirpath, getpath(str(dirnum))) + except: + logging.warning("Overviewer was interrupted during tile " + "re-arrangement.") + logging.warning("Rolling back changes...") + # Moonwalk the fuck out of here + for lastdir in range(dirnum - 1, -1, -1): + rollback_dirrename(lastdir) + rollback_filerename(lastdir) + rollback_mkdir(lastdir) + raise def _decrease_depth(self): """If the map size decreases, or perhaps the user has a depth override From 3c3db5d90937f56ed3c9b17e175638f6d4e287dc Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Sat, 18 Oct 2014 17:44:59 +0200 Subject: [PATCH 07/15] Add warning if Overviewer is run as root --- overviewer.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/overviewer.py b/overviewer.py index 83bb6de..41c1ff7 100755 --- a/overviewer.py +++ b/overviewer.py @@ -50,6 +50,16 @@ def main(): # bootstrap the logger with defaults logger.configure() + if os.name == "posix": + if os.geteuid() == 0: + logging.warning("You are running Overviewer as root. " + "It is recommended that you never do this, " + "as it is dangerous for your system. If you are running " + "into permission errors, fix your file/directory " + "permissions instead. Overviewer does not need access to " + "critical system resources and therefore does not require " + "root access.") + try: cpus = multiprocessing.cpu_count() except NotImplementedError: From 904e5abd8e55ee59146535d7540c73be69386a78 Mon Sep 17 00:00:00 2001 From: Thomas Lake Date: Mon, 20 Oct 2014 11:19:31 +0100 Subject: [PATCH 08/15] Fix east side large mushroom rendering --- overviewer_core/textures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 36ce0fd..144d762 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -3437,7 +3437,7 @@ def huge_mushroom(self, blockid, data): img = self.build_full_block(cap, None, None, cap, porous) if data == 2: # east side - img = self.build_full_block(cap, None, None, porous, porous) + img = self.build_full_block(cap, None, None, porous, cap) if data == 3: # south-east corner img = self.build_full_block(cap, None, None, porous, cap) From f1af25799e84893d96c0e667930c9339569ab794 Mon Sep 17 00:00:00 2001 From: Koza Date: Sat, 13 Dec 2014 17:17:11 +0100 Subject: [PATCH 09/15] Fixed rendering regions. genPOI.py: add new line to baseMakers.js; views.js: add polygons, with all the features that Google provides, polylines work fine; regions.js: example of usage --- overviewer_core/aux_files/genPOI.py | 1 + overviewer_core/data/js_src/views.js | 20 +++++++++++ overviewer_core/data/web_assets/regions.js | 41 +++++++++++++++++----- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/overviewer_core/aux_files/genPOI.py b/overviewer_core/aux_files/genPOI.py index a0a02de..f3dce53 100755 --- a/overviewer_core/aux_files/genPOI.py +++ b/overviewer_core/aux_files/genPOI.py @@ -452,6 +452,7 @@ def main(): with open(os.path.join(destdir, "baseMarkers.js"), "w") as output: output.write("overviewer.util.injectMarkerScript('markersDB.js');\n") output.write("overviewer.util.injectMarkerScript('markers.js');\n") + output.write("overviewer.util.injectMarkerScript('regions.js');\n") output.write("overviewer.collections.haveSigns=true;\n") logging.info("Done") diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index d5f6ea6..6259223 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -496,6 +496,26 @@ overviewer.views.SignControlView = Backbone.View.extend({ 'strokeColor': entity['strokeColor'] }); dataRoot[i].markerObjs.push(polyline); + } + + // Polygons + if (typeof entity['polygon'] != 'undefined') { + var polypath = new Array(); + for (point in entity.polygon) { + polypath.push(overviewer.util.fromWorldToLatLng(entity.polygon[point].x, entity.polygon[point].y, entity.polygon[point].z, overviewer.mapView.options.currentTileSet)); + } + + var polygon = new google.maps.Polygon({ + 'clickable': false, + 'fillColor': entity['fillColor'], + 'fillOpacity': entity['fillOpacity'], + 'map': overviewer.map, + 'path': polypath, + 'strokeColor': entity['strokeColor'], + 'strokeOpacity': entity['strokeOpacity'], + 'visible': false + }); + dataRoot[i].markerObjs.push(polygon); } } dataRoot[i].created = true; diff --git a/overviewer_core/data/web_assets/regions.js b/overviewer_core/data/web_assets/regions.js index acd83e2..cef6ff0 100644 --- a/overviewer_core/data/web_assets/regions.js +++ b/overviewer_core/data/web_assets/regions.js @@ -1,9 +1,34 @@ // This is just an example. You can run some scripts to generate regions that -// will be drawn here. -overviewer.collections.regionDatas.push([ - // {"color": "#FFAA00", "opacity": 0.5, "closed": true, "path": [ - // {"x": 0, "y": 0, "z": 0}, - // {"x": 0, "y": 10, "z": 0}, - // {"x": 0, "y": 0, "z": 10} - // ]}, -]); +/* +var groupName = "Regions"; +var displayName = "Areas"; +var world = "top"; + +markersDB[groupName] = { + "raw": [ + { + "fillColor": "#00FF00", + "fillOpacity": 0.2, + "strokeColor": "#FF0000", + "strokeOpacity": 1, + "polygon" : [ + {"x": 347, "y": 67, "z": 95}, + {"x": 347, "y": 77, "z": 95}, + {"x": 347, "y": 77, "z": 105}, + {"x": 347, "y": 67, "z": 105}, + {"x": 347, "y": 67, "z": 105} + ]} + ], + "name": "Regions", + "created": false +} + +markers[world].push( + { + "groupName": groupName, + "icon": "signpost_icon.png", + "createInfoWindow": false, + "displayName": displayName, + "checked": true + }); +*/ \ No newline at end of file From 97e598593380dc97e6ad40809b9bda83c1b21e78 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 3 Jan 2015 11:56:51 -0500 Subject: [PATCH 10/15] Allow worlds with a version of zero (temp fix for #1194) --- overviewer_core/world.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 0597317..910362f 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -105,8 +105,12 @@ class World(object): # Hard-code this to only work with format version 19133, "Anvil" if not ('version' in data and data['version'] == 19133): - logging.critical("Sorry, This version of Minecraft-Overviewer only works with the 'Anvil' chunk format") - raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir) + if 'version' in data and data['version'] == 0: + logging.debug("Note: Allowing a version of zero in level.dat!") + ## XXX temporary fix for #1194 + else: + logging.critical("Sorry, This version of Minecraft-Overviewer only works with the 'Anvil' chunk format") + raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir) # This isn't much data, around 15 keys and values for vanilla worlds. self.leveldat = data From a998911d0223e4ca9121f9e4490196ecba951bec Mon Sep 17 00:00:00 2001 From: Caleb Sander Date: Wed, 28 Jan 2015 22:44:14 -0500 Subject: [PATCH 11/15] Added information about --skip-scan to the docs --- docs/running.rst | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/running.rst b/docs/running.rst index 340f1e5..0e7acd3 100644 --- a/docs/running.rst +++ b/docs/running.rst @@ -78,7 +78,7 @@ Usage ===== For this section, we assume the executable is ``overviewer.py``. Replace that -with ``overviewer.exe`` for windows. +with ``overviewer.exe`` for windows. Overviewer usage:: @@ -131,7 +131,7 @@ is typically correct. is not detected. This means that changes in your render configuration will not be reflected in your world except in updated chunks. It could also cause problems if the system clock of the machine running Minecraft is not stable. - + **This option is the default** unless :option:`--forcerender` or :option:`--check-tiles` is in effect. This option conflicts with :option:`--forcerender` and :option:`--check-tiles`. @@ -148,13 +148,13 @@ is typically correct. automatically stored. This option was designed to handle the case where the last render was interrupted -- some tiles have been updated but others haven't, so each one is checked before it is rendered. - + This is slightly slower than :option:`--no-tile-checks` due to the additional disk-io involved in reading tile mtimes from the filesystem - + Since this option also checks for erroneous tiles, **It is also useful after you delete sections of your map, e.g. with worldedit, to delete tiles that - should no longer exist.** Overviewer greatly overestimates tiles to be + should no longer exist.** Overviewer greatly overestimates tiles to be rendered and time needed to complete. The caveats with this option are the same as for :option:`--no-tile-checks` @@ -186,15 +186,14 @@ is typically correct. conflicts with :option:`--check-tiles` and :option:`--no-tile-checks` .. cmdoption:: --genpoi - + .. note:: Don't use this flag without first reading :ref:`signsmarkers`! - Generates the POI markers for your map. This option does not do any tile/map - generation, and ONLY generates markers. See :ref:`signsmarkers` on how to + Generates the POI markers for your map. This option does not do any tile/map + generation, and ONLY generates markers. See :ref:`signsmarkers` on how to configure POI options. - .. cmdoption:: -p , --processes This specifies the number of worker processes to spawn on the local machine @@ -203,6 +202,17 @@ is typically correct. This option can also be specified in the config file as :ref:`processes ` +.. cmdoption:: --skip-scan + + .. note:: + Don't use this flag without first reading :ref:`signsmarkers`! + + When generating POI markers, this option prevents scanning for entities and + tile entities, and only creates the markers specified in the config file. + This considerably speeds up the POI marker generation process if no entities + or tile entities are being used for POI markers. See :ref:`signsmarkers` on + how to configure POI options. + .. cmdoption:: -v, --verbose Activate a more verbose logging format and turn on debugging output. This @@ -237,7 +247,7 @@ Installing the Textures If Overviewer is running on a machine with the Minecraft client installed, it will automatically use the default textures from Minecraft. - + .. note:: Overviewer will only search for installed client *release* versions, not snapshots. If you want to use a snapshot client jar for the textures, @@ -263,21 +273,21 @@ If you want or need to provide your own textures, you have several options: VERSION=1.8 wget https://s3.amazonaws.com/Minecraft.Download/versions/${VERSION}/${VERSION}.jar -P ~/.minecraft/versions/${VERSION}/ - + If that's too confusing for you, then just take this single line and paste it into a terminal to get 1.8 textures:: - + wget https://s3.amazonaws.com/Minecraft.Download/versions/1.8/1.8.jar -P ~/.minecraft/versions/1.8/ * You can also just run the launcher to install the client. - + * You can transfer the client jar to the correct place manually, from a computer that does have the client, to your server. The correct places are: - + * For Linux: ``~/.minecraft/versions//.jar`` * For Mac: ``~/Library/Application Support/minecraft/versions//.jar`` - + * For Windows: ``%APPDATA%/.minecraft/versions//.jar`` * You can download and use a custom resource pack. Download the resource pack From 32fbc1dea2c3ef2580fca21fa4e84018401c8ca2 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 2 Feb 2015 04:59:43 -0500 Subject: [PATCH 12/15] nbt array lengths are unsigned (see issue #1190) --- overviewer_core/nbt.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/overviewer_core/nbt.py b/overviewer_core/nbt.py index cadeccf..d892226 100644 --- a/overviewer_core/nbt.py +++ b/overviewer_core/nbt.py @@ -65,7 +65,9 @@ class NBTFileReader(object): # compile the unpacker's into a classes _byte = struct.Struct("b") _short = struct.Struct(">h") + _ushort = struct.Struct(">H") _int = struct.Struct(">i") + _uint = struct.Struct(">I") _long = struct.Struct(">q") _float = struct.Struct(">f") _double = struct.Struct(">d") @@ -128,17 +130,17 @@ class NBTFileReader(object): return self._double.unpack(bytes)[0] def _read_tag_byte_array(self): - length = self._read_tag_int() + length = self._uint.unpack(self._file.read(4))[0] bytes = self._file.read(length) return bytes def _read_tag_int_array(self): - length = self._read_tag_int() + length = self._uint.unpack(self._file.read(4))[0] int_bytes = self._file.read(length*4) return struct.unpack(">%ii" % length, int_bytes) def _read_tag_string(self): - length = self._read_tag_short() + length = self._ushort.unpack(self._file.read(2))[0] # Read the string string = self._file.read(length) # decode it and return @@ -146,7 +148,7 @@ class NBTFileReader(object): def _read_tag_list(self): tagid = self._read_tag_byte() - length = self._read_tag_int() + length = self._uint.unpack(self._file.read(4))[0] read_method = self._read_tagmap[tagid] l = [] From d3185734c4d4fa2c2a7898ab90850a157df495a8 Mon Sep 17 00:00:00 2001 From: Gizmokid2005 Date: Thu, 5 Feb 2015 13:52:31 -0500 Subject: [PATCH 13/15] Skip missing dimensions This fixes overviewer/Minecraft-Overviewer #1202 for renders --- overviewer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overviewer.py b/overviewer.py index 41c1ff7..71d7786 100755 --- a/overviewer.py +++ b/overviewer.py @@ -486,8 +486,8 @@ dir but you forgot to put quotes around the directory, since it contains spaces. logging.error("Sorry, I can't find anything to render! Are you sure there are .mca files in the world directory?") return 1 if rset == None: # indicates no such dimension was found: - logging.error("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'][0], render_name) - return 1 + logging.warn("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'][0], render_name) + continue ################# # Apply any regionset transformations here From 77c4d24660fd1682eb496c600340142904a56989 Mon Sep 17 00:00:00 2001 From: Gizmokid2005 Date: Thu, 5 Feb 2015 13:54:01 -0500 Subject: [PATCH 14/15] Skip missing dimensions in genPOI This fixes overviewer/Minecraft-Overviewer #1202 for genPOI renders. --- overviewer_core/aux_files/genPOI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overviewer_core/aux_files/genPOI.py b/overviewer_core/aux_files/genPOI.py index f3dce53..3b17812 100755 --- a/overviewer_core/aux_files/genPOI.py +++ b/overviewer_core/aux_files/genPOI.py @@ -308,8 +308,8 @@ def main(): rset = w.get_regionset(render['dimension'][1]) if rset == None: # indicates no such dimension was found: - logging.error("Sorry, you requested dimension '%s' for the render '%s', but I couldn't find it", render['dimension'][0], rname) - return 1 + logging.warn("Sorry, you requested dimension '%s' for the render '%s', but I couldn't find it", render['dimension'][0], rname) + continue for f in render['markers']: markersets.add(((f['name'], f['filterFunction']), rset)) From d696df2a1f648f225568bb266891a38c8a6aadcd Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Feb 2015 21:32:24 -0500 Subject: [PATCH 15/15] Don't crash if no renders/regionsets are found --- overviewer.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/overviewer.py b/overviewer.py index 71d7786..6739f0f 100755 --- a/overviewer.py +++ b/overviewer.py @@ -526,6 +526,12 @@ dir but you forgot to put quotes around the directory, since it contains spaces. tset = tileset.TileSet(w, rset, assetMrg, tex, tileSetOpts, tileset_dir) tilesets.append(tset) + # If none of the requested dimenstions exist, tilesets will be empty + if not tilesets: + logging.error("There are no tilesets to render! There's nothing to do, so exiting.") + return 1 + + # Do tileset preprocessing here, before we start dispatching jobs logging.info("Preprocessing...") for ts in tilesets: