0

Merge branch 'master' of git://github.com/overviewer/Minecraft-Overviewer into JSObserver

This commit is contained in:
Thomas Lake
2012-05-25 14:00:16 +01:00
29 changed files with 820 additions and 120 deletions

5
.mailmap Normal file
View File

@@ -0,0 +1,5 @@
Andrew Brown <brownan@gmail.com> <andrew@fry.(none)>
Alex Headley <aheadley@waysaboutstuff.com> <aheadley@nexcess.net>
Alex Headley <aheadley@waysaboutstuff.com> aheadley
Michael Fallows <michael@fallo.ws> redorkulated
Maciej Malecki <maciej.malecki@hotmail.com> Maciej Małecki

View File

@@ -39,28 +39,45 @@ Short-term Contributions
These contributors have made specific changes for a particular bug fix or
feature.
* Albireo <kappa7194@hotmail.it>
* arrai <array.of.intellect@gmail.com>
* asmodai <asmodai@in-nomine.org>
* Mark Barnes <mark.e.barnes@gmail.com>
* Kyle Brantley <kyle@averageurl.com>
* but2002 <barryt_9@hotmail.com>
* Eric Carr <eric@carr.no>
* cbarber <CraigBarber@taryx.com>
* Carter Charbonneau <zcarterc@gmail.com>
* Alex Cline <cline@vivisimo.com>
* Andrew Clunis <andrew@orospakr.ca>
* CounterPillow <spam@tes-cheese.ch>
* Johannes Dewender <github@JonnyJD.net>
* Michael Fallows <michael@fallo.ws>
* Ryan Finnie <ryan@feh.colobox.com>
* Stephen Fluin <stephen@mistuph.com>
* Pierre Guinoiseau <pierre@guinoiseau.eu>
* Lucas Hereld <duckman@piratehook.com>
* Benjamin Herr <ben@0x539.de>
* Ryan Hitchman <hitchmanr@gmail.com>
* Jenny <jennytoo@gmail.com>
* Michael Jensen <emjay1988@gmail.com>
* Sean Kilgore <krystalogik@gmail.com>
* Johan Kiviniemi <devel@johan.kiviniemi.name>
* Philip Kovac <pkovac@cs.uml.edu>
* Thomas Lake <tswsl1989@sucs.org>
* Maciej Malecki <maciej.malecki@hotmail.com>
* Ryan McCue <ryanmccue@cubegames.net>
* Zach McCullough <nosrepa@gmail.com>
* Mike <mike@snowcrash.ca>
* Morlok8k <otis.spankmeyer@gmail.com>
* Richard Pastrick <rpastric@contre.us>
* Ryan Rector <rmrector@gmail.com>
* Jason Scheirer <jason.scheirer@gmail.com>
* Gregory Short <gshort2@gmail.com>
* Sam Steele <sam@sigbox.c99.org>
* stoneLeaf <owi.stoneleaf@gmail.com>
* timwolla <timwolla@mail.develfusion.com>
* TJ09 <TJ09@localhost>
* untergrundbiber <untergrundbiber@github>
* Philippe Villiers <kissifrot@gmail.com>
* Jeffrey Warren <warren@mit.edu>

