Merge branch 'master' of git://github.com/overviewer/Minecraft-Overviewer into JSObserver
This commit is contained in:
5
.mailmap
Normal file
5
.mailmap
Normal 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
|
||||
@@ -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
116
contrib/contributors.py
Executable 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()
|
||||
@@ -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::
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
-------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
0
overviewer_core/aux_files/__init__.py
Normal file
0
overviewer_core/aux_files/__init__.py
Normal 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():
|
||||
|
||||
if os.path.basename(sys.argv[0]) == """genPOI.py""":
|
||||
helptext = """genPOI.py
|
||||
%prog --config=<config file> [--quiet]"""
|
||||
else:
|
||||
helptext = """genPOI
|
||||
%prog --config=<config file>"""
|
||||
%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:
|
||||
@@ -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();
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
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});
|
||||
|
||||
BIN
overviewer_core/data/web_assets/bed.png
Normal file
BIN
overviewer_core/data/web_assets/bed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 631 B |
@@ -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%;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
117
overviewer_core/src/primitives/hide.c
Normal file
117
overviewer_core/src/primitives/hide.c
Normal 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,
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
6
setup.py
6
setup.py
@@ -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')}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user