0

Merge remote branch 'origin/master' into JSObserver

This commit is contained in:
Andrew Chin
2012-06-10 14:59:17 -04:00
36 changed files with 1179 additions and 151 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,46 @@ 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>
* Adam Novak <interfect@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",
}
@@ -461,11 +461,19 @@ values. The valid configuration keys are listed below.
**Default:** ``#1a1a1a``
``base``
Allows you to specify a remote location for the tile folder, useful if you
rsync your map's images to a remote server. Leave a trailing slash and point
to the location that contains the tile folders for each render, not the
tiles folder itself. For example, if the tile images start at
http://domain.com/map/world_day/ you want to set this to http://domain.com/map/
.. _option_texture_pack:
``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:
@@ -574,6 +582,14 @@ 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 other renders.
**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.
@@ -629,6 +645,13 @@ HeightFading
Draws a colored overlay on the blocks that fades them out according to their
height.
**Options**
sealevel
sealevel of the word you're rendering. Note that the default,
128, is usually *incorrect* for most worlds. You should
probably set this to 64. Default: 128
Depth
Only renders blocks between the specified min and max heights.
@@ -640,6 +663,17 @@ Depth
max
highest level of blocks to render. Default: 255
Exposed
Only renders blocks that are exposed (adjacent to a transparent block).
**Options**
mode
when set to 1, inverts the render mode, only drawing unexposed blocks. Default: 0
NoFluids
Don't render fluid blocks (water, lava).
EdgeLines
Draw edge lines on the back side of blocks, to help distinguish them from
the background.
@@ -658,6 +692,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.
@@ -687,16 +730,16 @@ ClearBase
Forces the background to be transparent. Use this in place of Base
for rendering pure overlays.
.. warning::
Overlays are currently not functional in this branch of code. We are
working on them. Please inquire in :ref:`IRC<help>` for more information.
SpawnOverlay
Color the map red in areas where monsters can spawn. Either use
this on top of other modes, or on top of ClearBase to create a
pure overlay.
SlimeOverlay
Color the map green in chunks where slimes can spawn. Either use
this on top of other modes, or on top of ClearBase to create a
pure overlay.
MineralOverlay
Color the map according to what minerals can be found
underneath. Either use this on top of other modes, or on top of

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,100 @@ 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", createInfoWindow=False)]
}
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)
``createInfoWindow``
Optional. Specifies whether or not the icon displays an info window on click. Defaults to True
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,9 +421,9 @@ 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","base"])
tileSetOpts.update({"spawn": w.find_true_spawn()}) # TODO find a better way to do this
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
tset = tileset.TileSet(w, rset, assetMrg, tex, tileSetOpts, tileset_dir)
tilesets.append(tset)
# Do tileset preprocessing here, before we start dispatching jobs

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
@@ -26,6 +27,7 @@ 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,47 @@ 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", createInfoWindow=True)
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'], createInfoWindow=d['createInfoWindow']))
except KeyError:
markers[rname] = [dict(groupName=name, displayName=f.__doc__),]
markers[rname] = [dict(groupName=name, displayName=f['name'], icon=d['icon'], createInfoWindow=d['createInfoWindow']),]
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)
if "icon" in poi:
d.update({"icon": poi['icon']})
if "createInfoWindow" in poi:
d.update({"createInfoWindow": poi['createInfoWindow']})
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)
if "icon" in poi:
d.update({"icon": poi['icon']})
if "createInfoWindow" in poi:
d.update({"createInfoWindow": poi['createInfoWindow']})
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();
newMapType._ov_tileSet = tset;
this.options.mapTypes.push(newMapType);
this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name"));
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,16 +465,25 @@ 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'] == true) {
overviewer.util.createMarkerInfoWindow(marker);
} else {
if(dataRoot[i].createInfoWindow == true) {
overviewer.util.createMarkerInfoWindow(marker);
}
}
jQuery.extend(entity, {markerObj: marker});
}
@@ -368,13 +512,13 @@ overviewer.views.SignControlView = Backbone.View.extend({
var textNode = document.createElement('text');
if(item.icon) {
textNode.innerHTML = '<img width="15" height="15" src="' +
item.icon + '">' + item.label + '<br/>';
item.icon + '">' + item.label + '&nbsp;<br/>';
} else {
textNode.innerHTML = item.label + '<br/>';
textNode.innerHTML = item.label + '&nbsp;<br/>';
}
itemDiv.appendChild(textNode);
itemDiv.style.whiteSpace = "nowrap";
},
});

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