116
contrib/contributors.py Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/python2
"""Update the contributor list
Alias handling is done by git with .mailmap
New contributors are merged in the short-term list.
Moving them to a "higher" list should be a manual process.
"""
import fileinput
from subprocess import Popen, PIPE
def format_contributor(contributor):
return " * {0} {1}".format(
" ".join(contributor["name"]),
contributor["email"])
def main():
# generate list of contributors
contributors = []
p_git = Popen(["git", "shortlog", "-se"], stdout=PIPE)
for line in p_git.stdout:
contributors.append({
'count': int(line.split("\t")[0].strip()),
'name': line.split("\t")[1].split()[0:-1],
'email': line.split("\t")[1].split()[-1]
})
# cache listed contributors
old_contributors = []
with open("CONTRIBUTORS.rst", "r") as contrib_file:
for line in contrib_file:
if "@" in line:
old_contributors.append({
'name': line.split()[1:-1],
'email': line.split()[-1]
})
old = map(lambda x: (x['name'], x['email']), old_contributors)
old_emails = map(lambda x: x['email'], old_contributors)
old_names = map(lambda x: x['name'], old_contributors)
# check which contributors are new
new_contributors = []
update_mailmap = False
for contributor in contributors:
if (contributor['name'], contributor['email']) in old:
# this exact combination already in the list
pass
elif (contributor['email'] not in old_emails
and contributor['name'] not in old_names):
# name AND email are not in the list
new_contributors.append(contributor)
elif contributor['email'] in old_emails:
# email is listed, but with another name
old_name = filter(lambda x: x['email'] == contributor['email'],
old_contributors)[0]['name']
print "new alias %s for %s %s ?" % (
" ".join(contributor['name']),
" ".join(old_name),
contributor['email'])
update_mailmap = True
elif contributor['name'] in old_names:
# probably a new email for a previous contributor
other_mail = filter(lambda x: x['name'] == contributor['name'],
old_contributors)[0]['email']
print "new email %s for %s %s ?" % (
contributor['email'],
" ".join(contributor['name']),
other_mail)
update_mailmap = True
if update_mailmap:
print "Please update .mailmap"
# sort on the last word of the name
new_contributors = sorted(new_contributors,
key=lambda x: x['name'][-1].lower())
# show new contributors to be merged to the list
if new_contributors:
print "inserting:"
for contributor in new_contributors:
print format_contributor(contributor)
# merge with alphabetical (by last part of name) contributor list
i = 0
short_term_found = False
for line in fileinput.input("CONTRIBUTORS.rst", inplace=1):
if not short_term_found:
print line,
if "Short-term" in line:
short_term_found = True
else:
if i >= len(new_contributors) or "@" not in line:
print line,
else:
listed_name = line.split()[-2].lower()
contributor = new_contributors[i]
# insert all new contributors that fit here
while listed_name > contributor["name"][-1].lower():
print format_contributor(contributor)
i += 1
if i < len(new_contributors):
contributor = new_contributors[i]
else:
break
print line,
# append remaining contributors
with open("CONTRIBUTORS.rst", "a") as contrib_file:
while i < len(new_contributors):
contrib_file.write(format_contributor(new_contributors[i]) + "\n")
i += 1
if __name__ == "__main__":
main()

View File

