From 25fcd5e8bbb3cc64adda1e6970e53ae3a9279e60 Mon Sep 17 00:00:00 2001 From: Johannes Dewender Date: Tue, 27 Mar 2012 01:44:00 +0200 Subject: [PATCH 01/33] generate player POI out of players dat files The new ids are called "Player" and "PlayerSpawn". EntityId is the name of the player. Everything from the player dat is available in the poi filters. The Player position is the position on last logout. --- genPOI.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/genPOI.py b/genPOI.py index 1caad76..28664f2 100755 --- a/genPOI.py +++ b/genPOI.py @@ -39,6 +39,34 @@ def handleSigns(rset, outputdir, render, rname): rset._pois['TileEntities'] += data['TileEntities'] rset._pois['Entities'] += data['Entities'] +def handlePlayers(rset, render, worldpath): + if not hasattr(rset, "_pois"): + rset._pois = dict(TileEntities=[], Entities=[]) + dimension = {'overworld': 0, + 'nether': -1, + 'end': 1, + 'default': 0}[render['dimension']] + playerdir = os.path.join(worldpath, "players") + rset._pois['Players'] = [] + for playerfile in os.listdir(playerdir): + data = nbt.load(os.path.join(playerdir, playerfile))[1] + playername = playerfile.split(".")[0] + if data['Dimension'] == dimension: + # Position at last logout + data['id'] = "Player" + data['EntityId'] = playername + data['x'] = int(data['Pos'][0]) + data['y'] = int(data['Pos'][1]) + data['z'] = int(data['Pos'][2]) + rset._pois['Players'].append(data) + if "SpawnX" in data and dimension == 0: + # Spawn position (bed or main spawn) + spawn = {"id": "PlayerSpawn", + "EntityId": playername, + "x": data['SpawnX'], + "y": data['SpawnY'], + "z": data['SpawnZ']} + rset._pois['Players'].append(spawn) def main(): helptext = """genPOI @@ -102,6 +130,7 @@ def main(): markers[rname] = [dict(groupName=name, displayName=f.__doc__),] handleSigns(rset, os.path.join(destdir, rname), render, rname) + handlePlayers(rset, render, worldpath) logging.info("Done scanning regions") logging.info("Writing out javascript files") @@ -113,6 +142,9 @@ def main(): for poi in rset._pois['TileEntities']: if flter(poi): markerSetDict[name]['raw'].append(poi) + for poi in rset._pois['Players']: + if flter(poi): + markerSetDict[name]['raw'].append(poi) #print markerSetDict with open(os.path.join(destdir, "markersDB.js"), "w") as output: From 779b38f2a3897fe4f1fa44d5cd88de4a3a39ef65 Mon Sep 17 00:00:00 2001 From: Johannes Dewender Date: Tue, 27 Mar 2012 02:27:46 +0200 Subject: [PATCH 02/33] use avatars for player markers Avatars are taken from scripts at overviewer.org Do we need caching here? --- overviewer_core/data/js_src/views.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 323c1fc..1e8e5a5 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -302,7 +302,6 @@ overviewer.views.SignControlView = Backbone.View.extend({ }}); } - iconURL = overviewerConfig.CONST.image.signMarker; //dataRoot['markers'] = []; // for (i in dataRoot) { @@ -310,6 +309,14 @@ overviewer.views.SignControlView = Backbone.View.extend({ if (!markersDB[groupName].created) { for (j in markersDB[groupName].raw) { var entity = markersDB[groupName].raw[j]; + if (entity['id'] == 'Player') { + iconURL = "http://overviewer.org/avatar/" + + entity['EntityId']; + } else if (entity['id'] == 'Sign') { + iconURL = overviewerConfig.CONST.image.signMarker; + } else { + iconURL = overviewerConfig.CONST.image.defaultMarker; + } var marker = new google.maps.Marker({ 'position': overviewer.util.fromWorldToLatLng(entity.x, entity.y, entity.z, overviewer.mapView.options.currentTileSet), @@ -318,9 +325,7 @@ overviewer.views.SignControlView = Backbone.View.extend({ 'icon': iconURL, 'visible': false }); - if (entity['id'] == 'Sign') { - overviewer.util.createMarkerInfoWindow(marker); - } + overviewer.util.createMarkerInfoWindow(marker); jQuery.extend(entity, {markerObj: marker}); } markersDB[groupName].created = true; From 122eaee37d92dfefe3ff82024af3e72b3e5c4bcc Mon Sep 17 00:00:00 2001 From: Johannes Dewender Date: Tue, 27 Mar 2012 02:58:56 +0200 Subject: [PATCH 03/33] add bed marker icon from faithful pack Taken from the Faithful 32 texture pack: http://www.minecraftforum.net/viewtopic.php?f=25&t=77442 --- overviewer_core/assetmanager.py | 1 + overviewer_core/data/js_src/views.js | 2 ++ overviewer_core/data/web_assets/bed.png | Bin 0 -> 631 bytes 3 files changed, 3 insertions(+) create mode 100644 overviewer_core/data/web_assets/bed.png diff --git a/overviewer_core/assetmanager.py b/overviewer_core/assetmanager.py index 6407c1d..983f1e3 100644 --- a/overviewer_core/assetmanager.py +++ b/overviewer_core/assetmanager.py @@ -87,6 +87,7 @@ directory. dump['CONST']['image'] = { 'defaultMarker': 'signpost.png', 'signMarker': 'signpost_icon.png', + 'bedMarker': 'bed.png', 'compass': 'compass_upper-left.png', 'spawnMarker': 'http://google-maps-icons.googlecode.com/files/home.png', 'queryMarker': 'http://google-maps-icons.googlecode.com/files/regroup.png' diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 1e8e5a5..6b46e72 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -312,6 +312,8 @@ overviewer.views.SignControlView = Backbone.View.extend({ if (entity['id'] == 'Player') { iconURL = "http://overviewer.org/avatar/" + entity['EntityId']; + } else if (entity['id'] == 'PlayerSpawn') { + iconURL = overviewerConfig.CONST.image.bedMarker; } else if (entity['id'] == 'Sign') { iconURL = overviewerConfig.CONST.image.signMarker; } else { diff --git a/overviewer_core/data/web_assets/bed.png b/overviewer_core/data/web_assets/bed.png new file mode 100644 index 0000000000000000000000000000000000000000..3645ac0c798e2e81af06c3a255dc5ae8077f5a97 GIT binary patch literal 631 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>U|>t~ zc6VX;4}uH!E}zW6z`$AH5n0T@z_$T}8U0TGwPRplU@!6Xb!ET9EX|;<)7Z;o!@$7! z-P6S}B;xSh>Av1fjuOZJ3pcvV&=>HCThrX3AQB~iV6*#QA-Qt<7WMUYoXvX0r3?L> z+SnBrIp+tkaPJj*^zvnZagW*SbL+gD_<48^zj|+eevh%zA){no@4l}q{$JRZJM&W6 z?z8`V9U8K?N_j1vl=FG2!lKqL)(NSRPb~O$uKT?_Vw&scy7PzpoMsy`H6+>fpMITv zMTVUr<^9!+tx@w13r0Awt`-bC6yeYyyivkz`?i#7qp49Ihr$nVuW4I(B*TR1d5zbh z-~%i&tw&~w+X!|irdLNFn!Iv~&aR5;+NocY?D*r>dL=BFdm*hSHoTExr;=>0&Y(; zVfXd*{Y`F8*PNTI?=ohaX_@@)z0)zPm#Z`K*6aI=7x1`Gv7NVDnCq}#`GHmK)k`LK zaOIT9g!6yrXo!)0TxDsr;bW~sit86Et%3#R2QU8XD-)0IZ10@dkjET3lSkri*VU&x gMH;8p$<{Of+`KE@O(At10|Nttr>mdKI;Vst0Pryl3;+NC literal 0 HcmV?d00001 From 6528aa91e7a73c323b05acc2e1ed95a4cc3d103f Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 15:03:06 -0400 Subject: [PATCH 04/33] Added overlay control to HTML --- overviewer_core/data/js_src/util.js | 6 ++ overviewer_core/data/js_src/views.js | 126 ++++++++++++++++++++++++++- overviewer_core/tileset.py | 4 + 3 files changed, 133 insertions(+), 3 deletions(-) diff --git a/overviewer_core/data/js_src/util.js b/overviewer_core/data/js_src/util.js index 0e738da..fff2d77 100644 --- a/overviewer_core/data/js_src/util.js +++ b/overviewer_core/data/js_src/util.js @@ -63,6 +63,10 @@ overviewer.util = { signs.registerEvents(signs); } + var overlayControl = new overviewer.views.OverlayControlView(); + overlayControl.registerEvents(overlayControl); + overlayControl.render(); + var spawnmarker = new overviewer.views.SpawnIconView(); // Update coords on mousemove @@ -116,6 +120,8 @@ overviewer.util = { var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'}); overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector); + + // hook up some events overviewer.mapModel.bind("change:currentWorldView", overviewer.mapView.render, overviewer.mapView); diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 323c1fc..46d5379 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -4,8 +4,11 @@ overviewer.views= {} overviewer.views.WorldView = Backbone.View.extend({ initialize: function(opts) { this.options.mapTypes = []; + this.options.overlayMapTypes = []; this.options.mapTypeIds = []; + this.options.overlayMapTypeIds = []; this.model.get("tileSets").each(function(tset, index, list) { + // ignore overlays: var ops = { getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), tset.get("imgextension")), 'tileSize': new google.maps.Size( @@ -21,10 +24,21 @@ overviewer.views.WorldView = Backbone.View.extend({ newMapType.alt = "Minecraft " + tset.get("name") + " Map"; newMapType.projection = new overviewer.classes.MapProjection(); - this.options.mapTypes.push(newMapType); - this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name")); + if (tset.get("isOverlay")) { + this.options.overlayMapTypes.push(newMapType); + this.options.overlayMapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name")); + } else { + this.options.mapTypes.push(newMapType); + this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name")); + } }, this); + + this.model.get("tileSets").each(function(tset, index, list) { + // ignore non-overlays: + if (!tset.get("isOverlay")) { return; }; + + }); }, }); @@ -200,6 +214,112 @@ overviewer.views.GoogleMapView = Backbone.View.extend({ }); +/** + * OverlayControlView + */ +overviewer.views.OverlayControlView = Backbone.View.extend({ + /** OverlayControlVIew::initialize + */ + initialize: function(opts) { + $(this.el).addClass("customControl"); + overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.el); + }, + registerEvents: function(me) { + overviewer.mapModel.bind("change:currentWorldView", me.render, me); + + }, + + /** + * OverlayControlView::render + */ + render: function() { + this.el.innerHTML=""; + + // hide all visible overlays: + overviewer.map.overlayMapTypes.clear() + + // if this world has no overlays, don't create this control + var mapTypes = overviewer.mapModel.get('currentWorldView').options.overlayMapTypes; + if (mapTypes.length == 0) { return; } + + + var controlText = document.createElement('DIV'); + controlText.innerHTML = "Overlays"; + + var controlBorder = document.createElement('DIV'); + $(controlBorder).addClass('top'); + this.el.appendChild(controlBorder); + controlBorder.appendChild(controlText); + + var dropdownDiv = document.createElement('DIV'); + $(dropdownDiv).addClass('dropDown'); + this.el.appendChild(dropdownDiv); + dropdownDiv.innerHTML=''; + + $(controlText).click(function() { + $(controlBorder).toggleClass('top-active'); + $(dropdownDiv).toggle(); + }); + + + for (i in mapTypes) { + var mt = mapTypes[i]; + this.addItem({label: mt.name, + name: mt.name, + mt: mt, + + action: function(this_item, checked) { + //console.log(this_item); + if (checked) { + overviewer.map.overlayMapTypes.push(this_item.mt); + } else { + var idx_to_delete = -1; + overviewer.map.overlayMapTypes.forEach(function(e, j) { + if (e == this_item.mt) { + idx_to_delete = j; + } + }); + if (idx_to_delete >= 0) { + overviewer.map.overlayMapTypes.removeAt(idx_to_delete); + } + } + + } + }) + } + + + }, + + addItem: function(item) { + var itemDiv = document.createElement('div'); + var itemInput = document.createElement('input'); + itemInput.type='checkbox'; + + // if this overlay is already visible, set the checkbox + // to checked + overviewer.map.overlayMapTypes.forEach(function(e, j) { + if (e == item.mt) { + itemInput.checked=true; + } + }); + + // give it a name + $(itemInput).attr("_mc_overlayname", item.name); + jQuery(itemInput).click((function(local_item) { + return function(e) { + item.action(local_item, e.target.checked); + }; + })(item)); + + this.$(".dropDown")[0].appendChild(itemDiv); + itemDiv.appendChild(itemInput); + var textNode = document.createElement('text'); + textNode.innerHTML = item.label + '
'; + + itemDiv.appendChild(textNode); + } +}); /** @@ -265,7 +385,7 @@ overviewer.views.SignControlView = Backbone.View.extend({ //var dataRoot = overviewer.collections.markerInfo[curMarkerSet]; var dataRoot = markers[curMarkerSet]; - this.el.innerHTML="" + this.el.innerHTML=""; // if we have no markerSets for this tileset, do nothing: if (!dataRoot) { return; } diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index 4787c64..a2a8f0a 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -32,6 +32,7 @@ from .util import roundrobin from . import nbt from .files import FileReplacer from .optimizeimages import optimize_image +import rendermodes import c_overviewer """ @@ -507,6 +508,8 @@ class TileSet(object): """ def bgcolorformat(color): return "#%02x%02x%02x" % color[0:3] + isOverlay = True in [True for x in self.options.get("rendermode") if isinstance(x, rendermodes.Overlay)] + d = dict(name = self.options.get('title'), zoomLevels = self.treedepth, minZoom = 0, @@ -519,6 +522,7 @@ class TileSet(object): (" - " + self.options.get('dimension') if self.options.get('dimension') != 'default' else ''), last_rendertime = self.max_chunk_mtime, imgextension = self.imgextension, + isOverlay = isOverlay ) if (self.regionset.get_type() == "overworld"): d.update({"spawn": self.options.get("spawn")}) From 94b5f21d10adfae395688f0d91a017ac6e156774 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 16:15:01 -0400 Subject: [PATCH 05/33] Have the world selector control have a correct default --- overviewer_core/data/js_src/util.js | 9 +++++---- overviewer_core/data/js_src/views.js | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/overviewer_core/data/js_src/util.js b/overviewer_core/data/js_src/util.js index fff2d77..350f7f4 100644 --- a/overviewer_core/data/js_src/util.js +++ b/overviewer_core/data/js_src/util.js @@ -117,10 +117,6 @@ overviewer.util = { }); - var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'}); - overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector); - - // hook up some events @@ -131,6 +127,11 @@ overviewer.util = { // Jump to the hash if given overviewer.util.initHash(); + // create this control after initHash so it can correctly select the current world + var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'}); + overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector); + + overviewer.util.initializeMarkers(); /* diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 46d5379..7ecb169 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -54,6 +54,9 @@ overviewer.views.WorldSelectorView = Backbone.View.extend({ var o = document.createElement("option"); o.value = elem.model.get("name"); o.innerHTML = elem.model.get("name"); + if (elem.model == overviewer.mapModel.get("currentWorldView").model) { + o.selected=true; + } $(o).data("viewObj", elem); selectBox.appendChild(o); From f4d43d544ade6cc9dbe2595bfd8e2281b20046d4 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 17:56:30 -0400 Subject: [PATCH 06/33] Styling for the worldselector control --- overviewer_core/data/js_src/views.js | 2 ++ overviewer_core/data/web_assets/overviewer.css | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 7ecb169..cafcb1b 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -47,6 +47,8 @@ overviewer.views.WorldView = Backbone.View.extend({ overviewer.views.WorldSelectorView = Backbone.View.extend({ initialize: function() { if(overviewer.collections.worldViews.length > 1) { + $(this.el).addClass("customControl"); + // a div will have already been created for us, we just // need to register it with the google maps control var selectBox = document.createElement('select'); diff --git a/overviewer_core/data/web_assets/overviewer.css b/overviewer_core/data/web_assets/overviewer.css index 0ae76e5..831a138 100644 --- a/overviewer_core/data/web_assets/overviewer.css +++ b/overviewer_core/data/web_assets/overviewer.css @@ -40,6 +40,17 @@ body { font-family: Arial, sans-serif; } +.customControl > select { + font-size: 12px; + line-height: 160%; + text-align: center; + + border: 1px solid #A9BBDF; + border-radius: 2px 2px; + box-shadow: rgba(0, 0, 0, 0.347656) 2px 2px 3px; + +} + .customControl > div.top { font-size: 12px; line-height: 160%; From 5aefebecbe47e7dcd6a26872c6171a8dd60201e1 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 17:59:46 -0400 Subject: [PATCH 07/33] Fix dereferencing bug, hopefully should address mineral-overlay crash Should fix #633 --- overviewer_core/src/primitives/overlay-mineral.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/src/primitives/overlay-mineral.c b/overviewer_core/src/primitives/overlay-mineral.c index 5d6f22e..b658433 100644 --- a/overviewer_core/src/primitives/overlay-mineral.c +++ b/overviewer_core/src/primitives/overlay-mineral.c @@ -90,6 +90,7 @@ overlay_mineral_start(void *data, RenderState *state, PyObject *support) { /* now do custom initializations */ self = (RenderPrimitiveMineral *)data; + // opt is a borrowed reference. do not deref if (!render_mode_parse_option(support, "minerals", "O", &(opt))) return 1; if (opt && opt != Py_None) { @@ -119,7 +120,6 @@ overlay_mineral_start(void *data, RenderState *state, PyObject *support) { } else { self->minerals = default_minerals; } - Py_XDECREF(opt); /* setup custom color */ self->parent.get_color = get_color; From d6602caa045ba4388a9da57c265e06746e6a03f3 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 20:19:24 -0400 Subject: [PATCH 08/33] Output to stdout instead of stderr Fixes #645 --- overviewer_core/logger.py | 6 +++--- overviewer_core/settingsDefinition.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/overviewer_core/logger.py b/overviewer_core/logger.py index 96850cf..70acf21 100644 --- a/overviewer_core/logger.py +++ b/overviewer_core/logger.py @@ -267,15 +267,15 @@ def configure(loglevel=logging.INFO, verbose=False): logger = logging.getLogger() - outstream = sys.stderr + outstream = sys.stdout if platform.system() == 'Windows': # Our custom output stream processor knows how to deal with select ANSI # color escape sequences - outstream = WindowsOutputStream() + outstream = WindowsOutputStream(outstream) formatter = ANSIColorFormatter(verbose) - elif sys.stderr.isatty(): + elif outstream.isatty(): # terminal logging with ANSI color formatter = ANSIColorFormatter(verbose) diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index 7d1625f..e78c589 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -97,9 +97,9 @@ processes = Setting(required=True, validator=int, default=-1) # ends up adding overhead and isn't worth it. memcached_host = Setting(required=False, validator=str, default=None) -if platform.system() == 'Windows' or not sys.stderr.isatty(): +if platform.system() == 'Windows' or not sys.stdout.isatty(): obs = LoggingObserver() else: - obs = ProgressBarObserver() + obs = ProgressBarObserver(fd=sys.stdout) observer = Setting(required=True, validator=validateObserver, default=obs) From cb448cc58f2d867df68e29e3798e93920e827cd7 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 21:16:58 -0400 Subject: [PATCH 09/33] Added a --simple-output option This option turns off fancy output options like colors or progress bars. Address #649 --- overviewer.py | 5 ++++- overviewer_core/logger.py | 6 ++++-- overviewer_core/settingsDefinition.py | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/overviewer.py b/overviewer.py index 6786c36..210420a 100755 --- a/overviewer.py +++ b/overviewer.py @@ -89,6 +89,8 @@ def main(): help="Print less output. You can specify this option multiple times.") parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, help="Print more output. You can specify this option multiple times.") + parser.add_option("--simple-output", dest="simple", action="store_true", default=False, + help="Use a simple output format, with no colors or progress bars") # create a group for "plugin exes" (the concept of a plugin exe is only loosly defined at this point) exegroup = OptionGroup(parser, "Other Scripts", @@ -114,7 +116,8 @@ def main(): # re-configure the logger now that we've processed the command line options logger.configure(logging.INFO + 10*options.quiet - 10*options.verbose, - options.verbose > 0) + verbose=options.verbose > 0, + simple=options.simple) ########################################################################## # This section of main() runs in response to any one-time options we have, diff --git a/overviewer_core/logger.py b/overviewer_core/logger.py index 70acf21..4fd36a3 100644 --- a/overviewer_core/logger.py +++ b/overviewer_core/logger.py @@ -254,7 +254,7 @@ class ANSIColorFormatter(HighlightingFormatter): # No coloring if it's not to be highlighted or colored return logging.Formatter.format(self, record) -def configure(loglevel=logging.INFO, verbose=False): +def configure(loglevel=logging.INFO, verbose=False, simple=False): """Configures the root logger to our liking For a non-standard loglevel, pass in the level with which to configure the handler. @@ -268,8 +268,10 @@ def configure(loglevel=logging.INFO, verbose=False): logger = logging.getLogger() outstream = sys.stdout + if simple: + formatter = DumbFormatter(verbose) - if platform.system() == 'Windows': + elif platform.system() == 'Windows': # Our custom output stream processor knows how to deal with select ANSI # color escape sequences outstream = WindowsOutputStream(outstream) diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index e78c589..54fb763 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -97,7 +97,8 @@ processes = Setting(required=True, validator=int, default=-1) # ends up adding overhead and isn't worth it. memcached_host = Setting(required=False, validator=str, default=None) -if platform.system() == 'Windows' or not sys.stdout.isatty(): +# 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() else: obs = ProgressBarObserver(fd=sys.stdout) From 7433f06edbb6dc40f1ff33be360b53dc9941ede3 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 7 Apr 2012 22:12:10 -0400 Subject: [PATCH 10/33] Remove unneeded stuffs --- overviewer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/overviewer.py b/overviewer.py index 210420a..8a774f9 100755 --- a/overviewer.py +++ b/overviewer.py @@ -236,7 +236,6 @@ dir but you forgot to put quotes around the directory, since it contains spaces. return 1 # Parse the config file - mw_parser = configParser.MultiWorldParser() mw_parser.parse(options.config) # Add in the command options here, perhaps overriding values specified in From 3ded0cfa9e3a76f881952a0003e2bc680c3dc4a5 Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Mon, 9 Apr 2012 11:49:03 -0700 Subject: [PATCH 11/33] Add validator and definition for beginnings of overlay python glue so that only the right overlays show up for a specific render --- overviewer_core/settingsDefinition.py | 1 + overviewer_core/settingsValidators.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index 54fb763..dc8a518 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -79,6 +79,7 @@ renders = Setting(required=True, default=util.OrderedDict(), "crop": Setting(required=False, validator=validateCrop, default=None), "changelist": Setting(required=False, validator=validateStr, default=None), "markers": Setting(required=False, validator=validateMarkers, default=[]), + "overlay": Setting(required=False, validator=validateOverlays, default=[]), # Remove this eventually (once people update their configs) "worldname": Setting(required=False, default=None, diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index d8846b1..7b023a8 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -51,6 +51,13 @@ def validateMarkers(filterlist): raise ValidationException("%r must be a function"% x) return filterlist +def validateOverlays(renderlist): + if type(renderlist) != list: + raise ValidationException("Overlay must specify a list of renders") + for x in renderlist: + print x + return renderlist + def validateWorldPath(worldpath): _, worldpath = checkBadEscape(worldpath) abs_path = os.path.abspath(os.path.expanduser(worldpath)) From ebd8b287ed48dca08f46a3145cd8a8ad4d9a0763 Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Mon, 9 Apr 2012 12:20:58 -0700 Subject: [PATCH 12/33] Further glue for overlays. Make the validator actually validate. Also rough outlines for checking if the render you've specified in the overlay actually exists and isn't itself. --- overviewer.py | 15 +++++++++++++++ overviewer_core/settingsValidators.py | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/overviewer.py b/overviewer.py index 8a774f9..2a93845 100755 --- a/overviewer.py +++ b/overviewer.py @@ -304,6 +304,21 @@ dir but you forgot to put quotes around the directory, since it contains spaces. if render.get('forcerender', False): render['renderchecks'] = 2 + # check if overlays are set, if so, make sure that those renders exist + if render.get('overlay', []) != []: + for x in render.get('overlay'): + if x != rname: + try: + renderLink = config['renders'][x] + except KeyError: + logging.error("Render %s's overlay is '%s', but I could not find a corresponding entry in the renders dictionary.", + rname, x) + return 1 + # TODO: Link the overlay rendering to the render modes it is used for so that the JS can properly fill in the dropdown + else: + logging.error("Render %s's overlay contains itself.", rname) + return 1 + destdir = config['outputdir'] if not destdir: logging.error("You must specify the output directory in your config file.") diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index 7b023a8..227f4d3 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -55,7 +55,8 @@ def validateOverlays(renderlist): if type(renderlist) != list: raise ValidationException("Overlay must specify a list of renders") for x in renderlist: - print x + if validateStr(x) == '': + raise ValidationException("%r must be a string"% x) return renderlist def validateWorldPath(worldpath): From 77acd158c6414646a2e4830e5125c311900cd003 Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Mon, 9 Apr 2012 12:26:40 -0700 Subject: [PATCH 13/33] Add docs too! --- docs/config.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index 1884573..b2c9df6 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -480,6 +480,18 @@ values. The valid configuration keys are listed below. **Default:** ``[]`` (an empty list) +.. _option_overlay: + +``overlay`` + This specifies which renders that this render will be displayed on top of. + It should be a list of renders. + + .. warning:: + + At this time, this feature is not fully implemented. + + **Default:** ``[]`` (an empty list) + .. _customrendermodes: Custom Rendermodes and Rendermode Primitives From db0a1d3415e802a09bbac1770ed368747bc88fb4 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 9 Apr 2012 17:10:05 -0400 Subject: [PATCH 14/33] updated isOverlay to correctly recognize modes with no Base() primitive --- overviewer_core/tileset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index a2a8f0a..6919884 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -508,7 +508,7 @@ class TileSet(object): """ def bgcolorformat(color): return "#%02x%02x%02x" % color[0:3] - isOverlay = True in [True for x in self.options.get("rendermode") if isinstance(x, rendermodes.Overlay)] + isOverlay = not any(isinstance(x, rendermodes.Base) for x in self.options.get("rendermode")) d = dict(name = self.options.get('title'), zoomLevels = self.treedepth, From b13bbd02048c8c259b8381b533fd8e87975ba78f Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Thu, 12 Apr 2012 08:59:13 -0700 Subject: [PATCH 15/33] Add the JS stuff and a little bit more python glue to make overlays only display for a specified render --- overviewer.py | 2 +- overviewer_core/data/js_src/util.js | 5 +-- overviewer_core/data/js_src/views.js | 47 ++++++++++++++-------------- overviewer_core/tileset.py | 3 ++ 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/overviewer.py b/overviewer.py index 2a93845..0733ddf 100755 --- a/overviewer.py +++ b/overviewer.py @@ -418,7 +418,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces. # only pass to the TileSet the options it really cares about render['name'] = render_name # perhaps a hack. This is stored here for the asset manager - tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist"]) + tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist", "overlay"]) tileSetOpts.update({"spawn": w.find_true_spawn()}) # TODO find a better way to do this tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir) tilesets.append(tset) diff --git a/overviewer_core/data/js_src/util.js b/overviewer_core/data/js_src/util.js index 350f7f4..81cc5c0 100644 --- a/overviewer_core/data/js_src/util.js +++ b/overviewer_core/data/js_src/util.js @@ -64,8 +64,6 @@ overviewer.util = { } var overlayControl = new overviewer.views.OverlayControlView(); - overlayControl.registerEvents(overlayControl); - overlayControl.render(); var spawnmarker = new overviewer.views.SpawnIconView(); @@ -85,6 +83,9 @@ overviewer.util = { compass.render(); spawnmarker.render(); + // update list of spawn overlays + overlayControl.render(); + // re-center on the last viewport var currentWorldView = overviewer.mapModel.get("currentWorldView"); if (currentWorldView.options.lastViewport) { diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index cafcb1b..21fbaac 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -23,8 +23,9 @@ overviewer.views.WorldView = Backbone.View.extend({ newMapType.shortname = tset.get("name"); newMapType.alt = "Minecraft " + tset.get("name") + " Map"; newMapType.projection = new overviewer.classes.MapProjection(); - + if (tset.get("isOverlay")) { + newMapType.tiles = tset.get("tilesets"); this.options.overlayMapTypes.push(newMapType); this.options.overlayMapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name")); } else { @@ -231,7 +232,6 @@ overviewer.views.OverlayControlView = Backbone.View.extend({ }, registerEvents: function(me) { overviewer.mapModel.bind("change:currentWorldView", me.render, me); - }, /** @@ -246,8 +246,7 @@ overviewer.views.OverlayControlView = Backbone.View.extend({ // if this world has no overlays, don't create this control var mapTypes = overviewer.mapModel.get('currentWorldView').options.overlayMapTypes; if (mapTypes.length == 0) { return; } - - + var controlText = document.createElement('DIV'); controlText.innerHTML = "Overlays"; @@ -265,32 +264,34 @@ overviewer.views.OverlayControlView = Backbone.View.extend({ $(controlBorder).toggleClass('top-active'); $(dropdownDiv).toggle(); }); - + var currentTileSetPath = overviewer.mapView.options.currentTileSet.get('path'); + for (i in mapTypes) { var mt = mapTypes[i]; - this.addItem({label: mt.name, - name: mt.name, - mt: mt, + if (mt.tiles.indexOf(currentTileSetPath)!=-1) { + this.addItem({label: mt.name, + name: mt.name, + mt: mt, - action: function(this_item, checked) { - //console.log(this_item); - if (checked) { - overviewer.map.overlayMapTypes.push(this_item.mt); - } else { - var idx_to_delete = -1; - overviewer.map.overlayMapTypes.forEach(function(e, j) { - if (e == this_item.mt) { - idx_to_delete = j; + action: function(this_item, checked) { + if (checked) { + overviewer.map.overlayMapTypes.push(this_item.mt); + } else { + var idx_to_delete = -1; + overviewer.map.overlayMapTypes.forEach(function(e, j) { + if (e == this_item.mt) { + idx_to_delete = j; + } + }); + if (idx_to_delete >= 0) { + overviewer.map.overlayMapTypes.removeAt(idx_to_delete); } - }); - if (idx_to_delete >= 0) { - overviewer.map.overlayMapTypes.removeAt(idx_to_delete); } - } - } - }) + } + }) + } } diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index 6919884..a08443d 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -524,6 +524,9 @@ class TileSet(object): imgextension = self.imgextension, isOverlay = isOverlay ) + if isOverlay: + d.update({"tilesets": self.options.get("overlay")}) + if (self.regionset.get_type() == "overworld"): d.update({"spawn": self.options.get("spawn")}) try: From 805618dd2fd1763ac676c12fd19e8d598bf0cfdd Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Thu, 12 Apr 2012 09:03:14 -0700 Subject: [PATCH 16/33] remove todo since I did that part --- overviewer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/overviewer.py b/overviewer.py index 0733ddf..728ff82 100755 --- a/overviewer.py +++ b/overviewer.py @@ -314,7 +314,6 @@ dir but you forgot to put quotes around the directory, since it contains spaces. logging.error("Render %s's overlay is '%s', but I could not find a corresponding entry in the renders dictionary.", rname, x) return 1 - # TODO: Link the overlay rendering to the render modes it is used for so that the JS can properly fill in the dropdown else: logging.error("Render %s's overlay contains itself.", rname) return 1 From f5797dd8a0f60d1369d332a620a84584df78db10 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 12 Apr 2012 15:16:19 -0400 Subject: [PATCH 17/33] added a Hide() render primitive --- docs/config.rst | 9 +++ overviewer_core/rendermodes.py | 6 ++ overviewer_core/src/primitives/hide.c | 111 ++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 overviewer_core/src/primitives/hide.c diff --git a/docs/config.rst b/docs/config.rst index 1884573..e7e4c99 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -560,6 +560,15 @@ Cave only_lit Only render lit caves. Default: False +Hide + Hide blocks based on blockid. Blocks hidden in this way will be + treated exactly the same as air. + + **Options** + + minerals + A list of block ids, or (blockid, data) tuples to hide. + DepthTinting Tint blocks a color according to their depth (height) from bedrock. Useful mainly for cave renders. diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index e1adc9c..8ea0b52 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -188,6 +188,12 @@ class MineralOverlay(Overlay): 'minerals' : ('a list of (blockid, (r, g, b)) tuples for coloring minerals', None), } +class Hide(RenderPrimitive): + name = "hide" + options = { + 'blocks' : ('a list of blockids or (blockid, data) tuples of blocks to hide', []), + } + # Built-in rendermodes for your convenience! normal = [Base(), EdgeLines()] lighting = [Base(), EdgeLines(), Lighting()] diff --git a/overviewer_core/src/primitives/hide.c b/overviewer_core/src/primitives/hide.c new file mode 100644 index 0000000..5444e82 --- /dev/null +++ b/overviewer_core/src/primitives/hide.c @@ -0,0 +1,111 @@ +/* + * 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 "../overviewer.h" + +struct HideRule { + unsigned short blockid; + unsigned char has_data; + unsigned char data; +}; + +typedef struct { + struct HideRule* rules; +} RenderPrimitiveHide; + +static int +hide_start(void *data, RenderState *state, PyObject *support) { + PyObject *opt; + RenderPrimitiveHide* self = (RenderPrimitiveHide *)data; + self->rules = NULL; + + if (!render_mode_parse_option(support, "blocks", "O", &(opt))) + return 1; + if (opt && opt != Py_None) { + Py_ssize_t blocks_size = 0, i; + + if (!PyList_Check(opt)) { + PyErr_SetString(PyExc_TypeError, "'blocks' must be a list"); + return 1; + } + + blocks_size = PyList_GET_SIZE(opt); + self->rules = calloc(blocks_size + 1, sizeof(struct HideRule)); + if (self->rules == NULL) { + return 1; + } + + for (i = 0; i < blocks_size; i++) { + PyObject *block = PyList_GET_ITEM(opt, i); + + if (PyInt_Check(block)) { + /* format 1: just a block id */ + self->rules[i].blockid = PyInt_AsLong(block); + self->rules[i].has_data = 0; + } else if (PyArg_ParseTuple(block, "Hb", &(self->rules[i].blockid), &(self->rules[i].data))) { + /* format 2: (blockid, data) */ + self->rules[i].has_data = 1; + } else { + /* format not recognized */ + free(self->rules); + self->rules = NULL; + return 1; + } + } + } + + return 0; +} + +static void +hide_finish(void *data, RenderState *state) { + RenderPrimitiveHide *self = (RenderPrimitiveHide *)data; + + if (self->rules) { + free(self->rules); + } +} + +static int +hide_hidden(void *data, RenderState *state, int x, int y, int z) { + RenderPrimitiveHide *self = (RenderPrimitiveHide *)data; + unsigned int i; + + if (self->rules == NULL) + return 0; + + for (i = 0; self->rules[i].blockid != 0; i++) { + if (state->block == self->rules[i].blockid) { + if (!(self->rules[i].has_data)) + return 1; + if (state->block_data == self->rules[i].data) + return 1; + } + } + + return 0; +} + +RenderPrimitiveInterface primitive_hide = { + "hide", + sizeof(RenderPrimitiveHide), + hide_start, + hide_finish, + NULL, + hide_hidden, + NULL, +}; From 838bd2e5abf829357164433f4aeddf5489f26ac5 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 12 Apr 2012 15:33:24 -0400 Subject: [PATCH 18/33] fixed Hide() primitive and made EdgeLines() play nice --- overviewer_core/src/overviewer.h | 2 +- overviewer_core/src/primitives/edge-lines.c | 9 +++++---- overviewer_core/src/primitives/hide.c | 10 ++++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 1bb8f92..c9b7ae6 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 30 +#define OVERVIEWER_EXTENSION_VERSION 31 /* Python PIL, and numpy headers */ #include diff --git a/overviewer_core/src/primitives/edge-lines.c b/overviewer_core/src/primitives/edge-lines.c index dc3d1c1..00f12ae 100644 --- a/overviewer_core/src/primitives/edge-lines.c +++ b/overviewer_core/src/primitives/edge-lines.c @@ -38,6 +38,7 @@ edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, P Imaging img_i = imaging_python_to_c(state->img); unsigned char ink[] = {0, 0, 0, 255 * self->opacity}; unsigned short side_block; + int x = state->x, y = state->y, z = state->z; int increment=0; if (state->block == 44 && ((state->block_data & 0x8) == 0 )) // half-step BUT no upsidown half-step @@ -46,15 +47,15 @@ edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, P increment=9; /* +X side */ - side_block = get_data(state, BLOCKS, state->x+1, state->y, state->z); - if (side_block != state->block && is_transparent(side_block)) { + side_block = get_data(state, BLOCKS, x+1, y, z); + if (side_block != state->block && (is_transparent(side_block) || render_mode_hidden(state->rendermode, x+1, y, z))) { ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); } /* -Z side */ - side_block = get_data(state, BLOCKS, state->x, state->y, state->z-1); - if (side_block != state->block && is_transparent(side_block)) { + side_block = get_data(state, BLOCKS, x, y, z-1); + if (side_block != state->block && (is_transparent(side_block) || render_mode_hidden(state->rendermode, x, y, z-1))) { ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); } diff --git a/overviewer_core/src/primitives/hide.c b/overviewer_core/src/primitives/hide.c index 5444e82..cdb24e8 100644 --- a/overviewer_core/src/primitives/hide.c +++ b/overviewer_core/src/primitives/hide.c @@ -84,15 +84,21 @@ static int hide_hidden(void *data, RenderState *state, int x, int y, int z) { RenderPrimitiveHide *self = (RenderPrimitiveHide *)data; unsigned int i; + unsigned short block; if (self->rules == NULL) return 0; + block = get_data(state, BLOCKS, x, y, z); for (i = 0; self->rules[i].blockid != 0; i++) { - if (state->block == self->rules[i].blockid) { + if (block == self->rules[i].blockid) { + unsigned char data; + if (!(self->rules[i].has_data)) return 1; - if (state->block_data == self->rules[i].data) + + data = get_data(state, DATA, x, y, z); + if (data == self->rules[i].data) return 1; } } From 85e2ac202fad83e3ddd1a06a9fc43cab5146b0f9 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Fri, 13 Apr 2012 23:48:07 -0400 Subject: [PATCH 19/33] Fix overlay code so that overlays with no 'overlay' config are always displayed --- overviewer_core/data/js_src/views.js | 46 +++++++++++++++------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 21fbaac..ccff3bf 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -269,29 +269,31 @@ overviewer.views.OverlayControlView = Backbone.View.extend({ for (i in mapTypes) { var mt = mapTypes[i]; - if (mt.tiles.indexOf(currentTileSetPath)!=-1) { - this.addItem({label: mt.name, - name: mt.name, - mt: mt, - - action: function(this_item, checked) { - if (checked) { - overviewer.map.overlayMapTypes.push(this_item.mt); - } else { - var idx_to_delete = -1; - overviewer.map.overlayMapTypes.forEach(function(e, j) { - if (e == this_item.mt) { - idx_to_delete = j; - } - }); - if (idx_to_delete >= 0) { - overviewer.map.overlayMapTypes.removeAt(idx_to_delete); - } - } - - } - }) + // if this overlay specifies a list of valid tilesets, then skip over any invalid tilesets + if ((mt.tiles.length > 0) && (mt.tiles.indexOf(currentTileSetPath) ==-1)) { + continue; } + this.addItem({label: mt.name, + name: mt.name, + mt: mt, + + action: function(this_item, checked) { + if (checked) { + overviewer.map.overlayMapTypes.push(this_item.mt); + } else { + var idx_to_delete = -1; + overviewer.map.overlayMapTypes.forEach(function(e, j) { + if (e == this_item.mt) { + idx_to_delete = j; + } + }); + if (idx_to_delete >= 0) { + overviewer.map.overlayMapTypes.removeAt(idx_to_delete); + } + } + + } + }); } From 2a6769c5ac865b843a5b8483a5b333ea08dd6f18 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 14 Apr 2012 22:09:12 -0400 Subject: [PATCH 20/33] Revised syntax/method for specifying POIs --- docs/signs.rst | 50 +++++++++++++++++---------- genPOI.py | 26 +++++++++----- overviewer_core/data/js_src/views.js | 7 ++-- overviewer_core/settingsValidators.py | 8 +++-- 4 files changed, 57 insertions(+), 34 deletions(-) diff --git a/docs/signs.rst b/docs/signs.rst index d10e755..efa0514 100644 --- a/docs/signs.rst +++ b/docs/signs.rst @@ -20,32 +20,30 @@ Filter Functions ---------------- A filter function is a python function that is used to figure out if a given POI -should be part of a markerSet of not. The function should accept one argument -(a dictionary, also know as an associative array), and return a boolean:: +should be part of a markerSet of not, and to control how it is displayed. +The function should accept one argument (a dictionary, also know as an associative +array), and return a string representing the text to be displayed. For example:: def signFilter(poi): - "All signs" - return poi['id'] == 'Sign' + if poi['id'] == 'Sign': + return "\n".join([poi['Text1'], poi['Text2'], poi['Text3'], poi['Text4']]) + +If a POI doesn't match, the filter can return None (which is the default if a python +functions runs off the end without an explicit 'return'). The single argument will either a TileEntity, or an Entity taken directly from -the chunk file. In this example, this function returns true only if the type -of entity is a sign. For more information of TileEntities and Entities, see +the chunk file. In this example, this function returns all 4 lines from the sign +if the entity is a sign. +For more information of TileEntities and Entities, see the `Chunk Format `_ page on the Minecraft Wiki. -.. note:: - The doc string ("All signs" in this example) is important. It is the label - that appears in your rendered map +A more complicated filter function can construct a more customized display text:: -A more advanced filter may also look at other entity fields, such as the sign text:: + def chestFilter(poi): + if poi['id'] == "Chest": + return "Chest with %d items" % len(poi['Items']) - def goldFilter(poi): - "Gold" - return poi['id'] == 'Sign' and (\ - 'gold' in poi['Text1'] or - 'gold' in poi['Text2']) - -This looks for the word 'gold' in either the first or second line of the signtext. Since writing these filters can be a little tedious, a set of predefined filters functions are provided. See the :ref:`predefined_filter_functions` section for @@ -56,15 +54,29 @@ Render Dictionary Key Each render can specify a list of zero or more filter functions. Each of these filter functions become a selectable item in the 'Signs' drop-down menu in the -rendered map. For example:: +rendered map. Previously, this used to be a list of functions. Now it is a list +of dictionaries. For example:: renders['myrender'] = { 'world': 'myworld', 'title': "Example", - 'markers': [allFilter, anotherFilter], + 'markers': [dict(name="All signs", filterFunction=signFilter), + dict(name="Chests", filterFunction=chestFilter, icon="chest.png")] } +The following keys are accepted in the marker dictionary: + +``name`` + This is the text that is displayed in the 'Signs' dropdown. + +``filterFunction`` + This is the filter function. It must accept at least 1 argument (the POI to filter), + and it must return either None or a string. + +``icon`` + Optional. Specifies the icon to use for POIs in this group. If omitted, it defaults + to a signpost icon. Generating the POI Markers diff --git a/genPOI.py b/genPOI.py index 66a8360..b5c07d0 100755 --- a/genPOI.py +++ b/genPOI.py @@ -25,7 +25,8 @@ from overviewer_core import configParser, world def handleSigns(rset, outputdir, render, rname): - + + # if we're already handled the POIs for this region regionset, do nothing if hasattr(rset, "_pois"): return @@ -39,6 +40,7 @@ def handleSigns(rset, outputdir, render, rname): rset._pois['TileEntities'] += data['TileEntities'] rset._pois['Entities'] += data['Entities'] + print "Done." def main(): helptext = """genPOI @@ -97,13 +99,15 @@ def main(): return 1 for f in render['markers']: - markersets.add((f, rset)) - name = f.__name__ + hex(hash(f))[-4:] + "_" + hex(hash(rset))[-4:] + d = dict(icon="signpost_icon.png") + d.update(f) + markersets.add(((d['name'], d['filterFunction']), rset)) + name = f['name']+ hex(hash(f['filterFunction']))[-4:] + "_" + hex(hash(rset))[-4:] try: l = markers[rname] - l.append(dict(groupName=name, displayName = f.__doc__)) + l.append(dict(groupName=name, displayName = f['name'], icon=d['icon'])) except KeyError: - markers[rname] = [dict(groupName=name, displayName=f.__doc__),] + markers[rname] = [dict(groupName=name, displayName=f['name'], icon=d['icon']),] handleSigns(rset, os.path.join(destdir, rname), render, rname) @@ -112,11 +116,15 @@ def main(): markerSetDict = dict() for (flter, rset) in markersets: # generate a unique name for this markerset. it will not be user visible - name = flter.__name__ + hex(hash(flter))[-4:] + "_" + hex(hash(rset))[-4:] - markerSetDict[name] = dict(created=False, raw=[]) + filter_name = flter[0] + filter_function = flter[1] + + name = filter_name + hex(hash(filter_function))[-4:] + "_" + hex(hash(rset))[-4:] + markerSetDict[name] = dict(created=False, raw=[], name=filter_name) for poi in rset._pois['TileEntities']: - if flter(poi): - markerSetDict[name]['raw'].append(poi) + result = filter_function(poi) + if result: + markerSetDict[name]['raw'].append(dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True)) #print markerSetDict with open(os.path.join(destdir, "markersDB.js"), "w") as output: diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index ccff3bf..7e8a572 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -430,7 +430,6 @@ overviewer.views.SignControlView = Backbone.View.extend({ }}); } - iconURL = overviewerConfig.CONST.image.signMarker; //dataRoot['markers'] = []; // for (i in dataRoot) { @@ -442,11 +441,11 @@ overviewer.views.SignControlView = Backbone.View.extend({ 'position': overviewer.util.fromWorldToLatLng(entity.x, entity.y, entity.z, overviewer.mapView.options.currentTileSet), 'map': overviewer.map, - 'title': jQuery.trim(entity.Text1 + "\n" + entity.Text2 + "\n" + entity.Text3 + "\n" + entity.Text4), - 'icon': iconURL, + 'title': jQuery.trim(entity.text), + 'icon': dataRoot[i].icon, 'visible': false }); - if (entity['id'] == 'Sign') { + if (entity.createInfoWindow) { overviewer.util.createMarkerInfoWindow(marker); } jQuery.extend(entity, {markerObj: marker}); diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index 227f4d3..a0ba51e 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -47,8 +47,12 @@ def validateMarkers(filterlist): if type(filterlist) != list: raise ValidationException("Markers must specify a list of filters") for x in filterlist: - if not callable(x): - raise ValidationException("%r must be a function"% x) + if "name" not in x: + raise ValidationException("Must define a name") + if "filterFunction" not in x: + raise ValidationException("Must define a filter function") + if not callable(x['filterFunction']): + raise ValidationException("%r must be a function"% x['filterFunction']) return filterlist def validateOverlays(renderlist): From c583a94d7d7ea24646d355802084bfdf845c5f50 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 14 Apr 2012 23:36:52 -0400 Subject: [PATCH 21/33] Update JonnyJD's pullrequest to work with the new POI codes Also: only parse players in a regionset once ignore corrupt player.dat files --- genPOI.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/genPOI.py b/genPOI.py index 8e358db..b2aa442 100755 --- a/genPOI.py +++ b/genPOI.py @@ -45,6 +45,10 @@ def handleSigns(rset, outputdir, render, rname): def handlePlayers(rset, render, worldpath): if not hasattr(rset, "_pois"): rset._pois = dict(TileEntities=[], Entities=[]) + + # only handle this region set once + if 'Players' in rset._pois: + return dimension = {'overworld': 0, 'nether': -1, 'end': 1, @@ -52,7 +56,11 @@ def handlePlayers(rset, render, worldpath): playerdir = os.path.join(worldpath, "players") rset._pois['Players'] = [] for playerfile in os.listdir(playerdir): - data = nbt.load(os.path.join(playerdir, playerfile))[1] + try: + data = nbt.load(os.path.join(playerdir, playerfile))[1] + except IOError: + logging.warning("Skipping bad player dat file %r", playerfile) + continue playername = playerfile.split(".")[0] if data['Dimension'] == dimension: # Position at last logout @@ -158,7 +166,7 @@ def main(): for poi in rset._pois['Players']: result = filter_function(poi) if result: - markerSetDict[name]['raw'].append(poi) + markerSetDict[name]['raw'].append(dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True)) #print markerSetDict with open(os.path.join(destdir, "markersDB.js"), "w") as output: From 44042c3d75d056a4b763d9730cdd2bfa150f462c Mon Sep 17 00:00:00 2001 From: Thomas Lake Date: Sun, 15 Apr 2012 14:23:48 +0100 Subject: [PATCH 22/33] Update usage information for genPOI Usage text update to include --quiet and reflect whether script is called independently or via the --genpoi flag for overviewer.py --- genPOI.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/genPOI.py b/genPOI.py index 66a8360..274f9e6 100755 --- a/genPOI.py +++ b/genPOI.py @@ -17,6 +17,7 @@ markers.js holds a list of which markerSets are attached to each tileSet import os import logging import json +import sys from optparse import OptionParser from overviewer_core import logger @@ -41,8 +42,13 @@ def handleSigns(rset, outputdir, render, rname): def main(): - helptext = """genPOI - %prog --config=""" + + if os.path.basename(sys.argv[0]) == """genPOI.py""": + helptext = """genPOI.py + %prog --config= [--quiet]""" + else: + helptext = """genPOI + %prog --genpoi --config= [--quiet]""" logger.configure() From 07bdf2aa93a985a5cb979addbf7b3c4a84642c36 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sun, 15 Apr 2012 11:12:16 -0400 Subject: [PATCH 23/33] Handle spaces in filter names --- genPOI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/genPOI.py b/genPOI.py index 0d94fb7..800c8f5 100755 --- a/genPOI.py +++ b/genPOI.py @@ -145,7 +145,7 @@ def main(): d = dict(icon="signpost_icon.png") d.update(f) markersets.add(((d['name'], d['filterFunction']), rset)) - name = f['name']+ hex(hash(f['filterFunction']))[-4:] + "_" + hex(hash(rset))[-4:] + name = f['name'].replace(" ","_") + hex(hash(f['filterFunction']))[-4:] + "_" + hex(hash(rset))[-4:] try: l = markers[rname] l.append(dict(groupName=name, displayName = f['name'], icon=d['icon'])) @@ -163,7 +163,7 @@ def main(): filter_name = flter[0] filter_function = flter[1] - name = filter_name + hex(hash(filter_function))[-4:] + "_" + hex(hash(rset))[-4:] + name = filter_name.replace(" ","_") + hex(hash(filter_function))[-4:] + "_" + hex(hash(rset))[-4:] markerSetDict[name] = dict(created=False, raw=[], name=filter_name) for poi in rset._pois['TileEntities']: result = filter_function(poi) From 27acb575f7b029963130ebff4ec8c54bc3fc5137 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sun, 15 Apr 2012 12:00:44 -0400 Subject: [PATCH 24/33] Allow filter functions to specify icons --- genPOI.py | 10 ++++++++-- overviewer_core/data/js_src/views.js | 13 ++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/genPOI.py b/genPOI.py index 800c8f5..650a2d6 100755 --- a/genPOI.py +++ b/genPOI.py @@ -168,11 +168,17 @@ def main(): for poi in rset._pois['TileEntities']: result = filter_function(poi) if result: - markerSetDict[name]['raw'].append(dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True)) + d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True) + if "icon" in poi: + d.update({"icon": poi['icon']}) + markerSetDict[name]['raw'].append(d) for poi in rset._pois['Players']: result = filter_function(poi) if result: - markerSetDict[name]['raw'].append(dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True)) + d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True) + if "icon" in poi: + d.update({"icon": poi['icon']}) + markerSetDict[name]['raw'].append(d) #print markerSetDict with open(os.path.join(destdir, "markersDB.js"), "w") as output: diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 041d2ba..2fde73d 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -437,22 +437,17 @@ overviewer.views.SignControlView = Backbone.View.extend({ if (!markersDB[groupName].created) { for (j in markersDB[groupName].raw) { var entity = markersDB[groupName].raw[j]; - if (entity['id'] == 'Player') { - iconURL = "http://overviewer.org/avatar/" - + entity['EntityId']; - } else if (entity['id'] == 'PlayerSpawn') { - iconURL = overviewerConfig.CONST.image.bedMarker; - } else if (entity['id'] == 'Sign') { - iconURL = overviewerConfig.CONST.image.signMarker; + if (entity['icon']) { + iconURL = entity['icon']; } else { - iconURL = overviewerConfig.CONST.image.defaultMarker; + iconURL = dataRoot[i].icon; } var marker = new google.maps.Marker({ 'position': overviewer.util.fromWorldToLatLng(entity.x, entity.y, entity.z, overviewer.mapView.options.currentTileSet), 'map': overviewer.map, 'title': jQuery.trim(entity.text), - 'icon': dataRoot[i].icon, + 'icon': iconURL, 'visible': false }); if (entity.createInfoWindow) { From 24f360882dc1bd5ef167e3ff89addc9e537f1bd6 Mon Sep 17 00:00:00 2001 From: Thomas Lake Date: Sun, 15 Apr 2012 18:42:01 +0100 Subject: [PATCH 25/33] Improve support for player POIs on single player worlds --- genPOI.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/genPOI.py b/genPOI.py index 650a2d6..07af4c9 100755 --- a/genPOI.py +++ b/genPOI.py @@ -41,7 +41,7 @@ def handleSigns(rset, outputdir, render, rname): rset._pois['TileEntities'] += data['TileEntities'] rset._pois['Entities'] += data['Entities'] - print "Done." + logging.info("Done.") def handlePlayers(rset, render, worldpath): if not hasattr(rset, "_pois"): @@ -55,14 +55,25 @@ def handlePlayers(rset, render, worldpath): 'end': 1, 'default': 0}[render['dimension']] playerdir = os.path.join(worldpath, "players") + if os.path.isdir(playerdir): + playerfiles = os.listdir(playerdir) + isSinglePlayer = False + else: + playerfiles = [os.path.join(worldpath, "level.dat")] + isSinglePlayer = True + rset._pois['Players'] = [] - for playerfile in os.listdir(playerdir): + for playerfile in playerfiles: try: data = nbt.load(os.path.join(playerdir, playerfile))[1] + if isSinglePlayer: + data = data['Data']['Player'] except IOError: logging.warning("Skipping bad player dat file %r", playerfile) continue playername = playerfile.split(".")[0] + if isSinglePlayer: + playername = 'Player' if data['Dimension'] == dimension: # Position at last logout data['id'] = "Player" From b0dbad4d7029087a409eafdea21a13e7852b29f7 Mon Sep 17 00:00:00 2001 From: Thomas Lake Date: Sun, 15 Apr 2012 19:10:04 +0100 Subject: [PATCH 26/33] Fix tab/spacing issues in previous commit --- genPOI.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/genPOI.py b/genPOI.py index 07af4c9..9b80a60 100755 --- a/genPOI.py +++ b/genPOI.py @@ -57,10 +57,10 @@ def handlePlayers(rset, render, worldpath): playerdir = os.path.join(worldpath, "players") if os.path.isdir(playerdir): playerfiles = os.listdir(playerdir) - isSinglePlayer = False + isSinglePlayer = False else: playerfiles = [os.path.join(worldpath, "level.dat")] - isSinglePlayer = True + isSinglePlayer = True rset._pois['Players'] = [] for playerfile in playerfiles: @@ -72,7 +72,7 @@ def handlePlayers(rset, render, worldpath): logging.warning("Skipping bad player dat file %r", playerfile) continue playername = playerfile.split(".")[0] - if isSinglePlayer: + if isSinglePlayer: playername = 'Player' if data['Dimension'] == dimension: # Position at last logout From 419daa3881817536b9795fc0d65c4ff2c7b55fee Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Tue, 17 Apr 2012 16:15:55 -0700 Subject: [PATCH 27/33] Update Contributors list --- CONTRIBUTORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index fb514d5..f128059 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -58,6 +58,7 @@ feature. * Maciej Malecki * Ryan McCue * Morlok8k + * Richard Pastrick * Ryan Rector * Jason Scheirer * Gregory Short From 27c644d4ea66b09d4ab3f26c3bd21e0db32268fa Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Fri, 27 Apr 2012 09:19:31 -0700 Subject: [PATCH 28/33] Fix centering of the maps to the spawn point on first load --- overviewer_core/data/js_src/views.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 2fde73d..f3f2972 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -7,6 +7,14 @@ overviewer.views.WorldView = Backbone.View.extend({ this.options.overlayMapTypes = []; this.options.mapTypeIds = []; this.options.overlayMapTypeIds = []; + + var curTileSet = this.model.get("tileSets").at(0); + var spawn = curTileSet.get("spawn"); + if (spawn=="false") { + var spawn = [0,64,0]; + } + this.options.lastViewport = [spawn[0],spawn[1],spawn[2],curTileSet.get("defaultZoom")]; + this.model.get("tileSets").each(function(tset, index, list) { // ignore overlays: var ops = { @@ -139,17 +147,16 @@ overviewer.views.GoogleMapView = Backbone.View.extend({ var curWorld = this.model.get("currentWorldView").model; var curTset = curWorld.get("tileSets").at(0); + var spawn = curTset.get("spawn"); + if (spawn==false) { + var spawn = [0,64,0]; + } + var mapcenter = overviewer.util.fromWorldToLatLng( + spawn[0], + spawn[1], + spawn[2], + curTset); - /* - var defaultCenter = overviewer.util.fromWorldToLatLng( - overviewerConfig.map.center[0], - overviewerConfig.map.center[1], - overviewerConfig.map.center[2], - curTset.get("defaultZoom")); - */ - var lat = 0.62939453125;// TODO defaultCenter.lat(); - var lng = 0.38525390625; // TODO defaultCenter.lng(); - var mapcenter = new google.maps.LatLng(lat, lng); this.options.mapTypes=[]; this.options.mapTypeIds=[]; From 2ecb5dc9146fcbe40ff30a0e03ebcad005e6e61e Mon Sep 17 00:00:00 2001 From: Richard Pastrick Date: Fri, 27 Apr 2012 09:22:41 -0700 Subject: [PATCH 29/33] Forgot quotes --- overviewer_core/data/js_src/views.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index f3f2972..3ca20c4 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -10,7 +10,7 @@ overviewer.views.WorldView = Backbone.View.extend({ var curTileSet = this.model.get("tileSets").at(0); var spawn = curTileSet.get("spawn"); - if (spawn=="false") { + if (spawn == "false") { var spawn = [0,64,0]; } this.options.lastViewport = [spawn[0],spawn[1],spawn[2],curTileSet.get("defaultZoom")]; @@ -148,7 +148,7 @@ overviewer.views.GoogleMapView = Backbone.View.extend({ var curTset = curWorld.get("tileSets").at(0); var spawn = curTset.get("spawn"); - if (spawn==false) { + if (spawn == "false") { var spawn = [0,64,0]; } var mapcenter = overviewer.util.fromWorldToLatLng( From c5af1c81fc41e858a4a718e9cad065fd071dc986 Mon Sep 17 00:00:00 2001 From: CounterPillow Date: Sun, 29 Apr 2012 02:26:14 +0200 Subject: [PATCH 30/33] Added a special case for the swamp biome. Addresses issue #708. --- overviewer_core/src/primitives/base.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/overviewer_core/src/primitives/base.c b/overviewer_core/src/primitives/base.c index 5a66a9a..ae93db7 100644 --- a/overviewer_core/src/primitives/base.c +++ b/overviewer_core/src/primitives/base.c @@ -206,6 +206,7 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec }; if (color_table) { + unsigned char biome; int dx, dz; unsigned char tablex, tabley; float temp = 0.0, rain = 0.0; @@ -215,7 +216,7 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec /* average over all neighbors */ for (dx = -1; dx <= 1; dx++) { for (dz = -1; dz <= 1; dz++) { - unsigned char biome = get_data(state, BIOMES, state->x + dx, state->y, state->z + dz); + biome = get_data(state, BIOMES, state->x + dx, state->y, state->z + dz); if (biome >= NUM_BIOMES) { /* note -- biome 255 shows up on map borders. who knows what it is? certainly not I. @@ -257,6 +258,18 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0)); g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1)); b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)); + + /* swamp hack + All values are guessed. They are probably somewhat odd or + completely wrong, but this looks okay for me, and I'm male, + so I can only distinct about 10 different colors anyways. + Blame my Y-Chromosone. */ + if(biome == 6) { + r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0)) * 0.8; + g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1)) / 2.0; + b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)) * 1.0; + } + Py_DECREF(color); } From 9508573783919acc6e51d589fcaaf65364c3cbeb Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Tue, 1 May 2012 20:31:50 -0400 Subject: [PATCH 31/33] Added hint about checking the docs on a bad markers validation --- overviewer_core/settingsValidators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index a0ba51e..0df90d1 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -45,7 +45,7 @@ def checkBadEscape(s): def validateMarkers(filterlist): if type(filterlist) != list: - raise ValidationException("Markers must specify a list of filters") + raise ValidationException("Markers must specify a list of filters. This has recently changed, so check the docs.") for x in filterlist: if "name" not in x: raise ValidationException("Must define a name") From 216b26cbf208a7adbc41252e923999c7f9afa7bd Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Tue, 1 May 2012 20:38:14 -0400 Subject: [PATCH 32/33] hide traceback on config validation failure a fully traceback is needlessly scary. use --verbose to see the traceback. --- overviewer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/overviewer.py b/overviewer.py index 7b1226e..55400ce 100755 --- a/overviewer.py +++ b/overviewer.py @@ -246,8 +246,12 @@ dir but you forgot to put quotes around the directory, since it contains spaces. # Now parse and return the validated config try: config = mw_parser.get_validated_config() - except Exception: - logging.exception("An error was encountered with your configuration. See the info below.") + except Exception as ex: + if options.verbose: + logging.exception("An error was encountered with your configuration. See the info below.") + else: # no need to print scary traceback! just + logging.error("An error was encountered with your configuration.") + logging.error(str(ex)) return 1 From 6331200763255c54d6f9ffad3893e77fed2ae4f0 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Tue, 1 May 2012 21:40:53 -0400 Subject: [PATCH 33/33] More POI docs --- docs/signs.rst | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/signs.rst b/docs/signs.rst index efa0514..415e581 100644 --- a/docs/signs.rst +++ b/docs/signs.rst @@ -32,7 +32,10 @@ If a POI doesn't match, the filter can return None (which is the default if a py functions runs off the end without an explicit 'return'). The single argument will either a TileEntity, or an Entity taken directly from -the chunk file. In this example, this function returns all 4 lines from the sign +the chunk file. It could also be a special entity representing a player's location +or a player's spawn. See below for more details. + +In this example, this function returns all 4 lines from the sign if the entity is a sign. For more information of TileEntities and Entities, see the `Chunk Format `_ page on @@ -49,6 +52,35 @@ Since writing these filters can be a little tedious, a set of predefined filters functions are provided. See the :ref:`predefined_filter_functions` section for details. + +Special POIs +------------ + +There are currently two special types of POIs. They each have a special id: + +PlayerSpawn + Used to indicate the spawn location of a player. The player's name is set + in the ``EntityId`` key, and the location is in the x,y,z keys + +Player + Used to indicate the last known location of a player. The player's name is set + in the ``EntityId`` key, and the location is in the x,y,z keys. + +.. note:: + The player location is taken from level.dat (in the case of a single-player world) + or the player.dat files (in the case of a multi-player server). The locations are + only written to these files when the world is saved, so this won't give you real-time + player location information. + +Here's an example that displays icons for each player:: + + def playerIcons(poi): + if poi['id'] == 'Player': + poi['icon'] = "http://overviewer.org/avatar/%s" % poi['EntityId'] + return "Last known location for %s" % poi['EntityId'] + +Note how each POI can get a different icon by setting ``poi['icon']`` + Render Dictionary Key --------------------- @@ -76,7 +108,9 @@ The following keys are accepted in the marker dictionary: ``icon`` Optional. Specifies the icon to use for POIs in this group. If omitted, it defaults - to a signpost icon. + to a signpost icon. Note that each POI can have different icon by setting the key 'icon' + on the POI itself (this can be done by modifying the POI in the filter function. See the + example above) Generating the POI Markers