@@ -52,6 +52,10 @@ class Nether(RenderPrimitive):
class HeightFading(RenderPrimitive):
name = "height-fading"
options = {
# 128 is *WRONG*, it should be 64. but we're grandfathered in for now
"sealevel": ("target sea level", 128),
}
black_color = Image.new("RGB", (24,24), (0,0,0))
white_color = Image.new("RGB", (24,24), (255,255,255))
@@ -63,6 +67,15 @@ class Depth(RenderPrimitive):
"max": ("highest level of blocks to render", 255),
}
class Exposed(RenderPrimitive):
name = "exposed"
options = {
"mode": ("0 = exposed blocks only, 1 = unexposed blocks only", 0),
}
class NoFluids(RenderPrimitive):
name = "no-fluids"
class EdgeLines(RenderPrimitive):
name = "edge-lines"
options = {
@@ -182,12 +195,21 @@ class Overlay(RenderPrimitive):
class SpawnOverlay(Overlay):
name = "overlay-spawn"
class SlimeOverlay(Overlay):
name = "overlay-slime"
class MineralOverlay(Overlay):
name = "overlay-mineral"
options = {
'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,11 +75,13 @@ 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),
"base": Setting(required=False, validator=validateStr, default=""),
# Remove this eventually (once people update their configs)
"worldname": Setting(required=False, default=None,
@@ -98,9 +100,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

@@ -17,6 +17,12 @@ class Setting(object):
self.validator = validator
self.default = default
def expand_path(p):
p = os.path.expanduser(p)
p = os.path.expandvars(p)
p = os.path.abspath(p)
return p
def checkBadEscape(s):
fixed = False
fixed_string = s
@@ -45,15 +51,29 @@ 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))
abs_path = expand_path(worldpath)
if not os.path.exists(os.path.join(abs_path, "level.dat")):
raise ValidationException("No level.dat file in '%s'. Are you sure you have the right path?" % (abs_path,))
return abs_path
@@ -99,10 +119,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):
@@ -144,7 +164,7 @@ def validateOptImg(opt):
def validateTexturePath(path):
# Expand user dir in directories strings
path = os.path.expanduser(path)
path = expand_path(path)
# TODO assert this path exists?
return path
@@ -170,7 +190,7 @@ def validateOutputDir(d):
_, d = checkBadEscape(d)
if not d.strip():
raise ValidationException("You must specify a valid output directory")
return os.path.abspath(d)
return expand_path(d)
def validateCrop(value):
if len(value) != 4:

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