@@ -70,7 +70,7 @@ A more complicated example
renders["survivalnight"] = {
"world": "survival",
"title": "Survival Daytime",
"title": "Survival Nighttime",
"rendermode": smooth_night,
"dimension": "overworld",
}
@@ -115,6 +115,57 @@ individual renders to apply to just those renders.
See the ``sample_config.py`` file included in the repository for another
example.
A dynamic config file
=====================
It might be handy to dynamically retrieve parameters. For instance, if you
periodically render your last map backup which is located in a timestamped
directory, it is not convenient to edit the config file each time to fit the
new directory name.
Using environment variables, you can easily retrieve a parameter which has
been set by, for instance, your map backup script. In this example, Overviewer
is called from a *bash* script, but it can be done from other shell scripts
and languages.
::
#!/bin/bash
## Add these lines to your bash script
# Setting up an environment variable that child processes will inherit.
# In this example, the map's path is not static and depends on the
# previously set $timestamp var.
MYWORLD_DIR=/path/to/map/backup/$timestamp/YourWorld
export MYWORLD_DIR
# Running the Overviewer
overviewer.py --config=/path/to/yourConfig.py
.. note::
The environment variable will only be local to the process and its child
processes. The Overviewer, when run by the script, will be able to access
the variable since it becomes a child process.
::
## A config file example
# Importing the os python module
import os
# Retrieving the environment variable set up by the bash script
worlds["My world"] = os.environ['MYWORLD_DIR']
renders["normalrender"] = {
"world": "My world",
"title": "Normal Render of My World",
}
outputdir = "/home/username/mcmap"
Config File Specifications
==========================
@@ -389,6 +440,20 @@ values. The valid configuration keys are listed below.
**Default:** ``95``
``optimizeimg``
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
example the "PATH" environment variable or a directory like /usr/bin.
This should be an integer between 0 and 3.
* ``1 - Use pngcrush``
* ``2 - Use advdef``
* ``3 - Use pngcrush and advdef (Not recommended)``
Using this option may significantly increase render time, but will make
the resulting tiles smaller, with lossless image quality.
**Default:** ``0``
``bgcolor``
This is the background color to be displayed behind the map. Its value
should be either a string in the standard HTML color syntax or a 4-tuple in
@@ -396,11 +461,12 @@ values. The valid configuration keys are listed below.
**Default:** ``#1a1a1a``
.. _option_texture_pack:
.. _option_texturepath:
``texturepath``
This is a where a specific texture pack can be found to be used during this render.
It can be either a folder or a directory. Its value should be a string.
It can be either a folder or a zip file containing the texture pack.
Its value should be a string.
.. _crop:
@@ -509,6 +575,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)
``showspawn``
This is a boolean, and defaults to ``True``. If set to ``False``, then the spawn
icon will not be displayed on the rendered map.
@@ -593,6 +671,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.
@@ -672,7 +759,7 @@ are referencing the previously defined list, not one of the built-in
rendermodes.
Built-in Rendermodes
--------------------
====================
The built-in rendermodes are nothing but pre-defined lists of rendermode
primitives for your convenience. Here are their definitions::

View File

@@ -17,7 +17,7 @@ So let's get started!
.. note::
This page is still under construction
This page is continually under construction
.. contents::
@@ -82,7 +82,7 @@ pre-rendered sprite (a small image). The basic idea is to iterate over the
blocks of the world and draw these sprites to the appropriate location on the
map.
These are the high-level tasks The Overviewer must preform in rendering a map:
These are the high-level tasks The Overviewer must perform in rendering a map:
1. Render each block sprite from the textures
2. Scan the chunks of the world and determine which tiles need rendering
@@ -143,7 +143,7 @@ transformations can be chained together simply by multiplying the transformation
matrices together, only one transformation is actually done.
This can be seen in the function
:func:`overviewer_core.textures.transform_image`. It preforms three steps:
:func:`overviewer_core.textures.transform_image`. It performs three steps:
1. The texture is re-sized to 17 by 17 pixels. This is done because the diagonal
of a square with sides 17 is approximately 24, which is the target size for

View File

@@ -246,7 +246,7 @@ If you want or need to provide your own textures, you have several options:
overviewer.exe.
* Specify any terrain.png or texture pack you want with the
:ref:`texture_pack<option_texture_pack>` option.
:ref:`texturepath<option_texturepath>` option.
If you copy your world before you render it
-------------------------------------------

View File

@@ -20,51 +20,97 @@ 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. 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 <http://www.minecraftwiki.net/wiki/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
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
---------------------
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. 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

View File

@@ -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",
@@ -104,9 +106,9 @@ def main():
if options.genpoi:
# remove the "--genpoi" option from sys.argv before running genPI
sys.argv.remove("--genpoi")
sys.path.append(".")
g = __import__("genPOI", {}, {})
g.main()
#sys.path.append(".")
g = __import__("overviewer_core.aux_files", {}, {}, ["genPOI"])
g.genPOI.main()
return 0
if options.help:
parser.print_help()
@@ -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,
@@ -233,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
@@ -244,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
@@ -302,6 +308,20 @@ 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
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.")
@@ -401,7 +421,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","showspawn"])
tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist","showspawn", "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)

View File

@@ -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'

View File

View File

@@ -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
@@ -25,7 +26,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,10 +41,64 @@ def handleSigns(rset, outputdir, render, rname):
rset._pois['TileEntities'] += data['TileEntities']
rset._pois['Entities'] += data['Entities']
logging.info("Done.")
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,
'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 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"
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
%prog --config=<config file>"""
if os.path.basename(sys.argv[0]) == """genPOI.py""":
helptext = """genPOI.py
%prog --config=<config file> [--quiet]"""
else:
helptext = """genPOI
%prog --genpoi --config=<config file> [--quiet]"""
logger.configure()
@@ -97,26 +153,43 @@ 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'].replace(" ","_") + 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)
handlePlayers(rset, render, worldpath)
logging.info("Done scanning regions")
logging.info("Writing out javascript files")
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.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']:
if flter(poi):
markerSetDict[name]['raw'].append(poi)
result = filter_function(poi)
if result:
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:
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:

View File

@@ -67,6 +67,8 @@ overviewer.util = {
signs.registerEvents(signs);
}
var overlayControl = new overviewer.views.OverlayControlView();
var spawnmarker = new overviewer.views.SpawnIconView();
// Update coords on mousemove
@@ -85,6 +87,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) {
@@ -117,8 +122,6 @@ overviewer.util = {
});
var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'});
overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector);
// hook up some events
@@ -129,6 +132,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();
/*

View File

@@ -4,8 +4,19 @@ overviewer.views= {}
overviewer.views.WorldView = Backbone.View.extend({
initialize: function(opts) {
this.options.mapTypes = [];
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 = {
getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), tset.get("imgextension")),
'tileSize': new google.maps.Size(
@@ -20,11 +31,24 @@ overviewer.views.WorldView = Backbone.View.extend({
newMapType.shortname = tset.get("name");
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"));
newMapType._ov_tileSet = tset;
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 {
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; };
});
},
});
@@ -33,6 +57,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');
@@ -40,6 +66,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);
@@ -139,17 +168,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=[];
@@ -208,7 +236,7 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
var gmapCurrent = overviewer.map.getMapTypeId();
for (id in currentWorldView.options.mapTypeIds) {
if (currentWorldView.options.mapTypeIds[id] == gmapCurrent) {
this.options.currentTileSet = currentWorldView.model.get("tileSets").at(id);
this.options.currentTileSet = currentWorldView.options.mapTypes[id]._ov_tileSet;
}
}
@@ -220,6 +248,114 @@ 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();
});
var currentTileSetPath = overviewer.mapView.options.currentTileSet.get('path');
for (i in mapTypes) {
var mt = mapTypes[i];
// 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);
}
}
}
});
}
},
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 + '<br/>';
itemDiv.appendChild(textNode);
}
});
/**
@@ -285,7 +421,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; }
@@ -322,7 +458,6 @@ overviewer.views.SignControlView = Backbone.View.extend({
}});
}
iconURL = overviewerConfig.CONST.image.signMarker;
//dataRoot['markers'] = [];
//
for (i in dataRoot) {
@@ -330,15 +465,20 @@ overviewer.views.SignControlView = Backbone.View.extend({
if (!markersDB[groupName].created) {
for (j in markersDB[groupName].raw) {
var entity = markersDB[groupName].raw[j];
if (entity['icon']) {
iconURL = entity['icon'];
} else {
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.Text1 + "\n" + entity.Text2 + "\n" + entity.Text3 + "\n" + entity.Text4),
'title': jQuery.trim(entity.text),
'icon': iconURL,
'visible': false
});
if (entity['id'] == 'Sign') {
if (entity.createInfoWindow) {
overviewer.util.createMarkerInfoWindow(marker);
}
jQuery.extend(entity, {markerObj: marker});

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

View File

@@ -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%;

View File

@@ -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.
@@ -267,15 +267,17 @@ def configure(loglevel=logging.INFO, verbose=False):
logger = logging.getLogger()
outstream = sys.stderr
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 = WindowsOutputStream(outstream)
formatter = ANSIColorFormatter(verbose)
elif sys.stderr.isatty():
elif outstream.isatty():
# terminal logging with ANSI color
formatter = ANSIColorFormatter(verbose)

View File

@@ -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()]

View File

@@ -75,10 +75,11 @@ renders = Setting(required=True, default=util.OrderedDict(),
"nomarkers": Setting(required=False, validator=validateBool, default=None),
"texturepath": Setting(required=False, validator=validateTexturePath, default=None),
"renderchecks": Setting(required=False, validator=validateInt, default=None),
"rerenderprob": Setting(required=True, validator=validateFloat, default=0),
"rerenderprob": Setting(required=True, validator=validateRerenderprob, default=0),
"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=[]),
"showspawn": Setting(required=False, validator=validateBool, default=True),
# Remove this eventually (once people update their configs)
@@ -98,9 +99,10 @@ 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():
# 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()
obs = ProgressBarObserver(fd=sys.stdout)
observer = Setting(required=True, validator=validateObserver, default=obs)

View File

@@ -45,12 +45,26 @@ 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 not callable(x):
raise ValidationException("%r must be a function"% x)
if type(x) != dict:
raise ValidationException("Markers must specify a list of dictionaries. This has recently changed, so check the docs.")
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):
if type(renderlist) != list:
raise ValidationException("Overlay must specify a list of renders")
for x in renderlist:
if validateStr(x) == '':
raise ValidationException("%r must be a string"% x)
return renderlist
def validateWorldPath(worldpath):
_, worldpath = checkBadEscape(worldpath)
abs_path = os.path.abspath(os.path.expanduser(worldpath))
@@ -99,10 +113,10 @@ def validateNorthDirection(direction):
raise ValidationException("%r is not a valid north direction" % direction)
return intdir
def validateStochastic(s):
def validateRerenderprob(s):
val = float(s)
if val < 0 or val > 1:
raise ValidationException("%r is not a valid stochastic value. Should be between 0.0 and 1.0" % s)
if val < 0 or val >= 1:
raise ValidationException("%r is not a valid rerender probability value. Should be between 0.0 and 1.0." % s)
return val
def validateImgFormat(fmt):

View File

@@ -24,11 +24,6 @@
#include "overviewer.h"
/* like (a * b + 127) / 255), but much faster on most platforms
from PIL's _imaging.c */
#define MULDIV255(a, b, tmp) \
(tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
typedef struct {
PyObject_HEAD
Imaging image;

View File

@@ -26,13 +26,18 @@
// 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 33
/* Python PIL, and numpy headers */
#include <Python.h>
#include <Imaging.h>
#include <numpy/arrayobject.h>
/* like (a * b + 127) / 255), but much faster on most platforms
from PIL's _imaging.c */
#define MULDIV255(a, b, tmp) \
(tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
/* macro for getting a value out of various numpy arrays the 3D arrays have
interesting, swizzled coordinates because minecraft (anvil) stores blocks
in y/z/x order for 3D, z/x order for 2D */

View File

@@ -29,46 +29,53 @@ typedef struct {
typedef struct {
const char* name;
float temperature;
float rainfall;
unsigned int r, g, b;
} Biome;
/* each entry in this table is yanked *directly* out of the minecraft source
* temp/rainfall are taken from what MCP calls setTemperatureRainfall
*
* Some biomes, like Swamp, do a bit of post-processing by multiplying on a
* hard-coded color. The RGB tuple used follows the temp/rainfall.
* 255, 255, 255 is white, which means do nothing
*
* keep in mind the x/y coordinate in the color tables is found *after*
* multiplying rainfall and temperature for the second coordinate, *and* the
* origin is in the lower-right. <3 biomes.
*/
static Biome biome_table[] = {
/* 0 */
{"Ocean", 0.5, 0.5},
{"Plains", 0.8, 0.4},
{"Desert", 2.0, 0.0},
{"Extreme Hills", 0.2, 0.3},
{"Forest", 0.7, 0.8},
{"Ocean", 0.5, 0.5, 255, 255, 255},
{"Plains", 0.8, 0.4, 255, 255, 255},
{"Desert", 2.0, 0.0, 255, 255, 255},
{"Extreme Hills", 0.2, 0.3, 255, 255, 255},
{"Forest", 0.7, 0.8, 255, 255, 255},
/* 5 */
{"Taiga", 0.05, 0.8},
{"Swampland", 0.8, 0.9},
{"River", 0.5, 0.5},
{"Hell", 2.0, 0.0},
{"Sky", 0.5, 0.5},
{"Taiga", 0.05, 0.8, 255, 255, 255},
{"Swampland", 0.8, 0.9, 205, 128, 255},
{"River", 0.5, 0.5, 255, 255, 255},
{"Hell", 2.0, 0.0, 255, 255, 255},
{"Sky", 0.5, 0.5, 255, 255, 255},
/* 10 */
{"FrozenOcean", 0.0, 0.5},
{"FrozenRiver", 0.0, 0.5},
{"Ice Plains", 0.0, 0.5},
{"Ice Mountains", 0.0, 0.5},
{"MushroomIsland", 0.9, 1.0},
{"FrozenOcean", 0.0, 0.5, 255, 255, 255},
{"FrozenRiver", 0.0, 0.5, 255, 255, 255},
{"Ice Plains", 0.0, 0.5, 255, 255, 255},
{"Ice Mountains", 0.0, 0.5, 255, 255, 255},
{"MushroomIsland", 0.9, 1.0, 255, 255, 255},
/* 15 */
{"MushroomIslandShore", 0.9, 1.0},
{"Beach", 0.8, 0.4},
{"DesertHills", 2.0, 0.0},
{"ForestHills", 0.7, 0.8},
{"TaigaHills", 0.05, 0.8},
{"MushroomIslandShore", 0.9, 1.0, 255, 255, 255},
{"Beach", 0.8, 0.4, 255, 255, 255},
{"DesertHills", 2.0, 0.0, 255, 255, 255},
{"ForestHills", 0.7, 0.8, 255, 255, 255},
{"TaigaHills", 0.05, 0.8, 255, 255, 255},
/* 20 */
{"Extreme Hills Edge", 0.2, 0.3},
{"Jungle", 2.0, 0.45}, /* <-- GUESS, but a good one */
{"Jungle Mountains", 2.0, 0.45}, /* <-- also a guess */
{"Extreme Hills Edge", 0.2, 0.3, 255, 255, 255},
{"Jungle", 2.0, 0.45, 255, 255, 255}, /* <-- GUESS, but a good one */
{"Jungle Mountains", 2.0, 0.45, 255, 255, 255}, /* <-- also a guess */
};
#define NUM_BIOMES (sizeof(biome_table) / sizeof(Biome))
@@ -206,16 +213,19 @@ 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;
unsigned int multr = 0, multg = 0, multb = 0;
int tmp;
PyObject *color = NULL;
if (self->use_biomes) {
/* 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.
@@ -225,14 +235,24 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
temp += biome_table[biome].temperature;
rain += biome_table[biome].rainfall;
multr += biome_table[biome].r;
multg += biome_table[biome].g;
multb += biome_table[biome].b;
}
}
temp /= 9.0;
rain /= 9.0;
multr /= 9;
multg /= 9;
multb /= 9;
} else {
/* don't use biomes, just use the default */
temp = biome_table[DEFAULT_BIOME].temperature;
rain = biome_table[DEFAULT_BIOME].rainfall;
multr = biome_table[DEFAULT_BIOME].r;
multg = biome_table[DEFAULT_BIOME].g;
multb = biome_table[DEFAULT_BIOME].b;
}
/* second coordinate is actually scaled to fit inside the triangle
@@ -258,6 +278,11 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1));
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color);
/* do the after-coloration */
r = MULDIV255(r, multr, tmp);
g = MULDIV255(g, multg, tmp);
b = MULDIV255(b, multb, tmp);
}
/* final coloration */

View File

@@ -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);
}

View File

@@ -0,0 +1,117 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
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 (block == self->rules[i].blockid) {
unsigned char data;
if (!(self->rules[i].has_data))
return 1;
data = get_data(state, DATA, x, y, z);
if (data == self->rules[i].data)
return 1;
}
}
return 0;
}
RenderPrimitiveInterface primitive_hide = {
"hide",
sizeof(RenderPrimitiveHide),
hide_start,
hide_finish,
NULL,
hide_hidden,
NULL,
};

View File

@@ -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;

View File

@@ -213,7 +213,7 @@ class Textures(object):
if verbose: logging.info("Found %s in '%s'", filename, path)
return open(path, mode)
raise IOError("Could not find the file `{0}'. Try specifying the 'texturepath' option in your config file. Set it to the directory where I can find {0}.".format(filename))
raise IOError("Could not find the file `{0}'. Try specifying the 'texturepath' option in your config file. Set it to the directory where I can find {0}. Also see <http://docs.overviewer.org/en/latest/running/#installing-the-textures>".format(filename))
def load_image(self, filename):
"""Returns an image object"""

View File

@@ -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 = not any(isinstance(x, rendermodes.Base) for x in self.options.get("rendermode"))
d = dict(name = self.options.get('title'),
zoomLevels = self.treedepth,
minZoom = 0,
@@ -519,7 +522,11 @@ 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 isOverlay:
d.update({"tilesets": self.options.get("overlay")})
if (self.regionset.get_type() == "overworld" and self.options.get("showspawn", True)):
d.update({"spawn": self.options.get("spawn")})
else:
@@ -580,7 +587,6 @@ class TileSet(object):
self.xradius = xradius
self.yradius = yradius
def _rearrange_tiles(self):
"""If the target size of the tree is not the same as the existing size
on disk, do some re-arranging