@@ -432,7 +432,6 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) {
PyObject*
chunk_render(PyObject *self, PyObject *args) {
RenderState state;
PyObject *regionset;
PyObject *modeobj;
PyObject *blockmap;
@@ -453,7 +452,7 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *t = NULL;
if (!PyArg_ParseTuple(args, "OiiiOiiOO", &state.regionset, &state.chunkx, &state.chunky, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures))
if (!PyArg_ParseTuple(args, "OOiiiOiiOO", &state.world, &state.regionset, &state.chunkx, &state.chunky, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures))
return NULL;
/* set up the render mode */

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 37
/* 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 */
@@ -83,6 +88,7 @@ typedef struct {
} ChunkData;
typedef struct {
/* the regionset object, and chunk coords */
PyObject *world;
PyObject *regionset;
int chunkx, chunky, chunkz;

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,23 +38,24 @@ 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
if ((state->block == 44 || state->block == 126) && ((state->block_data & 0x8) == 0 )) // half-steps BUT no upsidown half-steps
increment=6;
else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off)
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,109 @@
/*
* 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"
typedef struct {
unsigned int mode; /* 0 = exposed only, 1 = unexposed only */
} PrimitiveExposed;
static int
exposed_start(void *data, RenderState *state, PyObject *support) {
PrimitiveExposed *self = (PrimitiveExposed *)data;
if (!render_mode_parse_option(support, "mode", "I", &(self->mode)))
return 1;
return 0;
}
static int
exposed_hidden(void *data, RenderState *state, int x, int y, int z) {
PrimitiveExposed *self = (PrimitiveExposed *)data;
/* Unset these flags if seeming exposure from any of these directions would
* be due to not having data there.
*/
int validMinusX = 1;
int validPlusX = 1;
int validMinusY = 1;
int validPlusY = 1;
int validMinusZ = 1;
int validPlusZ = 1;
/* special handling for section boundaries */
/* If the neighboring section has no block data, ignore exposure from that
* direction
*/
if (x == 0 && (!(state->chunks[0][1].loaded) || state->chunks[0][1].sections[state->chunky].blocks == NULL)) {
/* No data in -x direction */
validMinusX = 0;
}
if (x == 15 && (!(state->chunks[2][1].loaded) || state->chunks[2][1].sections[state->chunky].blocks == NULL)) {
/* No data in +x direction */
validPlusX = 0;
}
if (y == 0 && (state->chunky - 1 < 0 || state->chunks[1][1].sections[state->chunky - 1].blocks == NULL)) {
/* No data in -y direction */
validMinusY = 0;
}
if (y == 15 && (state->chunky + 1 >= SECTIONS_PER_CHUNK || state->chunks[1][1].sections[state->chunky + 1].blocks == NULL)) {
/* No data in +y direction */
validPlusY = 0;
}
if (z == 0 && (!(state->chunks[1][0].loaded) || state->chunks[1][0].sections[state->chunky].blocks == NULL)) {
/* No data in -z direction */
validMinusZ = 0;
}
if (z == 15 && (!(state->chunks[1][2].loaded) || state->chunks[1][2].sections[state->chunky].blocks == NULL)) {
/* No data in +z direction */
validPlusZ = 0;
}
/* If any of the 6 blocks adjacent to us are transparent, we're exposed */
if( (validMinusX && is_transparent(get_data(state, BLOCKS, x-1, y, z))) ||
(validPlusX && is_transparent(get_data(state, BLOCKS, x+1, y, z))) ||
(validMinusY && is_transparent(get_data(state, BLOCKS, x, y-1, z))) ||
(validPlusY && is_transparent(get_data(state, BLOCKS, x, y+1, z))) ||
(validMinusZ && is_transparent(get_data(state, BLOCKS, x, y, z-1))) ||
(validPlusZ && is_transparent(get_data(state, BLOCKS, x, y, z+1 ))) ) {
/* Block is exposed */
/* Returns 1 and hides us if we're rendering unexposed blocks, 0 and
* shows us if we're rendering exposed blocks
*/
return self->mode;
}
/* We have no valid evidence that the block is exposed */
return !(self->mode); /* Hide in normal mode, reveal in inverted mode */
}
RenderPrimitiveInterface primitive_exposed = {
"exposed", sizeof(PrimitiveExposed),
exposed_start,
NULL,
NULL,
exposed_hidden,
NULL,
};

View File

@@ -20,12 +20,16 @@
typedef struct {
PyObject *black_color;
PyObject *white_color;
unsigned int sealevel;
} PrimitiveHeightFading;
static int
height_fading_start(void *data, RenderState *state, PyObject *support) {
PrimitiveHeightFading *self = (PrimitiveHeightFading *)data;
if (!render_mode_parse_option(support, "sealevel", "I", &(self->sealevel)))
return 1;
self->black_color = PyObject_GetAttrString(support, "black_color");
self->white_color = PyObject_GetAttrString(support, "white_color");
@@ -50,7 +54,7 @@ height_fading_draw(void *data, RenderState *state, PyObject *src, PyObject *mask
PyObject *height_color = self->white_color;
/* current formula requires y to be between 0 and 127, so scale it */
y = (y * 128) / (16 * SECTIONS_PER_CHUNK);
y = (y * 128) / (2 * self->sealevel);
/* negative alpha => darkness, positive => light */
alpha = (1.0 / (1 + expf((70 - y) / 11.0))) * 0.6 - 0.55;

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

@@ -202,7 +202,7 @@ lighting_is_face_occluded(RenderState *state, int skip_sides, int x, int y, int
/* this face isn't visible, so don't draw anything */
return 1;
}
} else if (skip_sides) {
} else if (!skip_sides) {
unsigned short block = get_data(state, BLOCKS, x, y, z);
if (!is_transparent(block)) {
/* the same thing but for adjacent chunks, this solves an
@@ -242,8 +242,8 @@ lighting_start(void *data, RenderState *state, PyObject *support) {
RenderPrimitiveLighting* self;
self = (RenderPrimitiveLighting *)data;
/* skip sides by default */
self->skip_sides = 1;
/* don't skip sides by default */
self->skip_sides = 0;
if (!render_mode_parse_option(support, "strength", "f", &(self->strength)))
return 1;

View File

@@ -0,0 +1,37 @@
/*
* 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"
static int
no_fluids_start(void *data, RenderState *state, PyObject *support) {
return 0;
}
static int
no_fluids_hidden(void *data, RenderState *state, int x, int y, int z) {
return block_has_property(state->block, FLUID);
}
RenderPrimitiveInterface primitive_no_fluids = {
"no-fluids", 0,
no_fluids_start,
NULL,
NULL,
no_fluids_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

@@ -0,0 +1,126 @@
/*
* 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 "overlay.h"
#include <math.h>
typedef struct {
/* inherits from overlay */
RenderPrimitiveOverlay parent;
long seed;
} RenderPrimitiveSlime;
/*
* random_* are a re-implementation of java's Random() class
* since Minecraft's slime algorithm depends on it
* http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html
*/
static void random_set_seed(long *seed, long new_seed) {
*seed = (new_seed ^ 0x5deece66dL) & ((1L << 48) - 1);
}
static int random_next(long *seed, int bits) {
*seed = (*seed * 0x5deece66dL + 0xbL) & ((1L << 48) - 1);
return (int)(*seed >> (48 - bits));
}
static int random_next_int(long *seed, int n) {
int bits, val;
if (n <= 0) {
/* invalid */
return 0;
}
if ((n & -n) == n) {
/* n is a power of two */
return (int)((n * (long)random_next(seed, 31)) >> 31);
}
do {
bits = random_next(seed, 31);
val = bits % n;
} while (bits - val + (n - 1) < 0);
return val;
}
static int is_slime(long map_seed, long chunkx, long chunkz) {
/* lots of magic numbers, but they're all correct! I swear! */
long seed;
random_set_seed(&seed, map_seed + (chunkx * chunkx * 0x4c1906L) + (chunkx * 0x5ac0dbL) + (chunkz * chunkz * 0x4307a7L) + (chunkz * 0x5f24fL) ^ 0x3ad8025fL);
return (random_next_int(&seed, 10) == 0);
}
static void get_color(void *data, RenderState *state,
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
RenderPrimitiveSlime *self = (RenderPrimitiveSlime *)data;
/* set a nice, pretty green color */
*r = 40;
*g = 230;
*b = 40;
/* default to no overlay, until told otherwise */
*a = 0;
if (is_slime(self->seed, state->chunkx, state->chunkz)) {
/* slimes can spawn! */
*a = 240;
}
}
static int
overlay_slime_start(void *data, RenderState *state, PyObject *support) {
RenderPrimitiveSlime *self;
PyObject *pyseed;
/* first, chain up */
int ret = primitive_overlay.start(data, state, support);
if (ret != 0)
return ret;
/* now do custom initializations */
self = (RenderPrimitiveSlime *)data;
self->parent.get_color = get_color;
pyseed = PyObject_GetAttrString(state->world, "seed");
if (!pyseed)
return 1;
self->seed = PyInt_AsLong(pyseed);
Py_DECREF(pyseed);
if (PyErr_Occurred())
return 1;
return 0;
}
static void
overlay_slime_finish(void *data, RenderState *state) {
/* chain up */
primitive_overlay.finish(data, state);
}
RenderPrimitiveInterface primitive_overlay_slime = {
"overlay-slime",
sizeof(RenderPrimitiveSlime),
overlay_slime_start,
overlay_slime_finish,
NULL,
NULL,
overlay_draw,
};

View File

@@ -82,8 +82,8 @@ overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyOb
/* do the overlay */
if (a > 0) {
alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
alpha_over_full(state->img, self->white_color, self->facemask_top, a/255.f, state->imgx, state->imgy + increment, 0, 0);
tint_with_mask(state->img, r, g, b, 255, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
}
}

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"""
@@ -1336,6 +1336,8 @@ block(blockid=41, top_index=23)
block(blockid=42, top_index=22)
# double slabs and slabs
# these wooden slabs are unobtainable without cheating, they are still
# here because lots of pre-1.3 worlds use this blocks
@material(blockid=[43, 44], data=range(16), transparent=(44,), solid=True)
def slabs(self, blockid, data):
texture = data & 7
@@ -3169,3 +3171,52 @@ block(blockid=123, top_index=211)
# active redstone lamp
block(blockid=124, top_index=212)
# wooden double and normal slabs
# these are the new wooden slabs, blockids 43 44 still have wooden
# slabs, but those are unobtainable without cheating
@material(blockid=[125, 126], data=range(16), transparent=(44,), solid=True)
def slabs(self, blockid, data):
texture = data & 7
if texture== 0: # oak
top = side = self.terrain_images[4]
elif texture== 1: # spruce
top = side = self.terrain_images[198]
elif texture== 2: # birch
top = side = self.terrain_images[214]
elif texture== 3: # jungle
top = side = self.terrain_images[199]
else:
return None
if blockid == 125: # double slab
return self.build_block(top, side)
# cut the side texture in half
mask = side.crop((0,8,16,16))
side = Image.new(side.mode, side.size, self.bgcolor)
alpha_over(side, mask,(0,0,16,8), mask)
# plain slab
top = self.transform_image_top(top)
side = self.transform_image_side(side)
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
sidealpha = side.split()[3]
side = ImageEnhance.Brightness(side).enhance(0.9)
side.putalpha(sidealpha)
othersidealpha = otherside.split()[3]
otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
otherside.putalpha(othersidealpha)
# upside down slab
delta = 0
if data & 8 == 8:
delta = 6
img = Image.new("RGBA", (24,24), self.bgcolor)
alpha_over(img, side, (0,12 - delta), side)
alpha_over(img, otherside, (12,12 - delta), otherside)
alpha_over(img, top, (0,6 - delta), top)
return img

View File

@@ -17,6 +17,7 @@ import itertools
import logging
import os
import os.path
import sys
import shutil
import random
import functools
@@ -32,6 +33,7 @@ from .util import roundrobin
from . import nbt
from .files import FileReplacer
from .optimizeimages import optimize_image
import rendermodes
import c_overviewer
"""
@@ -163,13 +165,15 @@ class TileSet(object):
"""
def __init__(self, regionsetobj, assetmanagerobj, texturesobj, options, outputdir):
def __init__(self, worldobj, regionsetobj, assetmanagerobj, texturesobj, options, outputdir):
"""Construct a new TileSet object with the given configuration options
dictionary.
options is a dictionary of configuration parameters (strings mapping to
values) that are interpreted by the rendering engine.
worldobj is the World object that regionsetobj is from.
regionsetobj is the RegionSet object that is used to render the tiles.
assetmanagerobj is the AssetManager object that represents the
@@ -268,6 +272,7 @@ class TileSet(object):
"""
self.options = options
self.world = worldobj
self.regionset = regionsetobj
self.am = assetmanagerobj
self.textures = texturesobj
@@ -355,7 +360,7 @@ class TileSet(object):
# Only pickle the initial state. Don't pickle anything resulting from the
# do_preprocessing step
def __getstate__(self):
return self.regionset, self.am, self.textures, self.options, self.outputdir
return self.world, self.regionset, self.am, self.textures, self.options, self.outputdir
def __setstate__(self, state):
self.__init__(*state)
@@ -507,19 +512,25 @@ 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,
defaultZoom = 1,
maxZoom = self.treedepth,
path = self.options.get('name'),
base = '',
base = self.options.get('base'),
bgcolor = bgcolorformat(self.options.get('bgcolor')),
world = self.options.get('worldname_orig') +
(" - " + 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 +591,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
@@ -940,7 +950,7 @@ class TileSet(object):
# draw the chunk!
try:
c_overviewer.render_loop(self.regionset, chunkx, chunky,
c_overviewer.render_loop(self.world, self.regionset, chunkx, chunky,
chunkz, tileimg, xpos, ypos,
self.options['rendermode'], self.textures)
except nbt.CorruptionError:
@@ -948,8 +958,9 @@ class TileSet(object):
# get_chunk()
logging.debug("Skipping the render of corrupt chunk at %s,%s and moving on.", chunkx, chunkz)
except Exception, e:
logging.warning("Could not render chunk %s,%s for some reason. I'm going to ignore this and continue", chunkx, chunkz)
logging.debug("Full error was:", exc_info=1)
logging.error("Could not render chunk %s,%s for some reason. This is likely a render primitive option error.", chunkx, chunkz)
logging.error("Full error was:", exc_info=1)
sys.exit(1)
## Semi-handy routine for debugging the drawing routine
## Draw the outline of the top of the chunk

View File

@@ -137,6 +137,11 @@ class World(object):
# but very old ones might not? so we'll just go with the world dir name if they don't
self.name = os.path.basename(os.path.realpath(self.worlddir))
try:
# level.dat also has a RandomSeed attribute
self.seed = data['RandomSeed']
except KeyError:
self.seed = 0 # oh well
# TODO figure out where to handle regionlists
@@ -204,6 +209,7 @@ class World(object):
if section['Y'] == targetSection:
blockArray = section['Blocks']
return blockArray[inChunkX, inChunkZ, y % 16]
return 0
@@ -304,7 +310,7 @@ class RegionSet(object):
* For each chunk section:
* The "Blocks" byte string is transformed into a 16x16x16 numpy array
* The AddBlocks array, if it exists, is bitshifted left 8 bits and
* The Add array, if it exists, is bitshifted left 8 bits and
added into the Blocks array
* The "SkyLight" byte string is transformed into a 16x16x128 numpy
array
@@ -380,11 +386,11 @@ class RegionSet(object):
# Cast up to uint16, blocks can have up to 12 bits of data
blocks = blocks.astype(numpy.uint16)
blocks = blocks.reshape((16,16,16))
if "AddBlocks" in section:
if "Add" in section:
# This section has additional bits to tack on to the blocks
# array. AddBlocks is a packed array with 4 bits per slot, so
# array. Add is a packed array with 4 bits per slot, so
# it needs expanding
additional = numpy.frombuffer(section['AddBlocks'], dtype=numpy.uint8)
additional = numpy.frombuffer(section['Add'], dtype=numpy.uint8)
additional = additional.astype(numpy.uint16).reshape((16,16,8))
additional_expanded = numpy.empty((16,16,16), dtype=numpy.uint16)
additional_expanded[:,:,::2] = (additional & 0x0F) << 8
@@ -392,7 +398,7 @@ class RegionSet(object):
blocks += additional_expanded
del additional
del additional_expanded
del section['AddBlocks'] # Save some memory
del section['Add'] # Save some memory
section['Blocks'] = blocks
# Turn the skylight array into a 16x16x16 matrix. The array comes
@@ -483,6 +489,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')}