View File

@@ -204,6 +204,7 @@ class World(object):
if section['Y'] == targetSection:
blockArray = section['Blocks']
return blockArray[inChunkX, inChunkZ, y % 16]
return 0
@@ -483,6 +484,8 @@ class RegionSet(object):
p = f.split(".")
x = int(p[1])
y = int(p[2])
if abs(x) > 500000 or abs(y) > 500000:
logging.warning("Holy shit what is up with region file %s !?" % f)
yield (x, y, path)
class RegionSetWrapper(object):

View File

@@ -108,13 +108,13 @@ if py2exe is not None:
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/web_assets', 'web_assets')
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/js_src', 'js_src')
setup_kwargs['data_files'] += recursive_data_files('contrib', 'contrib')
setup_kwargs['data_files'] += [('', ['genPOI.py'])]
setup_kwargs['zipfile'] = None
if platform.system() == 'Windows' and '64bit' in platform.architecture():
b = 3
else:
b = 1
setup_kwargs['options']['py2exe'] = {'bundle_files' : b, 'excludes': 'Tkinter', 'includes':['fileinput', 'overviewer_core.items']}
setup_kwargs['options']['py2exe'] = {'bundle_files' : b, 'excludes': 'Tkinter', 'includes':
['fileinput', 'overviewer_core.items', 'overviewer_core.aux_files.genPOI']}
#
# py2app options
@@ -129,7 +129,7 @@ if py2app is not None:
# script, package, and data
#
setup_kwargs['packages'] = ['overviewer_core']
setup_kwargs['packages'] = ['overviewer_core', 'overviewer_core/aux_files']
setup_kwargs['scripts'] = ['overviewer.py']
setup_kwargs['package_data'] = {'overviewer_core': recursive_package_data('data/textures') + recursive_package_data('data/web_assets') + recursive_package_data('data/js_src')}